diff --git a/libs/blueprint/.clang-format b/libs/blueprint/.clang-format new file mode 100644 index 000000000..11c9595a0 --- /dev/null +++ b/libs/blueprint/.clang-format @@ -0,0 +1,94 @@ +--- +BasedOnStyle: WebKit +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakAfterJavaFieldAnnotations: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakStringLiterals: true +ColumnLimit: 120 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: true +ForEachMacros: [ 'BOOST_FOREACH' ] +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"(<)/' + Priority: 1 + - Regex: '^(<(boost)/)' + Priority: 2 + - Regex: '^(<(nil\/algebra)/)' + Priority: 3 + - Regex: '.*' + Priority: 4 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +Language: Cpp +NamespaceIndentation: All +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 4 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never + +... diff --git a/libs/blueprint/.envrc b/libs/blueprint/.envrc new file mode 100644 index 000000000..3550a30f2 --- /dev/null +++ b/libs/blueprint/.envrc @@ -0,0 +1 @@ +use flake diff --git a/libs/blueprint/.github/workflows/run_tests.yml b/libs/blueprint/.github/workflows/run_tests.yml new file mode 100644 index 000000000..35bb000d1 --- /dev/null +++ b/libs/blueprint/.github/workflows/run_tests.yml @@ -0,0 +1,60 @@ +name: Run tests + +on: + # Triggers the workflow on pull request events but only for the master branch + pull_request: + branches: [ master ] + push: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + SUITE_REPO: "NilFoundation/crypto3" + LIB_NAME: "blueprint" + CACHE_NAME: "blueprint-job-cache" + +jobs: + handle-syncwith: + if: github.event_name == 'pull_request' + name: Call Reusable SyncWith Handler + uses: NilFoundation/ci-cd/.github/workflows/reusable-handle-syncwith.yml@v1 + with: + ci-cd-ref: 'v1' + secrets: inherit + + build-and-test: + needs: [ handle-syncwith ] + runs-on: ["self-hosted", "aws_autoscaling"] + strategy: + fail-fast: false + steps: + # https://github.com/actions/checkout/issues/1552 + - name: Clean up after previous checkout + run: chmod +w -R ${GITHUB_WORKSPACE}; rm -rf ${GITHUB_WORKSPACE}/*; + + - name: Checkout Blueprint + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Checkout submodules to specified refs + if: inputs.submodules-refs != '' + uses: NilFoundation/ci-cd/actions/recursive-checkout@v1.2.1 + with: + refs: ${{ inputs.submodules-refs }} + paths: | + ${{ github.workspace }}/** + !${{ github.workspace }}/ + !${{ github.workspace }}/**/.git/** + + # nix is taken from the cloud-init template, no need to install it. + - name: Build and run tests + env: + NIX_CONFIG: | + cores = 8 + max-jobs = 4 + run: | + nix build -L .?submodules=1#checks.x86_64-linux.default diff --git a/libs/blueprint/.github/workflows/set_version.yml b/libs/blueprint/.github/workflows/set_version.yml new file mode 100644 index 000000000..8d811370f --- /dev/null +++ b/libs/blueprint/.github/workflows/set_version.yml @@ -0,0 +1,31 @@ +name: Set version + +on: + #Disabled because the workflow does not actually work with current runner permissions + #push: + # branches: [ master ] + workflow_dispatch: + +jobs: + set_version: + name: Set and tag version + runs-on: [ubuntu-latest] + env: + VERSION_FILE_NAME: VERSION + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set version + id: set_version + run: | + version=$(cat ${{ env.VERSION_FILE_NAME }} | tr -d '\r').$GITHUB_RUN_NUMBER + echo "VERSION=$version" >> $GITHUB_ENV + + - name: Tag new version + run: git tag v${{ env.VERSION }} + + - name: Push tags + uses: ad-m/github-push-action@master + with: + tags: true diff --git a/libs/blueprint/.gitignore b/libs/blueprint/.gitignore new file mode 100644 index 000000000..a51aff968 --- /dev/null +++ b/libs/blueprint/.gitignore @@ -0,0 +1,666 @@ + +# Created by https://www.gitignore.io/api/c,tex,c++,cmake,jetbrains+all +# Edit at https://www.gitignore.io/?templates=c,tex,c++,cmake,jetbrains+all + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized c file +.idea/caches/build_file_checksums.ser + +# JetBrains templates +**___jb_tmp___ + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### TeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind +*.ist + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +### TeX Patch ### +# glossaries +*.glstex + +# End of https://www.gitignore.io/api/c,tex,c++,cmake,jetbrains+all +# +# +# Created by https://www.toptal.com/developers/gitignore/api/vim,macos,intellij,cmake,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=vim,macos,intellij,cmake,c++ + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + +### CMake Patch ### +# External projects +*-prefix/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/vim,macos,intellij,cmake,c++ diff --git a/libs/blueprint/.gitmodules b/libs/blueprint/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/libs/blueprint/CMakeLists.txt b/libs/blueprint/CMakeLists.txt new file mode 100644 index 000000000..d234c0425 --- /dev/null +++ b/libs/blueprint/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.21.4) + +project(crypto3_blueprint VERSION 0.1.0 LANGUAGES C CXX) + +option(CMAKE_ENABLE_TESTS "Enable tests" FALSE) # used by CMTest module +option(BUILD_EXAMPLES "Build examples" FALSE) + +find_package(CM) + +add_library(blueprint INTERFACE) +add_library(crypto3::blueprint ALIAS blueprint) + +include(GNUInstallDirs) +target_include_directories(blueprint INTERFACE + "$" + "$" +) + +target_link_libraries(blueprint INTERFACE + ${Boost_LIBRARIES} +) + +include(CMTest) +cm_add_test_subdirectory(test) + +if(BUILD_EXAMPLES) + add_subdirectory(example) +endif() diff --git a/libs/blueprint/README.md b/libs/blueprint/README.md new file mode 100644 index 000000000..c3b0a2546 --- /dev/null +++ b/libs/blueprint/README.md @@ -0,0 +1,41 @@ +# Circuit Definition Library for =nil; Foundation's Cryptography Suite + +[![Run tests](https://github.com/NilFoundation/zkllvm-blueprint/actions/workflows/run_tests.yml/badge.svg)](https://github.com/NilFoundation/zkllvm-blueprint/actions/workflows/run_tests.yml) + +## Dependencies + +- [Boost](https://boost.org) (>= 1.76) +- [cmake](https://cmake.org/) (>=3.21.4) +- Following dependencies must be built and installed from sources: + - [CMake Modules](https://github.com/BoostCMake/cmake_modules.git) + - [crypto3](https://github.com/nilfoundation/crypto3.git) + +## Building and installation + +```bash +cmake -B build -DCMAKE_INSTALL_PREFIX=/path/to/install +make -C build install +``` + +> Note: if you got an error on `find_package` during cmake configuration, make sure that you provided paths to the installed dependencies (for example, via `CMAKE_PREFIX_PATH` environment variable) + +## Nix support +This repository provides Nix flake, so once you have installed Nix with flake support, you can use single command to fetch all the dependencies and build: + +```bash +nix build ?submodules=1# +``` + +To activate Nix development environment: + +```bash +nix develop +``` + +To run all tests: + +```bash +nix flake check -L ?submodules=1# +``` + +To build/develop/test with local crypto3 version, add an argument `--override-input nil-crypto3 /path/to/local/crypto3` to any of the above commands. diff --git a/libs/blueprint/cmake/Config.cmake.in b/libs/blueprint/cmake/Config.cmake.in new file mode 100644 index 000000000..af9b790a4 --- /dev/null +++ b/libs/blueprint/cmake/Config.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(Boost COMPONENTS REQUIRED + container json filesystem log log_setup program_options random thread system unit_test_framework) +find_dependency(crypto3 REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/crypto3_blueprint-targets.cmake") diff --git a/libs/blueprint/docs/concepts.md b/libs/blueprint/docs/concepts.md new file mode 100644 index 000000000..3534d4221 --- /dev/null +++ b/libs/blueprint/docs/concepts.md @@ -0,0 +1,32 @@ +# Concepts # {#component_concepts} + +A ```circuit``` is defined by ```Blueprint``` and ```Blueprint assignment table``` (contains ```Blueprint public assignment table``` and ```Blueprint private assignment table```) instances. +It consist of one or multiple components putted on these two. +While ```Blueprint``` holds information about the circuit itself, its gates, constraints and other fixed expressions, ```Blueprint assignment table``` contains public and private assignments needed by zk-SNARK system. + +## Blueprint + +## PLONK component concept ## {##plonk_component_concepts} + +### PLONK Component interface ### + +A ```Component``` ```X``` is a state-less type with following static functions to operate with it: + +* ```X::allocate_rows``` - allocates required amount of rows in the given ```Arithmetization table```. The amount of required rows amount is constexpr for the particular component; +* ```X::generate_circuit``` - generates gate expressions, copy constraints and lookup constraints and puts them on the given ```Blueprint```; +* ```X::generate_assignments``` - evaluates assignments values and puts them on the given ```Blueprint assignment table```; + +Note that ```generate_circuit``` can modify of the ```Blueprint public assignment table``` setting ```Constant``` or ```Selector``` columns, but they don't use or set data of the ```Blueprint private assignment table```. The only function managing ```Blueprint private assignment table``` is ```generate_assignments``` - which, by the way, also can modify the ```Blueprint public assignment table```. In short, it looks like that: + +|Function |Required Input |Can modify | +|-----------------------------|------------------------|-----------------------| +|```X::allocate_rows``` |```Blueprint``` |```Blueprint```| +|```X::generate_circuit``` |```Blueprint```, ```Component params```, ```Allocated data (auxiliary data for the component re-use)```, ```component start row``` |```Blueprint```, ```Allocated data```| +|```X::generate_assignments``` |```Blueprint assignment table```, ```Component params```, ```component start row``` |```Blueprint assignment table```| + +The process of adding a component is following: + +1. (Optional) Get ```component``` start row by calling ```allocate_rows```. If the ```component``` is used as part of other ```component``` logic, it's not necessary to call the function, because needed rows are allocated by the master ```component```. +2. (Optional) Allocate public input on the ```Blueprint assignment table``` via ```Blueprint assignment table::allocate_public_input```. +3. Set all the gates and constraints on the ```Blueprint``` by calling ```generate_circuit```. ```Allocated data``` is being modified in process of the function working. +4. Set all the assignments on the ```Blueprint assignment table``` table by calling ```generate_assignments```. \ No newline at end of file diff --git a/libs/blueprint/docs/extension.md b/libs/blueprint/docs/extension.md new file mode 100644 index 000000000..ba2bde58b --- /dev/null +++ b/libs/blueprint/docs/extension.md @@ -0,0 +1,3 @@ +# Extension {#blueprint_extension_manual} + +@tableofcontents diff --git a/libs/blueprint/docs/index.md b/libs/blueprint/docs/index.md new file mode 100644 index 000000000..ee18eb974 --- /dev/null +++ b/libs/blueprint/docs/index.md @@ -0,0 +1,3 @@ +# Blueprint (Circuit Definition Library) {#blueprint_index} + +@subpage blueprint_manual diff --git a/libs/blueprint/docs/manual.md b/libs/blueprint/docs/manual.md new file mode 100644 index 000000000..4ac479097 --- /dev/null +++ b/libs/blueprint/docs/manual.md @@ -0,0 +1,4 @@ +# Manual {#blueprint_manual} + +@subpage blueprint_usage_manual +@subpage blueprint_extension_manual diff --git a/libs/blueprint/docs/usage.md b/libs/blueprint/docs/usage.md new file mode 100644 index 000000000..774605c83 --- /dev/null +++ b/libs/blueprint/docs/usage.md @@ -0,0 +1,444 @@ +# Usage {#blueprint_usage_manual} + +This module is supposed to be used together with =nil;Crypto3 +[zk](https://github.com/NilFoundation/crypto3-zk). The blueprint module is used to generate the input data in form of a constraint system, while [crypto3-zk](https://github.com/NilFoundation/crypto3-zk) is used to process them as input for what to prove. + +In this document, we introduce the very basic concepts of blueprint. For the example of usage please follow the [usage markdown](https://github.com/NilFoundation/crypto3-blueprint/blob/master/docs/usage.md) or look through the [simple example](https://github.com/NilFoundation/crypto3-blueprint/blob/master/example/simple_example.hpp). + +## Preliminaries + +If you are a developer who is completely new to zk-SNARKS we would recommend you to look through this [great resource](https://zkp.science) with the list of the most meaningful zk-related papers and posts. You can find there both thorough pure-technical papers and high-level overview of zk technologies. + +## Quick intro to R1CS + +A *Rank One Constraint System* (R1CS) is a way to express a computation that makes it amenable to zero knowledge proofs. Basically any computation can be reduced (or flattened) to an R1CS. A single rank one constraint on a vector w is defined as + +``` + * = +``` + +Where `A`, `B`, `C` are vectors of the same length as `w`, and `<>` denotes inner product of vectors. A R1CS is then a system of these kinds of equations: + +``` + * = + * = +... + * = +``` + +The vector `w` is called a *witness* and zk-SNARK proofs can always be reduced to proving that *the prover knows a witness w such that the R1CS is satisfied*. + +## Basics: + +### 1. The Blueprint + +In the =nil; Crypto3 Blueprint tool, the blueprint is where our "circuits" (i.e. PLONK, R1CS and components) will be +collected. + +The C++ file defining the blueprint is [here](https://github.com/NilFoundation/crypto3-blueprint/blob/master/include/nil/crypto3/zk/snark/blueprint.hpp). We will first show how to add R1CS to the blueprint. + +Let's assume, that we want to prove knowing of a value x that satisfies the equation + +``` +x^3 + x + 5 == 35. +``` + +We can make this a little more general, and say that given a publicly known output value `out`, we want to prove that we know `x` such that + +``` +x^3 + x + 5 == out. +``` + +Recall that we can introduce some new variables `sym_1, y, sym_2` and flatten the above equation into the following quadratic equations: + +``` +x * x = sym_1 +sym_1 * x = y +y + x = sym_2 +sym_2 + 5 = out +``` + +We can verify that the above system can be written as an R1CS with + +``` +w = [one, x, out, sym_1, y, sym_2] +``` + +and the vectors `A_1, ..., A_4, B_1, ..., B4, C_1, ..., C_4` are given by + +``` +A_1 = [0, 1, 0, 0, 0, 0] +A_2 = [0, 0, 0, 1, 0, 0] +A_3 = [0, 1, 0, 0, 1, 0] +A_4 = [5, 0, 0, 0, 0, 1] +B_1 = [0, 1, 0, 0, 0, 0] +B_2 = [0, 1, 0, 0, 0, 0] +B_3 = [1, 0, 0, 0, 0, 0] +B_4 = [1, 0, 0, 0, 0, 0] +C_1 = [0, 0, 0, 1, 0, 0] +C_2 = [0, 0, 0, 0, 1, 0] +C_3 = [0, 0, 0, 0, 0, 1] +C_4 = [0, 0, 1, 0, 0, 0] +``` + +The original degree 3 polynomial equation has a solution `x=3` and we can verify that the R1CS has a corresponding solution + +``` +w = [1, 3, 35, 9, 27, 30]. +``` + +Now let’s see how we can enter this R1CS into =nil;Crypto3 Blueprint, produce proofs and verify them. We will use the `blueprint_variable` type to declare our variables. See the file `test.cpp` for the full code. + +First lets define the finite field where all our values live, and initialize the curve parameters: + +``` +typedef libff::Fr field_type; +default_r1cs_ppzksnark_pp::init_public_params(); +``` + +Next we define the blueprint and the variables we need. Note that the variable `one` is automatically defined in the blueprint. + +``` +blueprint bp; + +blueprint_variable out; +blueprint_variable x; +blueprint_variable sym_1; +blueprint_variable y; +blueprint_variable sym_2; +``` + +Next we need to "allocate" the variables on the blueprint. This will associate the variables to a blueprint and will allow us to use the variables to define R1CS constraints. + +``` +out.allocate(bp); +x.allocate(bp); +sym_1.allocate(bp); +y.allocate(bp); +sym_2.allocate(bp); +``` + +Note that we are allocating the `out` variable first. This is because =nil;Crypto3 Blueprint divides the allocated variables in a blueprint into "primary" (i.e. public) and "auxiliary" (i.e. private) variables. To specify which variables are public and which ones are private we use the blueprint function `set_input_sizes(n)` to specify that the first `n` variables are public, and the rest are private. In our case we have one public variable `out`, so we use + +``` +bp.set_input_sizes(1); +``` + +to specify that the variable `out` should be public, and the rest private. + +Next let's add the above R1CS constraints to the blueprint. This is straightforward once we have the variables allocated: + +``` +// x*x = sym_1 +bp.add_r1cs_constraint(r1cs_constraint(x, x, sym_1)); + +// sym_1 * x = y +bp.add_r1cs_constraint(r1cs_constraint(sym_1, x, y)); + +// y + x = sym_2 +bp.add_r1cs_constraint(r1cs_constraint(y + x, 1, sym_2)); + +// sym_2 + 5 = out +bp.add_r1cs_constraint(r1cs_constraint(sym_2 + 5, 1, out)); +``` + +Now that we have our circuit in the form of R1CS constraints on the blueprint we can run the Generator and generate proving keys and verification keys for our circuit: + +``` +const r1cs_constraint_system constraint_system = bp.get_constraint_system(); + +typename snark::r1cs_gg_ppzksnark>::keypair_type keypair = generate>>(constraint_system); +``` + +Note that the above is the so-called "trusted setup". We can access the proving key through `keypair.pk` and the verification key through `keypair.vk`. + +Next we want to generate a proof. For this we need to set the values of the public variables in the blueprint, and also set witness values for the private variables: + +``` +bp.val(out) = 35; + +bp.val(x) = 3; +bp.val(sym_1) = 9; +bp.val(y) = 27; +bp.val(sym_2) = 30; +``` + +Now that the values are set in the blueprint we can access the public values through `bp.primary_input()` and the private values through `bp.auxiliary_input()`. Let's use the proving key, the public inputs and the private inputs to create a proof that we know the witness values: + +``` +typename snark::r1cs_gg_ppzksnark>::proof_type proof = prove>>(keypair.pk, bp.primary_input(), bp.auxiliary_input()); +``` + +Now that we have a proof we can also verify it, using the previously created `proof`, the verifying key `keypair.vk` and the public input `bp.primary_input()`: + +``` +bool verified = verify>>(keypair.vk, bp.primary_input(), proof); +``` + +At this stage the boolean `verified` should have the value `true`, given that we put in the correct values for the witness variables. + +### 2. Components + +The =nil;Crypto3 Blueprint library uses *components* to package up R1CS into more manageable pieces and to create cleaner interfaces for developers. They do this by being a wrapper around a blueprint and handling generating R1CS constraints and also generating witness values. + +We're going to show how to create a component for the example R1CS above in order to make it a bit more manageable. + +First we create a new file `src/component.hpp` which contains the component file. In our case we want the developer using the component to be able to set the public variable `out`, as well as the private witness variable `x`, but the component itself would take care of the intermediate variables `y`, `sym_1` and `sym_2`. + +Thus we create a class `test_component`, derived from the base `component` class which has the variables `y`, `sym_1` and `sym_2` as private members (in the C++ sense). The variables `x` and `out` will be public class member variables. + +In the following sections we go over the functions of this component and how to use it. + +## Constructor + +As any component, the constructor takes as input a blueprint `bp`. We also have `blueprint_variable` inputs `x` and `out`. We assume that the user of the component has already allocated `x` and `out` to the blueprint. + +The constructor then allocates the intermediate variables to the blueprint: + +``` +sym_1.allocate(this->bp); +y.allocate(this->bp); +sym_2.allocate(this->bp); +``` + +### Function `generate_gates()` + +This function adds the R1CS constraints corresponding to the circuits. These are the same constraints as we added manually earlier, just bundled up inside this function. + +### Function `generate_assignments()` + +This function assumes that we've already set the public value `out`, and the witness value `x`. It then computes the inferred witness values for the intermediate variables `sym_1`, `y`, `sym_2`. Thus the user of the component never needs to worry about the intermediate variables. + +## Using the component + +In the file `src/test-component.cpp` we can see how the component it used. This file is very similar to the file in the previous section. We start as before by generating curve parameters. After this we initialize the blueprint, and allocate the variables `out`, `x` to the blueprint: + +``` +blueprint bp; +blueprint_variable out; +blueprint_variable x; + +out.allocate(bp); +x.allocate(bp); +``` + +After this we specify which variables are public and which are private (in the zk-SNARK sense). This would be `out` as the only public variable and the rest as private variables. We also create a new `test_component`: + +``` +bp.set_input_sizes(1); +test_component g(bp, out, x); +``` + +Next generate the R1CS constraints by simply calling the corresponding function: + +``` +g.generate_gates(); +``` + +Now we add the witness values. We add the value 35 for the public variable `out` and the value 3 for the witness variable `x`. The rest of the values will be computed inside the component: + +``` +bp.val(out) = 35; +bp.val(x) = 3; +g.generate_assignments(); +``` + +That's it! Now we can run the Generator to generate proving and verification keys, create the proof and verify it as we did before. + +## Examples + +Below are two examples, how the constraint system can be generated by =nil; Crypto3 +[blueprint](https://github.com/NilFoundation/crypto3-blueprint) module. + +### Inner-product component + +Let's show how to create a simple circuit for the calculation of the public inner +product of two secret vectors. +In [crypto3-blueprint](https://github.com/NilFoundation/crypto3-blueprint) library, the +blueprint is where arithmetic circuits are collected. The statement (or public values) +is called primary_input and the witness (or secret values) is called auxiliary_input. +Let `bp` be a blueprint and `A` and `B` are vectors which inner product `res` has +to be calculated. + +```cpp +blueprint bp; +blueprint_variable_vector A; +blueprint_variable_vector B; +variable res; +``` + +Then we associate the variables to a blueprint by using the function `allocate()`. +The variable `n` shows the size of the vectors `A` and `B`. Note, that each use of +`allocate()` increases the size of `auxiliary_input`. + +```cpp +res.allocate(bp); +A.allocate(bp, n); +B.allocate(bp, n); +bp.set_input_sizes(1); +``` + +Note, that the first allocated variable on the blueprint is a constant 1. So, the +variables on the blueprint would be `1` , `res`, `A[0]`, ..., `A[n-1]`, `B[0]`, ..., `B[n-1]`. + +To specify which variables are public and which ones are private we use the function +`set_input_sizes(1)`, so only `res`value is a primary input. Thus, usually, the +primary input is allocated before the auxiliary input in the program. + + + +*Component* is a class for constructing a particular constraint system. The component's +constructor allocates intermediate variables, so the developer is responsible for +allocation only primary and auxiliary variables. Any Component has to implement +at least two methods: `generate_gates()` and `generate_assignments()`. + +Now we initialize the simple component `inner_product`. The function `generate_gates()` +adds R1CS constraints to the blueprint corresponding to the circuit. + + +```cpp +inner_product compute_inner_product(bp, A, B, res, "compute_inner_product"); +compute_inner_product.generate_gates(); +``` + +Next, we set the random values to vectors. + +```cpp +for (std::size_t i = 0; i < n; ++i) { + bp.val(A[i]) = algebra::random_element(); + bp.val(B[i]) = algebra::random_element(); +} +``` + +The function `generate_assignments()` computes intermediate witness value for the +public values and the inner product for the `res`. + +```cpp +compute_inner_product.generate_assignments(); +``` + +### SHA2-256 component + +Now we want to consider a more complicated construction of a circuit. Assume that +the prover wants to prove that they know a preimage for a hash digest chosen by +the verifier, without revealing what the preimage is. Let hash function be a 2-to-1 +SHA256 compression function for our example. + +We will show the process for some pairing-friendly curve `curve_type` and its scalar +field `field_type`. + +Firstly, we need to create a `blueprint` and allocate the variables `left`, `right` +and `output` at the blueprint. The allocation on the blueprint proceeds at the constructor +of digest_variable. Then we initialize the component ` sha256_two_to_one_hash_component ` +and add constraints at the `generate_gates()` function. + +```cpp +blueprint bp; + +digest_variable left(bp, hashes::sha2<256>::digest_bits); +digest_variable right(bp, hashes::sha2<256>::digest_bits); +digest_variable output(bp, hashes::sha2<256>::digest_bits); + +sha256_two_to_one_hash_component f(bp, left, right, output); + +f.generate_gates(); +``` + +After the generation of r1cs constraints, we need to transform data blocks into +bit vectors. +We use a custom `pack`, which allows us to convert data from an arbitrary data +type to bit vectors. The following code can be used for this purpose: + +```cpp +std::array array_a_intermediate; +std::array array_b_intermediate; +std::array array_c_intermediate; + +std::array array_a = {0x426bc2d8, 0x4dc86782, 0x81e8957a, 0x409ec148, + 0xe6cffbe8, 0xafe6ba4f, 0x9c6f1978, 0xdd7af7e9}; +std::array array_b = {0x038cce42, 0xabd366b8, 0x3ede7e00, 0x9130de53, + 0x72cdf73d, 0xee825114, 0x8cb48d1b, 0x9af68ad0}; +std::array array_c = {0xeffd0b7f, 0x1ccba116, 0x2ee816f7, 0x31c62b48, + 0x59305141, 0x990e5c0a, 0xce40d33d, 0x0b1167d1}; + +std::vector left_bv(hashes::sha2<256>::digest_bits), + right_bv(hashes::sha2<256>::digest_bits), + hash_bv(hashes::sha2<256>::digest_bits); + +detail::pack( + array_a.begin(), + array_a.end(), + array_a_intermediate.begin()); + +detail::pack( + array_b.begin(), + array_b.end(), + array_b_intermediate.begin()); + +detail::pack( + array_c.begin(), + array_c.end(), + array_c_intermediate.begin()); + +detail::pack_to( + array_a_intermediate, + left_bv.begin()); + +detail::pack_to( + array_b_intermediate, + right_bv.begin()); + +detail::pack_to( + array_c_intermediate, + hash_bv.begin()); +``` + +After getting bit vectors, we can generate r1cs witnesses. + +```cpp +left.generate_assignments(left_bv); + +right.generate_assignments(right_bv); + +f.generate_assignments(); +output.generate_assignments(hash_bv); +``` + +Now we have the `blueprint` with SHA2-256 component on it and can prove our knowledge +of the source message using Groth-16 (`r1cs_gg_ppzksnark`). + +### Keys & Proof Generation + +Using the example above we can finally create and verify `proof`. We assume here, +that `prover` and `generator` from [crypto3-zk](https://github.com/NilFoundation/crypto3-zk) +are used. + +* The generator `grth16::generator` creates proving keys and verification keys for +our constraints system. +* The proving key `keypair.first`, public input `bp.primary_input`, and private input +`bp.auxiliary_input` are used for the constructing of the proof (`grth16::prover`). + +```cpp +using grth16 = r1cs_gg_ppzksnark; +typename grth16::keypair_type keypair = grth16::generator(bp.get_constraint_system()); + +typename grth16::proof_type proof = + grth16::prover(keypair.first, bp.primary_input, bp.auxiliary_input); + +``` + +### Proof Verification + +To verify proof you only need to put all the data in the byte vector and give it as parameter +to the TON vm instruction `__builtin_tvm_vergrth16` . + +zk-SNARK verifier argument has to contain of 3 parts packed together: +* `verification_key_type vk` +* `primary_input_type primary_input` +* `proof_type proof` + +Type requirements for those are described in the [Groth16 zk-SNARK policy](https://github.com/NilFoundation/crypto3-zk/blob/master/include/nil/crypto3/zk/snark/schemes/ppzksnark/r1cs_gg_ppzksnark.hpp) + +Byte vector assumes to be byte representation of all the underlying data types, +recursively unwrapped to Fp field element and integral `std::size_t` values. +All the values should be putted in the same order the recursion calculated. diff --git a/libs/blueprint/example/CMakeLists.txt b/libs/blueprint/example/CMakeLists.txt new file mode 100644 index 000000000..050135348 --- /dev/null +++ b/libs/blueprint/example/CMakeLists.txt @@ -0,0 +1,36 @@ +#---------------------------------------------------------------------------# +# Copyright (c) 2018-2021 Mikhail Komarov +# Copyright (c) 2020-2021 Nikita Kaskov +# +# Distributed under the Boost Software License, Version 1.0 +# See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt +#---------------------------------------------------------------------------# + +macro(define_blueprint_example name) + add_executable(blueprint_${name}_example ${name}.cpp) + target_link_libraries(blueprint_${name}_example PRIVATE + ${CMAKE_WORKSPACE_NAME}_blueprint + + ${CMAKE_WORKSPACE_NAME}::algebra + ${CMAKE_WORKSPACE_NAME}::math + ${CMAKE_WORKSPACE_NAME}::block + ${CMAKE_WORKSPACE_NAME}::hash + ${CMAKE_WORKSPACE_NAME}::multiprecision + ${CMAKE_WORKSPACE_NAME}::zk + marshalling::crypto3_zk + + ${Boost_LIBRARIES}) + set_target_properties(blueprint_${name}_example PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE) +endmacro() + + + +set(EXAMPLES_NAMES + "curves" + "test_component" + "plonk/addition_component") + +foreach(EXAMPLE_NAME ${EXAMPLES_NAMES}) + define_blueprint_example(${EXAMPLE_NAME}) +endforeach() \ No newline at end of file diff --git a/libs/blueprint/example/curves.cpp b/libs/blueprint/example/curves.cpp new file mode 100644 index 000000000..0e6d2efa0 --- /dev/null +++ b/libs/blueprint/example/curves.cpp @@ -0,0 +1,143 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void print_field_element(typename fields::detail::element_fp e) { + std::cout << e.data << std::endl; +} + +template +void print_fp_curve_group_element(FpCurveGroupElement e) { + std::cout << e.X.data << " " << e.Y.data << " " << e.Z.data << std::endl; +} + +template +void verify_component(blueprint bp){ + using field_type = typename CurveType::scalar_field_type; + using curve_type = CurveType; + + const snark::r1cs_constraint_system constraint_system = bp.get_constraint_system(); + + const typename snark::r1cs_gg_ppzksnark::keypair_type keypair = snark::generate>(constraint_system); + + const typename snark::r1cs_gg_ppzksnark::proof_type proof = snark::prove>(keypair.first, bp.primary_input(), bp.auxiliary_input()); + + bool verified = snark::verify>(keypair.second, bp.primary_input(), proof); + + std::cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << std::endl; + std::cout << "Verification status: " << verified << std::endl; + + assert(verified); +} + +template +blueprint addition_example( + typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type p1, + typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type p2){ + + using main_curve_type = CurveType; + using chained_curve_type = typename CurveType::pairing::chained_curve_type; + using scalar_field_type = typename CurveType::scalar_field_type; + + // Create blueprint + + blueprint bp; + + components::blueprint_variable a; + components::blueprint_variable d; + + a.allocate(bp); + d.allocate(bp); + + bp.val(a) = typename scalar_field_type::value_type(chained_curve_type::a); + bp.val(d) = typename scalar_field_type::value_type(chained_curve_type::d); + + std::cout << "a: " << chained_curve_type::a << std::endl; + std::cout << "modulus: " << scalar_field_type::modulus << std::endl; + std::cout << "d: " << chained_curve_type::d << std::endl; + + components::element_g1 P1(bp, p1); + components::element_g1 P2(bp, p2); + components::element_g1 P1pP2(bp); + + bp.set_input_sizes(2); + + std::cout << "element_g1 size: " << P1.num_variables() << std::endl; + + std::cout << "blueprint size: " << bp.num_variables() << std::endl; + + print_fp_curve_group_element(p1); + + std::cout << "P1:" << std::endl; + print_field_element(bp.lc_val(P1.X)); + print_field_element(bp.lc_val(P1.Y)); + + std::cout << "P2:" << std::endl; + print_field_element(bp.lc_val(P2.X)); + print_field_element(bp.lc_val(P2.Y)); + + components::element_g1_add el_add(bp, a, d, P1, P2, P1pP2); + components::element_g1_is_well_formed + el_is_well_formed(bp, a, d, P1); + + el_add.generate_gates(); + el_is_well_formed.generate_gates(); + + el_add.generate_assignments(); + el_is_well_formed.generate_assignments(); + + std::cout << "blueprint size: " << bp.num_variables() << std::endl; + + return bp; +} + +int main(){ + + using main_curve_type = curves::bls12<381>; + using chained_curve_type = typename main_curve_type::pairing::chained_curve_type; + using scalar_field_type = typename main_curve_type::scalar_field_type; + + typename chained_curve_type::template g1_type<>::value_type p1 = + random_element(); + typename chained_curve_type::template g1_type<>::value_type p2 = + random_element(); + + blueprint bp = addition_example(p1, p2); + + assert(bp.is_satisfied()); + + verify_component(bp); + + return 0; +} diff --git a/libs/blueprint/example/plonk/addition_component.cpp b/libs/blueprint/example/plonk/addition_component.cpp new file mode 100644 index 000000000..3fb1becde --- /dev/null +++ b/libs/blueprint/example/plonk/addition_component.cpp @@ -0,0 +1,82 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_addition_example_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "addition_component.hpp" +#include "../test/test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_addition_example_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_addition_example) { + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::addition; + + // test_component set public input to the first rows of the public_input columns + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type x = 1; + typename BlueprintFieldType::value_type y = 3; + typename BlueprintFieldType::value_type sum = 4; + + std::vector public_input = {x, y, sum}; + + test_component(params, public_input); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/example/plonk/addition_component.hpp b/libs/blueprint/example/plonk/addition_component.hpp new file mode 100644 index 000000000..b9358b90a --- /dev/null +++ b/libs/blueprint/example/plonk/addition_component.hpp @@ -0,0 +1,205 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_EXAMPLE_PLONK_ADDITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_EXAMPLE_PLONK_ADDITION_HPP + +#include + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class addition; + + /// Additiona component takes (x, y, sum) as an input and proves that x + y = sum + /// We always prove some statement about the data in the table + /// Table constrains elements of the finite field + /// Addition Component' table Layout: + /// W0 | W1 | W2 + /// x | y | sum + /// To prove "something" about the data in the table, we need to define this "something" + /// We do it via "constraints" - expressions over cells of the table + /// Constraints: + /// x + y = sum <=> W0 + W1 = W2 + template + class addition< + snark::plonk_constraint_system, + W0, W1, W2>{ + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + + public: + // Addition Component takes only one row in the table + // More complex components may contain more rows + constexpr static const std::size_t rows_amount = 1; + + // params_type defines input data for the component + // it constains either variables allocated on the table (var) or some auxiliary data + // Addition Component input contains tree variables: + struct params_type { + var x; + var y; + var sum; + }; + + // Addition Component doesn't calculate anything, so result_type contains nothing + struct result_type { + result_type(const params_type ¶ms, + std::size_t component_start_row) { + + } + }; + + // allocated_data_type transfers component-related data through the bigger circuits + // we don't interested in it for this particular example + struct allocated_data_type { + allocated_data_type() { + previously_allocated = false; + } + + // TODO access modifiers + bool previously_allocated; + std::size_t add_selector; + }; + + // Allocate rows in the table required for Addition Component + static std::size_t allocate_rows (blueprint &bp, + std::size_t components_amount = 1){ + return bp.allocate_rows(rows_amount * + components_amount); + } + + // generate_circuit represents basic interface to put constraints on the table + static result_type generate_circuit( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + std::size_t component_start_row) { + + // generate_gates defines algebraic expressions over cells + // for instance, x + y = z or x * y * z - 25 = 0 + generate_gates(bp, assignment, params, allocated_data, component_start_row); + // generate_copy_constraints enforces equality between cells of the table + generate_copy_constraints(bp, assignment, params, component_start_row); + + return result_type(params, component_start_row); + } + + static result_type generate_assignments( + blueprint_assignment_table + &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + + std::size_t row = component_start_row; + // variables keeps only data about cells in the table, not the value itself + // assignment.var_value resturns value of the cell in the table + // here we assign input to the corresponding cells in the table (see table description above) + assignment.witness(0)[row] = assignment.var_value(params.x); + assignment.witness(1)[row] = assignment.var_value(params.y); + assignment.witness(2)[row] = assignment.var_value(params.sum); + + return result_type(params, component_start_row); + } + + private: + static void generate_gates( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + const std::size_t start_row_index) { + + // selectors define on which rows which constraints are avaiable + std::size_t selector_index; + if (!allocated_data.previously_allocated) { + selector_index = assignment.add_selector(start_row_index); + allocated_data.add_selector = selector_index; + } else { + selector_index = allocated_data.add_selector; + assignment.enable_selector(selector_index, start_row_index); + } + + // var(i, 0) defines cell at the column i with rotation 0 (we'll elaborate rotation in the next examples) + auto constraint_1 = bp.add_constraint( + var(0, 0) + (var(1, 0) - var(2, 0))); + + if (!allocated_data.previously_allocated) { + // gate composes multiple constraints together + bp.add_gate(selector_index, + { constraint_1 + }); + allocated_data.previously_allocated = true; + } + + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row){ + // recall that params contains variables which refers to the cells in the table + // these cells can be allocated outside ot the component + // however generate_gates enforces contraint only on the rows of the component + // thus, we need to enforce equality between the cells from the input and cells of the component + std::size_t row = component_start_row; + var component_x = var(W0, static_cast(row), false); + var component_y = var(W1, static_cast(row), false); + var component_sum = var(W2, static_cast(row), false); + bp.add_copy_constraint({component_x, params.x}); + bp.add_copy_constraint({component_y, params.y}); + bp.add_copy_constraint({component_sum, params.sum}); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_EXAMPLE_PLONK_ADDITION_HPP \ No newline at end of file diff --git a/libs/blueprint/example/plonk/component_template.hpp b/libs/blueprint/example/plonk/component_template.hpp new file mode 100644 index 000000000..72a81b680 --- /dev/null +++ b/libs/blueprint/example/plonk/component_template.hpp @@ -0,0 +1,156 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_COMPONENT_TEMPLATE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_COMPONENT_TEMPLATE_HPP + +#include + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class component_template; + + template + class component_template< + snark::plonk_constraint_system, + CurveType, + W0, W1, W2, W3, W4, + W5, W6, W7, W8, W9, + W10>{ + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + + public: + constexpr static const std::size_t rows_amount = 1; + + struct params_type { + }; + + struct result_type { + result_type(params_type ¶ms, + std::size_t component_start_row) { + + } + }; + + struct allocated_data_type { + allocated_data_type() { + previously_allocated = false; + } + + // TODO access modifiers + bool previously_allocated; + }; + + static std::size_t allocate_rows (blueprint &bp, + std::size_t components_amount = 1){ + return bp.allocate_rows(rows_amount * + components_amount); + } + + static result_type generate_circuit( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + std::size_t component_start_row) { + + generate_gates(bp, assignment, params, allocated_data, component_start_row); + generate_copy_constraints(bp, assignment, params, component_start_row); + + return result_type(params, component_start_row); + } + + static result_type generate_assignments( + blueprint_assignment_table + &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + + return result_type(params, component_start_row); + } + + private: + static void generate_gates( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + const std::size_t start_row_index) { + + if (!allocated_data.previously_allocated) { + } else { + } + + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row){ + 5 + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_COMPONENT_TEMPLATE_HPP \ No newline at end of file diff --git a/libs/blueprint/example/simple_example.hpp b/libs/blueprint/example/simple_example.hpp new file mode 100644 index 000000000..d68be2524 --- /dev/null +++ b/libs/blueprint/example/simple_example.hpp @@ -0,0 +1,80 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_SIMPLE_EXAMPLE_HPP +#define CRYPTO3_SIMPLE_EXAMPLE_HPP + +#include "relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp" + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + r1cs_example gen_r1cs_example_from_blueprint(const std::size_t num_constraints, + const std::size_t num_inputs); + + /* NOTE: all examples here actually generate one constraint less to account for soundness constraint in + * QAP */ + + template + r1cs_example gen_r1cs_example_from_blueprint(const std::size_t num_constraints) { + const std::size_t new_num_constraints = num_constraints - 1; + + /* construct dummy example: inner products of two vectors */ + blueprint bp; + blueprint_variable_vector A; + blueprint_variable_vector B; + blueprint_variable res; + + // the variables on the blueprint are (ONE (constant 1 term), res, A[0], ..., A[num_constraints-1], + // B[0], ..., B[num_constraints-1]) + res.allocate(bp); + A.allocate(bp, new_num_constraints); + B.allocate(bp, new_num_constraints); + + inner_product compute_inner_product(bp, A, B, res, "compute_inner_product"); + compute_inner_product.generate_gates(); + + /* fill in random example */ + for (std::size_t i = 0; i < new_num_constraints; ++i) { + bp.val(A[i]) = algebra::random_element(); + bp.val(B[i]) = algebra::random_element(); + } + + compute_inner_product.generate_assignments(); + return r1cs_example( + bp.get_constraint_system(), bp.primary_input(), bp.auxiliary_input()); + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_SIMPLE_EXAMPLE_HPP diff --git a/libs/blueprint/example/test_component.cpp b/libs/blueprint/example/test_component.cpp new file mode 100644 index 000000000..d1497ab89 --- /dev/null +++ b/libs/blueprint/example/test_component.cpp @@ -0,0 +1,76 @@ +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test_component.hpp" + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +int main(){ + + using curve_type = curves::bls12<381>; + using field_type = typename curve_type::scalar_field_type; + + // Create blueprint + + blueprint bp; + blueprint::value_type out; + blueprint::value_type x; + + // Allocate variables + + out.allocate(bp); + x.allocate(bp); + + // This sets up the blueprint variables + // so that the first one (out) represents the public + // input and the rest is private input + + bp.set_input_sizes(1); + + // Initialize component + + test_component g(bp, out, x); + g.generate_gates(); + + // Add witness values + + bp.val(out) = 35; + bp.val(x) = 3; + + g.generate_assignments(); + + assert(bp.is_satisfied()); + + const snark::r1cs_constraint_system constraint_system = bp.get_constraint_system(); + + const typename snark::r1cs_gg_ppzksnark::keypair_type keypair = snark::generate>(constraint_system); + + const typename snark::r1cs_gg_ppzksnark::proof_type proof = snark::prove>(keypair.first, bp.primary_input(), bp.auxiliary_input()); + + bool verified = snark::verify>(keypair.second, bp.primary_input(), proof); + + std::cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << std::endl; + std::cout << "Verification status: " << verified << std::endl; + + const typename snark::r1cs_gg_ppzksnark::verification_key_type vk = keypair.second; + + return 0; +} diff --git a/libs/blueprint/example/test_component.hpp b/libs/blueprint/example/test_component.hpp new file mode 100644 index 000000000..196dd69e2 --- /dev/null +++ b/libs/blueprint/example/test_component.hpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_EXAMPLE_TEST_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_EXAMPLE_TEST_COMPONENT_HPP + +#include + +#include + +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +class test_component : public components::component { + using field_type = FieldType; + components::blueprint_variable sym_1; + components::blueprint_variable y; + components::blueprint_variable sym_2; +public: + const components::blueprint_variable out; + const components::blueprint_variable x; + + test_component(blueprint &bp, + const components::blueprint_variable &out, + const components::blueprint_variable &x) : + components::component(bp), out(out), x(x) { + + // Allocate variables to blueprint + + sym_1.allocate(this->bp); + y.allocate(this->bp); + sym_2.allocate(this->bp); + } + + void generate_gates() { + // x*x = sym_1 + this->bp.add_r1cs_constraint(snark::r1cs_constraint(x, x, sym_1)); + + // sym_1 * x = y + this->bp.add_r1cs_constraint(snark::r1cs_constraint(sym_1, x, y)); + + // y + x = sym_2 + this->bp.add_r1cs_constraint(snark::r1cs_constraint(y + x, 1, sym_2)); + + // sym_2 + 5 = ~out + this->bp.add_r1cs_constraint(snark::r1cs_constraint(sym_2 + 5, 1, out)); + } + + void generate_assignments() { + this->bp.val(sym_1) = this->bp.val(x) * this->bp.val(x); + this->bp.val(y) = this->bp.val(sym_1) * this->bp.val(x); + this->bp.val(sym_2) = this->bp.val(y) + this->bp.val(x); + } +}; + +#endif // CRYPTO3_BLUEPRINT_EXAMPLE_TEST_COMPONENT_HPP diff --git a/libs/blueprint/flake.nix b/libs/blueprint/flake.nix new file mode 100644 index 000000000..4434d8c1d --- /dev/null +++ b/libs/blueprint/flake.nix @@ -0,0 +1,77 @@ +{ + description = "Nix flake for zkllvm-blueprint"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + nix-3rdparty = { + url = "github:NilFoundation/nix-3rdparty"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + nil-crypto3 = { + url = "https://github.com/NilFoundation/crypto3"; + type = "git"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + }; + + outputs = { self, nixpkgs, nil-crypto3, flake-utils, nix-3rdparty }: + (flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + stdenv = pkgs.llvmPackages_16.stdenv; + crypto3 = nil-crypto3.packages.${system}.crypto3; + cmake_modules = nix-3rdparty.packages.${system}.cmake_modules; + in { + packages = rec { + zkllvm-blueprint = (pkgs.callPackage ./zkllvm-blueprint.nix { + src_repo = self; + crypto3 = crypto3; + cmake_modules = cmake_modules; + }); + zkllvm-blueprint-debug = (pkgs.callPackage ./zkllvm-blueprint.nix { + src_repo = self; + crypto3 = crypto3; + cmake_modules = cmake_modules; + enableDebug = true; + }); + zkllvm-blueprint-debug-tests = (pkgs.callPackage ./zkllvm-blueprint.nix { + src_repo = self; + crypto3 = crypto3; + cmake_modules = cmake_modules; + enableDebug = true; + runTests = true; + }); + default = zkllvm-blueprint-debug-tests; + }; + checks = rec { + gcc = (pkgs.callPackage ./zkllvm-blueprint.nix { + src_repo = self; + crypto3 = crypto3; + cmake_modules = cmake_modules; + runTests = true; + }); + clang = (pkgs.callPackage ./zkllvm-blueprint.nix { + stdenv = pkgs.llvmPackages_18.stdenv; + src_repo = self; + crypto3 = crypto3; + cmake_modules = cmake_modules; + runTests = true; + }); + all = pkgs.symlinkJoin { + name = "all"; + paths = [ gcc clang ]; + }; + default = all; + }; + })); +} + +# `nix flake -L check` to run all tests (-L to output build logs) +# `nix flake show` to show derivations tree +# If build fails due to OOM, run `export NIX_CONFIG="cores = 2"` to set desired parallel level diff --git a/libs/blueprint/include/nil/blueprint/algorithms/allocate.hpp b/libs/blueprint/include/nil/blueprint/algorithms/allocate.hpp new file mode 100644 index 000000000..7be57c6af --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/algorithms/allocate.hpp @@ -0,0 +1,50 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_COMPONENTS_ALGORITHMS_ALLOCATE_HPP +#define CRYPTO3_ZK_COMPONENTS_ALGORITHMS_ALLOCATE_HPP + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + std::uint32_t allocate( + ComponentType component_instance, + BlueprintType &bp, + const std::uint32_t components_amount = 1) { + + return bp.allocate_rows(component_instance.rows_amount() * components_amount); + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_COMPONENTS_ALGORITHMS_ALLOCATE_HPP diff --git a/libs/blueprint/include/nil/blueprint/algorithms/generate_circuit.hpp b/libs/blueprint/include/nil/blueprint/algorithms/generate_circuit.hpp new file mode 100644 index 000000000..0f74bc422 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/algorithms/generate_circuit.hpp @@ -0,0 +1,98 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_COMPONENTS_ALGORITHMS_GENERATE_CIRCUIT_HPP +#define CRYPTO3_ZK_COMPONENTS_ALGORITHMS_GENERATE_CIRCUIT_HPP + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + BOOST_TTI_HAS_STATIC_MEMBER_FUNCTION(generate_circuit) + + template + typename std::enable_if< + (!(has_static_member_function_generate_circuit &, + blueprint_public_assignment_table &, + const typename ComponentType::params_type &, + const std::size_t>>::value)), + typename ComponentType::result_type>::type + generate_circuit( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const typename ComponentType::params_type & params, + const std::size_t start_row_index){ + + auto selector_iterator = assignment.find_selector(ComponentType::selector_seed); + std::size_t first_selector_index; + + if (selector_iterator == assignment.selectors_end()){ + first_selector_index = assignment.allocate_selector(ComponentType::selector_seed, + ComponentType::gates_amount); + ComponentType::generate_gates(bp, assignment, params, first_selector_index); + } else { + first_selector_index = selector_iterator->second; + } + + assignment.enable_selector(first_selector_index, start_row_index); + + ComponentType::generate_copy_constraints(bp, assignment, params, start_row_index); + + return typename ComponentType::result_type(params, start_row_index); + } + + template + typename std::enable_if< + (has_static_member_function_generate_circuit &, + blueprint_public_assignment_table &, + const typename ComponentType::params_type &, + const std::size_t>>::value), + typename ComponentType::result_type>::type + generate_circuit( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const typename ComponentType::params_type & params, + const std::size_t start_row_index){ + + return ComponentType::generate_circuit(bp, assignment, params, start_row_index); + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_COMPONENTS_ALGORITHMS_GENERATE_CIRCUIT_HPP diff --git a/libs/blueprint/include/nil/blueprint/assert.hpp b/libs/blueprint/include/nil/blueprint/assert.hpp new file mode 100644 index 000000000..21b7e5940 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/assert.hpp @@ -0,0 +1,55 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_ASSERT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_ASSERT_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace detail { + template + static void blueprint_assert(T1 line, T2 file, T3 expr){ + std::stringstream errMsg; + errMsg << "Assertion " << expr << " failed on line " << line << " in file " << file; + throw std::runtime_error(errMsg.str().c_str()); + } + } // namespace detail + } // namespace blueprint +} // namespace nil + +#define BLUEPRINT_RELEASE_ASSERT( expr ) \ + ( (expr) ? (void)0 : nil::blueprint::detail::blueprint_assert( __LINE__, __FILE__, #expr)) + +#ifdef BLUEPRINT_DEBUG_ENABLED +#define BLUEPRINT_ASSERT( expr ) \ + BLUEPRINT_RELEASE_ASSERT( expr ) +#else +#define BLUEPRINT_ASSERT( expr ) ((void)0) +#endif + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_ASSERT_HPP diff --git a/libs/blueprint/include/nil/blueprint/basic_non_native_policy.hpp b/libs/blueprint/include/nil/blueprint/basic_non_native_policy.hpp new file mode 100644 index 000000000..a2d2ee643 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/basic_non_native_policy.hpp @@ -0,0 +1,453 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2022 Mikhail Komarov +// Copyright (c) 2020-2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_BASIC_NON_NATIVE_POLICY_HPP +#define CRYPTO3_BLUEPRINT_BASIC_NON_NATIVE_POLICY_HPP + +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace detail { + template + struct basic_non_native_policy_field_type; + + /* + * Specialization for non-native Ed25519 base field element on Pallas base field + */ + template<> + struct basic_non_native_policy_field_type { + + constexpr static const std::uint32_t ratio = 4; // 66,66,66,66 bits + using non_native_field_type = typename crypto3::algebra::curves::ed25519::base_field_type; + using native_field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using var = crypto3::zk::snark::plonk_variable; + + typedef std::array non_native_var_type; + typedef std::array chopped_value_type; + + constexpr static const std::array chunk_sizes = {66, 66, 66, 66}; + + + static native_field_type::value_type get_i_th_chunk(non_native_field_type::value_type input, + std::size_t i_th) { + assert(i_th < ratio && "non-native type does not have that much chunks!"); + native_field_type::extended_integral_type result = native_field_type::extended_integral_type( + non_native_field_type::integral_type(input.data)); + native_field_type::integral_type base = 1; + native_field_type::integral_type mask = (base << chunk_sizes[i_th]) - 1; + std::size_t shift = 0; + for (std::size_t i = 1; i <= i_th; i++) { + shift += chunk_sizes[i - 1]; + } + + return (result >> shift) & mask; + } + + + static chopped_value_type chop_non_native(non_native_field_type::value_type input) { + chopped_value_type result; + for (std::size_t i = 0; i < ratio; i++) { + result[i] = get_i_th_chunk(input, i); + } + return result; + } + + static non_native_field_type::value_type glue_non_native(chopped_value_type input) { + non_native_field_type::value_type result; + result = non_native_field_type::value_type(native_field_type::integral_type(input[0].data)); + for (std::size_t i = 1; i < ratio; i++) { + std::size_t shift = 0; + for (std::size_t j = 0; j < i; j++) { + shift += chunk_sizes[j]; + } + result += non_native_field_type::value_type(native_field_type::integral_type(input[i].data) << shift); + } + return result; + } + + }; + + /* + * Specialization for non-native Ed25519 scalar field element on Pallas base field + */ + template<> + struct basic_non_native_policy_field_type { + + constexpr static const std::uint32_t ratio = 1; + + typedef crypto3::zk::snark::plonk_variable + non_native_var_type; + }; + + /* + * Specialization for non-native Pallas scalar field element on Pallas base field + */ + template<> + struct basic_non_native_policy_field_type { + + constexpr static const std::uint32_t ratio = 2; // 254, 1 bits + using non_native_field_type = typename crypto3::algebra::curves::pallas::scalar_field_type; + using native_field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using var = crypto3::zk::snark::plonk_variable; + + typedef std::array non_native_var_type; + typedef std::array chopped_value_type; + + constexpr static const std::array chunk_sizes = {254, 1}; + + + static native_field_type::value_type get_i_th_chunk(non_native_field_type::value_type input, + std::size_t i_th) { + assert(i_th < ratio && "non-native type does not have that much chunks!"); + native_field_type::extended_integral_type result = native_field_type::extended_integral_type::backend_type( + input.data.backend().base_data()); + native_field_type::integral_type base = 1; + native_field_type::integral_type mask = (base << chunk_sizes[i_th]) - 1; + std::size_t shift = 0; + for (std::size_t i = 1; i <= i_th; i++) { + shift += chunk_sizes[i - 1]; + } + + return (result >> shift) & mask; + } + + + static chopped_value_type chop_non_native(non_native_field_type::value_type input) { + chopped_value_type result; + for (std::size_t i = 0; i < ratio; i++) { + result[i] = get_i_th_chunk(input, i); + } + return result; + } + + static non_native_field_type::value_type glue_non_native(chopped_value_type input) { + non_native_field_type::value_type result; + result = non_native_field_type::value_type(native_field_type::integral_type(input[0].data)); + for (std::size_t i = 1; i < ratio; i++) { + std::size_t shift = 0; + for (std::size_t j = 0; j < i; j++) { + shift += chunk_sizes[j]; + } + result += non_native_field_type::value_type(native_field_type::integral_type(input[i].data) << shift); + } + return result; + } + + }; + + /* + * Specialization for non-native bls12381 scalar field element on pallas base field + */ + template<> + struct basic_non_native_policy_field_type> { + + constexpr static const std::uint32_t ratio = 1; + + typedef crypto3::zk::snark::plonk_variable::value_type> + non_native_var_type; + }; + + + /* + * Specialization for non-native bls12381 scalar field element on bls12381 base field + */ + template<> + struct basic_non_native_policy_field_type, + typename crypto3::algebra::fields::bls12_scalar_field<381>> { + + constexpr static const std::uint32_t ratio = 1; + + typedef crypto3::zk::snark::plonk_variable::value_type> + non_native_var_type; + }; + + + /* + * Specialization for non-native bls12-381 base field element on Pallas base field + */ + template<> + struct basic_non_native_policy_field_type> { + constexpr static const std::uint32_t ratio = 0; // not implemented yet + using var = crypto3::zk::snark::plonk_variable; + typedef std::array non_native_var_type; + }; + + /* + * Specialization for non-native Pallas base field element on bls12-381 base field + */ + template<> + struct basic_non_native_policy_field_type, + typename crypto3::algebra::curves::pallas::base_field_type> { + + constexpr static const std::uint32_t ratio = 0; // not implemented yet + using var = crypto3::zk::snark::plonk_variable>; + typedef std::array non_native_var_type; + }; + + /* + * Specialization for non-native Pallas scalar field element on bls12-381 base field + */ + template<> + struct basic_non_native_policy_field_type, + typename crypto3::algebra::curves::pallas::scalar_field_type> { + + constexpr static const std::uint32_t ratio = 0; // not implemented yet + using var = crypto3::zk::snark::plonk_variable>; + typedef std::array non_native_var_type; + }; + + /* + * Specialization for non-native Ed25519 base field element on bls12-381 base field + */ + template<> + struct basic_non_native_policy_field_type, + typename crypto3::algebra::curves::ed25519::base_field_type> { + constexpr static const std::uint32_t ratio = 0; // not implemented yet + using var = crypto3::zk::snark::plonk_variable>; + typedef std::array non_native_var_type; + }; + + /* + * Specialization for non-native Ed25519 scalar field element on bls12-381 base field + */ + template<> + struct basic_non_native_policy_field_type, + typename crypto3::algebra::curves::ed25519::scalar_field_type> { + constexpr static const std::uint32_t ratio = 0; // not implemented yet + using var = crypto3::zk::snark::plonk_variable>; + typedef std::array non_native_var_type; + }; + + + + /* + * Native element type. + */ + template + struct basic_non_native_policy_field_type { + + constexpr static const std::uint32_t ratio = 1; + + typedef crypto3::zk::snark::plonk_variable value_type; + }; + + /* + * Big unsigned numbers + */ + template + struct basic_non_native_policy_field_type>> { + + constexpr static const std::uint32_t ratio = 2; // 128, 128 bits + // not actually a field, but we preserve the interface + using non_native_field_type = typename boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<256>>; + using native_field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using var = crypto3::zk::snark::plonk_variable; + + typedef std::array non_native_var_type; + typedef std::array chopped_value_type; + + constexpr static const std::array chunk_sizes = {128, 128}; + + + static native_field_type::value_type get_i_th_chunk(non_native_field_type::value_type input, + std::size_t i_th) { + assert(i_th < ratio && "non-native type does not have that much chunks!"); + native_field_type::extended_integral_type result = native_field_type::extended_integral_type::backend_type( + input.backend()); + + native_field_type::integral_type base = 1; + native_field_type::integral_type mask = (base << chunk_sizes[i_th]) - 1; + std::size_t shift = 0; + for (std::size_t i = 1; i <= i_th; i++) { + shift += chunk_sizes[i - 1]; + } + + return (result >> shift) & mask; + } + + + static chopped_value_type chop_non_native(non_native_field_type::value_type input) { + chopped_value_type result; + for (std::size_t i = 0; i < ratio; i++) { + result[i] = get_i_th_chunk(input, i); + } + return result; + } + + static non_native_field_type::value_type glue_non_native(chopped_value_type input) { + non_native_field_type::value_type result; + result = non_native_field_type::value_type(native_field_type::integral_type(input[0].data)); + for (std::size_t i = 1; i < ratio; i++) { + std::size_t shift = 0; + for (std::size_t j = 0; j < i; j++) { + shift += chunk_sizes[j]; + } + result += non_native_field_type::value_type(native_field_type::integral_type(input[i].data) << shift); + } + return result; + } + }; + + /* + * Small and big signed numbers. This one is different from the others, it accepts + * boost::multiprecision::cpp_int_backend, which is not supposed to be used in algebra, so + * conversions need to happen before it can be used. + */ + template + struct basic_non_native_policy_field_type>> { + + constexpr static const std::uint32_t ratio = BitsAmount < 256 ? 2 : 3; // sign and all other bits + static constexpr std::array chunk_sizes_init() { + if constexpr (BitsAmount < 256) { + return {1, BitsAmount - 1}; + } else { + return {1, 127, 128}; + } + } + + // not actually a field, but we preserve the interface + using non_native_field_type = typename boost::multiprecision::number< + boost::multiprecision::cpp_int_backend>; + + using non_native_unsigned_integral_type = typename boost::multiprecision::number< + boost::multiprecision::cpp_int_backend>; + + using native_field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using native_integral_type = typename native_field_type::integral_type; + using native_backend_type = typename native_integral_type::backend_type; + using var = crypto3::zk::snark::plonk_variable; + + typedef std::array non_native_var_type; + typedef std::array chopped_value_type; + + constexpr static const std::array chunk_sizes = chunk_sizes_init(); + + static native_field_type::value_type get_i_th_chunk(typename non_native_field_type::value_type input, + std::size_t i_th) { + assert(i_th < ratio && "non-native type does not have that much chunks!"); + + if constexpr (BitsAmount < 256) { + if (i_th == 0) { + return input.sign() == 0 ? 1 : -1; + } else { + return native_field_type::value_type(input.sign() == 0 ? 1 : -1) * + native_integral_type(native_backend_type( + non_native_unsigned_integral_type(input).data.backend())); + } + } else { + static const non_native_field_type top_mask = ((non_native_field_type(1) << 128) - 1) << 128; + static const non_native_field_type bottom_mask = (non_native_field_type(1) << 128) - 1; + + if (i_th == 0) { + return input.sign() == 0 ? 1 : -1; + } else if (i_th == 1) { + return native_integral_type(native_backend_type( + non_native_unsigned_integral_type(top_mask & input).data.backend())); + } else { + return native_integral_type(native_backend_type( + non_native_unsigned_integral_type(bottom_mask & input).data.backend())); + } + } + } + + + static chopped_value_type chop_non_native(typename non_native_field_type::value_type input) { + chopped_value_type result; + for (std::size_t i = 0; i < ratio; i++) { + result[i] = get_i_th_chunk(input, i); + } + return result; + } + + static typename non_native_field_type::value_type glue_non_native(chopped_value_type input) { + typename non_native_field_type::value_type result; + if constexpr (BitsAmount < 256) { + result = + (non_native_field_type::value_type(native_field_type::integral_type(input[0].data)) == 0 ? 1 : -1) * non_native_field_type::value_type(native_field_type::integral_type(input[1].data)); + } else { + static const non_native_field_type two_128 = + boost::multiprecision::pow(non_native_field_type(2), 128); + result = + (non_native_field_type::value_type(native_field_type::integral_type(input[0].data)) == 0 ? 1 : -1) * + (non_native_field_type::value_type(native_field_type::integral_type(input[1].data) * two_128 + + non_native_field_type::value_type(input[2].data))); + } + return result; + } + }; + } // namespace detail + + template + class basic_non_native_policy; + + template<> + class basic_non_native_policy { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + public: + template + using field = typename detail::basic_non_native_policy_field_type; + }; + + template<> + class basic_non_native_policy> { + + using BlueprintFieldType = typename crypto3::algebra::fields::bls12_base_field<381>; + + public: + template + using field = typename detail::basic_non_native_policy_field_type; + }; + + + + + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_BASIC_NON_NATIVE_POLICY_HPP diff --git a/libs/blueprint/include/nil/blueprint/benchmarks/circuit_generator.hpp b/libs/blueprint/include/nil/blueprint/benchmarks/circuit_generator.hpp new file mode 100644 index 000000000..5e7da681e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/benchmarks/circuit_generator.hpp @@ -0,0 +1,261 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin = +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace nil { + namespace blueprint { + + template + crypto3::zk::snark::plonk_variable generate_random_global_var( + const assignment> &assignments, + boost::random::mt19937 &random_engine) { + + using var = crypto3::zk::snark::plonk_variable; + const std::size_t witness_amount = assignments.witnesses_amount(); + const std::size_t public_input_amount = assignments.public_inputs_amount(); + const std::size_t constant_amount = assignments.constants_amount(); + const std::size_t total_col_amount = witness_amount + public_input_amount + constant_amount; + const std::size_t rows_amount = assignments.rows_amount(); + const std::size_t random_row = + boost::random::uniform_int_distribution(0, rows_amount - 1)(random_engine); + std::size_t random_col = + boost::random::uniform_int_distribution(0, total_col_amount - 1)(random_engine); + typename var::column_type column_type; + if (random_col < witness_amount) { + column_type = var::column_type::witness; + } else if (random_col < witness_amount + public_input_amount) { + column_type = var::column_type::public_input; + random_col -= witness_amount; + } else { + column_type = var::column_type::constant; + random_col -= witness_amount + public_input_amount; + } + return var(random_col, random_row, true, column_type); + } + + template + crypto3::zk::snark::plonk_variable generate_random_local_var( + const assignment> &assignments, + boost::random::mt19937 &random_engine) { + + using var = crypto3::zk::snark::plonk_variable; + const std::size_t witness_amount = assignments.witnesses_amount(); + const std::size_t constant_amount = assignments.constants_amount(); + const std::size_t total_col_amount = witness_amount + constant_amount; + const std::int32_t random_offset = + boost::random::uniform_int_distribution(-1, 1)(random_engine); + std::size_t random_col = + boost::random::uniform_int_distribution(0, total_col_amount - 1)(random_engine); + typename var::column_type column_type; + if (random_col < witness_amount) { + column_type = var::column_type::witness; + } else { + column_type = var::column_type::constant; + random_col -= witness_amount; + } + return var(random_col, random_offset, true, column_type); + } + + template + void generate_random_copy_constraints( + const assignment> &assignments, + circuit> &bp, + const std::size_t num_constraints, + boost::random::mt19937 &random_engine) { + + using var = crypto3::zk::snark::plonk_variable; + for (std::size_t i = 0; i < num_constraints; ++i) { + const var a = generate_random_global_var(assignments, random_engine); + var b = generate_random_global_var(assignments, random_engine); + // note that we technically might not generate a unique copy constraint here and it + // might be already present + // for the sake of simplicity we don't check for that, as the probability of that is really small + // for the assignment tables of a reasonable size compared to the number of constraints + while (a == b) { [[unlikely]] + b = generate_random_global_var(assignments, random_engine); + } + bp.add_copy_constraint({a, b}); + } + // Sanity check + BOOST_ASSERT(bp.copy_constraints().size() == num_constraints); + } + + template + void fill_assignment_table( + assignment> &assignments, + const std::size_t rows_amount, + boost::random::mt19937 &random_engine) { + + using value_type = typename BlueprintFieldType::value_type; + crypto3::random::algebraic_engine engine(random_engine); + std::array, 3> access_functions = { + [&assignments](std::size_t col, std::size_t row) -> value_type& { + return assignments.witness(col, row); + }, + [&assignments](std::size_t col, std::size_t row) -> value_type& { + return assignments.public_input(col, row); + }, + [&assignments](std::size_t col, std::size_t row) -> value_type& { + return assignments.constant(col, row); + } + }; + std::array sizes = { + assignments.witnesses_amount(), assignments.public_inputs_amount(), assignments.constants_amount()}; + for (const auto &column_access_pair : + {std::pair(sizes[0], access_functions[0]), + std::pair(sizes[1], access_functions[1]), + std::pair(sizes[2], access_functions[2])}) { + const std::size_t column_amount = column_access_pair.first; + const auto &column_access_function = column_access_pair.second; + for (std::size_t col = 0; col < column_amount; ++col) { + for (std::size_t row = 0; row < rows_amount; ++row) { + column_access_function(col, row) = engine(); + } + } + } + } + + template + void fill_selectors( + assignment> &assignments, + const circuit> &bp, + boost::random::mt19937 &random_engine) { + + // We use a separate algorithm for filling selectors, as they are 0/1 + // In practicde the distribution is not uniform, but we ignore that for the purposes of this benchmark + // TODO: do something more clever + for (std::size_t i = 0; i < assignments.selectors_amount(); ++i) { + for (std::size_t row = 0; row < assignments.rows_amount(); ++row) { + assignments.selector(i, row) = random_engine() % 2; + } + } + } + + template + nil::crypto3::zk::snark::plonk_constraint generate_random_constraint( + const assignment> &assignments, + const std::size_t max_degree, + const std::size_t max_linear_comb_size, + boost::random::mt19937 &random_engine) { + // Strategy: generate two random polynomials of max_degree / 2, and then multiply them + // If max_degree % 2 != 0, we multiply the result by a random linear combination + // Which is incidentally the ouput of this function with max_degree = 1 + // This generates very "wide" gates on average. + // I need a different algorithm probably? Unsure. + if (max_degree > 1) { + auto a = generate_random_constraint( + assignments, max_degree / 2, max_linear_comb_size, random_engine); + auto b = generate_random_constraint( + assignments, max_degree / 2, max_linear_comb_size, random_engine); + if (max_degree % 2 != 0) { + auto c = generate_random_constraint( + assignments, 1, max_linear_comb_size, random_engine); + return a * b * c; + } else { + return a * b; + } + } else if (max_degree == 1) { + crypto3::random::algebraic_engine engine(random_engine); + nil::crypto3::zk::snark::plonk_constraint linear_comb; + const std::size_t linear_comb_size = + boost::random::uniform_int_distribution(1, max_linear_comb_size)(random_engine); + for (std::size_t i = 0; i < linear_comb_size; i++) { + linear_comb += engine() * generate_random_local_var(assignments, random_engine); + } + linear_comb += engine(); + return linear_comb; + } else { + BOOST_ASSERT_MSG(false, "max_degree must be > 0"); + } + __builtin_unreachable(); + } + + template + void generate_random_gate( + const assignment> &assignments, + circuit> &bp, + const std::size_t max_degree, + const std::size_t max_linear_comb_size, + const std::size_t constraints_amount, + boost::random::mt19937 &random_engine) { + + std::vector> constraints; + constraints.reserve(constraints_amount); + // first, ensure that we have at least one of the constraints with the given max_degree + constraints.emplace_back(generate_random_constraint( + assignments, max_degree, max_linear_comb_size, random_engine)); + // next, generate the rest of them + for (std::size_t i = 1; i < constraints_amount; ++i) { + const std::size_t degree = max_degree > 1 ? + boost::random::uniform_int_distribution(1, max_degree)(random_engine) + : 1; + constraints.emplace_back(generate_random_constraint( + assignments, degree, max_linear_comb_size, random_engine)); + } + bp.add_gate(constraints); + } + + + template + void generate_random_gates( + const assignment> &assignments, + circuit> &bp, + const std::size_t gates_amount, + const std::size_t max_degree, + const std::size_t max_linear_comb_size, + const std::size_t constraints_amount, + boost::random::mt19937 &random_engine) { + + BOOST_ASSERT_MSG(max_degree > 0, "max_degree must be > 0"); + BOOST_ASSERT_MSG(max_linear_comb_size > 0, "max_linear_comb_size must be > 0"); + BOOST_ASSERT_MSG(constraints_amount > 0, "constraints_amount must be > 0"); + + // Generate a gate with a given max_degree + generate_random_gate(assignments, bp, max_degree, max_linear_comb_size, constraints_amount, random_engine); + // Generate the rest of the gates with random max degrees + for (std::size_t i = 1; i < gates_amount; ++i) { + const std::size_t degree = max_degree > 1 ? + boost::random::uniform_int_distribution(1, max_degree)(random_engine) + : 1; + generate_random_gate(assignments, bp, degree, max_linear_comb_size, constraints_amount, random_engine); + } + } + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment.hpp b/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment.hpp new file mode 100644 index 000000000..df9e40e29 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment.hpp @@ -0,0 +1,782 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_ASSIGNMENT_PLONK_HPP +#define CRYPTO3_BLUEPRINT_ASSIGNMENT_PLONK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace detail { + template + struct constant_batch_ref_compare { + using value_type = typename BlueprintFieldType::value_type; + using ref_type = std::reference_wrapper; + using pair_type = std::pair; + + bool operator()(const pair_type &p1, const pair_type &p2) const { + return p1.first.get() < p2.first.get(); + } + }; + } // namespace detail + + template + class assignment; + + template + class circuit; + + template + class component_batch; + + template + struct has_add_input; + + template + struct has_finalize_batch; + + template + struct has_name; + + template + struct input_type_v; + + template + struct result_type_v; + + template + struct component_params_type_v;; + + struct _batch : boost::type_erasure::placeholder {}; + struct _component : boost::type_erasure::placeholder {}; + struct _input_type : boost::type_erasure::placeholder {}; + struct _result_type : boost::type_erasure::placeholder {}; + struct _variadics : boost::type_erasure::placeholder {}; + + template + class assignment> + : public crypto3::zk::snark::plonk_assignment_table { + + using zk_type = crypto3::zk::snark::plonk_assignment_table; + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + using var = crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + using column_type = typename crypto3::zk::snark::plonk_column; + using shared_container_type = typename std::array; + using constant_set_compare_type = detail::constant_batch_ref_compare; + + std::uint32_t assignment_allocated_rows = 0; + std::vector assignment_private_storage; + // for variables used in component batching + std::vector assignment_batch_private_storage; + using batcher_type = boost::type_erasure::any< + boost::mpl::vector< + has_add_input<_batch, _input_type, _result_type>, + has_finalize_batch<_batch, ArithmetizationType, var>, + has_name<_batch>, + boost::type_erasure::same_type>, _input_type>, + boost::type_erasure::same_type>, _result_type>, + boost::type_erasure::same_type>, _variadics>, + boost::type_erasure::less_than_comparable<_batch>, + boost::type_erasure::copy_constructible<_batch>, + boost::type_erasure::constructible<_batch(assignment&, _variadics)>, + boost::type_erasure::destructible<_batch>, + boost::type_erasure::typeid_<_batch>, + boost::type_erasure::relaxed>, + _batch>; + std::set component_batches; + // technically we can delete this one after finalization + // but tests require it to replace the outputs + std::unordered_map batch_variable_map; + // for constants which we are going to try to put into aribtrary places + boost::container::stable_vector assignment_batch_constant_storage; + std::set, std::size_t>, constant_set_compare_type> + assignment_batch_constant_storage_set; + shared_container_type shared_storage; // results of the previously prover + std::set lookup_constant_cols; + std::set lookup_selector_cols; + + /// Used to specify ranges of indexes for columns or rows + using ranges = std::vector>; + + public: + static constexpr const std::size_t private_storage_index = std::numeric_limits::max(); + static constexpr const std::size_t batch_private_storage_index = std::numeric_limits::max() - 1; + static constexpr const std::size_t batch_constant_storage_index = std::numeric_limits::max() - 2; + + assignment(std::size_t witness_amount, std::size_t public_input_amount, + std::size_t constant_amount, std::size_t selector_amount) + : zk_type(witness_amount, public_input_amount, constant_amount, selector_amount) { + } + + assignment(const crypto3::zk::snark::plonk_table_description &desc) + : zk_type(desc.witness_columns, desc.public_input_columns, + desc.constant_columns, desc.selector_columns) { + } + + template + typename ComponentType::result_type add_input_to_batch_assignment( + const typename ComponentType::input_type &input, + ComponentParams... params) { + + return add_input_to_batch(input, false, params...); + } + + template + typename ComponentType::result_type add_input_to_batch_circuit( + const typename ComponentType::input_type &input, + ComponentParams... params) { + + return add_input_to_batch(input, true, params...); + } + + template + typename ComponentType::result_type add_input_to_batch( + const typename ComponentType::input_type &input, + bool called_from_generate_circuit, + ComponentParams... params) { + using batching_type = component_batch; + batching_type batch(*this, std::tuple(params...)); + auto it = component_batches.find(batch); + if (it == component_batches.end()) { + auto result = batch.add_input(input, called_from_generate_circuit); + component_batches.insert(batch); + return result; + } else { + // safe because the ordering doesn't depend on the batch inputs + return boost::type_erasure::any_cast(const_cast(*it)) + .add_input(input, called_from_generate_circuit); + } + } + + std::size_t finalize_component_batches(nil::blueprint::circuit &bp, + std::size_t start_row_index) { + std::size_t next_row_index = start_row_index; + for (auto& batch : component_batches) { + // safe because the ordering doesn't depend on the batch inputs + next_row_index = const_cast(batch).finalize_batch( + bp, batch_variable_map, next_row_index); + } + auto ©_constraints = bp.mutable_copy_constraints(); + for (auto &constraint : copy_constraints) { + for (auto variable : {&(constraint.first), &(constraint.second)}) { + if (batch_variable_map.find(*variable) != batch_variable_map.end()) { + *variable = batch_variable_map[*variable]; + } + } + } + return next_row_index; + } + + const std::unordered_map& get_batch_variable_map() const { + return batch_variable_map; + } + + // currently only supports a single constant column to batch things into + // ideally we should not require more than one + std::size_t finalize_constant_batches( + nil::blueprint::circuit &bp, + std::size_t const_column, + std::size_t start_row_index = 1) { + if (assignment_batch_constant_storage.size() == 0) { + return start_row_index; + } + BOOST_ASSERT(start_row_index >= 1); + std::vector used_constants; + if (this->constant_column_size(const_column) > start_row_index) { + used_constants.resize(this->constant_column_size(const_column) - start_row_index, false); + const auto &immutable_copy_constraints = bp.copy_constraints(); + auto var_check = [const_column, start_row_index](const var &variable) -> bool { + return variable.type == var::column_type::constant && + variable.index == const_column && + variable.rotation >= start_row_index; + }; + for (const auto &constraint : immutable_copy_constraints) { + for (const auto variable : {&(constraint.first), &(constraint.second)}) { + if (var_check(*variable)) { + used_constants[variable->rotation - start_row_index] = true; + } + } + } + const auto &gates = bp.gates(); + for (const auto &gate : gates) { + std::unordered_set variable_set; + std::function variable_extractor = + [&variable_set, &var_check](const var &variable) { + if (var_check(variable)) { + variable_set.insert(variable); + } + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &constraint : gate.constraints) { + visitor.visit(constraint); + } + for (const auto &variable : variable_set) { + for (std::size_t row = start_row_index - 1; + row < this->selector_column_size(gate.selector_index); row++) { + if (this->selector(gate.selector_index, row) == value_type::one()) { + used_constants[row + variable.rotation - start_row_index] = true; + } + } + } + } + const auto &lookup_gates = bp.lookup_gates(); + for (const auto &gate : lookup_gates) { + std::unordered_set variable_set; + std::function variable_extractor = + [&variable_set, &var_check](const var &variable) { + if (var_check(variable)) { + variable_set.insert(variable); + } + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &lookup_constraint : gate.constraints) { + for (const auto &constraint : lookup_constraint.lookup_input) { + visitor.visit(constraint); + } + } + for (const auto &variable : variable_set) { + for (std::size_t row = start_row_index - 1; + row < this->selector_column_size(gate.tag_index); row++) { + if (this->selector(gate.tag_index, row) == BlueprintFieldType::value_type::one()) { + used_constants[row + variable.rotation - start_row_index] = true; + } + } + } + } + } + std::size_t row = start_row_index; + std::unordered_map batch_variable_map; + for (std::size_t constant_index = 0; constant_index < assignment_batch_constant_storage.size(); + constant_index++) { + while (row < (used_constants.size() + start_row_index) && used_constants[row - start_row_index]) { + row++; + } + const var curr_batch_var = + var(batch_constant_storage_index, constant_index, false, var::column_type::constant); + const var curr_bp_var = var(const_column, row, false, var::column_type::constant); + this->constant(const_column, row) = assignment_batch_constant_storage[constant_index]; + batch_variable_map[curr_batch_var] = curr_bp_var; + row++; + } + auto ©_constraints = bp.mutable_copy_constraints(); + for (auto &constraint : copy_constraints) { + for (auto variable : {&(constraint.first), &(constraint.second)}) { + if (batch_variable_map.find(*variable) != batch_variable_map.end()) { + *variable = batch_variable_map[*variable]; + } + } + } + return row; + } + + virtual value_type &selector(std::size_t selector_index, std::uint32_t row_index) { + + assert(selector_index < this->_public_table._selectors.size()); + + if (this->_public_table._selectors[selector_index].size() <= row_index) + this->_public_table._selectors[selector_index].resize(row_index + 1); + + return this->_public_table._selectors[selector_index][row_index]; + } + + virtual value_type selector(std::size_t selector_index, std::uint32_t row_index) const { + + assert(selector_index < this->_public_table._selectors.size()); + assert(row_index < this->_public_table._selectors[selector_index].size()); + + return this->_public_table._selectors[selector_index][row_index]; + } + + virtual const column_type& selector(std::uint32_t index) const { + return zk_type::selector(index); + } + + virtual std::uint32_t selector_column_size(std::uint32_t col_idx) const { + return zk_type::selector_column_size(col_idx); + } + + virtual std::uint32_t selectors_amount() const { + return zk_type::selectors_amount(); + } + + virtual std::uint32_t allocated_rows() const { + return assignment_allocated_rows; + } + + virtual std::uint32_t rows_amount() const { + return zk_type::rows_amount(); + } + + virtual void enable_selector(const std::size_t selector_index, const std::size_t row_index) { + + selector(selector_index, row_index) = BlueprintFieldType::value_type::one(); + assignment_allocated_rows = std::uint32_t(std::max(std::size_t(assignment_allocated_rows), row_index + 1)); + } + + virtual void enable_selector(const std::size_t selector_index, + const std::size_t begin_row_index, + const std::size_t end_row_index, + const std::size_t index_step = 1) { + + for (std::size_t row_index = begin_row_index; row_index <= end_row_index; row_index += index_step) { + enable_selector(selector_index, row_index); + } + assignment_allocated_rows = std::uint32_t(std::max(std::size_t(assignment_allocated_rows), end_row_index)); + } + + void fill_selector(std::uint32_t index, const column_type& column) override { + lookup_selector_cols.insert(index); + zk_type::fill_selector(index, column); + } + + virtual const std::set& get_lookup_selector_cols() const { + return lookup_selector_cols; + } + + virtual std::uint32_t get_lookup_selector_amount() const { + return lookup_selector_cols.size(); + } + + virtual value_type &shared(std::uint32_t shared_index, std::uint32_t row_index) { + if (shared_storage[shared_index].size() <= row_index) { + shared_storage[shared_index].resize(row_index + 1); + } + return shared_storage[shared_index][row_index]; + } + + virtual value_type shared(std::uint32_t shared_index, std::uint32_t row_index) const { + BLUEPRINT_ASSERT(row_index < shared_storage[shared_index].size()); + return shared_storage[shared_index][row_index]; + } + + virtual std::uint32_t shared_column_size(std::uint32_t index) const { + return shared_storage[index].size(); + } + + virtual std::uint32_t shareds_amount() const { + return shared_storage.size(); + } + + virtual const column_type& shared(std::uint32_t index) const { + return shared_storage[index]; + } + + virtual value_type &witness(std::uint32_t witness_index, std::uint32_t row_index) { + BLUEPRINT_ASSERT(witness_index < this->_private_table._witnesses.size()); + + if (this->_private_table._witnesses[witness_index].size() <= row_index) + this->_private_table._witnesses[witness_index].resize(row_index + 1); + + assignment_allocated_rows = std::max(assignment_allocated_rows, row_index + 1); + return this->_private_table._witnesses[witness_index][row_index]; + } + + virtual value_type witness(std::uint32_t witness_index, std::uint32_t row_index) const { + BLUEPRINT_RELEASE_ASSERT(witness_index < this->_private_table._witnesses.size()); + BLUEPRINT_RELEASE_ASSERT(row_index < this->_private_table._witnesses[witness_index].size()); + + return this->_private_table._witnesses[witness_index][row_index]; + } + + virtual std::uint32_t witness_column_size(std::uint32_t col_idx) const { + return this->_private_table.witness_column_size(col_idx); + } + + virtual std::uint32_t witnesses_amount() const { + return zk_type::witnesses_amount(); + } + + virtual const column_type& witness(std::uint32_t index) const { + return zk_type::witness(index); + } + + virtual value_type &public_input( + std::uint32_t public_input_index, std::uint32_t row_index) { + + BLUEPRINT_ASSERT(public_input_index < zk_type::public_inputs_amount()); + + if (zk_type::public_input_column_size(public_input_index) <= row_index) + this->_public_table._public_inputs[public_input_index].resize(row_index + 1); + + return this->_public_table._public_inputs[public_input_index][row_index]; + } + + virtual value_type public_input( + std::uint32_t public_input_index, std::uint32_t row_index) const { + + BLUEPRINT_ASSERT(public_input_index < zk_type::public_inputs_amount()); + BLUEPRINT_ASSERT(row_index < zk_type::public_input_column_size(public_input_index)); + + return zk_type::public_input(public_input_index)[row_index]; + } + + virtual std::uint32_t public_input_column_size(std::uint32_t col_idx) const { + return this->_public_table.public_input_column_size(col_idx); + } + + virtual std::uint32_t public_inputs_amount() const { + return zk_type::public_inputs_amount(); + } + + virtual const column_type& public_input(std::uint32_t index) const { + return zk_type::public_input(index); + } + + virtual value_type &constant( + std::uint32_t constant_index, std::uint32_t row_index) { + + BLUEPRINT_ASSERT(constant_index < zk_type::constants_amount()); + + if (zk_type::constant_column_size(constant_index) <= row_index) + this->_public_table._constants[constant_index].resize(row_index + 1); + + assignment_allocated_rows = std::max(assignment_allocated_rows, row_index + 1); + return this->_public_table._constants[constant_index][row_index]; + } + + virtual value_type constant( + std::uint32_t constant_index, std::uint32_t row_index) const { + + BLUEPRINT_ASSERT(constant_index < zk_type::constants_amount()); + BLUEPRINT_ASSERT(row_index < zk_type::constant_column_size(constant_index)); + + return zk_type::constant(constant_index)[row_index]; + } + + virtual const column_type& constant(std::uint32_t index) const { + return zk_type::constant(index); + } + + void fill_constant(std::uint32_t index, const column_type& column) override { + lookup_constant_cols.insert(index); + zk_type::fill_constant(index, column); + } + + virtual const std::set& get_lookup_constant_cols() const { + return lookup_constant_cols; + } + + virtual std::uint32_t get_lookup_constant_amount() const { + return lookup_constant_cols.size(); + } + + virtual std::uint32_t constant_column_size(std::uint32_t col_idx) const { + return this->_public_table.constant_column_size(col_idx); + } + + virtual std::uint32_t constants_amount() const { + return zk_type::constants_amount(); + } + + virtual value_type private_storage(std::uint32_t storage_index) const { + BLUEPRINT_ASSERT(storage_index < assignment_private_storage.size()); + return assignment_private_storage[storage_index]; + } + + virtual value_type &private_storage(std::uint32_t storage_index) { + if (assignment_private_storage.size() <= storage_index) { + assignment_private_storage.resize(storage_index + 1); + } + return assignment_private_storage[storage_index]; + } + + // Not required to be called; private_storage calls will automatically resize + virtual void resize_private_storage(std::uint32_t new_size) { + assignment_private_storage.resize(new_size); + } + + virtual void clear_private_storage() { + assignment_private_storage.clear(); + } + + virtual std::size_t private_storage_size() const { + return assignment_private_storage.size(); + } + + virtual std::size_t batch_private_storage_size() const { + return assignment_batch_private_storage.size(); + } + + virtual value_type batch_private_storage(std::uint32_t storage_index) const { + BLUEPRINT_ASSERT(storage_index < assignment_batch_private_storage.size()); + return assignment_batch_private_storage[storage_index]; + } + + virtual value_type &batch_private_storage(std::uint32_t storage_index) { + if (assignment_batch_private_storage.size() <= storage_index) { + assignment_batch_private_storage.resize(storage_index + 1); + } + return assignment_batch_private_storage[storage_index]; + } + + virtual var add_batch_variable(const value_type &value) { + assignment_batch_private_storage.push_back(value); + return var(batch_private_storage_index, assignment_batch_private_storage.size() - 1, false, + var::column_type::public_input); + } + + virtual value_type batch_constant_storage(std::uint32_t storage_index) const { + BLUEPRINT_ASSERT(storage_index < assignment_batch_constant_storage.size()); + return assignment_batch_constant_storage[storage_index]; + } + + virtual var add_batch_constant_variable(const value_type &value) { + auto existing_const = assignment_batch_constant_storage_set.find( + std::make_pair, std::size_t>(std::cref(value), 0)); + if (existing_const == assignment_batch_constant_storage_set.end()) { + assignment_batch_constant_storage.push_back(value); + assignment_batch_constant_storage_set.insert(std::make_pair(std::cref(assignment_batch_constant_storage.back()), + assignment_batch_constant_storage.size() - 1)); + return var(batch_constant_storage_index, assignment_batch_constant_storage.size() - 1, false, + var::column_type::constant); + } else { + return var(batch_constant_storage_index, existing_const->second, false, + var::column_type::constant); + } + } + + /// @brief Max size of witness columns. + std::uint32_t max_witnesses_size() const { + std::uint32_t size = 0; + std::size_t ammount = this->_private_table.witnesses_amount(); + for (std::uint32_t i = 0; i < ammount; i++) { + size = std::max(size, this->_private_table.witness_column_size(i)); + } + return size; + } + + /// @brief Max size of public input columns. + std::uint32_t max_public_inputs_size() const { + std::uint32_t size = 0; + std::size_t ammount = this->_public_table.public_inputs_amount(); + for (std::uint32_t i = 0; i < ammount; i++) { + size = std::max(size, this->_public_table.public_input_column_size(i)); + } + return size; + } + + /// @brief Max size of constant columns. + std::uint32_t max_constants_size() const { + std::uint32_t size = 0; + std::size_t ammount = this->_public_table.constants_amount(); + for (std::uint32_t i = 0; i < ammount; i++) { + size = std::max(size, this->_public_table.constant_column_size(i)); + } + return size; + } + + /// @brief Max size of selector columns. + std::uint32_t max_selectors_size() const { + std::uint32_t size = 0; + std::size_t ammount = this->_public_table.selectors_amount(); + for (std::uint32_t i = 0; i < ammount; i++) { + size = std::max(size, this->_public_table.selector_column_size(i)); + } + return size; + } + + /// @brief Max size of all columns. + std::uint32_t max_size() const { + return std::max( + {max_witnesses_size(), max_public_inputs_size(), max_constants_size(), max_selectors_size()}); + } + + virtual void export_table(std::ostream &os, bool wide_export = false) const { + std::size_t witnesses_size = this->_private_table.witnesses_amount(), + public_size = this->_public_table.public_inputs_amount(), + constants_size = this->_public_table.constants_amount(), + selectors_size = this->_public_table.selectors_amount(); + std::uint32_t size = this->max_size(); + + ranges rows; + if (size) { + rows.push_back({0, size - 1}); + } + ranges witnesses; + if (witnesses_size) { + witnesses.push_back({0, witnesses_size - 1}); + } + ranges public_inputs; + if (public_size) { + public_inputs.push_back({0, public_size - 1}); + } + ranges constants; + if (constants_size) { + constants.push_back({0, constants_size - 1}); + } + ranges selectors; + if (selectors_size) { + selectors.push_back({0, selectors_size - 1}); + } + return export_table(os, witnesses, public_inputs, constants, selectors, rows, wide_export); + } + + /** + * @brief Partial export of the table with specifies columns and rows. + * Headers are still will describe the whole table. + */ + virtual void export_table(std::ostream &os, ranges witnesses, ranges public_inputs, ranges constants, + ranges selectors, ranges rows, bool wide_export = false) const { + // wide_export is for e.g. potentiall fuzzer: does fixed width elements + std::ios_base::fmtflags os_flags(os.flags()); + std::size_t total_witnesses_size = this->_private_table.witnesses_amount(), + total_public_size = this->_public_table.public_inputs_amount(), + total_constants_size = this->_public_table.constants_amount(), + total_selectors_size = this->_public_table.selectors_amount(); + std::uint32_t total_size = this->max_size(); + + os << std::dec; + + os << "witnesses_size: " << total_witnesses_size << " " + << "public_inputs_size: " << total_public_size << " " + << "constants_size: " << total_constants_size << " " + << "selectors_size: " << total_selectors_size << " " + << "max_size: " << total_size << "\n"; + + os << std::hex << std::setfill('0'); + std::uint32_t width = wide_export ? (BlueprintFieldType::modulus_bits + 4 - 1) / 4 : 0; + + for (auto [lower_row, upper_row] : rows) { + for (std::uint32_t i = lower_row; i <= upper_row; i++) { + for (auto [lower_witness, upper_witness] : witnesses) { + for (std::uint32_t j = lower_witness; j <= upper_witness; j++) { + os << std::setw(width) + << (i < this->_private_table.witness_column_size(j) ? + this->_private_table.witness(j)[i] : + 0) + .data + << " "; + } + } + os << "| "; + for (auto [lower_public_input, upper_public_input] : public_inputs) { + for (std::uint32_t j = lower_public_input; j <= upper_public_input; j++) { + os << std::setw(width) + << (i < this->_public_table.public_input_column_size(j) ? + this->_public_table.public_input(j)[i] : + 0) + .data + << " "; + } + } + os << "| "; + for (auto [lower_constant, upper_constant] : constants) { + for (std::uint32_t j = lower_constant; j <= upper_constant; j++) { + os << std::setw(width) + << (i < this->_public_table.constant_column_size(j) ? + this->_public_table.constant(j)[i] : + 0) + .data + << " "; + } + } + os << "| "; + for (auto [lower_selector, upper_selector] : selectors) { + // Selectors only need a single bit, so we do not renew the size here + for (std::uint32_t j = lower_selector; j <= upper_selector; j++) { + os << (i < this->_public_table.selector_column_size(j) ? + this->_public_table.selector(j)[i] : + 0) + .data + << " "; + } + } + os << "\n"; + } + } + + os.flush(); + os.flags(os_flags); + } + }; + + template + typename BlueprintFieldType::value_type var_value( + const assignment> &input_assignment, + const crypto3::zk::snark::plonk_variable &input_var) { + using var_column_type = + typename crypto3::zk::snark::plonk_variable::column_type; + using assignment_type = assignment>; + // This SHOULD be handled by a separate variable type + // (Or even better: properly extracted from the component) + // But adding a new variable type breaks assigner + // So we add a type without actually adding a type + if (input_var.index == assignment_type::private_storage_index) { + return input_assignment.private_storage(input_var.rotation); + } + if (input_var.index == assignment_type::batch_private_storage_index) { + return input_assignment.batch_private_storage(input_var.rotation); + } + if (input_var.index == assignment_type::batch_constant_storage_index) { + return input_assignment.batch_constant_storage(input_var.rotation); + } + if (input_var.type == var_column_type::public_input && input_var.index > 0) { + return input_assignment.shared(input_var.index - 1, input_var.rotation); + } + switch(input_var.type){ + case var_column_type::witness: + return input_assignment.witness(input_var.index, input_var.rotation); + case var_column_type::public_input: + return input_assignment.public_input(input_var.index, input_var.rotation); + default: + return input_assignment.constant(input_var.index, input_var.rotation); + } + } + + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_ASSIGNMENT_PLONK_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment_proxy.hpp b/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment_proxy.hpp new file mode 100644 index 000000000..0f26a6519 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/plonk/assignment_proxy.hpp @@ -0,0 +1,462 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Aleksei Kokoshnikov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_ASSIGNMENT_PROXY_PLONK_HPP +#define CRYPTO3_BLUEPRINT_ASSIGNMENT_PROXY_PLONK_HPP + +#include +#include + +namespace nil { + namespace blueprint { + + template + class assignment_proxy; + + template + class circuit; + + template + class assignment_proxy> + : public assignment> { + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + using value_type = typename BlueprintFieldType::value_type; + using column_type = typename crypto3::zk::snark::plonk_column; + + private: + std::shared_ptr> assignment_ptr; + std::uint32_t id; + bool check; + std::set used_rows; + std::set used_selector_rows; + public: + assignment_proxy(std::shared_ptr> assignment_, + std::uint32_t _id) : + assignment>( + assignment_->witnesses_amount(), + assignment_->public_inputs_amount(), + assignment_->constants_amount(), + assignment_->selectors_amount()), + assignment_ptr(assignment_), + id(_id), + check(false) { + assert(assignment_ptr); + } + + assignment_proxy() = delete; + + const assignment& get() const { + return *assignment_ptr; + } + + assignment& get() { + return *assignment_ptr; + } + + std::uint32_t get_id() const { + return id; + } + + void set_check(bool flag) { + check = flag; + } + + const std::set& get_used_rows() const { + return used_rows; + } + + const std::set& get_used_selector_rows() const { + return used_selector_rows; + } + + std::uint32_t rows_amount() const override { + return assignment_ptr->rows_amount(); + } + + std::uint32_t allocated_rows() const override { + return assignment_ptr->allocated_rows(); + } + + value_type &selector(std::size_t selector_index, std::uint32_t row_index) override { + used_rows.insert(row_index); + used_selector_rows.insert(row_index); + return assignment_ptr->selector(selector_index, row_index); + } + + value_type selector(std::size_t selector_index, std::uint32_t row_index) const override { + if (check) { + const auto lookup_selector_cols = assignment_ptr->get_lookup_selector_cols(); + if (lookup_selector_cols.find(selector_index) == lookup_selector_cols.end() && + used_selector_rows.find(row_index) == used_selector_rows.end()) { + std::cout << id << ": Not found selector " << selector_index << " on row " << row_index << std::endl; + BLUEPRINT_ASSERT(false); + } + } + return std::const_pointer_cast>(assignment_ptr)->selector(selector_index, row_index); + } + + const column_type& selector(std::uint32_t index) const override { + return assignment_ptr->selector(index); + } + + std::uint32_t selector_column_size(std::uint32_t col_idx) const override { + return assignment_ptr->selector_column_size(col_idx); + } + + std::uint32_t selectors_amount() const override { + return assignment_ptr->selectors_amount(); + } + + void enable_selector(const std::size_t selector_index, const std::size_t row_index) override { + used_rows.insert(row_index); + used_selector_rows.insert(row_index); + assignment_ptr->enable_selector(selector_index, row_index); + } + + void enable_selector(const std::size_t selector_index, + const std::size_t begin_row_index, + const std::size_t end_row_index, + const std::size_t index_step = 1) override { + for (auto i = begin_row_index; i <= end_row_index; i = i + index_step) { + enable_selector(selector_index, i); + } + } + + void fill_selector(std::uint32_t index, const column_type& column) override { + assignment_ptr->fill_selector(index, column); + } + + const std::set& get_lookup_selector_cols() const override { + return assignment_ptr->get_lookup_selector_cols(); + } + + std::uint32_t get_lookup_selector_amount() const override { + return assignment_ptr->get_lookup_selector_amount(); + } + + value_type &shared(std::uint32_t shared_index, std::uint32_t row_index) override { + return assignment_ptr->shared(shared_index, row_index); + } + + value_type shared(std::uint32_t shared_index, std::uint32_t row_index) const override{ + if (check && row_index >= assignment_ptr->shared_column_size(shared_index)) { + std::cout << id << ": Not found shared " << shared_index << " on row " << row_index << std::endl; + BLUEPRINT_ASSERT(false); + } + return std::const_pointer_cast>(assignment_ptr)->shared(shared_index, row_index); + } + + std::uint32_t shared_column_size(std::uint32_t index) const override { + return assignment_ptr->shared_column_size(index); + } + + const column_type& shared(std::uint32_t index) const override { + return assignment_ptr->shared(index); + } + + std::uint32_t shareds_amount() const override { + return assignment_ptr->shareds_amount(); + } + + value_type &witness(std::uint32_t witness_index, std::uint32_t row_index) override { + used_rows.insert(row_index); + return assignment_ptr->witness(witness_index, row_index); + } + + value_type witness(std::uint32_t witness_index, std::uint32_t row_index) const override { + if (check && used_rows.find(row_index) == used_rows.end()) { + std::cout << id << ": Not found witness " << witness_index << " on row " << row_index << std::endl; + BLUEPRINT_ASSERT(false); + } + return std::const_pointer_cast>(assignment_ptr)->witness(witness_index, row_index); + } + + const column_type& witness(std::uint32_t index) const override { + return assignment_ptr->witness(index); + } + + std::uint32_t witnesses_amount() const override { + return assignment_ptr->witnesses_amount(); + } + + std::uint32_t witness_column_size(std::uint32_t index) const override { + return assignment_ptr->witness_column_size(index); + } + + value_type &public_input( + std::uint32_t public_input_index, std::uint32_t row_index) override { + return assignment_ptr->public_input(public_input_index, row_index); + } + + value_type public_input( + std::uint32_t public_input_index, std::uint32_t row_index) const override { + return std::const_pointer_cast>(assignment_ptr)->public_input(public_input_index, row_index); + } + + const column_type& public_input(std::uint32_t index) const override { + return assignment_ptr->public_input(index); + } + + std::uint32_t public_inputs_amount() const override { + return assignment_ptr->public_inputs_amount(); + } + + std::uint32_t public_input_column_size(std::uint32_t index) const override { + return assignment_ptr->public_input_column_size(index); + } + + value_type &constant( + std::uint32_t constant_index, std::uint32_t row_index) override { + used_rows.insert(row_index); + return assignment_ptr->constant(constant_index, row_index); + } + + value_type constant(std::uint32_t constant_index, std::uint32_t row_index) const override { + if (check) { + const auto lookup_constant_cols = assignment_ptr->get_lookup_constant_cols(); + if (lookup_constant_cols.find(constant_index) == lookup_constant_cols.end() && + used_rows.find(row_index) == used_rows.end()) { + std::cout << id << ": Not found constant " << constant_index << " on row " << row_index << std::endl; + BLUEPRINT_ASSERT(false); + } + } + return std::const_pointer_cast>(assignment_ptr)->constant(constant_index, row_index); + } + + std::uint32_t constants_amount() const override { + return assignment_ptr->constants_amount(); + } + + std::uint32_t constant_column_size(std::uint32_t index) const override { + return assignment_ptr->constant_column_size(index); + } + + void fill_constant(std::uint32_t index, const column_type& column) override { + assignment_ptr->fill_constant(index, column); + } + + const column_type& constant(std::uint32_t index) const override { + return assignment_ptr->constant(index); + } + + const std::set& get_lookup_constant_cols() const override { + return assignment_ptr->get_lookup_constant_cols(); + } + + std::uint32_t get_lookup_constant_amount() const override { + return assignment_ptr->get_lookup_constant_amount(); + } + + void resize_witnesses(std::uint32_t new_size) override { + assignment_ptr->resize_witnesses(new_size); + } + + void resize_public_inputs(std::uint32_t new_size) override { + assignment_ptr->resize_public_inputs(new_size); + } + + void resize_constants(std::uint32_t new_size) override { + assignment_ptr->resize_constants(new_size); + } + + void resize_selectors(std::uint32_t new_size) override { + assignment_ptr->resize_selectors(new_size); + } + + value_type private_storage(std::uint32_t storage_index) const override { + return assignment_ptr->private_storage(storage_index); + } + + value_type &private_storage(std::uint32_t storage_index) override { + return assignment_ptr->private_storage(storage_index); + } + + // Not required to be called; get_private_storage will automatically resize + // But you might want to use this to clear + void resize_private_storage(std::uint32_t new_size) override { + assignment_ptr->resize_private_storage(new_size); + } + + void clear_private_storage() override { + assignment_ptr->clear_private_storage(); + } + + std::size_t private_storage_size() const override { + return assignment_ptr->private_storage_size(); + } + + void export_table(std::ostream& os, bool wide_export = false) const override { + std::ios_base::fmtflags os_flags(os.flags()); + + std::uint32_t witnesses_size = witnesses_amount(); + std::uint32_t shared_size = shareds_amount(); + std::uint32_t public_size = public_inputs_amount(); + std::uint32_t constants_size = constants_amount(); + std::uint32_t selectors_size = selectors_amount(); + + std::uint32_t max_size = 0, + max_witnesses_size = 0, + max_shared_size = 0, + max_public_inputs_size = 0, + max_constants_size = 0, + max_selectors_size = 0; + for (std::uint32_t i = 0; i < witnesses_size; i++) { + max_witnesses_size = std::max(max_witnesses_size, witness_column_size(i)); + } + for (std::uint32_t i = 0; i < shared_size; i++) { + max_shared_size = std::max(max_shared_size, shared_column_size(i)); + } + for (std::uint32_t i = 0; i < public_size; i++) { + max_public_inputs_size = std::max(max_public_inputs_size, public_input_column_size(i)); + } + for (std::uint32_t i = 0; i < constants_size; i++) { + max_constants_size = std::max(max_constants_size, constant_column_size(i)); + } + for (std::uint32_t i = 0; i < selectors_size; i++) { + max_selectors_size = std::max(max_selectors_size, selector_column_size(i)); + } + + max_size = std::max({max_witnesses_size, + max_public_inputs_size, + max_constants_size, + max_selectors_size}); + + os << "witnesses_size: " << witnesses_size << " " + << "shared_size: " << shared_size << " " + << "public_inputs_size: " << public_size << " " + << "constants_size: " << constants_size << " " + << "selectors_size: " << selectors_size << " " + << "max_size: " << max_size << " " + << "internal used rows size: " << used_rows.size() << "\n"; + + os << "internal used rows: "; + for (const auto& it : used_rows) { + os << it << " "; + } + os << "\n"; + + os << "internal used selector rows: "; + for (const auto& it : used_selector_rows) { + os << it << " "; + } + os << "\n"; + + os << "lookup constants: "; + for (const auto &it : assignment_ptr->get_lookup_constant_cols()) { + os << it << " "; + } + os << "\n"; + + os << "lookup selectors: "; + for (const auto &it : assignment_ptr->get_lookup_selector_cols()) { + os << it << " "; + } + os << "\n"; + + os << std::dec; + os << std::hex << std::setfill('0'); + std::uint32_t width = wide_export ? (BlueprintFieldType::modulus_bits + 4 - 1) / 4 : 0; + + for (std::uint32_t i = 0; i < max_size; i++) { + os << i << ": "; + const auto is_used_row = used_rows.find(i) != used_rows.end(); + for (std::uint32_t j = 0; j < witnesses_size; j++) { + os << std::setw(width) + << (i < assignment_ptr->witness_column_size(j) && is_used_row ? + assignment_ptr->witness(j, i) : 0).data << " "; + } + os << "| "; + for (std::uint32_t j = 0; j < shared_size; j++) { + os << std::setw(width) + << (i < shared_column_size(j) ? + shared(j, i) : 0).data << " "; + } + os << "| "; + for (std::uint32_t j = 0; j < public_size; j++) { + os << std::setw(width) + << (i < assignment_ptr->public_input_column_size(j) ? + assignment_ptr->public_input(j, i) : 0).data << " "; + } + os << "| "; + for (std::uint32_t j = 0; j < constants_size; j++) { + os << std::setw(width) + << (i < assignment_ptr->constant_column_size(j) ? + assignment_ptr->constant(j, i) : 0).data << " "; + } + os << "| "; + // Selectors only need a single bit, so we do not renew the size here + for (std::uint32_t j = 0; j < selectors_size - 1; j++) { + os << (i < assignment_ptr->selector_column_size(j) ? + assignment_ptr->selector(j, i) : 0).data << " "; + } + os << "\n"; + } + os.flush(); + os.flags(os_flags); + } + }; + + template + crypto3::zk::snark::plonk_variable save_shared_var( + assignment_proxy> &input_assignment, + const crypto3::zk::snark::plonk_variable &input_var) { + using var = crypto3::zk::snark::plonk_variable; + std::uint32_t row_index = input_assignment.shared_column_size(0); + auto res = var(1, row_index, false, var::column_type::public_input); + input_assignment.shared(0, row_index) = var_value(input_assignment, input_var); + return res; + } + + template + std::vector> save_shared_var( + assignment_proxy> &input_assignment, + const std::vector> &input_vars) { + std::vector> res; + for (const auto &it : input_vars) { + res.push_back(save_shared_var(input_assignment, it)); + } + return res; + } + + template + bool is_satisfied(const circuit_proxy> + &bp, + const assignment_proxy> + &assignments) { + + const auto& used_gates = bp.get_used_gates(); + + const auto& used_lookup_gates = bp.get_used_lookup_gates(); + + const auto& used_copy_constraints = bp.get_used_copy_constraints(); + + const auto& selector_rows = assignments.get_used_selector_rows(); + + return is_satisfied(bp, assignments, used_gates, used_lookup_gates, used_copy_constraints, selector_rows); + } + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_ASSIGNMENT_PROXY_PLONK_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit.hpp b/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit.hpp new file mode 100644 index 000000000..999bf9296 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit.hpp @@ -0,0 +1,277 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_CIRCUIT_PLONK_HPP +#define CRYPTO3_BLUEPRINT_CIRCUIT_PLONK_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + class circuit; + + template + class assignment; + + template + class circuit> + : public crypto3::zk::snark::plonk_constraint_system { + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + private: + using gate_id_type = gate_id; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using gate_selector_map = std::map; + using gate_type = crypto3::zk::snark::plonk_gate; + + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using lookup_gate_type = crypto3::zk::snark::plonk_lookup_gate; + using lookup_gate_id_type = lookup_gate_id; + using lookup_gate_selector_map = std::map; + + using lookup_table_definition = typename nil::crypto3::zk::snark::lookup_table_definition; + using dynamic_table_definition = typename nil::crypto3::zk::snark::dynamic_table_definition; + + gate_selector_map selector_map = {}; + lookup_gate_selector_map lookup_selector_map = {}; + std::size_t next_selector_index = 0; + protected: + lookup_library _lookup_library; + public: + typedef BlueprintFieldType blueprint_field_type; + + circuit(ArithmetizationType constraint_system) : + ArithmetizationType(constraint_system) { } + + circuit() : ArithmetizationType() {} + + virtual const typename ArithmetizationType::gates_container_type& gates() const { + return ArithmetizationType::gates(); + } + + virtual const typename ArithmetizationType::copy_constraints_container_type& copy_constraints() const { + return ArithmetizationType::copy_constraints(); + } + + virtual typename ArithmetizationType::copy_constraints_container_type& mutable_copy_constraints() { + return ArithmetizationType::mutable_copy_constraints(); + } + + virtual const typename ArithmetizationType::lookup_gates_container_type& lookup_gates() const { + return ArithmetizationType::lookup_gates(); + } + + virtual const typename ArithmetizationType::lookup_tables_type& lookup_tables() const { + return ArithmetizationType::lookup_tables(); + } + + virtual std::size_t num_gates() const { + return ArithmetizationType::num_gates(); + } + + virtual std::size_t num_lookup_gates() const { + return ArithmetizationType::num_lookup_gates(); + } + + #define GENERIC_GATE_ADDER_MACRO(mapping, gate_container) \ + auto it = mapping.find(gate_id); \ + if (it != mapping.end()) { \ + return it->second; \ + } else { \ + const std::size_t selector_index = next_selector_index; \ + mapping[gate_id] = selector_index; \ + this->gate_container.emplace_back(selector_index, args); \ + next_selector_index++; \ + return selector_index; \ + } + + #define GATE_ADDER_MACRO(mapping, gate_container) \ + auto gate_id = gate_id_type(args); \ + GENERIC_GATE_ADDER_MACRO(mapping, gate_container) + + #define LOOKUP_GATE_ADDER_MACRO(mapping, gate_container) \ + auto gate_id = lookup_gate_id_type(args); \ + GENERIC_GATE_ADDER_MACRO(mapping, gate_container) + + virtual std::size_t add_gate(const std::vector &args) { + GATE_ADDER_MACRO(selector_map, _gates); + } + + virtual std::size_t add_gate(const constraint_type &args) { + GATE_ADDER_MACRO(selector_map, _gates); + } + + virtual std::size_t add_gate(const std::initializer_list &&args) { + GATE_ADDER_MACRO(selector_map, _gates); + } + + virtual std::size_t add_lookup_gate(const std::vector &args) { + LOOKUP_GATE_ADDER_MACRO(lookup_selector_map, _lookup_gates); + } + + virtual std::size_t add_lookup_gate(const lookup_constraint_type &args) { + LOOKUP_GATE_ADDER_MACRO(lookup_selector_map, _lookup_gates); + } + + virtual std::size_t add_lookup_gate(const std::initializer_list &&args) { + LOOKUP_GATE_ADDER_MACRO(lookup_selector_map, _lookup_gates); + } + + + // Sometimes we want to connect new constraints to existing selector + // Use only with deep understanding + virtual std::size_t add_gate(std::size_t selector_id, const std::vector &args) { + this->_gates.push_back({selector_id, args}); + return selector_id; + } + + // Sometimes existing gate is already on existing selector + // and we are sure that lookup and usual part are always together + virtual std::size_t add_lookup_gate(std::size_t selector_id, const std::vector &args) { + this->_lookup_gates.push_back({selector_id, args}); + return selector_id; + } + + virtual const typename ArithmetizationType::lookup_table_type &lookup_table(std::size_t table_id) const { + return ArithmetizationType::lookup_table(table_id); + } + + // Each component that creates dynamic lookup needs separate selector for it + virtual std::size_t get_dynamic_lookup_table_selector(){ + const std::size_t selector_index = next_selector_index; + next_selector_index++; + return selector_index; + } + + virtual void add_lookup_table(const typename ArithmetizationType::lookup_table_type &table) { + ArithmetizationType::add_lookup_table(table); + } + + virtual void register_lookup_table(std::shared_ptr table) { + _lookup_library.register_lookup_table(table); + } + + virtual void register_dynamic_table(std::string name) { + _lookup_library.register_dynamic_table(name); + } + + virtual void reserve_table(std::string name){ + _lookup_library.reserve_table(name); + } + + virtual void reserve_dynamic_table(std::string name){ + _lookup_library.reserve_dynamic_table(name); + } + + std::shared_ptr> get_dynamic_table_definition(std::string name){ + return _lookup_library.get_dynamic_table_definition(name); + } + + virtual void define_dynamic_table(std::string name, const crypto3::zk::snark::plonk_lookup_table &table){ + _lookup_library.define_dynamic_table(name, table); + } + + virtual const typename lookup_library::left_reserved_type + &get_reserved_indices() const { + return _lookup_library.get_reserved_indices().left; + } + + // used in satisfiability check + virtual const typename lookup_library::right_reserved_type + &get_reserved_indices_right() const { + return _lookup_library.get_reserved_indices().right; + } + + virtual const std::map> &get_reserved_tables() const { + return _lookup_library.get_reserved_tables(); + } + + virtual const std::map> &get_reserved_dynamic_tables() const { + return _lookup_library.get_reserved_dynamic_tables(); + } + + #undef GATE_ADDER_MACRO + #undef LOOKUP_GATE_ADDER_MACRO + #undef GENERIC_GATE_ADDER_MACRO + + virtual void add_copy_constraint(const crypto3::zk::snark::plonk_copy_constraint ©_constraint) { + static const std::size_t private_storage_index = + assignment>::private_storage_index; + if (copy_constraint.first == copy_constraint.second) { + return; + } + if (copy_constraint.first.index == private_storage_index || + copy_constraint.second.index == private_storage_index) { + return; + } + this->_copy_constraints.emplace_back(copy_constraint); + } + + virtual std::size_t get_next_selector_index() const { + return next_selector_index; + } + + virtual void export_circuit(std::ostream& os) const { + std::ios_base::fmtflags os_flags(os.flags()); + std::size_t gates_size = this->_gates.size(), + copy_constraints_size = this->_copy_constraints.size(), + lookup_gates_size = this->_lookup_gates.size(); + os << "gates_size: " << gates_size << " " + << "copy_constraints_size: " << copy_constraints_size << " " + << "lookup_gates_size: " << lookup_gates_size << "\n"; + for (std::size_t i = 0; i < gates_size; i++) { + os << "selector: " << this->_gates[i].selector_index + << " constraints_size: " << this->_gates[i].constraints.size() << "\n"; + for (std::size_t j = 0; j < this->_gates[i].constraints.size(); j++) { + os << this->_gates[i].constraints[j] << "\n"; + } + } + for (std::size_t i = 0; i < copy_constraints_size; i++) { + os << this->_copy_constraints[i].first << " " + << this->_copy_constraints[i].second << "\n"; + } + os.flush(); + os.flags(os_flags); + } + }; + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_CIRCUIT_PLONK_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit_proxy.hpp b/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit_proxy.hpp new file mode 100644 index 000000000..5373578b1 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/plonk/circuit_proxy.hpp @@ -0,0 +1,279 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Kokoshnikov Aleksei +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_CIRCUIT_PROXY_PLONK_HPP +#define CRYPTO3_BLUEPRINT_CIRCUIT_PROXY_PLONK_HPP + +#include + +namespace nil { + namespace blueprint { + + template + class circuit_proxy; + + template + class assignment; + + template + class circuit_proxy> + : public circuit> { + + private: + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using lookup_table_definition = typename nil::crypto3::zk::snark::lookup_table_definition; + + std::shared_ptr> circuit_ptr; + std::uint32_t id; + std::set used_gates; + std::set used_copy_constraints; + std::set used_lookup_gates; + std::set used_lookup_tables; + + std::uint32_t get_gate_index(std::size_t selector_index) const { + const auto& gates = circuit_ptr->gates(); + auto it = std::find_if(gates.begin(), gates.end(), [selector_index](const typename ArithmetizationType::gates_container_type::value_type& gate) -> bool + { return selector_index == gate.selector_index; }); + return it - gates.begin(); + } + + std::uint32_t get_lookup_gate_index(std::size_t selector_index) const { + const auto& lookup_gates = circuit_ptr->lookup_gates(); + auto it = std::find_if(lookup_gates.begin(), lookup_gates.end(), + [selector_index](const typename ArithmetizationType::lookup_gates_container_type::value_type& lookup_gate) -> bool + { return selector_index == lookup_gate.tag_index; }); + return it - lookup_gates.begin(); + } + + public: + + circuit_proxy(std::shared_ptr> circuit, std::uint32_t _id) : + circuit_ptr(circuit), + id(_id) {} + + circuit_proxy() = delete; + + const circuit& get() const { + return *circuit_ptr; + } + + circuit& get() { + return *circuit_ptr; + } + + std::uint32_t get_id() const { + return id; + } + + const std::set& get_used_gates() const { + return used_gates; + } + + const typename ArithmetizationType::gates_container_type& gates() const override { + return circuit_ptr->gates(); + } + + const std::set& get_used_copy_constraints() const { + return used_copy_constraints; + } + + const typename ArithmetizationType::copy_constraints_container_type& copy_constraints() const override { + return circuit_ptr->copy_constraints(); + } + + virtual typename ArithmetizationType::copy_constraints_container_type& mutable_copy_constraints() override { + return circuit_ptr->mutable_copy_constraints(); + } + + const std::set& get_used_lookup_gates() const { + return used_lookup_gates; + } + + const typename ArithmetizationType::lookup_gates_container_type& lookup_gates() const override { + return circuit_ptr->lookup_gates(); + } + + const std::set& get_used_lookup_tables() const { + return used_lookup_tables; + } + + const typename ArithmetizationType::lookup_tables_type& lookup_tables() const override { + return circuit_ptr->lookup_tables(); + } + + std::size_t num_gates() const override { + return circuit_ptr->num_gates(); + } + + std::size_t num_lookup_gates() const override { + return circuit_ptr->num_lookup_gates(); + } + + std::size_t add_gate(const std::vector &args) override { + const auto selector_index = circuit_ptr->add_gate(args); + used_gates.insert(get_gate_index(selector_index)); + return selector_index; + } + + std::size_t add_gate(const constraint_type &args) override { + const auto selector_index = circuit_ptr->add_gate(args); + used_gates.insert(get_gate_index(selector_index)); + return selector_index; + } + + std::size_t add_gate(const std::initializer_list &&args) override { + const auto selector_index = circuit_ptr->add_gate(args); + used_gates.insert(get_gate_index(selector_index)); + return selector_index; + } + + std::size_t add_lookup_gate(const std::vector &args) override { + const auto selector_index = circuit_ptr->add_lookup_gate(args); + used_lookup_gates.insert(get_lookup_gate_index(selector_index)); + return selector_index; + } + + std::size_t add_lookup_gate(const lookup_constraint_type &args) override { + const auto selector_index = circuit_ptr->add_lookup_gate(args); + used_lookup_gates.insert(get_lookup_gate_index(selector_index)); + return selector_index; + } + + std::size_t add_lookup_gate(const std::initializer_list &&args) override { + const auto selector_index = circuit_ptr->add_lookup_gate(args); + used_lookup_gates.insert(get_lookup_gate_index(selector_index)); + return selector_index; + } + + const typename ArithmetizationType::lookup_table_type &lookup_table(std::size_t table_id) const override { + return circuit_ptr->lookup_table(table_id); + } + + void add_lookup_table(const typename ArithmetizationType::lookup_table_type &table) override { + circuit_ptr->add_lookup_table(table); + } + + void register_lookup_table(std::shared_ptr table) override { + circuit_ptr->register_lookup_table(table); + } + + void reserve_table(std::string name) override { + circuit_ptr->reserve_table(name); + const auto idx = circuit_ptr->get_reserved_indices().at(name) - 1; + used_lookup_tables.insert(idx); + } + + const typename lookup_library::left_reserved_type + &get_reserved_indices() const override { + return circuit_ptr->get_reserved_indices(); + } + + const typename lookup_library::right_reserved_type & + get_reserved_indices_right() const override { + return circuit_ptr->get_reserved_indices_right(); + } + + const std::map> &get_reserved_tables() const override { + return circuit_ptr->get_reserved_tables(); + } + + void add_copy_constraint(const crypto3::zk::snark::plonk_copy_constraint ©_constraint) override { + circuit_ptr->add_copy_constraint(copy_constraint); + if (circuit_ptr->copy_constraints().size() > 0) { + used_copy_constraints.insert(circuit_ptr->copy_constraints().size() - 1); + } + } + + std::size_t get_next_selector_index() const override { + return circuit_ptr->get_next_selector_index(); + } + + void export_circuit(std::ostream& os) const override { + std::ios_base::fmtflags os_flags(os.flags()); + + const auto gates = circuit_ptr->gates(); + const auto copy_constraints = circuit_ptr->copy_constraints(); + const auto lookup_gates = circuit_ptr->lookup_gates(); + const auto lookup_tables = circuit_ptr->lookup_tables(); + const auto lookup_table_indexes = circuit_ptr->get_reserved_indices(); + + os << "used_gates_size: " << used_gates.size() << " " + << "gates_size: " << gates.size() << " " + << "used_copy_constraints_size: " << used_copy_constraints.size() << " " + << "copy_constraints_size: " << copy_constraints.size() << " " + << "lookup_gates_size: " << used_lookup_gates.size() << "\n"; + + for (const auto& i : used_gates) { + os << i << ": selector: " << gates[i].selector_index + << " constraints_size: " << gates[i].constraints.size() << "\n"; + for (std::size_t j = 0; j < gates[i].constraints.size(); j++) { + os << gates[i].constraints[j] << "\n"; + } + } + + for (const auto& i : used_copy_constraints) { + os << i << ": " << copy_constraints[i].first << " " + << copy_constraints[i].second << "\n"; + } + + std::cout << "\nlookup gates:\n"; + for (const auto& i : used_lookup_gates) { + os << i << ": selector: " << lookup_gates[i].tag_index + << " lookup constraints size: " << lookup_gates[i].constraints.size() << "\n"; + for (std::size_t j = 0; j < lookup_gates[i].constraints.size(); j++) { + os << "constraints size: " << lookup_gates[i].constraints[j].lookup_input.size() << "\n"; + os << "table index: " << lookup_gates[i].constraints[j].table_id << "\n"; + for (std::size_t k = 0; k < lookup_gates[i].constraints[j].lookup_input.size(); k++) { + os << lookup_gates[i].constraints[j].lookup_input[k] << "\n"; + } + std::cout << "\n"; + } + std::cout << "\n"; + } + + std::cout << "\nlookup tables:\n"; + for (const auto& i : used_lookup_tables) { + bool found = false; + for (const auto& it : lookup_table_indexes) { + if (it.second == (i + 1)) { + os << i << ": " << it.first << "\n"; + found = true; + break; + } + } + if (!found) { + os << i << ": not found\n"; + } + } + + os.flush(); + os.flags(os_flags); + } + }; + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_CIRCUIT_PROXY_PLONK_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/r1cs/circuit.hpp b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/circuit.hpp new file mode 100644 index 000000000..df959e60c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/circuit.hpp @@ -0,0 +1,170 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Noam Yemini <@NoamDev at GitHub> +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BLUEPRINT_R1CS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BLUEPRINT_R1CS_HPP + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + + template + class blueprint; + + template + class blueprint> { + typedef zk::snark::r1cs_constraint_system ArithmetizationType; + + zk::snark::r1cs_variable_assignment> + values; /* values[0] will hold the value of the first + allocated variable of the blueprint, *NOT* constant 1 */ + typename BlueprintFieldType::value_type constant_term; + + typename math::linear_variable::index_type next_free_var; + typename detail::blueprint_linear_combination::index_type next_free_lc; + std::vector lc_values; + zk::snark::r1cs_constraint_system constraint_system; + + public: + // typedef BlueprintFieldType field_type; + + using value_type = detail::blueprint_variable; + + blueprint() { + constant_term = BlueprintFieldType::value_type::one(); + + next_free_var = 1; /* to account for constant 1 term */ + next_free_lc = 0; + } + + void clear_values() { + std::fill(values.begin(), values.end(), BlueprintFieldType::value_type::zero()); + } + + typename BlueprintFieldType::value_type &val(const value_type &var) { + assert(var.index <= values.size()); + return (var.index == 0 ? constant_term : values[var.index - 1]); + } + + typename BlueprintFieldType::value_type val(const value_type &var) const { + assert(var.index <= values.size()); + return (var.index == 0 ? constant_term : values[var.index - 1]); + } + + typename BlueprintFieldType::value_type & + lc_val(const detail::blueprint_linear_combination &lc) { + if (lc.is_variable) { + return this->val(value_type(lc.index)); + } else { + assert(lc.index < lc_values.size()); + return lc_values[lc.index]; + } + } + + typename BlueprintFieldType::value_type + lc_val(const detail::blueprint_linear_combination &lc) const { + if (lc.is_variable) { + return this->val(value_type(lc.index)); + } else { + assert(lc.index < lc_values.size()); + return lc_values[lc.index]; + } + } + + void add_r1cs_constraint(const zk::snark::r1cs_constraint &constr) { + constraint_system.constraints.emplace_back(constr); + } + + bool is_satisfied() const { + return constraint_system.is_satisfied(primary_input(), auxiliary_input()); + } + + std::size_t num_constraints() const { + return constraint_system.num_constraints(); + } + + std::size_t num_inputs() const { + return constraint_system.num_inputs(); + } + + std::size_t num_variables() const { + return next_free_var - 1; + } + + void set_input_sizes(const std::size_t primary_input_size) { + assert(primary_input_size <= num_variables()); + constraint_system.primary_input_size = primary_input_size; + constraint_system.auxiliary_input_size = num_variables() - primary_input_size; + } + + zk::snark::r1cs_variable_assignment> full_variable_assignment() const { + return values; + } + + zk::snark::r1cs_primary_input primary_input() const { + return zk::snark::r1cs_primary_input(values.begin(), + values.begin() + num_inputs()); + } + + zk::snark::r1cs_auxiliary_input auxiliary_input() const { + return zk::snark::r1cs_auxiliary_input(values.begin() + num_inputs(), + values.end()); + } + + zk::snark::r1cs_constraint_system get_constraint_system() const { + return constraint_system; + } + + friend class detail::blueprint_variable; + friend class detail::blueprint_linear_combination; + + private: + typename math::linear_variable::index_type allocate_var_index() { + ++constraint_system.auxiliary_input_size; + values.emplace_back(BlueprintFieldType::value_type::zero()); + return next_free_var++; + } + + typename detail::blueprint_linear_combination::index_type allocate_lc_index() { + lc_values.emplace_back(BlueprintFieldType::value_type::zero()); + return next_free_lc++; + } + }; + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BLUEPRINT_R1CS_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_linear_combination.hpp b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_linear_combination.hpp new file mode 100644 index 000000000..ec9060d76 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_linear_combination.hpp @@ -0,0 +1,285 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Noam Yemini <@NoamDev at GitHub> +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_LINEAR_COMBINATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_LINEAR_COMBINATION_HPP + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + + template + class blueprint; + + namespace detail { + + template + class blueprint_linear_combination; + + template + class blueprint_linear_combination> + : public math::linear_combination { + + typedef zk::snark::r1cs_constraint_system ArithmetizationType; + typedef BlueprintFieldType field_type; + typedef typename field_type::value_type field_value_type; + + public: + using index_type = std::size_t; + bool is_variable; + index_type index; + + blueprint_linear_combination() { + this->is_variable = false; + } + + blueprint_linear_combination(const blueprint_variable &var) { + this->is_variable = true; + this->index = var.index; + this->terms.emplace_back(math::linear_term(var)); + } + + void assign(blueprint &bp, const math::linear_combination &lc) { + assert(this->is_variable == false); + this->index = bp.allocate_lc_index(); + this->terms = lc.terms; + } + + void evaluate(blueprint &bp) const { + if (this->is_variable) { + return; // do nothing + } + + field_value_type sum = 0; + for (auto term : this->terms) { + sum += term.coeff * bp.val(blueprint_variable(term.index)); + } + + bp.lc_val(*this) = sum; + } + + bool is_constant() const { + if (is_variable) { + return (index == 0); + } else { + for (auto term : this->terms) { + if (term.index != 0) { + return false; + } + } + + return true; + } + } + + field_value_type constant_term() const { + if (is_variable) { + return (index == 0 ? field_value_type::one() : field_value_type::zero()); + } else { + field_value_type result = field_value_type::zero(); + for (auto term : this->terms) { + if (term.index == 0) { + result += term.coeff; + } + } + return result; + } + } + }; + + template + class blueprint_linear_combination_vector; + + template + class blueprint_linear_combination_vector< + crypto3::zk::snark::r1cs_constraint_system> + : private std::vector>> { + + typedef zk::snark::r1cs_constraint_system ArithmetizationType; + typedef typename BlueprintFieldType::value_type field_value_type; + typedef std::vector> contents; + + public: + using typename contents::const_iterator; + using typename contents::const_reverse_iterator; + using typename contents::iterator; + using typename contents::reverse_iterator; + + using contents::begin; + using contents::emplace_back; + using contents::empty; + using contents::end; + using contents::insert; + using contents::rbegin; + using contents::rend; + using contents::reserve; + using contents::size; + using contents::operator[]; + using contents::resize; + + blueprint_linear_combination_vector() : contents() {}; + blueprint_linear_combination_vector(const blueprint_variable_vector &arr) { + for (auto &v : arr) + this->emplace_back(blueprint_linear_combination(v)); + }; + blueprint_linear_combination_vector(std::size_t count) : contents(count) {}; + blueprint_linear_combination_vector( + std::size_t count, + const blueprint_linear_combination &value) : + contents(count, value) {}; + blueprint_linear_combination_vector(typename contents::const_iterator first, + typename contents::const_iterator last) : + contents(first, last) {}; + blueprint_linear_combination_vector(typename contents::const_reverse_iterator first, + typename contents::const_reverse_iterator last) : + contents(first, last) {}; + + void evaluate(blueprint &bp) const { + for (std::size_t i = 0; i < this->size(); ++i) { + (*this)[i].evaluate(bp); + } + } + + void fill_with_field_elements(blueprint &bp, + const std::vector &vals) const { + assert(this->size() == vals.size()); + for (std::size_t i = 0; i < vals.size(); ++i) { + bp.lc_val((*this)[i]) = vals[i]; + } + } + + void fill_with_bits(blueprint &bp, const std::vector &bits) const { + assert(this->size() == bits.size()); + for (std::size_t i = 0; i < bits.size(); ++i) { + bp.lc_val((*this)[i]) = (bits[i] ? field_value_type::one() : field_value_type::zero()); + } + } + + void fill_with_bits_of_ulong(blueprint &bp, const unsigned long i) const { + this->fill_with_bits_of_field_element(bp, field_value_type(i)); + } + + void fill_with_bits_of_field_element(blueprint &bp, + const field_value_type &r) const { + for (std::size_t i = 0; i < this->size(); ++i) { + bp.lc_val((*this)[i]) = boost::multiprecision::bit_test(r.data, i) ? field_value_type::one() : + field_value_type::zero(); + } + } + + std::vector get_vals(const blueprint &bp) const { + std::vector result(this->size()); + for (std::size_t i = 0; i < this->size(); ++i) { + result[i] = bp.lc_val((*this)[i]); + } + return result; + } + + std::vector get_bits(const blueprint &bp) const { + std::vector result; + for (std::size_t i = 0; i < this->size(); ++i) { + const field_value_type v = bp.lc_val((*this)[i]); + assert(v.is_zero() || v.is_one()); + result.push_back(v.is_one()); + } + return result; + } + + field_value_type get_field_element_from_bits(const blueprint &bp) const { + field_value_type result = field_value_type::zero(); + + for (std::size_t i = 0; i < this->size(); ++i) { + /* push in the new bit */ + const field_value_type v = bp.lc_val((*this)[this->size() - 1 - i]); + assert(v.is_zero() || v.is_one()); + result += result + v; + } + + return result; + } + }; + + template + math::linear_combination + blueprint_sum(const blueprint_linear_combination_vector &v) { + + math::linear_combination result; + for (auto &term : v) { + result = result + term; + } + + return result; + } + + template + math::linear_combination + blueprint_packing_sum(const blueprint_linear_combination_vector &v) { + + typename FieldType::value_type twoi = + FieldType::value_type::one(); // will hold 2^i entering each iteration + std::vector> all_terms; + for (auto &lc : v) { + for (auto &term : lc.terms) { + all_terms.emplace_back(twoi * term); + } + twoi += twoi; + } + + return math::linear_combination(all_terms); + } + + template + math::linear_combination + blueprint_coeff_sum(const blueprint_linear_combination_vector &v, + const std::vector &coeffs) { + + assert(v.size() == coeffs.size()); + std::vector> all_terms; + + auto coeff_it = coeffs.begin(); + for (auto &lc : v) { + for (auto &term : lc.terms) { + all_terms.emplace_back((*coeff_it) * term); + } + ++coeff_it; + } + + return math::linear_combination(all_terms); + } + } // namespace detail + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_LINEAR_COMBINATION_HPP diff --git a/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_variable.hpp b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_variable.hpp new file mode 100644 index 000000000..51f4c050d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/blueprint/r1cs/detail/r1cs/blueprint_variable.hpp @@ -0,0 +1,193 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Noam Yemini <@NoamDev at GitHub> +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_HPP + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + + template + class blueprint; + + namespace detail { + + template + class blueprint_variable; + + // template class blueprint_variable; + + template + class blueprint_variable> + : public math::linear_variable { + public: + blueprint_variable(const typename math::linear_variable::index_type index = 0) : + math::linear_variable(index) {}; + + template + void allocate(blueprint &bp) { + this->index = bp.allocate_var_index(); + } + + static blueprint_variable constant() { + return blueprint_variable(0); + } + }; + + template + class blueprint_variable_vector; + + template + class blueprint_variable_vector> : + private std::vector>> { + + typedef zk::snark::r1cs_constraint_system ArithmetizationType; + typedef typename BlueprintFieldType::value_type field_value_type; + typedef std::vector> contents; + + public: + using typename contents::const_iterator; + using typename contents::const_reverse_iterator; + using typename contents::iterator; + using typename contents::reverse_iterator; + + using contents::begin; + using contents::emplace_back; + using contents::empty; + using contents::end; + using contents::erase; + using contents::insert; + using contents::rbegin; + using contents::rend; + using contents::reserve; + using contents::size; + using contents::operator[]; + using contents::resize; + + blueprint_variable_vector() : contents() {}; + blueprint_variable_vector(std::size_t count, const blueprint_variable &value) : + contents(count, value) {}; + blueprint_variable_vector(typename contents::const_iterator first, + typename contents::const_iterator last) : + contents(first, last) {}; + blueprint_variable_vector(typename contents::const_reverse_iterator first, + typename contents::const_reverse_iterator last) : + contents(first, last) {}; + + /* allocates blueprint_variable vector in MSB->LSB order */ + void allocate(blueprint &bp, const std::size_t n) { + (*this).resize(n); + + for (std::size_t i = 0; i < n; ++i) { + (*this)[i].allocate(bp); + } + } + + void fill_with_field_elements(blueprint &bp, + const std::vector &vals) const { + assert(this->size() == vals.size()); + for (std::size_t i = 0; i < vals.size(); ++i) { + bp.val((*this)[i]) = vals[i]; + } + } + + template + typename std::enable_if::value_type>::value>::type + fill_with_bits(blueprint &bp, const InputRange &bits) const { + BOOST_RANGE_CONCEPT_ASSERT((boost::RandomAccessRangeConcept)); + assert(this->size() == bits.size()); + for (std::size_t i = 0; i < bits.size(); ++i) { + bp.val((*this)[i]) = (bits[i] ? field_value_type::one() : field_value_type::zero()); + } + } + + void fill_with_bits_of_ulong(blueprint &bp, const unsigned long i) const { + this->fill_with_bits_of_field_element(bp, field_value_type(i)); + } + + void fill_with_bits_of_field_element(blueprint &bp, + const field_value_type &r) const { + for (std::size_t i = 0; i < this->size(); ++i) { + bp.val((*this)[i]) = boost::multiprecision::bit_test(r.data, i) ? + field_value_type::one() : + field_value_type::zero(); + } + } + + std::vector values(const blueprint &bp) const { + std::vector result(this->size()); + for (std::size_t i = 0; i < this->size(); ++i) { + result[i] = bp.val((*this)[i]); + } + return result; + } + + std::vector bits(const blueprint &bp) const { + std::vector result; + for (std::size_t i = 0; i < this->size(); ++i) { + const field_value_type v = bp.val((*this)[i]); + assert(v.is_zero() || v.is_one()); + result.push_back(v.is_one()); + } + return result; + } + + field_value_type field_element_from_bits(const blueprint &bp) const { + field_value_type result = field_value_type::zero(); + + for (std::size_t i = 0; i < this->size(); ++i) { + /* push in the new bit */ + const field_value_type v = bp.val((*this)[this->size() - 1 - i]); + assert(v.is_zero() || v.is_one()); + result = result + (result + v); + } + + return result; + } + }; + } // namespace detail + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_HPP diff --git a/libs/blueprint/include/nil/blueprint/chips/plonk/bit_check.hpp b/libs/blueprint/include/nil/blueprint/chips/plonk/bit_check.hpp new file mode 100644 index 000000000..27df14f71 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/chips/plonk/bit_check.hpp @@ -0,0 +1,69 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BIT_CHECK_CHIP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BIT_CHECK_CHIP_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace chips { + + template + class bit_check; + + template + class bit_check< + snark::plonk_constraint_system>{ + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + public: + constexpr static const std::size_t rows_amount = 0; + + static snark::plonk_constraint generate( + blueprint &bp, + const var& X1) { + + return bp.add_constraint(X1 * (X1 - 1)); + } + }; + } // namespace chips + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BIT_CHECK_CHIP_HPP diff --git a/libs/blueprint/include/nil/blueprint/chips/plonk/incomplete_addition.hpp b/libs/blueprint/include/nil/blueprint/chips/plonk/incomplete_addition.hpp new file mode 100644 index 000000000..cf03b5fe5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/chips/plonk/incomplete_addition.hpp @@ -0,0 +1,74 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_INCOMPLETE_ADDITION_CHIP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_INCOMPLETE_ADDITION_CHIP_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace chips { + + template + class incomplete_addition; + + template + class incomplete_addition< + snark::plonk_constraint_system, + CurveType>{ + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + public: + constexpr static const std::size_t rows_amount = 0; + + static snark::plonk_constraint generate( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const var& X1, const var& Y1, const var& X2, const var& Y2, + const var& X3, const var& Y3) { + + return bp.add_constraint(); + } + }; + } // namespace chips + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_INCOMPLETE_ADDITION_CHIP_HPP diff --git a/libs/blueprint/include/nil/blueprint/component.hpp b/libs/blueprint/include/nil/blueprint/component.hpp new file mode 100644 index 000000000..d9ac76f72 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/component.hpp @@ -0,0 +1,143 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2022 Mikhail Komarov +// Copyright (c) 2020-2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENT_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + class blueprint; + + namespace components { + + template + class component{}; + + template + class plonk_component: + public component> { + public: + using manifest_type = nil::blueprint::plonk_component_manifest; + using witness_container_type = std::vector; + using constant_container_type = std::vector; + using public_input_container_type = std::vector; + + witness_container_type _W; + constant_container_type _C; + public_input_container_type _PI; + + using var = crypto3::zk::snark::plonk_variable; + + /** + * Get Witness column global index by its internal index. + * + * @param[in] internal witness signed index. For -1, last witness assumed. + */ + typename witness_container_type::value_type W(std::int32_t index) const { + return _W[(_W.size() + index) % _W.size()]; + } + + /** + * Get Constant column global index by its internal index. + * + * @param[in] internal constant signed index. For -1, last constant assumed. + */ + typename constant_container_type::value_type C(std::int32_t index) const { + return _C[(_C.size() + index) % _C.size()]; + } + + /** + * Get Public Input column global index by its internal index. + * + * @param[in] internal public input signed index. For -1, last public input assumed. + */ + typename public_input_container_type::value_type PI(std::int32_t index) const { + return _PI[(_PI.size() + index) % _PI.size()]; + } + + /** + * Constructor from arbitrary container types. + * + * @tparam WitnessContainerType Input Witness Container Type + * @tparam ConstantContainerType Input Constant Container Type + * @tparam PublicInputContainerType Input PublicInput Container Type + * @param[in] witness Container with witness columns global indices. + * @param[in] constant Container with constant columns global indices. + * @param[in] public_input Container with public input columns global indices. + */ + template + plonk_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, const manifest_type &manifest) { + _W.resize(witness.size()); + _C.resize(constant.size()); + _PI.resize(public_input.size()); + std::copy_n(std::make_move_iterator(witness.begin()), witness.size(), _W.begin()); + std::copy_n(std::make_move_iterator(constant.begin()), constant.size(), _C.begin()); + std::copy_n(std::make_move_iterator(public_input.begin()), public_input.size(), _PI.begin()); + + BLUEPRINT_RELEASE_ASSERT(manifest.check_manifest(*this)); + } + + std::size_t witness_amount() const { + return _W.size(); + } + + std::size_t constant_amount() const { + return _C.size(); + } + + std::size_t public_input_amount() const { + return _PI.size(); + } + }; + + template + class r1cs_component: + public component> { + protected: + typedef crypto3::zk::snark::r1cs_constraint_system + ArithmetizationType; + + blueprint &bp; + + public: + r1cs_component(blueprint &bp) : bp(bp) { + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/component_batch.hpp b/libs/blueprint/include/nil/blueprint/component_batch.hpp new file mode 100644 index 000000000..c55debdcb --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/component_batch.hpp @@ -0,0 +1,438 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace detail { + template + struct comparison_for_inputs_results { + bool operator()(const InputType &lhs, const InputType &rhs) const { + // all_vars is not const, so we const_cast here + // we do not modify anything I swear + // copying into non-const is inefficient, and this would get called a ton for large batch sizes + InputType &lhs_input = const_cast(lhs); + InputType &rhs_input = const_cast(rhs); + auto lhs_vars = lhs_input.all_vars(); + auto rhs_vars = rhs_input.all_vars(); + auto result = std::lexicographical_compare( + lhs_vars.begin(), lhs_vars.end(), + rhs_vars.begin(), rhs_vars.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.get() < rhs.get(); + } + ); + return result; + } + }; + + template + ComponentType component_builder( + WitnessContainerType witnesses, + ConstantContainerType constants, + PublicInputContainerType public_inputs, + const std::tuple ¶ms) { + + auto construct = [&witnesses, &constants, &public_inputs](auto... args) { + return ComponentType( + std::forward(witnesses), std::forward(constants), + std::forward(public_inputs), + std::forward(args)...); + }; + + return std::apply(construct, params); + } + } // namespace detail + + using detail::comparison_for_inputs_results; + + struct _batch; + + template + class assignment; + + template + class circuit; + + template + struct input_type_v { + typedef typename ComponentType::input_type type; + }; + + template<> + struct input_type_v<_batch> { + typedef typename boost::mpl::identity type; + }; + + template + struct result_type_v { + typedef typename ComponentType::result_type type; + }; + + template<> + struct result_type_v<_batch> { + typedef typename boost::mpl::identity type; + }; + + template + struct component_params_type_v { + typedef typename ComponentType::component_params_type type; + }; + + template<> + struct component_params_type_v<_batch> { + typedef typename boost::mpl::identity type; + }; + + template + struct has_add_input { + static ResultType apply(BatchType& batch, const InputType& input) { + return batch.add_input(input); + } + }; + + template + struct has_finalize_batch { + static std::size_t apply(BatchType& batch, nil::blueprint::circuit &bp, + std::unordered_map &variable_map, + const std::uint32_t start_row_index) { + return batch.finalize_batch(bp, variable_map, start_row_index); + } + }; + + template + struct has_name { + static std::string apply(const BatchType& batch) { + return batch.name(); + } + }; + + // Generic-ish enough batching solution for single-line components + // Lookups currently unsupported + // Partially supports component prarameterization -- only if passed through template parameters + template + class component_batch { + public: + using input_type = typename ComponentType::input_type; + using result_type = typename ComponentType::result_type; + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using gate_type = crypto3::zk::snark::plonk_gate; + using component_params_type = typename std::tuple; + // input-output pairs for batched components + // the second bool determines whether the result has been actually filled; is left unfilled if called + // from generate_circuit + std::map, comparison_for_inputs_results> + inputs_results; + // pointer to the assignment we are going to use in the end + assignment &parent_assignment; + // we cache this; use this to store intermediate results + assignment internal_assignment; + // stroing the component parameters + std::tuple params_tuple; + + std::function, std::vector, std::vector, + const std::tuple&)> + component_builder = detail::component_builder< + ComponentType, + std::vector, std::vector, std::vector, + ComponentParams...>; + + component_batch(assignment &_assignment, + component_params_type params) + : parent_assignment(_assignment), + internal_assignment(_assignment.witnesses_amount(), 1, 0, 0), + params_tuple(params) + {} + + ~component_batch() = default; + + std::string name() const { + std::string result = typeid(ComponentType).name(); + std::apply([&result](auto... args) { + ((result += "__" + std::to_string(args)), ...); + }, params_tuple); + return result; + } + + void variable_transform(std::reference_wrapper variable) { + variable.get() = parent_assignment.add_batch_variable( + var_value(internal_assignment, variable.get())); + } + + ComponentType build_component_instance(const std::size_t component_witness_amount, + const std::size_t start_value = 0) const { + const std::vector constants = {}, public_inputs = {}; + std::vector witness_columns(component_witness_amount); + std::iota(witness_columns.begin(), witness_columns.end(), start_value); + return std::apply(component_builder, std::make_tuple(witness_columns, constants, public_inputs, params_tuple)); + } + + std::size_t get_component_witness_amount() const { + const compiler_manifest assignment_manifest(parent_assignment.witnesses_amount(), false); + const auto component_manifest = std::apply(ComponentType::get_manifest, params_tuple); + const auto intersection = assignment_manifest.intersect(component_manifest); + BOOST_ASSERT_MSG(intersection.is_satisfiable(), "Component either has a constant or does not fit"); + const std::size_t component_witness_amount = intersection.witness_amount->max_value_if_sat(); + return component_witness_amount; + } + + // call this in both generate_assignments and generate_circuit + result_type add_input(const input_type &input, bool called_from_generate_circuit = false) { + // short-circuit if the input has already been through batching + bool unassigned_result_found = false; + if (inputs_results.find(input) != inputs_results.end()) { + auto result_pair = inputs_results.at(input); + if (result_pair.second || called_from_generate_circuit) { + return result_pair.first; + } + unassigned_result_found = true; + } + + std::size_t component_witness_amount = get_component_witness_amount(); + ComponentType component_instance = build_component_instance(component_witness_amount); + + if (called_from_generate_circuit) { + // if we found a result we have already returned before this point + // generating a dummy result + result_type result(component_instance, 0); + for (auto variable : result.all_vars()) { + variable.get() = parent_assignment.add_batch_variable(0); + } + bool insertion_result = inputs_results.insert({input, {result, false}}).second; + BOOST_ASSERT(insertion_result); + return result; + } + + // now we need to actually calculate the result without instantiating the component + // luckily, we already have the mechanism for that + input_type input_copy = input; + std::vector> vars = input_copy.all_vars(); + std::vector values; + for (const auto &var : vars) { + values.push_back(var_value(parent_assignment, var.get())); + } + // safety resize for the case where parent assignment got resized during the lifetime + internal_assignment.resize_witnesses(component_witness_amount); + // move the variables to internal_assignment's public_input column + for (std::size_t i = 0 ; i < vars.size(); i++) { + internal_assignment.public_input(0, i) = values[i]; + vars[i].get() = var(0, i, false, var::column_type::public_input); + } + auto result = generate_empty_assignments(component_instance, internal_assignment, input_copy, 0); + // and replace the variables with placeholders, while saving their values + if (!unassigned_result_found) { + for (auto variable : result.all_vars()) { + variable_transform(variable); + } + bool insertion_result = inputs_results.insert({input, {result, true}}).second; + BOOST_ASSERT(insertion_result); + return result; + } else { + // already have some vars + auto unassigned_result = inputs_results.find(input)->second.first; + auto unsassigned_vars = unassigned_result.all_vars(); + auto result_vars = result.all_vars(); + BOOST_ASSERT(unsassigned_vars.size() == result_vars.size()); + for (std::size_t i = 0; i < unsassigned_vars.size(); i++) { + parent_assignment.batch_private_storage(unsassigned_vars[i].get().rotation) = + var_value(internal_assignment, result_vars[i].get()); + } + inputs_results.erase(input); + bool insertion_result = inputs_results.insert({input, {unassigned_result, true}}).second; + BOOST_ASSERT(insertion_result); + return unassigned_result; + } + } + + // call this once in the end in assignment + // note that the copy constraint replacement is done by assignment in order to reduce the amount of + // spinning through the constraints; we pass variable_map for this purpose + // returns the first free row index + std::size_t finalize_batch( + circuit &bp, + std::unordered_map &variable_map, + const std::uint32_t start_row_index) { + + if (inputs_results.empty()) { + return start_row_index; + } + // First figure out how much we can scale the component + const std::size_t component_witness_amount = get_component_witness_amount(); + std::size_t row = start_row_index, + col_offset = 0; + const std::vector constants = {}, public_inputs = {}; + std::size_t gate_id = generate_batch_gate( + bp, inputs_results.begin()->first, component_witness_amount); + for (auto &input_result : inputs_results) { + const input_type &input = input_result.first; + result_type &result = input_result.second.first; + bool result_status = input_result.second.second; + BOOST_ASSERT(result_status); + if (col_offset == 0) { + parent_assignment.enable_selector(gate_id, row); + } + ComponentType component_instance = build_component_instance(component_witness_amount, col_offset); + auto actual_result = generate_assignments(component_instance, parent_assignment, input, row); + generate_copy_constraints(component_instance, bp, parent_assignment, input, row); + std::size_t vars_amount = result.all_vars().size(); + for (std::size_t i = 0; i < vars_amount; i++) { + const var batch_var = result.all_vars()[i].get(); + variable_map[batch_var] = actual_result.all_vars()[i].get(); + } + + col_offset += component_witness_amount; + if (col_offset + component_witness_amount - 1 >= parent_assignment.witnesses_amount()) { + col_offset = 0; + row += 1; + } + } + // we fill the unused places with copies of components for the first input to satisfy the gate + if (col_offset != 0 && + (col_offset + component_witness_amount - 1 < parent_assignment.witnesses_amount())) { + + while (col_offset + component_witness_amount - 1 < parent_assignment.witnesses_amount()) { + std::vector witness_columns(component_witness_amount); + std::iota(witness_columns.begin(), witness_columns.end(), col_offset); + ComponentType component_instance = + std::apply(component_builder, std::make_tuple(witness_columns, constants, public_inputs, params_tuple)); + generate_assignments(component_instance, parent_assignment, inputs_results.begin()->first, row); + col_offset += component_witness_amount; + } + row += 1; + } + return row; + } + + std::vector move_constraints( + const std::vector& constraints, + const std::size_t offset) { + + gate_mover mover([&offset](var v) -> var { + return var(v.index + offset, v.rotation, v.relative, v.type); + }); + std::vector result; + for (const auto& constraint : constraints) { + result.push_back(mover.visit(constraint)); + } + return result; + } + + std::size_t generate_batch_gate( + circuit &bp, + const input_type &example_input, + const std::size_t component_witness_amount) { + + circuit tmp_bp; + ComponentType component_instance = build_component_instance(component_witness_amount); + generate_gates(component_instance, tmp_bp, parent_assignment, example_input); + const auto &gates = tmp_bp.gates(); + BOOST_ASSERT(gates.size() == 1); + + std::vector new_gate_constraints, one_gate_constraints; + auto curr_gate = gates[0]; + for (const auto &constraint : curr_gate.constraints) { + new_gate_constraints.push_back(constraint); + one_gate_constraints.push_back(constraint); + } + const std::size_t scaling_amount = parent_assignment.witnesses_amount() / component_witness_amount; + // Technically, we could generate 'not full' gate for the last batch + // We are unlikely to be able to use that space for anything else, so we reduce the amount of selectors + for (std::size_t i = 1; i < scaling_amount; i++) { + auto moved_constraints = move_constraints(one_gate_constraints, i * component_witness_amount); + for (auto &constraint : moved_constraints) { + new_gate_constraints.push_back(constraint); + } + } + return bp.add_gate(new_gate_constraints); + } + + template + bool operator<(const OtherBatchType &other) const { + if (std::type_index(typeid(*this)) != std::type_index(typeid(other))) { + return std::type_index(typeid(*this)) < std::type_index(typeid(other)); + } else { + const auto &other_batch = reinterpret_cast< + const component_batch&>(other); + // compare params_tuple + return params_tuple < other_batch.params_tuple; + } + } + }; + } // namespace blueprint +} // namespace nil + +namespace boost { + namespace type_erasure { + template + struct concept_interface, Base, BatchType> + : Base { + + ResultType add_input(typename as_param::type input, + bool called_from_generate_circuit) { + return call(nil::blueprint::has_add_input(), *this, input, + called_from_generate_circuit); + } + }; + + template + struct concept_interface, + Base, BatchType> : Base { + std::size_t finalize_batch( + typename as_param&>::type bp, + typename as_param&>::type variable_map, + typename as_param::type start_row_index) { + + return call(nil::blueprint::has_finalize_batch(), *this, + bp, variable_map, start_row_index); + } + }; + + template + struct concept_interface, Base, BatchType> : Base { + std::string name() const { + return call(nil::blueprint::has_name(), *this); + } + }; + } // namespace type_erasure +} // namespace boost diff --git a/libs/blueprint/include/nil/blueprint/component_stretcher.hpp b/libs/blueprint/include/nil/blueprint/component_stretcher.hpp new file mode 100644 index 000000000..7ab175f10 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/component_stretcher.hpp @@ -0,0 +1,530 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_PLONK_COMPONENT_STRETCHER_HPP +#define CRYPTO3_BLUEPRINT_PLONK_COMPONENT_STRETCHER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + struct zoning_info { + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using gate_type = nil::crypto3::zk::snark::plonk_gate; + + zoning_info() = default; + zoning_info(std::size_t rows_amount_, std::size_t selector_amount_) + : rows_amount(rows_amount_), selector_amount(selector_amount_), + zones(rows_amount_ + selector_amount_ + 1), + constant_priority(rows_amount_, false) {} + + std::size_t rows_amount; + std::size_t zones_amount; + std::size_t selector_amount; + // also stores gates as part of the zones structure + // we want to move the same gates to the same columns to avoid gate duplication + // after the gates, there is a special zone for the constant column + boost::disjoint_sets_with_storage<> zones; + std::unordered_map zone_sizes; + std::vector constant_priority; + + std::size_t selector_zone_idx(std::size_t selector_num) { + return rows_amount + selector_num; + } + + std::size_t constant_zone_idx() { + return rows_amount + selector_amount; + } + + void count_zones() { + std::set seen; + for (std::size_t i = 0; i < rows_amount; i++) { + auto zone = zones.find_set(i); + seen.insert(zone); + if (zone_sizes.find(zone) != zone_sizes.end()) { + zone_sizes[zone]++; + } else { + zone_sizes[zone] = 1; + } + } + zones_amount = seen.size(); + } + }; + // We want to know which parts of the circuit are connected to each other via gate constraints + // And which can be rearranged with relative impunity + // Additionally, we want to separate the zones by gate types inside the zones + // Otherwise this would result in gate duplication, which is worse than doing nothing to the component + template + zoning_info generate_zones( + const circuit> &bp, + const assignment> &assignment, + std::size_t start_row_index, + std::size_t rows_amount) { + using var = typename nil::crypto3::zk::snark::plonk_variable; + + std::map> gate_range_map; + for (const auto &gate : bp.gates()) { + gate_range_map[gate.selector_index] = {false, false, false, false, false, false}; + std::array &island = gate_range_map[gate.selector_index]; + for (const auto &constraint : gate.constraints) { + std::set variable_set; + std::function variable_extractor = [&variable_set](var variable) { + variable_set.insert(variable); + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + visitor.visit(constraint); + + for (const auto &variable : variable_set) { + BOOST_ASSERT(variable.rotation == -1 || variable.rotation == 0 || variable.rotation == 1); + if (variable.type == var::column_type::constant) { + island[variable.rotation + 4] = true; + } else { + island[variable.rotation + 1] = true; + } + } + } + } + + zoning_info zones(rows_amount, bp.num_gates()); + for (std::size_t i = start_row_index; i < start_row_index + rows_amount; i++) { + for (const auto &[selector, connection] : gate_range_map) { + if (i >= assignment.selector_column_size(selector) || assignment.selector(selector, i) == 0u) { + continue; + } + std::size_t row_idx = i - start_row_index; + if (connection[0]) { + zones.zones.union_set(row_idx - 1, row_idx); + zones.zones.union_set(row_idx - 1, zones.selector_zone_idx(selector)); + } + if (connection[1]) { + zones.zones.union_set(row_idx, zones.selector_zone_idx(selector)); + } + if (connection[2]) { + zones.zones.union_set(row_idx, row_idx + 1); + zones.zones.union_set(row_idx + 1, zones.selector_zone_idx(selector)); + } + // The proper way of dealing with constants requires changing the NP-hard problem being solved + // This is a hack + if (connection[3]) { + zones.zones.union_set(row_idx - 1, zones.constant_zone_idx()); + zones.constant_priority[row_idx - 1] = true; + } + if (connection[4]) { + zones.zones.union_set(row_idx, zones.constant_zone_idx()); + zones.constant_priority[row_idx] = true; + } + if (connection[5]) { + zones.zones.union_set(row_idx + 1, zones.constant_zone_idx()); + zones.constant_priority[row_idx + 1] = true; + } + } + } + zones.count_zones(); + return zones; + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class component_stretcher { + public: + typedef typename ComponentType::input_type input_type; + typedef typename ComponentType::result_type result_type; + typedef zoning_info zone_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + using gate_type = typename nil::crypto3::zk::snark::plonk_gate>; + + // overloading this because this isn't a proper component (not in the inheritance hierarchy) + std::size_t witness_amount() const { + return stretched_witness_amount; + } + + ComponentType &component; + const std::size_t old_witness_amount; + const std::size_t stretched_witness_amount; + + mutable std::size_t stretch_coeff; + mutable zone_type zones; + mutable std::unordered_map zone_mapping; + mutable std::unordered_map gate_mapping; + mutable std::vector line_mapping; + // Hack to avoid changing the NP-hard problem being solved to properly include the constant column + mutable std::unordered_map constant_remapping; + mutable std::size_t rows_amount; + mutable bool remapping_computed = false; + + void compute_remapping( + assignment> &assignment, + std::size_t old_witness_amount, + const input_type &instance_input) const { + circuit> tmp_circuit; + nil::blueprint::assignment> tmp_assignment( + assignment.witnesses_amount(), assignment.public_inputs_amount(), + assignment.constants_amount(), assignment.selectors_amount() + ); + auto converted_input = input_type_converter::convert(instance_input, assignment, + tmp_assignment); + generate_circuit(this->component, tmp_circuit, tmp_assignment, converted_input, 0); + generate_assignments(this->component, tmp_assignment, converted_input, 0); + zones = generate_zones(tmp_circuit, tmp_assignment, 0, this->component.rows_amount); + + // Computing the optimal packing is NP-hard. + // See [https://en.wikipedia.org/wiki/Identical-machines_scheduling]. + // We use an approximation algorithm. + std::list> zone_sizes_for_packing; + for (auto [zone, size] : zones.zone_sizes) { + zone_sizes_for_packing.push_back({zone, size}); + } + zone_mapping = detail::huang_lu(zone_sizes_for_packing, stretch_coeff); + + std::vector zone_rows(zones.zone_sizes.size(), 0); + line_mapping.resize(this->component.rows_amount); + std::vector constant_assigned(this->component.rows_amount, false); + for (std::size_t i = 0; i < this->component.rows_amount; i++) { + auto zone = zone_mapping[zones.zones.find_set(i)]; + line_mapping[i] = zone_rows[zone]; + if (zones.constant_priority[i]) { + constant_assigned[line_mapping[i]] = true; + } + zone_rows[zone]++; + } + + // Hack to deal with constants + std::set constrained_constant_vars; + for (auto &[first_var, second_var] : tmp_circuit.copy_constraints()) { + for (auto &curr_var : {std::ref(first_var), std::ref(second_var)}) { + if (curr_var.get().type != var::column_type::constant) { + continue; + } + constrained_constant_vars.insert(curr_var); + } + } + std::size_t first_unassigned_const = 0; + while (constant_assigned[first_unassigned_const]) { + first_unassigned_const++; + } + for (auto curr_var : constrained_constant_vars) { + std::size_t new_rotation = line_mapping[curr_var.rotation]; + if (constant_assigned[new_rotation]) { + // Need to remap + constant_remapping[curr_var.rotation] = first_unassigned_const; + constant_assigned[first_unassigned_const] = true; + while (constant_assigned[first_unassigned_const]) { + first_unassigned_const++; + } + } else { + // We rely on default remapping + constant_remapping[curr_var.rotation] = new_rotation; + constant_assigned[new_rotation] = true; + if (first_unassigned_const == new_rotation) { + while (constant_assigned[first_unassigned_const]) { + first_unassigned_const++; + } + } + } + } + this->rows_amount = std::max_element(zones.zone_sizes.begin(), zones.zone_sizes.end(), + [](auto a, auto b) { return a.second < b.second; })->second; + this->remapping_computed = true; + } + + var move_var(const var &old, std::int32_t new_rotation, const input_type &input) const { + var new_var; + switch (old.type) { + case var::column_type::constant: + // Assumes a single constant column + new_var = var( + old.index, + new_rotation, + old.relative, + var::column_type::constant); + break; + case var::column_type::public_input: + // public input is actually the original input variables + new_var = input_type_converter::deconvert_var(input, old); + break; + case var::column_type::witness: + BOOST_ASSERT_MSG( + old.relative == false, + "We should not move relative variables with move_var, use move_gate_var for gate variables. Copy constraints should be absolute."); + new_var = var(zone_mapping[zones.zones.find_set(old.rotation)] * old_witness_amount + old.index, + new_rotation, + old.relative, + var::column_type::witness); + break; + case var::column_type::selector: + BOOST_ASSERT_MSG(false, "Selectors should be moved while moving gates."); + break; + case var::column_type::uninitialized: + BOOST_ASSERT_MSG(false, "Uninitialized variable should not be moved."); + break; + } + return new_var; + } + + var move_gate_var(var old, std::size_t selector) const { + var new_var; + switch (old.type) { + case var::column_type::public_input: + BOOST_ASSERT_MSG(false, "Public input should not belong to a gate."); + break; + case var::column_type::constant: + // Assumes only a single constant column + new_var = var( + old.index, + old.rotation, + old.relative, + var::column_type::constant); + break; + case var::column_type::witness: + new_var = var( + zone_mapping[zones.zones.find_set(this->component.rows_amount + selector)] * old_witness_amount + + old.index, + old.rotation, + old.relative, + var::column_type::witness); + break; + case var::column_type::selector: + BOOST_ASSERT_MSG(false, "Selector columns should not be inside gates."); + break; + case var::column_type::uninitialized: + BOOST_ASSERT_MSG(false, "Uninitialized variable should not be moved."); + break; + } + return new_var; + } + + void move_circuit( + const circuit> + &tmp_circuit, + assignment> + &tmp_assignment, + circuit> + &bp, + assignment> + &assignment, + const input_type &instance_input, + std::size_t start_row_index) const { + // Need to do multiple things here: + // 1) Move gates, including properly generating them + for (auto gate : tmp_circuit.gates()) { + std::vector> new_constraints; + gate_mover gate_displacer = gate_mover( + std::bind(&component_stretcher::move_gate_var, + this, std::placeholders::_1, gate.selector_index)); + for (auto constraint: gate.constraints) { + auto new_constraint = gate_displacer.visit(constraint); + new_constraints.push_back(new_constraint); + } + gate_mapping[gate.selector_index] = bp.add_gate(new_constraints); + } + // 2) Move selectors. + for (std::size_t i = 0; i < this->component.rows_amount; i++) { + for (std::size_t s = 0; s < tmp_assignment.selectors_amount(); s++) { + if (i < tmp_assignment.selector_column_size(s) && tmp_assignment.selector(s, i) != 0) { + assignment.selector(gate_mapping[s], start_row_index + line_mapping[i]) = + tmp_assignment.selector(s, i); + } + } + } + // 3) Move constants + if (tmp_assignment.constants_amount() > 0 && tmp_assignment.constant_column_size(0) > 0) { + for (std::size_t i = 0; i < this->component.rows_amount; i++) { + for (std::size_t c = 0; c < tmp_assignment.constants_amount(); c++) { + if (i >= tmp_assignment.constant_column_size(c)) { + continue; + } + if (constant_remapping.find(i) != constant_remapping.end()) { + assignment.constant(c, start_row_index + constant_remapping[i]) = + tmp_assignment.constant(c, i); + } else if (zones.constant_priority[i]) { + assignment.constant(c, start_row_index + line_mapping[i]) = + tmp_assignment.constant(c, i); + } + } + } + } + // 4) Move copy constraints + for (auto constraint : tmp_circuit.copy_constraints()) { + var new_first, new_second; + for (auto &[new_var, old_var] : + {std::make_pair(std::ref(new_first), constraint.first), + std::make_pair(std::ref(new_second), constraint.second)}) { + if (old_var.type == var::column_type::constant && + constant_remapping.find(old_var.rotation) != constant_remapping.end()) { + new_var = move_var( + old_var, + start_row_index + constant_remapping[old_var.rotation], + instance_input); + } else { + new_var = move_var( + old_var, + start_row_index + line_mapping[old_var.rotation], + instance_input); + } + } + if (constraint.first.type == var::column_type::constant || + constraint.second.type == var::column_type::constant) { + } + nil::crypto3::zk::snark::plonk_copy_constraint + new_constraint(new_first, new_second); + bp.add_copy_constraint(new_constraint); + } + } + + void move_assignment( + const component_stretcher + &component, + const assignment> + &tmp_assignment, + assignment> + &assignment, + std::size_t start_row_index) const { + + for (std::size_t i = 0; i < this->component.rows_amount; i++) { + std::size_t zone = zone_mapping[zones.zones.find_set(i)]; + for (std::size_t w = 0; w < old_witness_amount; w++) { + if (i < tmp_assignment.witness_column_size(w)) { + assignment.witness(w + old_witness_amount * zone, start_row_index + line_mapping[i]) = + tmp_assignment.witness(w, i); + } + } + } + // Public input column is NOT moved + // Selectors and constants are moved in move_circuit, + // because generate_assignments doesn't create them + } + + component_stretcher(ComponentType &component_, + std::size_t old_witness_amount_, + std::size_t stretched_witness_amount_) + : component(component_), old_witness_amount(old_witness_amount_), + stretched_witness_amount(stretched_witness_amount_), + stretch_coeff(stretched_witness_amount_ / old_witness_amount_) {} + }; + + template + typename ComponentType::result_type generate_circuit( + const component_stretcher + &component, + circuit> + &bp, + assignment> + &assignment, + const typename component_stretcher::input_type + &instance_input, + const std::uint32_t start_row_index) { + + if (!component.remapping_computed) { + component.compute_remapping(assignment, component.witness_amount(), instance_input); + } + nil::blueprint::assignment> + tmp_assignment(assignment.witnesses_amount(), assignment.public_inputs_amount(), + assignment.constants_amount(), assignment.selectors_amount()); + circuit> tmp_circuit; + + auto result = generate_circuit( + component.component, tmp_circuit, tmp_assignment, instance_input, 0); + component.move_circuit(tmp_circuit, tmp_assignment, bp, assignment, + instance_input, start_row_index); + return result_type_converter::convert(component, result, instance_input, + start_row_index); + } + + template + typename ComponentType::result_type generate_assignments( + const component_stretcher + &component, + assignment> + &assignment, + const typename component_stretcher::input_type + &instance_input, + const std::uint32_t start_row_index) { + + if (!component.remapping_computed) { + component.compute_remapping(assignment, component.witness_amount(),instance_input); + } + + nil::blueprint::assignment> tmp_assignment( + assignment.witnesses_amount(), assignment.public_inputs_amount(), + assignment.constants_amount(), assignment.selectors_amount()); + circuit> tmp_circuit; + + auto converted_input = input_type_converter::convert(instance_input, assignment, + tmp_assignment); + // We need to generate a circuit here, because the constants are generated in generate_circuit. + // The assignment might rely on the generated constants. + generate_circuit( + component.component, tmp_circuit, tmp_assignment, converted_input, 0); + auto result = generate_assignments( + component.component, tmp_assignment, converted_input, 0); + component.move_assignment(component, tmp_assignment, assignment, start_row_index); + + return result_type_converter::convert(component, result, instance_input, + start_row_index); + } + + template + struct is_component_stretcher : std::false_type {}; + + template + struct is_component_stretcher< + BlueprintFieldType, + component_stretcher> + : std::true_type {}; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_PLONK_COMPONENT_STRETCHER_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_g1_affine.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_g1_affine.hpp new file mode 100644 index 000000000..ffa08007a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_g1_affine.hpp @@ -0,0 +1,117 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of affine G1 element component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_G1_AFFINE_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_G1_AFFINE_COMPONENT_HPP + +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct element_g1; + + /** + * @brief Component that represents a G1 element in affine coordinates. + */ + template + struct element_g1 + : public component { + using curve_type = Curve; + using form = Form; + using coordinates = algebra::curves::coordinates::affine; + using group_type = typename curve_type::template g1_type; + using field_type = typename curve_type::base_field_type; + using group_value_type = typename group_type::value_type; + using field_value_type = typename field_type::value_type; + + using underlying_element_type = algebra::fields::detail::element_fp; + + using addition_component = element_g1_addition; + using is_well_formed_component = element_g1_is_well_formed; + using to_twisted_edwards_component = element_g1_to_twisted_edwards; + using to_bits_component = element_g1_to_bits; + + underlying_element_type X; + underlying_element_type Y; + + element_g1(blueprint &bp) : component(bp) { + detail::blueprint_variable X_var, Y_var; + + X_var.allocate(bp); + Y_var.allocate(bp); + + X = X_var; + Y = Y_var; + } + + element_g1(blueprint &bp, const group_value_type &p) : element_g1(bp) { + bp.lc_val(X) = p.X.data; + bp.lc_val(Y) = p.Y.data; + } + + element_g1(blueprint &bp, const underlying_element_type &in_X, + const underlying_element_type &in_Y) : + component(bp), + X(in_X), Y(in_Y) { + } + + // TODO: maybe add is_well_formed constraints + void generate_gates() { + } + + void generate_assignments(const group_value_type &p) { + this->bp.lc_val(X) = p.X.data; + this->bp.lc_val(Y) = p.Y.data; + } + + // (See a comment in r1cs_ppzksnark_verifier_component.hpp about why + // we mark this function noinline.) TODO: remove later + static std::size_t BOOST_NOINLINE size_in_bits() { + return 2 * field_type::modulus_bits; // This probably should be value_bits, not + // modulus_bits + } + + static std::size_t num_variables() { + return 2; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_G1_AFFINE_COMPONENT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_ops.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_ops.hpp new file mode 100644 index 000000000..1022ae5ec --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/element_ops.hpp @@ -0,0 +1,55 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of available operation components over curve group elements. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_OPS_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_OPS_COMPONENT_HPP + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct element_g1_is_well_formed { }; + + template + struct element_g1_addition { }; + + template + struct element_g1_conditional_addition { }; + + template + struct element_g1_to_twisted_edwards { }; + + template + struct element_g1_to_bits { }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_OPS_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/fixed_base_mul_zcash.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/fixed_base_mul_zcash.hpp new file mode 100644 index 000000000..9c7d9bd81 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/fixed_base_mul_zcash.hpp @@ -0,0 +1,333 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FIXED_BASE_MUL_ZCASH_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FIXED_BASE_MUL_ZCASH_COMPONENT_HPP + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct fixed_base_mul_zcash : public component { + using curve_type = Curve; + using field_type = typename curve_type::base_field_type; + using field_value_type = typename field_type::value_type; + using montgomery_element_component = element_g1; + using twisted_edwards_element_component = + element_g1; + + static_assert(std::is_same::value); + static_assert( + std::is_same::value); + + using lookup_component = lookup_signed_3bit; + using result_type = twisted_edwards_element_component; + + /// See definition of \p c in https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash + static constexpr std::size_t chunks_per_base_point = + nil::crypto3::hashes::detail::chunks_per_base_point( + lookup_component::chunk_bits); + + std::vector montgomery_adders; + std::vector point_converters; + std::vector edward_adders; + std::vector> m_windows_x; + std::vector m_windows_y; + result_type result; + + private: + template::value_type>::value, + bool>::type = true> + void init(const BasePoints &base_points, + const detail::blueprint_variable_vector &in_scalar) { + BOOST_RANGE_CONCEPT_ASSERT((boost::RandomAccessRangeConcept)); + assert(!in_scalar.empty()); + assert((in_scalar.size() % lookup_component::chunk_bits) == 0); + assert(basepoints_required(in_scalar.size()) <= base_points.size()); + + const std::size_t window_size_items = 1 << lookup_component::lookup_bits; + const std::size_t n_windows = in_scalar.size() / lookup_component::chunk_bits; + + typename twisted_edwards_element_component::group_value_type start = base_points[0]; + // Precompute values for all lookup window tables + for (std::size_t i = 0; i < n_windows; ++i) { + std::vector lookup_x; + std::vector lookup_y; + + lookup_x.reserve(window_size_items); + lookup_y.reserve(window_size_items); + + if (i % chunks_per_base_point == 0) { + start = base_points[i / chunks_per_base_point]; + } + + // For each window, generate 4 points, in little endian: + // (0,0) = 0 = start = base*2^4i + // (1,0) = 1 = 2*start + // (0,1) = 2 = 3*start + // (1,1) = 3 = 4*start + typename twisted_edwards_element_component::group_value_type current = start; + for (std::size_t j = 0; j < window_size_items; ++j) { + if (j != 0) { + current = current + start; + } + const typename montgomery_element_component::group_value_type montgomery = + current.to_montgomery(); + lookup_x.emplace_back(montgomery.X); + lookup_y.emplace_back(montgomery.Y); + + assert(montgomery.to_twisted_edwards() == current); + } + + const auto bits_begin = in_scalar.begin() + (i * lookup_component::chunk_bits); + const detail::blueprint_variable_vector window_bits_x( + bits_begin, bits_begin + lookup_component::lookup_bits); + const detail::blueprint_variable_vector window_bits_y( + bits_begin, bits_begin + lookup_component::chunk_bits); + this->m_windows_y.emplace_back(this->bp, lookup_y, window_bits_y); + + // Pass x lookup as a linear combination to avoid extra constraint. + // x_lc = c[0] + b[0] * (c[1]-c[0]) + b[1] * (c[2]-c[0]) + b[0]&b[1] * (c[3] - c[2] - c[1] + + // c[0]) + detail::blueprint_linear_combination x_lc; + x_lc.assign( + this->bp, + math::linear_term(detail::blueprint_variable(0), lookup_x[0]) + + math::linear_term(window_bits_x[0], (lookup_x[1] - lookup_x[0])) + + math::linear_term(window_bits_x[1], (lookup_x[2] - lookup_x[0])) + + math::linear_term( + this->m_windows_y.back().b0b1, + (lookup_x[3] - lookup_x[2] - lookup_x[1] + lookup_x[0]))); + this->m_windows_x.emplace_back(x_lc); + + // current is at 2^2 * start, for next iteration start needs to be 2^4 + start = current.doubled().doubled(); + } + + // Chain adders within one segment together via montgomery adders + for (std::size_t i = 0; i < n_windows; ++i) { + if (i % chunks_per_base_point == 0) { + if (i + 1 < n_windows) { + // 0th lookup will be used in the next iteration to connect + // the first two adders of a new base point. + continue; + } else { + // This is the last point. No need to add it to anything in its + // montgomery form, but we have to make sure it will be part of + // the final edwards addition at the end + this->point_converters.emplace_back( + this->bp, montgomery_element_component(this->bp, this->m_windows_x[i], + this->m_windows_y[i].result)); + } + } else if (i % chunks_per_base_point == 1) { + this->montgomery_adders.emplace_back( + this->bp, + montgomery_element_component(this->bp, this->m_windows_x[i - 1], + this->m_windows_y[i - 1].result), + montgomery_element_component(this->bp, this->m_windows_x[i], + this->m_windows_y[i].result)); + } else { + this->montgomery_adders.emplace_back( + this->bp, this->montgomery_adders.back().result, + montgomery_element_component(this->bp, this->m_windows_x[i], + this->m_windows_y[i].result)); + } + } + + // Convert every point at the end of a segment back to edwards format + const std::size_t segment_width = chunks_per_base_point - 1; + + for (std::size_t i = segment_width; i < this->montgomery_adders.size(); i += segment_width) { + this->point_converters.emplace_back(this->bp, this->montgomery_adders[i - 1].result); + } + // The last segment might be incomplete + if (n_windows > 1) { + this->point_converters.emplace_back(this->bp, this->montgomery_adders.back().result); + } + + // Chain adders of converted segment tails together + for (std::size_t i = 1; i < this->point_converters.size(); ++i) { + if (i == 1) { + this->edward_adders.emplace_back(this->bp, this->point_converters[i - 1].result, + this->point_converters[i].result); + } else { + this->edward_adders.emplace_back(this->bp, this->edward_adders[i - 2].result, + this->point_converters[i].result); + } + } + } + + static detail::blueprint_variable_vector + pad_input(blueprint &bp, + const detail::blueprint_variable_vector &input) { + detail::blueprint_variable_vector padded_input = input; + for (std::size_t i = 0; + // TODO: simplify calculation of the padding length + i < (input.size() % lookup_component::chunk_bits ? + (lookup_component::chunk_bits - input.size() % lookup_component::chunk_bits) : + 0); + ++i) { + detail::blueprint_variable pad_i; + pad_i.allocate(bp); + bp.val(pad_i) = field_value_type::zero(); + padded_input.template emplace_back(pad_i); + } + return padded_input; + } + + public: + /// Number of segments + static std::size_t basepoints_required(std::size_t n_bits) { + return std::ceil(n_bits / float(lookup_component::chunk_bits * chunks_per_base_point)); + } + + /// Auto allocation of the result + template + fixed_base_mul_zcash(blueprint &bp, + const BasePoints &base_points, + const detail::blueprint_variable_vector &in_scalar, + const bool do_pad_input = true) : + component(bp), + result(bp) { + init(base_points, do_pad_input ? pad_input(bp, in_scalar) : in_scalar); + } + + /// Manual allocation of the result + template + fixed_base_mul_zcash(blueprint &bp, + const BasePoints &base_points, + const detail::blueprint_variable_vector &in_scalar, + const result_type &in_result, + const bool do_pad_input = true) : + component(bp), + result(in_result) { + init(base_points, do_pad_input ? pad_input(bp, in_scalar) : in_scalar); + } + + void generate_gates() { + for (auto &lut_y : this->m_windows_y) { + lut_y.generate_gates(); + } + + for (auto &adder : this->montgomery_adders) { + adder.generate_gates(); + } + + for (auto &converter : this->point_converters) { + converter.generate_gates(); + } + + for (auto &adder : this->edward_adders) { + adder.generate_gates(); + } + + // formal check + if (!this->edward_adders.empty()) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {detail::blueprint_variable(0)}, {this->result.X}, + {this->edward_adders.back().result.X})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {detail::blueprint_variable(0)}, {this->result.Y}, + {this->edward_adders.back().result.Y})); + } else { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {detail::blueprint_variable(0)}, {this->result.X}, + {this->point_converters.back().result.X})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {detail::blueprint_variable(0)}, {this->result.Y}, + {this->point_converters.back().result.Y})); + } + } + + void generate_assignments() { + // y lookups have to be solved first, because + // x depends on the `b0 && b1` constraint. + for (auto &lut_y : this->m_windows_y) { + lut_y.generate_assignments(); + } + + for (auto &lut_x : this->m_windows_x) { + lut_x.evaluate(this->bp); + } + + for (auto &adder : this->montgomery_adders) { + adder.generate_assignments(); + } + + for (auto &converter : this->point_converters) { + converter.generate_assignments(); + } + + for (auto &adder : this->edward_adders) { + adder.generate_assignments(); + } + + if (!this->edward_adders.empty()) { + this->bp.lc_val(this->result.X) = this->bp.lc_val(this->edward_adders.back().result.X); + this->bp.lc_val(this->result.Y) = this->bp.lc_val(this->edward_adders.back().result.Y); + } else { + this->bp.lc_val(this->result.X) = this->bp.lc_val(this->point_converters.back().result.X); + this->bp.lc_val(this->result.Y) = this->bp.lc_val(this->point_converters.back().result.Y); + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FIXED_BASE_MUL_ZCASH_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt4.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt4.hpp new file mode 100644 index 000000000..108af2d4a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt4.hpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of specializations of basic_curve_component_policy to +// - basic_curve_component_policy. +// +// See pairing_params.hpp . +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_CURVE_COMPONENT_POLICY_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_CURVE_COMPONENT_POLICY_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + using namespace nil::crypto3::algebra; + + template + class basic_curve_component_policy; + + /** + * Specialization for MNT4. + */ + template + class basic_curve_component_policy> { + using curve_type = typename curves::mnt4; + + typedef typename curve_type::chained_on_curve_type chained_on_curve_type; // mnt6 + + typedef typename chained_on_curve_type::pairing::fqe_type fqe_type; + typedef typename chained_on_curve_type::pairing::fqk_type fqk_type; + + typedef typename curve_type::pairing::fp_type field_type; + + public: + typedef element_fp3 Fqe_variable_type; + typedef element_fp3_mul Fqe_mul_component_type; + typedef element_fp3_mul_by_lc Fqe_mul_by_lc_component_type; + typedef element_fp3_squared Fqe_sqr_component_type; + + typedef element_fp6_2over3 Fqk_variable_type; + typedef element_fp6_2over3_mul Fqk_mul_component_type; + typedef element_fp6_2over3_mul_by_2345 Fqk_special_mul_component_type; + typedef element_fp6_2over3_squared Fqk_sqr_component_type; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_CURVE_COMPONENT_POLICY_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt6.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt6.hpp new file mode 100644 index 000000000..c7c9a4c6e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/mnt6.hpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of specializations of basic_curve_component_policy to +// - basic_curve_component_policy. +// +// See pairing_params.hpp . +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_CURVE_COMPONENT_POLICY_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_CURVE_COMPONENT_POLICY_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + using namespace nil::crypto3::algebra; + + template + class basic_curve_component_policy; + + /** + * Specialization for MNT6. + */ + template + class basic_curve_component_policy> { + using curve_type = typename curves::mnt6; + + typedef typename curve_type::chained_on_curve_type chained_on_curve_type; // mnt4 + + typedef typename chained_on_curve_type::pairing::fqe_type fqe_type; + typedef typename chained_on_curve_type::pairing::fqk_type fqk_type; + + typedef typename curve_type::pairing::fp_type field_type; + + public: + typedef element_fp2 Fqe_variable_type; + typedef element_fp2_mul Fqe_mul_component_type; + typedef element_fp2_mul_by_lc Fqe_mul_by_lc_component_type; + typedef element_fp2_squared Fqe_sqr_component_type; + + typedef element_fp4 Fqk_variable_type; + typedef element_fp4_mul Fqk_mul_component_type; + typedef element_fp4_mul Fqk_special_mul_component_type; + typedef element_fp4_squared Fqk_sqr_component_type; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_CURVE_COMPONENT_POLICY_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/scalar_mul.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/scalar_mul.hpp new file mode 100644 index 000000000..9e27e4a0b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/detail/r1cs/scalar_mul.hpp @@ -0,0 +1,163 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G1 components. +// +// The components verify curve arithmetic in G1 = E(F) where E/F: y^2 = x^3 + A * X + B +// is an elliptic curve over F in short Weierstrass form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1; + + template + class element_g1_add; + + template + class element_g1_doubled; + + /** + * Component that creates constraints for G1 multi-scalar multiplication. + */ + template + class scalar_mul : public component { + typedef typename CurveType::scalar_field_type FieldType; + + public: + std::vector> computed_results; + std::vector> chosen_results; + std::vector> adders; + std::vector> doublers; + + element_g1 base; + blueprint_variable_vector scalars; + std::vector> points; + std::vector> points_and_powers; + element_g1 result; + + const std::size_t elt_size; + const std::size_t num_points; + const std::size_t scalar_size; + + scalar_mul(blueprint &bp, + const element_g1 &base, + const blueprint_variable_vector &scalars, + const std::size_t elt_size, + const std::vector> &points, + const element_g1 &result) : + component(bp), + base(base), scalars(scalars), points(points), result(result), elt_size(elt_size), + num_points(points.size()), scalar_size(scalars.size()) { + + assert(num_points >= 1); + assert(num_points * elt_size == scalar_size); + + for (std::size_t i = 0; i < num_points; ++i) { + points_and_powers.emplace_back(points[i]); + for (std::size_t j = 0; j < elt_size - 1; ++j) { + points_and_powers.emplace_back(element_g1(bp)); + doublers.emplace_back(element_g1_doubled( + bp, points_and_powers[i * elt_size + j], points_and_powers[i * elt_size + j + 1])); + } + } + + chosen_results.emplace_back(base); + for (std::size_t i = 0; i < scalar_size; ++i) { + computed_results.emplace_back(element_g1(bp)); + if (i < scalar_size - 1) { + chosen_results.emplace_back(element_g1(bp)); + } else { + chosen_results.emplace_back(result); + } + + adders.emplace_back(element_g1_add( + bp, chosen_results[i], points_and_powers[i], computed_results[i])); + } + } + + void generate_gates() { + const std::size_t num_constraints_before = this->bp.num_constraints(); + + for (std::size_t i = 0; i < scalar_size - num_points; ++i) { + doublers[i].generate_gates(); + } + + for (std::size_t i = 0; i < scalar_size; ++i) { + adders[i].generate_gates(); + + /* + chosen_results[i+1].X = scalars[i] * computed_results[i].X + (1-scalars[i]) * + chosen_results[i].X chosen_results[i+1].X - chosen_results[i].X = scalars[i] * + (computed_results[i].X - chosen_results[i].X) + */ + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(scalars[i], + computed_results[i].X - chosen_results[i].X, + chosen_results[i + 1].X - chosen_results[i].X)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(scalars[i], + computed_results[i].Y - chosen_results[i].Y, + chosen_results[i + 1].Y - chosen_results[i].Y)); + } + + const std::size_t num_constraints_after = this->bp.num_constraints(); + assert(num_constraints_after - num_constraints_before == + 4 * (scalar_size - num_points) + (4 + 2) * scalar_size); + } + + void generate_assignments() { + for (std::size_t i = 0; i < scalar_size - num_points; ++i) { + doublers[i].generate_assignments(); + } + + for (std::size_t i = 0; i < scalar_size; ++i) { + adders[i].generate_assignments(); + this->bp.lc_val(chosen_results[i + 1].X) = + (this->bp.val(scalars[i]) == typename CurveType::scalar_field_type::value_type::zero() ? + this->bp.lc_val(chosen_results[i].X) : + this->bp.lc_val(computed_results[i].X)); + this->bp.lc_val(chosen_results[i + 1].Y) = + (this->bp.val(scalars[i]) == typename CurveType::scalar_field_type::value_type::zero() ? + this->bp.lc_val(chosen_results[i].Y) : + this->bp.lc_val(computed_results[i].Y)); + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/bool_scalar_multiplication.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/bool_scalar_multiplication.hpp new file mode 100644 index 000000000..0c3911ffa --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/bool_scalar_multiplication.hpp @@ -0,0 +1,286 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the MERKLE_TREE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BOOL_SCALAR_MULTIPLICATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BOOL_SCALAR_MULTIPLICATION_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class bool_scalar_multiplication; + + template + class bool_scalar_multiplication, + typename crypto3::algebra::curves::ed25519, basic_non_native_policy>: + public plonk_component { + + using operating_field_type = typename crypto3::algebra::fields::curve25519_base_field; + using non_native_policy_type = basic_non_native_policy; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return bool_scalar_multiplication::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_single_value_param(9)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 2; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + + var_ec_point T; + var k; + + std::vector> all_vars() { + return {T.x[0], T.x[1], T.x[2], T.x[3], + T.y[0], T.y[1], T.y[2], T.y[3], + k}; + } + }; + + struct result_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + var_ec_point output; + + result_type(const bool_scalar_multiplication &component, std::uint32_t start_row_index) { + output.y = { + var(component.W(5), start_row_index, false), + var(component.W(6), start_row_index, false), + var(component.W(7), start_row_index, false), + var(component.W(8), start_row_index, false)}; + output.x = { + var(component.W(5), start_row_index + 1, false), + var(component.W(6), start_row_index + 1, false), + var(component.W(7), start_row_index + 1, false), + var(component.W(8), start_row_index + 1, false)}; + } + + std::vector> all_vars() { + return {output.x[0], output.x[1], output.x[2], output.x[3], + output.y[0], output.y[1], output.y[2], output.y[3]}; + } + }; + + template + explicit bool_scalar_multiplication(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + bool_scalar_multiplication(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + bool_scalar_multiplication(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + }; + + template + using plonk_bool_scalar_multiplication = + bool_scalar_multiplication, + typename crypto3::algebra::curves::ed25519, + basic_non_native_policy>; + + template + typename plonk_bool_scalar_multiplication::result_type + generate_assignments( + const plonk_bool_scalar_multiplication &component, + assignment> + &assignment, + const typename plonk_bool_scalar_multiplication::input_type &instance_input, + const std::uint32_t start_row_index) { + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type b = typename Ed25519Type::base_field_type::integral_type(var_value(assignment, instance_input.k).data); + std::array T_x_array = {var_value(assignment, instance_input.T.x[0]), + var_value(assignment, instance_input.T.x[1]), var_value(assignment, instance_input.T.x[2]), var_value(assignment, instance_input.T.x[3])}; + std::array T_y_array = {var_value(assignment, instance_input.T.y[0]), + var_value(assignment, instance_input.T.y[1]), var_value(assignment, instance_input.T.y[2]), var_value(assignment, instance_input.T.y[3])}; + + assignment.witness(component.W(0), row) = T_y_array[0]; + assignment.witness(component.W(1), row) = T_y_array[1]; + assignment.witness(component.W(2), row) = T_y_array[2]; + assignment.witness(component.W(3), row) = T_y_array[3]; + assignment.witness(component.W(4), row) = b; + assignment.witness(component.W(5), row) = b * T_y_array[0] + (1u - b); + assignment.witness(component.W(6), row) = b * T_y_array[1]; + assignment.witness(component.W(7), row) = b * T_y_array[2]; + assignment.witness(component.W(8), row) = b * T_y_array[3]; + row++; + assignment.witness(component.W(0), row) = T_x_array[0]; + assignment.witness(component.W(1), row) = T_x_array[1]; + assignment.witness(component.W(2), row) = T_x_array[2]; + assignment.witness(component.W(3), row) = T_x_array[3]; + assignment.witness(component.W(4), row) = b; + assignment.witness(component.W(5), row) = b * T_x_array[0]; + assignment.witness(component.W(6), row) = b * T_x_array[1]; + assignment.witness(component.W(7), row) = b * T_x_array[2]; + assignment.witness(component.W(8), row) = b * T_x_array[3]; + + return typename plonk_bool_scalar_multiplication::result_type + (component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_bool_scalar_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bool_scalar_multiplication::input_type + &instance_input) { + using var = typename plonk_bool_scalar_multiplication::var; + + auto constraint_9 = + var(component.W(4), 0) * ( var(component.W(4), 0) - 1); + auto constraint_10 = + var(component.W(4), 0) - var(component.W(4), +1); + auto constraint_1 = + var(component.W(5), 0) - (var(component.W(0), 0) * var(component.W(4), 0) + (1 - var(component.W(4), 0))); + auto constraint_2 = + var(component.W(6), 0) - var(component.W(1), 0) * var(component.W(4), 0); + auto constraint_3 = + var(component.W(7), 0) - var(component.W(2), 0) * var(component.W(4), 0); + auto constraint_4 = + var(component.W(8), 0) - var(component.W(3), 0) * var(component.W(4), 0); + auto constraint_5 = + var(component.W(5), +1) - var(component.W(0), +1) * var(component.W(4), +1); + auto constraint_6 = + var(component.W(6), +1) - var(component.W(1), +1) * var(component.W(4), +1); + auto constraint_7 = + var(component.W(7), +1) - var(component.W(2), +1) * var(component.W(4), +1); + auto constraint_8 = + var(component.W(8), +1) - var(component.W(3), +1) * var(component.W(4), +1); + + return bp.add_gate( + {constraint_9, constraint_10, + constraint_1, constraint_2, constraint_3, constraint_4, + constraint_5, constraint_6, constraint_7, constraint_8}); + + } + + template + void generate_copy_constraints( + const plonk_bool_scalar_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bool_scalar_multiplication::input_type + &instance_input, + const std::size_t start_row_index) { + using var = typename plonk_bool_scalar_multiplication::var; + + std::size_t row = start_row_index; + + bp.add_copy_constraint({var(component.W(0), row + 1, false), instance_input.T.x[0]}); + bp.add_copy_constraint({var(component.W(1), row + 1, false), instance_input.T.x[1]}); + bp.add_copy_constraint({var(component.W(2), row + 1, false), instance_input.T.x[2]}); + bp.add_copy_constraint({var(component.W(3), row + 1, false), instance_input.T.x[3]}); + bp.add_copy_constraint({var(component.W(4), row + 1, false), instance_input.k}); + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.T.y[0]}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.T.y[1]}); + bp.add_copy_constraint({var(component.W(2), row, false), instance_input.T.y[2]}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.T.y[3]}); + bp.add_copy_constraint({var(component.W(4), row, false), instance_input.k}); + } + + template + typename plonk_bool_scalar_multiplication::result_type + generate_circuit( + const plonk_bool_scalar_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bool_scalar_multiplication::input_type &instance_input, + const std::size_t start_row_index){ + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::size_t row = start_row_index; + assignment.enable_selector(selector_index, row); + + generate_copy_constraints(component, bp, assignment, instance_input, row); + + return typename plonk_bool_scalar_multiplication::result_type + (component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/complete_addition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/complete_addition.hpp new file mode 100644 index 000000000..9fbeaf5f2 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/complete_addition.hpp @@ -0,0 +1,710 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the MERKLE_TREE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_COMPLETE_ADDITION_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_COMPLETE_ADDITION_EDWARD25519_HPP + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class complete_addition; + + template + class complete_addition< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + Ed25519Type, + basic_non_native_policy>: + public plonk_component { + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return + 2 * non_native_range_component::get_rows_amount(witness_amount) + + 8 * multiplication_component::get_rows_amount(witness_amount) + + 3 * addition_component::get_rows_amount(witness_amount) + + subtraction_component::get_rows_amount(witness_amount); + } + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = typename component_type::manifest_type; + using non_native_policy_type = basic_non_native_policy; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return complete_addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with( + non_native_range_component::get_gate_manifest(witness_amount)) + .merge_with(multiplication_component::get_gate_manifest(witness_amount)) + .merge_with(addition_component::get_gate_manifest(witness_amount)) + .merge_with(subtraction_component::get_gate_manifest(witness_amount)); + + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + true + ).merge_with(multiplication_component::get_manifest()) + .merge_with(addition_component::get_manifest()) + .merge_with(subtraction_component::get_manifest()) + .merge_with(non_native_range_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using non_native_range_component = components::range< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount()); + + constexpr static const std::size_t gates_amount = 0; + const std::string component_name = "non-native curve addition"; + + struct input_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + + var_ec_point T; + var_ec_point R; + + std::vector> all_vars() { + return {T.x[0], T.x[1], T.x[2], T.x[3], T.y[0], T.y[1], T.y[2], T.y[3], + R.x[0], R.x[1], R.x[2], R.x[3], R.y[0], R.y[1], R.y[2], R.y[3]}; + } + }; + + struct result_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + var_ec_point output; + + result_type(const complete_addition &component, std::uint32_t start_row_index) { + output.x = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false)}; + std::size_t non_native_range_component_rows_amount = + non_native_range_component::get_rows_amount(component.witness_amount()); + output.y = { + var(component.W(0), start_row_index + non_native_range_component_rows_amount, false), + var(component.W(1), start_row_index + non_native_range_component_rows_amount, false), + var(component.W(2), start_row_index + non_native_range_component_rows_amount, false), + var(component.W(3), start_row_index + non_native_range_component_rows_amount, false)}; + } + + std::vector> all_vars() { + return {output.x[0], output.x[1], output.x[2], output.x[3], + output.y[0], output.y[1], output.y[2], output.y[3]}; + } + }; + + template + complete_addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + complete_addition(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_ed25519_complete_addition = complete_addition< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + typename crypto3::algebra::curves::ed25519, + basic_non_native_policy>; + + template + typename plonk_ed25519_complete_addition::result_type + generate_assignments( + const plonk_ed25519_complete_addition &component, + assignment> &assignment, + const typename plonk_ed25519_complete_addition::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = basic_non_native_policy; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using var = typename plonk_ed25519_complete_addition::var; + + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + using non_native_range_component = components::range< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + + non_native_range_component non_native_range_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + multiplication_component multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + addition_component addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + subtraction_component subtraction_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + + + std::size_t row = start_row_index; + typename Ed25519Type::base_field_type::integral_type base = 1; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + std::array T_x_array = { + var_value(assignment, instance_input.T.x[0]), + var_value(assignment, instance_input.T.x[1]), + var_value(assignment, instance_input.T.x[2]), + var_value(assignment, instance_input.T.x[3])}; + std::array T_y_array = { + var_value(assignment, instance_input.T.y[0]), + var_value(assignment, instance_input.T.y[1]), + var_value(assignment, instance_input.T.y[2]), + var_value(assignment, instance_input.T.y[3])}; + + std::array R_x = instance_input.R.x; + std::array R_y = instance_input.R.y; + std::array R_x_array = { + var_value(assignment, instance_input.R.x[0]), + var_value(assignment, instance_input.R.x[1]), + var_value(assignment, instance_input.R.x[2]), + var_value(assignment, instance_input.R.x[3])}; + std::array R_y_array = { + var_value(assignment, instance_input.R.y[0]), + var_value(assignment, instance_input.R.y[1]), + var_value(assignment, instance_input.R.y[2]), + var_value(assignment, instance_input.R.y[3])}; + + typename Ed25519Type::template g1_type::value_type T( + (typename Ed25519Type::base_field_type::integral_type(T_x_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[3].data) * (base << 198)), + (typename Ed25519Type::base_field_type::integral_type(T_y_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[3].data) * (base << 198))); + typename Ed25519Type::template g1_type::value_type R( + (typename Ed25519Type::base_field_type::integral_type(R_x_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(R_x_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(R_x_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(R_x_array[3].data) * (base << 198)), + (typename Ed25519Type::base_field_type::integral_type(R_y_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(R_y_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(R_y_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(R_y_array[3].data) * (base << 198))); + + typename Ed25519Type::template g1_type::value_type P = T + R; + if (P == Ed25519Type::template g1_type::value_type::zero()) { + P.Y = 1; + } + if (T == Ed25519Type::template g1_type::value_type::zero()) { + T.Y = 1; + } + if (R == Ed25519Type::template g1_type::value_type::zero()) { + R.Y = 1; + } + + typename Ed25519Type::base_field_type::integral_type mask = (base << 66) - 1; + + typename Ed25519Type::base_field_type::integral_type Px_integral = + typename Ed25519Type::base_field_type::integral_type(P.X.data); + std::array x3 = { + Px_integral & mask, (Px_integral >> 66) & mask, (Px_integral >> 132) & mask, + (Px_integral >> 198) & mask}; + + typename Ed25519Type::base_field_type::integral_type Py_integral = + typename Ed25519Type::base_field_type::integral_type(P.Y.data); + std::array y3 = { + Py_integral & mask, (Py_integral >> 66) & mask, (Py_integral >> 132) & mask, + (Py_integral >> 198) & mask}; + + assignment.witness(component.W(0), row) = x3[0]; + assignment.witness(component.W(1), row) = x3[1]; + assignment.witness(component.W(2), row) = x3[2]; + assignment.witness(component.W(3), row) = x3[3]; + std::array P_x = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_assignments(non_native_range_instance, assignment, + typename non_native_range_component::input_type({P_x}), row); + row += non_native_range_instance.rows_amount; + + assignment.witness(component.W(1), row) = y3[1]; + assignment.witness(component.W(0), row) = y3[0]; + assignment.witness(component.W(2), row) = y3[2]; + assignment.witness(component.W(3), row) = y3[3]; + std::array P_y = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_assignments(non_native_range_instance, assignment, + typename non_native_range_component::input_type({P_y}), row); + row += non_native_range_instance.rows_amount; + + typename multiplication_component::result_type t0 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_x, R_y}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t1 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_y, R_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t2 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_x, R_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t3 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_y, R_y}), row); + row += multiplication_instance.rows_amount; + + generate_assignments( // z0 + addition_instance, assignment, + typename addition_component::input_type({t0.output, t1.output}), row); + row += addition_instance.rows_amount; + + generate_assignments( // z1 + addition_instance, assignment, + typename addition_component::input_type({t2.output, t3.output}), row); + row += addition_instance.rows_amount; + + typename multiplication_component::result_type z2 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({t0.output, t1.output}), row); + row += multiplication_instance.rows_amount; + + std::array d_var_array = {var(component.C(0), row + 4, false, var::column_type::constant), + var(component.C(0), row + 5, false, var::column_type::constant), + var(component.C(0), row + 6, false, var::column_type::constant), + var(component.C(0), row + 7, false, var::column_type::constant)}; + + typename multiplication_component::result_type k0 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({d_var_array, z2.output}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type k1 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({P_x, k0.output}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type k2 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({P_y, k0.output}), row); + row += multiplication_instance.rows_amount; + + generate_assignments( // k3 + addition_instance, assignment, + typename addition_component::input_type({P_x, k1.output}), row); + row += addition_instance.rows_amount; + + generate_assignments( // k4 + subtraction_instance, assignment, + typename subtraction_component::input_type({P_y, k2.output}), row); + row += subtraction_instance.rows_amount; + + return typename plonk_ed25519_complete_addition::result_type(component, start_row_index); + } + + template + typename plonk_ed25519_complete_addition::result_type + generate_circuit( + const plonk_ed25519_complete_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_complete_addition::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = basic_non_native_policy; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using var = typename plonk_ed25519_complete_addition::var; + + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + using non_native_range_component = components::range< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + + non_native_range_component non_native_range_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + multiplication_component multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + addition_component addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + subtraction_component subtraction_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + std::size_t row = start_row_index; + std::array P_x = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_circuit(non_native_range_instance, bp, assignment, + typename non_native_range_component::input_type({P_x}), row); + row += non_native_range_instance.rows_amount; + + std::array P_y = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_circuit(non_native_range_instance, bp, assignment, + typename non_native_range_component::input_type({P_y}), row); + row += non_native_range_instance.rows_amount; + + std::array R_x = instance_input.R.x; + std::array R_y = instance_input.R.y; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + + typename multiplication_component::result_type t0 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_x, R_y}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t1 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_y, R_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t2 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_x, R_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t3 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_y, R_y}), row); + row += multiplication_instance.rows_amount; + + generate_circuit( // z0 + addition_instance, bp, assignment, + typename addition_component::input_type({t0.output, t1.output}), row); + row += addition_instance.rows_amount; + + generate_circuit( // z1 + addition_instance, bp, assignment, + typename addition_component::input_type({t2.output, t3.output}), row); + row += addition_instance.rows_amount; + + typename multiplication_component::result_type z2 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({t0.output, t1.output}), row); + row += multiplication_instance.rows_amount; + + std::array d_var_array = {var(component.C(0), row + 4, false, var::column_type::constant), + var(component.C(0), row + 5, false, var::column_type::constant), + var(component.C(0), row + 6, false, var::column_type::constant), + var(component.C(0), row + 7, false, var::column_type::constant)}; + + typename multiplication_component::result_type k0 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({d_var_array, z2.output}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type k1 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({P_x, k0.output}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type k2 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({P_y, k0.output}), row); + row += multiplication_instance.rows_amount; + + generate_circuit( // k3 + addition_instance, bp, assignment, + typename addition_component::input_type({P_x, k1.output}), row); + row += addition_instance.rows_amount; + + generate_circuit( // k4 + subtraction_instance, bp, assignment, + typename subtraction_component::input_type({P_y, k2.output}), row); + row += subtraction_instance.rows_amount; + + generate_constants(component, bp, assignment, instance_input, start_row_index); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_ed25519_complete_addition::result_type(component, start_row_index); + } + + + template + void generate_copy_constraints( + const plonk_ed25519_complete_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_complete_addition::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_ed25519_complete_addition; + + row += component_type::non_native_range_component::get_rows_amount(component.witness_amount()); + row += component_type::non_native_range_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + + for (std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint({{component.W(i), (std::int32_t)(row + 2), false}, + {component.W(i), + (std::int32_t)(start_row_index + component.rows_amount - 4 - 2), + false}}); + } + row += component_type::addition_component::get_rows_amount(component.witness_amount()); + + for (std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint({{component.W(i), (std::int32_t)(row + 2), false}, + {component.W(i), + (std::int32_t)(start_row_index + component.rows_amount - 2), + false}}); + } + } + + template + void generate_constants( + const plonk_ed25519_complete_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_complete_addition::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + using non_native_policy_type = basic_non_native_policy; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + using non_native_range_component = components::range< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + + row += non_native_range_component::get_rows_amount(component.witness_amount()); + row += non_native_range_component::get_rows_amount(component.witness_amount()); + row += multiplication_component::get_rows_amount(component.witness_amount()); + row += multiplication_component::get_rows_amount(component.witness_amount()); + row += multiplication_component::get_rows_amount(component.witness_amount()); + row += multiplication_component::get_rows_amount(component.witness_amount()); + row += addition_component::get_rows_amount(component.witness_amount()); + row += addition_component::get_rows_amount(component.witness_amount()); + row += multiplication_component::get_rows_amount(component.witness_amount()); + + typename Ed25519Type::base_field_type::integral_type base = 1; + typename Ed25519Type::base_field_type::integral_type mask = (base << 66) - 1; + + typename Ed25519Type::base_field_type::integral_type d = + typename Ed25519Type::base_field_type::integral_type( + 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3_cppui_modular256); + assignment.constant(component.C(0), row + 4) = d & mask; + assignment.constant(component.C(0), row + 5) = (d >> 66) & mask; + assignment.constant(component.C(0), row + 6) = (d >> 132) & mask; + assignment.constant(component.C(0), row + 7) = (d >> 198) & mask; + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter< + plonk_ed25519_complete_addition> { + + using component_type = + plonk_ed25519_complete_addition; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + + for (std::size_t i = 0; i < input.T.x.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.T.x[i]); + new_input.T.x[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.T.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.T.y[i]); + new_input.T.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.R.x.size(); i++) { + std::size_t new_idx = input.T.x.size() + input.T.y.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.R.x[i]); + new_input.R.x[i] = var(0, new_idx, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.R.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + input.T.y.size() + input.R.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.R.y[i]); + new_input.R.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.T.x.size()) { + return input.T.x[variable.rotation]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size()) { + return input.T.y[variable.rotation - input.T.x.size()]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size() + input.R.x.size()) { + return input.R.x[variable.rotation - input.T.x.size() - input.T.y.size()]; + } else { + return input.R.y[variable.rotation - input.T.x.size() - input.T.y.size() - input.R.x.size()]; + } + } + }; + + template + class result_type_converter< + plonk_ed25519_complete_addition> { + + using component_type = + plonk_ed25519_complete_addition; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 4; i++) { + new_result.output.x[i] = component.move_var( + old_result.output.x[i], + start_row_index + component.line_mapping[old_result.output.x[i].rotation], + instance_input + ); + new_result.output.y[i] = component.move_var( + old_result.output.y[i], + start_row_index + component.line_mapping[old_result.output.y[i].rotation], + instance_input + ); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/doubling.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/doubling.hpp new file mode 100644 index 000000000..90ffe1e2f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/doubling.hpp @@ -0,0 +1,600 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the DOUBLING component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_DOUBLING_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_DOUBLING_EDWARD25519_HPP + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class doubling; + + template + class doubling< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + Ed25519Type, + basic_non_native_policy>: + public plonk_component { + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = typename component_type::manifest_type; + using non_native_policy_type = basic_non_native_policy; + + using non_native_range_component = components::range< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return doubling::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with( + non_native_range_component::get_gate_manifest(witness_amount)) + .merge_with(multiplication_component::get_gate_manifest(witness_amount)) + .merge_with(addition_component::get_gate_manifest(witness_amount)) + .merge_with(subtraction_component::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(multiplication_component::get_manifest()) + .merge_with(addition_component::get_manifest()) + .merge_with(subtraction_component::get_manifest()) + .merge_with(non_native_range_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return + 2 * non_native_range_component::get_rows_amount(witness_amount) + + 5 * multiplication_component::get_rows_amount(witness_amount) + + 4 * addition_component::get_rows_amount(witness_amount) + + 2 * subtraction_component::get_rows_amount(witness_amount); + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 0; + + struct input_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + + var_ec_point T; + + std::vector> all_vars() { + return {T.x[0], T.x[1], T.x[2], T.x[3], T.y[0], T.y[1], T.y[2], T.y[3]}; + } + }; + + struct result_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + var_ec_point output; + + result_type(const doubling &component, std::size_t start_row_index) { + output.x = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false)}; + std::size_t non_native_rows_amount = non_native_range_component::get_rows_amount( + component.witness_amount()); + output.y = { + var(component.W(0), start_row_index + non_native_rows_amount, false), + var(component.W(1), start_row_index + non_native_rows_amount, false), + var(component.W(2), start_row_index + non_native_rows_amount, false), + var(component.W(3), start_row_index + non_native_rows_amount, false)}; + } + + std::vector> all_vars() { + return {output.x[0], output.x[1], output.x[2], output.x[3], + output.y[0], output.y[1], output.y[2], output.y[3]}; + } + }; + + template + explicit doubling(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + doubling(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + doubling(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_ed25519_doubling = doubling< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + typename crypto3::algebra::curves::ed25519, + basic_non_native_policy>; + + + template + typename plonk_ed25519_doubling::result_type + generate_assignments( + const plonk_ed25519_doubling &component, + assignment> &assignment, + const typename plonk_ed25519_doubling::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = typename + plonk_ed25519_doubling::non_native_policy_type; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using var = typename plonk_ed25519_doubling::var; + + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + using non_native_range_component = components::range< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + + non_native_range_component non_native_range_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + multiplication_component multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + addition_component addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + subtraction_component subtraction_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + std::size_t row = start_row_index; + typename Ed25519Type::base_field_type::integral_type base = 1; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + std::array T_x_array = { + var_value(assignment, instance_input.T.x[0]), + var_value(assignment, instance_input.T.x[1]), + var_value(assignment, instance_input.T.x[2]), + var_value(assignment, instance_input.T.x[3])}; + std::array T_y_array = { + var_value(assignment, instance_input.T.y[0]), + var_value(assignment, instance_input.T.y[1]), + var_value(assignment, instance_input.T.y[2]), + var_value(assignment, instance_input.T.y[3])}; + + typename Ed25519Type::template g1_type::value_type T( + (typename Ed25519Type::base_field_type::integral_type(T_x_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(T_x_array[3].data) * (base << 198)), + (typename Ed25519Type::base_field_type::integral_type(T_y_array[0].data) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[1].data) * (base << 66) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[2].data) * (base << 132) + + typename Ed25519Type::base_field_type::integral_type(T_y_array[3].data) * (base << 198))); + + typename Ed25519Type::template g1_type::value_type P = T + T; + + typename Ed25519Type::base_field_type::integral_type mask = (base << 66) - 1; + + typename Ed25519Type::base_field_type::integral_type Px_integral = + typename Ed25519Type::base_field_type::integral_type(P.X.data); + std::array x3 = { + Px_integral & mask, (Px_integral >> 66) & mask, (Px_integral >> 132) & mask, + (Px_integral >> 198) & mask}; + + typename Ed25519Type::base_field_type::integral_type Py_integral = + typename Ed25519Type::base_field_type::integral_type(P.Y.data); + std::array y3 = { + Py_integral & mask, (Py_integral >> 66) & mask, (Py_integral >> 132) & mask, + (Py_integral >> 198) & mask}; + + assignment.witness(component.W(0), row) = x3[0]; + assignment.witness(component.W(1), row) = x3[1]; + assignment.witness(component.W(2), row) = x3[2]; + assignment.witness(component.W(3), row) = x3[3]; + std::array P_x = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_assignments(non_native_range_instance, assignment, + typename non_native_range_component::input_type({P_x}), row); + row += non_native_range_instance.rows_amount; + + assignment.witness(component.W(0), row) = y3[0]; + assignment.witness(component.W(1), row) = y3[1]; + assignment.witness(component.W(2), row) = y3[2]; + assignment.witness(component.W(3), row) = y3[3]; + std::array P_y = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_assignments(non_native_range_instance, assignment, + typename non_native_range_component::input_type({P_y}), row); + row += non_native_range_instance.rows_amount; + + typename multiplication_component::result_type t0 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_y, T_y}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t1 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_x, T_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t2 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({T_x, T_y}), row); + row += multiplication_instance.rows_amount; + + typename subtraction_component::result_type t3 = generate_assignments( + subtraction_instance, assignment, + typename subtraction_component::input_type({t0.output, t1.output}), row); + row += subtraction_instance.rows_amount; + + generate_assignments( // t4 + addition_instance, assignment, + typename addition_component::input_type({t2.output, t2.output}), row); + row += addition_instance.rows_amount; + + generate_assignments( // t5 + addition_instance, assignment, + typename addition_component::input_type({t1.output, t0.output}), row); + row += addition_instance.rows_amount; + + typename subtraction_component::result_type t6 = generate_assignments( + subtraction_instance, assignment, + typename subtraction_component::input_type({t1.output, t0.output}), row); + row += subtraction_instance.rows_amount; + + generate_assignments( // t7 + multiplication_instance, assignment, + typename multiplication_component::input_type({P_x, t3.output}), row); + row += multiplication_instance.rows_amount; + + typename addition_component::result_type t8 = generate_assignments( + addition_instance, assignment, + typename addition_component::input_type({P_y, P_y}), row); + row += addition_instance.rows_amount; + + typename multiplication_component::result_type t9 = generate_assignments( + multiplication_instance, assignment, + typename multiplication_component::input_type({P_y, t6.output}), row); + row += multiplication_instance.rows_amount; + + generate_assignments( // t10 + addition_instance, assignment, + typename addition_component::input_type({t8.output, t9.output}), row); + row += addition_instance.rows_amount; + + return typename plonk_ed25519_doubling::result_type(component, start_row_index); + } + + + + template + typename plonk_ed25519_doubling::result_type + generate_circuit( + const plonk_ed25519_doubling &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_doubling::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = typename + plonk_ed25519_doubling::non_native_policy_type; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using var = typename plonk_ed25519_doubling::var; + + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + using non_native_range_component = components::range< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using multiplication_component = multiplication< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using addition_component = addition< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + using subtraction_component = subtraction< + ArithmetizationType, Ed25519Type::base_field_type, non_native_policy_type>; + + non_native_range_component non_native_range_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + multiplication_component multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + addition_component addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + subtraction_component subtraction_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + std::size_t row = start_row_index; + std::array P_x = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + + generate_circuit(non_native_range_instance, bp, assignment, + typename non_native_range_component::input_type({P_x}), row); + row += non_native_range_instance.rows_amount; + + std::array P_y = { + var(component.W(0), row, false), + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false)}; + generate_circuit(non_native_range_instance, bp, assignment, + typename non_native_range_component::input_type({P_y}), row); + row += non_native_range_instance.rows_amount; + + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + + + typename multiplication_component::result_type t0 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_y, T_y}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t1 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_x, T_x}), row); + row += multiplication_instance.rows_amount; + + typename multiplication_component::result_type t2 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({T_x, T_y}), row); + row += multiplication_instance.rows_amount; + + typename subtraction_component::result_type t3 = generate_circuit( + subtraction_instance, bp, assignment, + typename subtraction_component::input_type({t0.output, t1.output}), row); + row += subtraction_instance.rows_amount; + + generate_circuit( // t4 + addition_instance, bp, assignment, + typename addition_component::input_type({t2.output, t2.output}), row); + row += addition_instance.rows_amount; + + generate_circuit( // t5 + addition_instance, bp, assignment, + typename addition_component::input_type({t1.output, t0.output}), row); + row += addition_instance.rows_amount; + + typename subtraction_component::result_type t6 = generate_circuit( + subtraction_instance, bp, assignment, + typename subtraction_component::input_type({t1.output, t0.output}), row); + row += subtraction_instance.rows_amount; + + generate_circuit( // t7 + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({P_x, t3.output}), row); + row += multiplication_instance.rows_amount; + + typename addition_component::result_type t8 = generate_circuit( + addition_instance, bp, assignment, + typename addition_component::input_type({P_y, P_y}), row); + row += addition_instance.rows_amount; + + typename multiplication_component::result_type t9 = generate_circuit( + multiplication_instance, bp, assignment, + typename multiplication_component::input_type({P_y, t6.output}), row); + row += multiplication_instance.rows_amount; + + generate_circuit( // t10 + addition_instance, bp, assignment, + typename addition_component::input_type({t8.output, t9.output}), row); + row += addition_instance.rows_amount; + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_ed25519_doubling::result_type(component, start_row_index); + } + + template + void generate_copy_constraints( + const plonk_ed25519_doubling &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_doubling::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + using component_type = plonk_ed25519_doubling; + + row += component_type::non_native_range_component::get_rows_amount(component.witness_amount()); + row += component_type::non_native_range_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::subtraction_component::get_rows_amount(component.witness_amount()); + std::size_t t4_row = row; + row += component_type::addition_component::get_rows_amount(component.witness_amount()); + std::size_t t5_row = row; + row += component_type::addition_component::get_rows_amount(component.witness_amount()); + row += component_type::subtraction_component::get_rows_amount(component.witness_amount()); + std::size_t t7_row = row; + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + row += component_type::addition_component::get_rows_amount(component.witness_amount()); + row += component_type::multiplication_component::get_rows_amount(component.witness_amount()); + std::size_t t10_row = row; + row += component_type::addition_component::get_rows_amount(component.witness_amount()); + + for (std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint( + {{component.W(3 + i), (std::int32_t)(t7_row + 5), false}, + {component.W(i ), (std::int32_t)(t4_row + 2), false}}); + } + + for (std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint( + {{component.W(3 + i), (std::int32_t)(t5_row + 2), false}, + {component.W(3 + i), (std::int32_t)(t10_row + 2), false}}); + } + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter> { + + using component_type = plonk_ed25519_doubling; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + + for (std::size_t i = 0; i < input.T.x.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.T.x[i]); + new_input.T.x[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.T.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.T.y[i]); + new_input.T.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.T.x.size()) { + return input.T.x[variable.rotation]; + } else { + return input.T.y[variable.rotation - input.T.x.size()]; + } + } + }; + + template + class result_type_converter> { + + using component_type = plonk_ed25519_doubling; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 4; i++) { + new_result.output.x[i] = component.move_var( + old_result.output.x[i], + start_row_index + component.line_mapping[old_result.output.x[i].rotation], + instance_input + ); + new_result.output.y[i] = component.move_var( + old_result.output.y[i], + start_row_index + component.line_mapping[old_result.output.y[i].rotation], + instance_input + ); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/ec_point.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/ec_point.hpp new file mode 100644 index 000000000..7e882268c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/ec_point.hpp @@ -0,0 +1,244 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for component to check if a point is on ed25519 curve. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_EC_POINT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_EC_POINT_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class ec_point; + + template + class ec_point, + CurveType, + Ed25519Type, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + using variable_base_mult_component = variable_base_multiplication; + using mult_component = non_native_field_element_multiplication; + using add_component = non_native_field_element_addition; + using sub_component = non_native_field_element_subtraction; + using non_native_range_component = non_native_range; + + using var = snark::plonk_variable; + constexpr static const std::size_t selector_seed = 0xfcd1; + + public: + constexpr static const std::size_t rows_amount = 2 * non_native_range_component::rows_amount + + 5 * mult_component::rows_amount + + 2 * add_component::rows_amount; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + struct var_ec_point { + std::array x; + std::array y; + }; + var_ec_point pnt; + }; + + struct result_type { + result_type(std::size_t component_start_row) { + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + + std::array constant_one = {1, 0, 0, 0}; + + typename Ed25519Type::scalar_field_type::integral_type base = 1; + typename Ed25519Type::scalar_field_type::integral_type mask = (base << 66) - 1; + + typename Ed25519Type::base_field_type::integral_type a_coef_val = typename Ed25519Type::base_field_type::integral_type(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec_cppui_modular255); + std::array a_coef = {a_coef_val & mask, (a_coef_val >>66) & mask, (a_coef_val >>132) & mask, (a_coef_val >>198) & mask}; + + typename Ed25519Type::base_field_type::integral_type d_coef_val = typename Ed25519Type::base_field_type::integral_type(0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3_cppui_modular255); + std::array d_coef = {d_coef_val & mask, (d_coef_val >>66) & mask, (d_coef_val >>132) & mask, (d_coef_val >>198) & mask}; + + for (int i = 0; i < 4; i++) { + assignment.constant(0)[component_start_row + i] = constant_one[i]; + assignment.constant(0)[component_start_row + 4 + i] = a_coef[i]; + assignment.constant(0)[component_start_row + 8 + i] = d_coef[i]; + } + + std::array one_var = {var(0, component_start_row, false, var::column_type::constant), + var(0, component_start_row + 1, false, var::column_type::constant), + var(0, component_start_row + 2, false, var::column_type::constant), + var(0, component_start_row + 3, false, var::column_type::constant)}; + std::array a_var = {var(0, component_start_row + 4, false, var::column_type::constant), + var(0, component_start_row + 5, false, var::column_type::constant), + var(0, component_start_row + 6, false, var::column_type::constant), + var(0, component_start_row + 7, false, var::column_type::constant)}; + std::array d_var = {var(0, component_start_row + 8, false, var::column_type::constant), + var(0, component_start_row + 9, false, var::column_type::constant), + var(0, component_start_row + 10, false, var::column_type::constant), + var(0, component_start_row + 11, false, var::column_type::constant)}; + + /* a * x^2 + y^2 = 1 + d * x^2 * y^2 */ + non_native_range_component::generate_assignments(assignment, {params.pnt.x}, row); + row += non_native_range_component::rows_amount; + non_native_range_component::generate_assignments(assignment, {params.pnt.y}, row); + row += non_native_range_component::rows_amount; + + auto y_2 = mult_component::generate_assignments(assignment, {params.pnt.y, params.pnt.y}, row).output; + row += mult_component::rows_amount; + auto x_2 = mult_component::generate_assignments(assignment, {params.pnt.x, params.pnt.x}, row).output; + row += mult_component::rows_amount; + + auto t0 = mult_component::generate_assignments(assignment, {x_2, a_var}, row).output; + row += mult_component::rows_amount; + auto left = add_component::generate_assignments(assignment, {y_2, t0}, row).output; + row += add_component::rows_amount; + auto t1 = mult_component::generate_assignments(assignment, {y_2, x_2}, row).output; + row += mult_component::rows_amount; + auto t2 = mult_component::generate_assignments(assignment, {d_var, t1}, row).output; + row += mult_component::rows_amount; + auto right = add_component::generate_assignments(assignment, {one_var, t2}, row).output; + row += add_component::rows_amount; + return result_type(component_start_row); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + std::size_t row = start_row_index; + + std::array one_var = {var(0, start_row_index, false, var::column_type::constant), + var(0, start_row_index + 1, false, var::column_type::constant), + var(0, start_row_index + 2, false, var::column_type::constant), + var(0, start_row_index + 3, false, var::column_type::constant)}; + std::array a_var = {var(0, start_row_index + 4, false, var::column_type::constant), + var(0, start_row_index + 5, false, var::column_type::constant), + var(0, start_row_index + 6, false, var::column_type::constant), + var(0, start_row_index + 7, false, var::column_type::constant)}; + std::array d_var = {var(0, start_row_index + 8, false, var::column_type::constant), + var(0, start_row_index + 9, false, var::column_type::constant), + var(0, start_row_index + 10, false, var::column_type::constant), + var(0, start_row_index + 11, false, var::column_type::constant)}; + + /* a * x^2 + y^2 = 1 + d * x^2 * y^2 */ + non_native_range_component::generate_circuit(bp, assignment, {params.pnt.x}, row); + row += non_native_range_component::rows_amount; + non_native_range_component::generate_circuit(bp, assignment, {params.pnt.y}, row); + row += non_native_range_component::rows_amount; + + auto y_2 = mult_component::generate_circuit(bp, assignment, {params.pnt.y, params.pnt.y}, row).output; + row += mult_component::rows_amount; + auto x_2 = mult_component::generate_circuit(bp, assignment, {params.pnt.x, params.pnt.x}, row).output; + row += mult_component::rows_amount; + + auto t0 = mult_component::generate_circuit(bp, assignment, {x_2, a_var}, row).output; + row += mult_component::rows_amount; + auto left = add_component::generate_circuit(bp, assignment, {y_2, t0}, row).output; + row += add_component::rows_amount; + auto t1 = mult_component::generate_circuit(bp, assignment, {y_2, x_2}, row).output; + row += mult_component::rows_amount; + auto t2 = mult_component::generate_circuit(bp, assignment, {d_var, t1}, row).output; + row += mult_component::rows_amount; + auto right = add_component::generate_circuit(bp, assignment, {one_var, t2}, row).output; + row += add_component::rows_amount; + + generate_copy_constraints(bp, assignment, params, start_row_index); + + return result_type(start_row_index); + } + + private: + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + } + + static void generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row + 2 * non_native_range_component::rows_amount + 3 * mult_component::rows_amount; + auto left = (typename add_component::result_type(component_start_row + 25)).output; + row += 2 * mult_component::rows_amount + add_component::rows_amount; + auto right = (typename add_component::result_type(component_start_row + 43)).output; + + bp.add_copy_constraint({left[0], right[0]}); + bp.add_copy_constraint({left[1], right[1]}); + bp.add_copy_constraint({left[2], right[2]}); + bp.add_copy_constraint({left[3], right[3]}); + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/fixed_base_multiplication.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/fixed_base_multiplication.hpp new file mode 100644 index 000000000..076fad38b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/fixed_base_multiplication.hpp @@ -0,0 +1,283 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the FIXED_BASE_MULTIPLICATION_EDWARD25519 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FIXED_BASE_MULTIPLICATION_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FIXED_BASE_MULTIPLICATION_EDWARD25519_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class fixed_base_multiplication; + + template + class fixed_base_multiplication< + crypto3::zk::snark::plonk_constraint_system, CurveType, + Ed25519Type, W0, W1, W2, W3, W4, W5, W6, W7, W8> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + using non_native_range_component = + components::non_native_range; + using scalar_non_native_range_component = + components::scalar_non_native_range; + + using complete_addition_component = + complete_addition; + + using var = snark::plonk_variable; + constexpr static const std::size_t selector_seed = 0xff88; + + public: + constexpr static const std::size_t rows_amount = + scalar_non_native_range_component::rows_amount + 13 + 11 * complete_addition_component::rows_amount; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var k; + }; + + struct result_type { + componentsar_ec_point { + std::array x; + std::array y; + }; + var_ec_point output; + result_type(std::size_t row) { + auto res = (typename complete_addition_component::result_type(row)).output; + output.x = res.x; + output.y = res.y; + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + auto k_chunks_vars = + scalar_non_native_range_component::generate_assignments( + assignment, typename scalar_non_native_range_component::params_type({params.k}), row) + .output; + row += scalar_non_native_range_component::rows_amount; + typename Ed25519Type::scalar_field_type::integral_type base = 1; + std::array k_chunks; + for (std::size_t i = 0; i < 12; i++) { + k_chunks[i] = typename Ed25519Type::scalar_field_type::integral_type( + assignment.var_value(k_chunks_vars[i]).data); + } + + typename Ed25519Type::template g1_type::value_type B = + Ed25519Type::template g1_type::value_type::one(); + + typename Ed25519Type::scalar_field_type::integral_type mask = (base << 66) - 1; + + typename Ed25519Type::template g1_type::value_type P = + typename Ed25519Type::scalar_field_type::value_type(k_chunks[0]) * B; + + typename Ed25519Type::base_field_type::integral_type Px_integral = + typename Ed25519Type::base_field_type::integral_type(P.X.data); + std::array x3 = { + Px_integral & mask, (Px_integral >> 66) & mask, (Px_integral >> 132) & mask, + (Px_integral >> 198) & mask}; + + typename Ed25519Type::base_field_type::integral_type Py_integral = + typename Ed25519Type::base_field_type::integral_type(P.Y.data); + std::array y3 = { + Py_integral & mask, (Py_integral >> 66) & mask, (Py_integral >> 132) & mask, + (Py_integral >> 198) & mask}; + + assignment.witness(W0)[row] = x3[0]; + assignment.witness(W1)[row] = x3[1]; + assignment.witness(W2)[row] = x3[2]; + assignment.witness(W3)[row] = x3[3]; + std::array P_x = {var(W0, row), var(W1, row), var(W2, row), var(W3, row)}; + assignment.witness(W4)[row] = y3[0]; + assignment.witness(W5)[row] = y3[1]; + assignment.witness(W6)[row] = y3[2]; + assignment.witness(W7)[row] = y3[3]; + std::array P_y = {var(W4, row), var(W5, row), var(W6, row), var(W7, row)}; + assignment.witness(W8)[row] = k_chunks[0]; + row++; + + for (std::size_t i = 0; i < 11; i++) { + typename Ed25519Type::template g1_type::value_type Q = + typename Ed25519Type::scalar_field_type::value_type(k_chunks[i + 1]) * + (base << 22 * (i + 1)) * B; + + typename Ed25519Type::base_field_type::integral_type Qx_integral = + typename Ed25519Type::base_field_type::integral_type(Q.X.data); + std::array x3 = { + Qx_integral & mask, (Qx_integral >> 66) & mask, (Qx_integral >> 132) & mask, + (Qx_integral >> 198) & mask}; + + typename Ed25519Type::base_field_type::integral_type Qy_integral = + typename Ed25519Type::base_field_type::integral_type(Q.Y.data); + std::array y3 = { + Qy_integral & mask, (Qy_integral >> 66) & mask, (Qy_integral >> 132) & mask, + (Qy_integral >> 198) & mask}; + + assignment.witness(W0)[row] = x3[0]; + assignment.witness(W1)[row] = x3[1]; + assignment.witness(W2)[row] = x3[2]; + assignment.witness(W3)[row] = x3[3]; + std::array Q_x = {var(W0, row), var(W1, row), var(W2, row), var(W3, row)}; + assignment.witness(W4)[row] = y3[0]; + assignment.witness(W5)[row] = y3[1]; + assignment.witness(W6)[row] = y3[2]; + assignment.witness(W7)[row] = y3[3]; + std::array Q_y = {var(W4, row), var(W5, row), var(W6, row), var(W7, row)}; + assignment.witness(W8)[row] = k_chunks[0]; + row++; + auto t = complete_addition_component::generate_assignments( + assignment, typename complete_addition_component::params_type({{P_x, P_y}, {Q_x, Q_y}}), + row); + row += complete_addition_component::rows_amount; + if (i != 10) { + P_x = t.output.x; + P_y = t.output.y; + P.X = typename Ed25519Type::base_field_type::value_type( + (typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_x[0]).data) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_x[1]).data) * + (base << 66) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_x[2]).data) * + (base << 132) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_x[3]).data) * + (base << 198))); + P.Y = typename Ed25519Type::base_field_type::value_type( + (typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_y[0]).data) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_y[1]).data) * + (base << 66) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_y[2]).data) * + (base << 132) + + typename Ed25519Type::base_field_type::integral_type( + assignment.var_value(P_y[3]).data) * + (base << 198))); + } else { + + typename Ed25519Type::template g1_type::value_type Q = + typename Ed25519Type::scalar_field_type::value_type((k_chunks[i + 1]) * (base << 11)) * + P; + + typename Ed25519Type::base_field_type::integral_type Qx_integral = + typename Ed25519Type::base_field_type::integral_type(Q.X.data); + std::array x3 = { + Qx_integral & mask, (Qx_integral >> 66) & mask, (Qx_integral >> 132) & mask, + (Qx_integral >> 198) & mask}; + + typename Ed25519Type::base_field_type::integral_type Qy_integral = + typename Ed25519Type::base_field_type::integral_type(Q.Y.data); + std::array y3 = { + Qy_integral & mask, (Qy_integral >> 66) & mask, (Qy_integral >> 132) & mask, + (Qy_integral >> 198) & mask}; + + assignment.witness(W0)[row] = x3[0]; + assignment.witness(W1)[row] = x3[1]; + assignment.witness(W2)[row] = x3[2]; + assignment.witness(W3)[row] = x3[3]; + std::array Q_x = {var(W0, row), var(W1, row), var(W2, row), var(W3, row)}; + assignment.witness(W4)[row] = y3[0]; + assignment.witness(W5)[row] = y3[1]; + assignment.witness(W6)[row] = y3[2]; + assignment.witness(W7)[row] = y3[3]; + std::array Q_y = {var(W4, row), var(W5, row), var(W6, row), var(W7, row)}; + assignment.witness(W8)[row] = k_chunks[0]; + row++; + } + } + return result_type(row - 1 - complete_addition_component::rows_amount); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + auto k_chunks = + scalar_non_native_range_component::generate_circuit( + bp, assignment, typename scalar_non_native_range_component::params_type({params.k}), row) + .output; + row += scalar_non_native_range_component::rows_amount; + + std::array P_x = {var(W0, row), var(W1, row), var(W2, row), var(W3, row)}; + std::array P_y = {var(W4, row), var(W5, row), var(W6, row), var(W7, row)}; + row++; + + for (std::size_t i = 0; i < 11; i++) { + std::array Q_x = {var(W0, row), var(W1, row), var(W2, row), var(W3, row)}; + std::array Q_y = {var(W4, row), var(W5, row), var(W6, row), var(W7, row)}; + row++; + auto t = complete_addition_component::generate_circuit( + bp, assignment, typename complete_addition_component::params_type({{P_x, P_y}, {Q_x, Q_y}}), + row); + row += complete_addition_component::rows_amount; + P_x = t.output.x; + P_y = t.output.y; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + + return result_type(row - 1 - complete_addition_component::rows_amount); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/scalar_non_native_range.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/scalar_non_native_range.hpp new file mode 100644 index 000000000..6806e4732 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/scalar_non_native_range.hpp @@ -0,0 +1,260 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the RANGE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SCALAR_RANGE_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SCALAR_RANGE_EDWARD25519_HPP + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class scalar_non_native_range; + + template + class scalar_non_native_range, + typename crypto3::algebra::curves::ed25519>: + public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return scalar_non_native_range::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 3; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + var k; + + std::vector> all_vars() { + return {k}; + } + }; + + struct result_type { + std::array output; + result_type(const scalar_non_native_range &component, std::uint32_t start_row_index) { + output = { var(component.W(1), start_row_index, false), var(component.W(2), start_row_index, false), var(component.W(3), start_row_index, false), + var(component.W(4), start_row_index, false), var(component.W(5), start_row_index, false), var(component.W(6), start_row_index, false), + var(component.W(7), start_row_index, false), var(component.W(8), start_row_index, false), var(component.W(0), start_row_index + 1, false), + var(component.W(1), start_row_index + 1, false), var(component.W(2), start_row_index + 1, false), var(component.W(3), start_row_index + 1, false) + }; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3], output[4], output[5], + output[6], output[7], output[8], output[9], output[10], output[11]}; + } + }; + + template + scalar_non_native_range(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + scalar_non_native_range(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + scalar_non_native_range(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + }; + + template + using plonk_scalar_range = + scalar_non_native_range, + typename crypto3::algebra::curves::ed25519>; + + template + typename plonk_scalar_range::result_type + generate_assignments( + const plonk_scalar_range &component, + assignment> + &assignment, + const typename plonk_scalar_range::input_type &instance_input, + const std::uint32_t start_row_index) { + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + std::size_t row = start_row_index; + typename Ed25519Type::scalar_field_type::integral_type base = 1; + typename Ed25519Type::scalar_field_type::extended_integral_type extended_base = 1; + typename Ed25519Type::scalar_field_type::integral_type mask = (base << 22) - 1; + typename BlueprintFieldType::integral_type pasta_k = typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.k).data); + typename Ed25519Type::scalar_field_type::integral_type k = typename Ed25519Type::scalar_field_type::integral_type(pasta_k); + typename Ed25519Type::scalar_field_type::extended_integral_type q = Ed25519Type::scalar_field_type::modulus; + typename Ed25519Type::scalar_field_type::extended_integral_type d = (extended_base << 253) - q; + typename Ed25519Type::scalar_field_type::integral_type dk = k + typename Ed25519Type::scalar_field_type::integral_type(d); + std::array k_chunks; + std::array dk_chunks; + for (std::size_t i = 0; i < 12 ; i++){ + k_chunks[i] = (k >> i*22) & mask; + dk_chunks[i] = (dk >> i*22) & mask; + } + assignment.witness(component.W(0), row) = k; + assignment.witness(component.W(1), row) = k_chunks[0]; + assignment.witness(component.W(2), row) = k_chunks[1]; + assignment.witness(component.W(3), row) = k_chunks[2]; + assignment.witness(component.W(4), row) = k_chunks[3]; + assignment.witness(component.W(5), row) = k_chunks[4]; + assignment.witness(component.W(6), row) = k_chunks[5]; + assignment.witness(component.W(7), row) = k_chunks[6]; + assignment.witness(component.W(8), row) = k_chunks[7]; + row++; + assignment.witness(component.W(0), row) = k_chunks[8]; + assignment.witness(component.W(1), row) = k_chunks[9]; + assignment.witness(component.W(2), row) = k_chunks[10]; + assignment.witness(component.W(3), row) = k_chunks[11]; + assignment.witness(component.W(4), row) = dk; + assignment.witness(component.W(5), row) = dk_chunks[0]; + assignment.witness(component.W(6), row) = dk_chunks[1]; + assignment.witness(component.W(7), row) = dk_chunks[2]; + assignment.witness(component.W(8), row) = dk_chunks[3]; + row++; + assignment.witness(component.W(0), row) = dk_chunks[4]; + assignment.witness(component.W(1), row) = dk_chunks[5]; + assignment.witness(component.W(2), row) = dk_chunks[6]; + assignment.witness(component.W(3), row) = dk_chunks[7]; + assignment.witness(component.W(4), row) = dk_chunks[8]; + assignment.witness(component.W(5), row) = dk_chunks[9]; + assignment.witness(component.W(6), row) = dk_chunks[10]; + assignment.witness(component.W(7), row) = dk_chunks[11]; + + return typename plonk_scalar_range::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_scalar_range &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_scalar_range::input_type + &instance_input) { + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + using var = typename plonk_scalar_range::var; + + typename BlueprintFieldType::integral_type base = 1; + typename Ed25519Type::scalar_field_type::extended_integral_type extended_base = 1; + typename Ed25519Type::scalar_field_type::extended_integral_type q = Ed25519Type::scalar_field_type::modulus; + typename Ed25519Type::scalar_field_type::extended_integral_type d = (extended_base << 253) - q; + auto constraint_1 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(2), -1) * (base<< 22) + + var(component.W(3), -1) * (base << 44) + var(component.W(4), -1)* (base << 66) + + var(component.W(5), -1) * (base <<88) + var(component.W(6), -1) * (base << 110) + + var(component.W(7), -1) * (base << 132) + var(component.W(8), -1) * (base << 154) + + var(component.W(0), 0)* (base << 176) + var(component.W(1), 0) * (base << 198) + + var(component.W(2), 0) * (base << 220) + var(component.W(3), 0) * (base << 242)); + auto constraint_2 = var(component.W(4), 0) - var(component.W(0), -1) - d; + auto constraint_3 = var(component.W(4), 0) - + (var(component.W(5), 0) + var(component.W(6), 0) * (base<< 22) + + var(component.W(7), 0) * (base << 44) + var(component.W(8), 0)* (base << 66) + + var(component.W(0), +1) * (base <<88) + var(component.W(1), +1) * (base << 110) + + var(component.W(2), +1) * (base << 132) + var(component.W(3), +1) * (base << 154) + + var(component.W(4), +1)* (base << 176) + var(component.W(5), +1) * (base << 198) + + var(component.W(6), +1) * (base << 220) + var(component.W(7), +1) * (base << 242)); + return bp.add_gate({constraint_1, constraint_2, constraint_3}); + } + + template + void generate_copy_constraints( + const plonk_scalar_range &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_scalar_range::input_type &instance_input, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + bp.add_copy_constraint({{component.W(0), static_cast(row), false}, + instance_input.k}); + } + + template + typename plonk_scalar_range::result_type + generate_circuit( + const plonk_scalar_range &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_scalar_range::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::size_t j = start_row_index; + assignment.enable_selector(selector_index, j + 1); + generate_copy_constraints(component, bp, assignment, instance_input, j); + + return typename plonk_scalar_range::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_REDUCTION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication.hpp new file mode 100644 index 000000000..99d5913de --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication.hpp @@ -0,0 +1,397 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the VARIABLE_BASE_MULTIPLICATION component. +//---------------------------------------------------------------------------// + +#pragma once + +#include "nil/blueprint/components/algebra/fields/plonk/non_native/detail/bit_builder_component.hpp" +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class variable_base_multiplication; + + template + class variable_base_multiplication< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + Ed25519Type, + basic_non_native_policy>: + public plonk_component { + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount, + std::size_t bits_amount) { + return + decomposition_component_type::get_rows_amount(witness_amount, bits_amount, + bit_composition_mode::MSB) + + 252 * mul_per_bit_component::get_rows_amount(witness_amount) + + bool_scalar_mul_component::get_rows_amount(witness_amount); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = typename component_type::manifest_type; + using non_native_policy_type = basic_non_native_policy; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using mul_per_bit_component = variable_base_multiplication_per_bit< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using decomposition_component_type = bit_decomposition; + + using bool_scalar_mul_component = bool_scalar_multiplication< + ArithmetizationType, Ed25519Type, non_native_policy_type>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return variable_base_multiplication::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, bit_composition_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with( + bool_scalar_mul_component::get_gate_manifest(witness_amount)) + .merge_with(mul_per_bit_component::get_gate_manifest(witness_amount)) + .merge_with( + decomposition_component_type::get_gate_manifest(witness_amount, bits_amount, mode)); + + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, bit_composition_mode mode) { + manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(mul_per_bit_component::get_manifest()) + .merge_with(decomposition_component_type::get_manifest(bits_amount, mode)) + .merge_with(bool_scalar_mul_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, bit_composition_mode mode) { + return rows_amount_internal(witness_amount, bits_amount); + } + + // We use bits_amount from decomposition subcomponent to initialize rows_amount + // CRITICAL: do not move decomposition_subcomponent below rows_amount + const decomposition_component_type decomposition_subcomponent; + // CRITICAL: do not move decomposition_subcomponent below rows_amount + const mul_per_bit_component mul_per_bit_subcomponent; + const bool_scalar_mul_component bool_scalar_mul_subcomponent; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), decomposition_subcomponent.bits_amount); + constexpr static const std::size_t gates_amount = 0; + const std::string component_name = "non-native curve multiplication"; + + struct input_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + + var_ec_point T; + var k; + + std::vector> all_vars() { + return {T.x[0], T.x[1], T.x[2], T.x[3], T.y[0], T.y[1], T.y[2], T.y[3], k}; + } + }; + + struct result_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + var_ec_point output; + + result_type(const variable_base_multiplication &component, std::uint32_t start_row_index) { + using mul_per_bit_component = + components::variable_base_multiplication_per_bit; + mul_per_bit_component component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + auto final_mul_per_bit_res = typename plonk_ed25519_mul_per_bit::result_type( + component_instance, start_row_index + component.rows_amount - component.mul_per_bit_subcomponent.rows_amount); + + + output.x = {final_mul_per_bit_res.output.x[0], + final_mul_per_bit_res.output.x[1], + final_mul_per_bit_res.output.x[2], + final_mul_per_bit_res.output.x[3]}; + output.y = {final_mul_per_bit_res.output.y[0], + final_mul_per_bit_res.output.y[1], + final_mul_per_bit_res.output.y[2], + final_mul_per_bit_res.output.y[3]}; + } + + std::vector> all_vars() { + return {output.x[0], output.x[1], output.x[2], output.x[3], + output.y[0], output.y[1], output.y[2], output.y[3]}; + } + }; + + template + explicit variable_base_multiplication(ContainerType witness, std::uint32_t bits_amount, + bit_composition_mode mode_) : + component_type(witness, {}, {}, get_manifest(bits_amount, mode_)), + decomposition_subcomponent(witness, bits_amount, mode_), + mul_per_bit_subcomponent(witness), + bool_scalar_mul_subcomponent(witness) {}; + + template + variable_base_multiplication(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::uint32_t bits_amount = 253, + bit_composition_mode mode_ = bit_composition_mode::MSB) : + component_type(witness, constant, public_input, get_manifest(bits_amount, mode_)), + decomposition_subcomponent(witness, constant, public_input, + bits_amount, mode_), + mul_per_bit_subcomponent(witness, constant, public_input), + bool_scalar_mul_subcomponent(witness, constant, public_input) {}; + + variable_base_multiplication( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::uint32_t bits_amount = 253, bit_composition_mode mode_ = bit_composition_mode::MSB) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount, mode_)), + decomposition_subcomponent(witnesses, constants, public_inputs, + bits_amount, mode_), + mul_per_bit_subcomponent(witnesses, constants, public_inputs), + bool_scalar_mul_subcomponent(witnesses, constants, public_inputs) {}; + }; + + template + using plonk_ed25519_var_base_mul = variable_base_multiplication< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + typename crypto3::algebra::curves::ed25519, + basic_non_native_policy>; + + template + typename plonk_ed25519_var_base_mul::result_type + generate_assignments( + const plonk_ed25519_var_base_mul &component, + assignment> &assignment, + const typename plonk_ed25519_var_base_mul::input_type instance_input, + const std::uint32_t start_row_index) { + + using component_type = + plonk_ed25519_var_base_mul; + using var = typename plonk_ed25519_mul_per_bit::var; + + using mul_per_bit_component = typename component_type::mul_per_bit_component; + using decomposition_component_type = typename component_type::decomposition_component_type; + using bool_scalar_mul_component = typename component_type::bool_scalar_mul_component; + + std::size_t row = start_row_index; + // check if T is zero + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + + typename decomposition_component_type::result_type bits = + generate_assignments(component.decomposition_subcomponent, + assignment, {instance_input.k}, row); + row += component.decomposition_subcomponent.rows_amount; + + typename bool_scalar_mul_component::result_type bool_mul_res = + generate_assignments(component.bool_scalar_mul_subcomponent, assignment, + typename bool_scalar_mul_component::input_type({{T_x, T_y}, bits.output[0]}), row); + row += component.bool_scalar_mul_subcomponent.rows_amount; + + typename mul_per_bit_component::result_type res_per_bit = + generate_assignments(component.mul_per_bit_subcomponent, assignment, + typename mul_per_bit_component::input_type({{T_x, T_y}, + {bool_mul_res.output.x, bool_mul_res.output.y}, bits.output[1]}), + row); + row += component.mul_per_bit_subcomponent.rows_amount; + + for (std::size_t i = 2; i < 253; i++) { + res_per_bit = generate_assignments(component.mul_per_bit_subcomponent, assignment, + typename mul_per_bit_component::input_type({{T_x, T_y}, + {res_per_bit.output.x, res_per_bit.output.y}, bits.output[i]}), row); + row += component.mul_per_bit_subcomponent.rows_amount; + } + + return typename plonk_ed25519_var_base_mul::result_type(component, start_row_index); + } + + template + typename plonk_ed25519_var_base_mul::result_type + generate_circuit( + const plonk_ed25519_var_base_mul &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_var_base_mul::input_type instance_input, + const std::uint32_t start_row_index) { + + using component_type = + plonk_ed25519_var_base_mul; + using var = typename plonk_ed25519_mul_per_bit::var; + + using mul_per_bit_component = typename component_type::mul_per_bit_component; + using decomposition_component_type = typename component_type::decomposition_component_type; + using bool_scalar_mul_component = typename component_type::bool_scalar_mul_component; + + + std::size_t row = start_row_index; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + + typename decomposition_component_type::result_type bits = + generate_circuit(component.decomposition_subcomponent, bp, assignment, {instance_input.k}, + row); + row += component.decomposition_subcomponent.rows_amount; + + typename bool_scalar_mul_component::result_type bool_mul_res = + generate_circuit(component.bool_scalar_mul_subcomponent, bp, assignment, + typename bool_scalar_mul_component::input_type({{T_x, T_y}, bits.output[0]}), row); + row += component.bool_scalar_mul_subcomponent.rows_amount; + + typename mul_per_bit_component::result_type res_per_bit = + generate_circuit(component.mul_per_bit_subcomponent, bp, assignment, + typename mul_per_bit_component::input_type({{T_x, T_y}, + {bool_mul_res.output.x, bool_mul_res.output.y}, bits.output[1]}), + row); + row += component.mul_per_bit_subcomponent.rows_amount; + + for (std::size_t i = 2; i < 253; i++) { + res_per_bit = generate_circuit(component.mul_per_bit_subcomponent, bp, assignment, + typename mul_per_bit_component::input_type({{T_x, T_y}, + {res_per_bit.output.x, res_per_bit.output.y}, bits.output[i]}), row); + row += component.mul_per_bit_subcomponent.rows_amount; + } + + return typename plonk_ed25519_var_base_mul::result_type(component, start_row_index); + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter< + plonk_ed25519_var_base_mul> { + + using component_type = + plonk_ed25519_var_base_mul; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + for (std::size_t i = 0; i < input.T.x.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.T.x[i]); + new_input.T.x[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.T.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.T.y[i]); + new_input.T.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + tmp_assignment.public_input(0, input.T.x.size() + input.T.y.size()) = + var_value(assignment, input.k); + new_input.k = var(0, input.T.x.size() + input.T.y.size(), + false, var::column_type::public_input); + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.T.x.size()) { + return input.T.x[variable.rotation]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size()) { + return input.T.y[variable.rotation - input.T.x.size()]; + } else { + return input.k; + } + } + }; + + template + class result_type_converter< + plonk_ed25519_var_base_mul> { + + using component_type = + plonk_ed25519_var_base_mul; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 4; i++) { + new_result.output.x[i] = component.move_var( + old_result.output.x[i], + start_row_index + component.line_mapping[old_result.output.x[i].rotation], + instance_input + ); + new_result.output.y[i] = component.move_var( + old_result.output.y[i], + start_row_index + component.line_mapping[old_result.output.y[i].rotation], + instance_input + ); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication_per_bit.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication_per_bit.hpp new file mode 100644 index 000000000..7b4913870 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/plonk/non_native/variable_base_multiplication_per_bit.hpp @@ -0,0 +1,423 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the MERKLE_TREE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_PER_BIT_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_PER_BIT_EDWARD25519_HPP + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class variable_base_multiplication_per_bit; + + template + class variable_base_multiplication_per_bit< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + Ed25519Type, + basic_non_native_policy>: + public plonk_component { + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount) { + return + doubling_component::get_rows_amount(witness_amount) + + complete_addition_component::get_rows_amount(witness_amount) + + bool_scalar_multiplication_component::get_rows_amount(witness_amount); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = typename component_type::manifest_type; + using non_native_policy_type = basic_non_native_policy; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using doubling_component = doubling< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using complete_addition_component = complete_addition< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using bool_scalar_multiplication_component = bool_scalar_multiplication< + ArithmetizationType, Ed25519Type, non_native_policy_type>; + + using non_native_range_component = components::range< + ArithmetizationType, typename Ed25519Type::base_field_type, non_native_policy_type>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return variable_base_multiplication_per_bit::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with( + non_native_range_component::get_gate_manifest(witness_amount)) + .merge_with(doubling_component::get_gate_manifest(witness_amount)) + .merge_with(complete_addition_component::get_gate_manifest(witness_amount)) + .merge_with(bool_scalar_multiplication_component::get_gate_manifest(witness_amount)); + + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(non_native_range_component::get_manifest()) + .merge_with(doubling_component::get_manifest()) + .merge_with(complete_addition_component::get_manifest()) + .merge_with(bool_scalar_multiplication_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount()); + constexpr static const std::size_t gates_amount = 0; + + struct input_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + + var_ec_point T; + var_ec_point R; + var k; + + std::vector> all_vars() { + return {T.x[0], T.x[1], T.x[2], T.x[3], T.y[0], T.y[1], T.y[2], T.y[3], + R.x[0], R.x[1], R.x[2], R.x[3], R.y[0], R.y[1], R.y[2], R.y[3], + k}; + } + }; + + struct result_type { + struct var_ec_point { + typename non_native_policy_type::template field::non_native_var_type x; + typename non_native_policy_type::template field::non_native_var_type y; + }; + var_ec_point output; + + result_type(const variable_base_multiplication_per_bit &component, std::uint32_t start_row_index) { + using complete_addition_component = + components::complete_addition>; + complete_addition_component component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + auto final_addition_res = typename plonk_ed25519_complete_addition::result_type( + component_instance, start_row_index + component.rows_amount - complete_addition_component::get_rows_amount(component.witness_amount())); + + output.x = {final_addition_res.output.x[0], + final_addition_res.output.x[1], + final_addition_res.output.x[2], + final_addition_res.output.x[3]}; + output.y = {final_addition_res.output.y[0], + final_addition_res.output.y[1], + final_addition_res.output.y[2], + final_addition_res.output.y[3]}; + } + + std::vector> all_vars() { + return {output.x[0], output.x[1], output.x[2], output.x[3], + output.y[0], output.y[1], output.y[2], output.y[3]}; + } + }; + + template + explicit variable_base_multiplication_per_bit(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + variable_base_multiplication_per_bit(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + variable_base_multiplication_per_bit(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_ed25519_mul_per_bit = variable_base_multiplication_per_bit< + crypto3::zk::snark::plonk_constraint_system, + CurveType, + typename crypto3::algebra::curves::ed25519, + basic_non_native_policy>; + + + template + typename plonk_ed25519_mul_per_bit::result_type + generate_assignments( + const plonk_ed25519_mul_per_bit &component, + assignment> &assignment, + const typename plonk_ed25519_mul_per_bit::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = basic_non_native_policy; + using var = typename plonk_ed25519_mul_per_bit::var; + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using doubling_component = doubling< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using complete_addition_component = complete_addition< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using bool_scalar_multiplication_component = bool_scalar_multiplication< + ArithmetizationType, Ed25519Type, non_native_policy_type>; + + doubling_component doubling_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + + complete_addition_component complete_addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + + bool_scalar_multiplication_component bool_scalar_multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + std::size_t row = start_row_index; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + std::array R_x = instance_input.R.x; + std::array R_y = instance_input.R.y; + + typename bool_scalar_multiplication_component::result_type bool_mul_res = + generate_assignments(bool_scalar_multiplication_instance, assignment, + typename bool_scalar_multiplication_component::input_type({{T_x, T_y}, instance_input.k}), row); + row += bool_scalar_multiplication_instance.rows_amount; + + typename doubling_component::result_type doubling_res = + generate_assignments(doubling_instance, assignment, + typename doubling_component::input_type({R_x, R_y}), row); + row += doubling_instance.rows_amount; + + // add_res + generate_assignments(complete_addition_instance, assignment, + typename complete_addition_component::input_type( + {{doubling_res.output.x, doubling_res.output.y}, + {bool_mul_res.output.x, bool_mul_res.output.y}}), row); + row += complete_addition_instance.rows_amount; + + return typename plonk_ed25519_mul_per_bit::result_type(component, start_row_index); + + } + + template + typename plonk_ed25519_mul_per_bit::result_type + generate_circuit( + const plonk_ed25519_mul_per_bit &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_ed25519_mul_per_bit::input_type instance_input, + const std::uint32_t start_row_index) { + + using non_native_policy_type = basic_non_native_policy; + using var = typename plonk_ed25519_mul_per_bit::var; + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using doubling_component = doubling< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using complete_addition_component = complete_addition< + ArithmetizationType, CurveType, Ed25519Type, non_native_policy_type>; + + using bool_scalar_multiplication_component = bool_scalar_multiplication< + ArithmetizationType, Ed25519Type, non_native_policy_type>; + + doubling_component doubling_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + + complete_addition_component complete_addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + + bool_scalar_multiplication_component bool_scalar_multiplication_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{},{}); + + std::size_t row = start_row_index; + std::array T_x = instance_input.T.x; + std::array T_y = instance_input.T.y; + std::array R_x = instance_input.R.x; + std::array R_y = instance_input.R.y; + + typename bool_scalar_multiplication_component::result_type bool_mul_res = + generate_circuit(bool_scalar_multiplication_instance, bp, assignment, + typename bool_scalar_multiplication_component::input_type({{T_x, T_y}, instance_input.k}), row); + row += bool_scalar_multiplication_instance.rows_amount; + + typename doubling_component::result_type doubling_res = + generate_circuit(doubling_instance, bp, assignment, + typename doubling_component::input_type({R_x, R_y}), row); + row += doubling_instance.rows_amount; + + // add_res + generate_circuit(complete_addition_instance, bp, assignment, + typename complete_addition_component::input_type( + {{doubling_res.output.x, doubling_res.output.y}, + {bool_mul_res.output.x, bool_mul_res.output.y}}), row); + row += complete_addition_instance.rows_amount; + + return typename plonk_ed25519_mul_per_bit::result_type(component, start_row_index); + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter< + plonk_ed25519_mul_per_bit> { + + using component_type = + plonk_ed25519_mul_per_bit; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + for (std::size_t i = 0; i < input.T.x.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.T.x[i]); + new_input.T.x[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.T.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.T.y[i]); + new_input.T.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.R.x.size(); i++) { + std::size_t new_idx = input.T.x.size() + input.T.y.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.R.x[i]); + new_input.R.x[i] = var(0, new_idx, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.R.y.size(); i++) { + std::size_t new_idx = input.T.x.size() + input.T.y.size() + input.R.x.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.R.y[i]); + new_input.R.y[i] = var(0, new_idx, false, var::column_type::public_input); + } + tmp_assignment.public_input(0, input.T.x.size() + input.T.y.size() + + input.R.x.size() + input.R.y.size()) = + var_value(assignment, input.k); + new_input.k = var(0, input.T.x.size() + input.T.y.size() + input.R.x.size() + input.R.y.size(), + false, var::column_type::public_input); + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.T.x.size()) { + return input.T.x[variable.rotation]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size()) { + return input.T.y[variable.rotation - input.T.x.size()]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size() + input.R.x.size()) { + return input.R.x[variable.rotation - input.T.x.size() - input.T.y.size()]; + } else if (std::size_t(variable.rotation) < input.T.x.size() + input.T.y.size() + + input.R.x.size() + input.R.y.size()) { + return input.R.y[variable.rotation - input.T.x.size() - input.T.y.size() - input.R.x.size()]; + } else { + return input.k; + } + } + }; + + template + class result_type_converter< + plonk_ed25519_mul_per_bit> { + + using component_type = + plonk_ed25519_mul_per_bit; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 4; i++) { + new_result.output.x[i] = component.move_var( + old_result.output.x[i], + start_row_index + component.line_mapping[old_result.output.x[i].rotation], + instance_input + ); + new_result.output.y[i] = component.move_var( + old_result.output.y[i], + start_row_index + component.line_mapping[old_result.output.y[i].rotation], + instance_input + ); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/r1cs/element_g1.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/r1cs/element_g1.hpp new file mode 100644 index 000000000..cbc9ea404 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/edwards/r1cs/element_g1.hpp @@ -0,0 +1,406 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G1 components. +// +// TODO: Change the curve equation +// The components verify curve arithmetic in G1 = E(F) where E/F: y^2 = x^3 + A * X + B +// is an elliptic curve over F in short Weierstrass form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /** + * Component that represents a G1 element for JubJub/Bls12-381 and BabyJubJub/Alt-BN128. + * + * CurveType is BLS12-381 or BN128 + */ + template + class element_g1 : public component { + using underlying_field_type = typename CurveType::scalar_field_type; + using underlying_element_type = element_fp; + + public: + underlying_element_type X; + underlying_element_type Y; + + blueprint_linear_combination_vector all_vars; + + element_g1(blueprint &bp) : component(bp) { + blueprint_variable X_var, Y_var; + + X_var.allocate(bp); + Y_var.allocate(bp); + + X = underlying_element_type(X_var); + Y = underlying_element_type(Y_var); + + all_vars.emplace_back(X); + all_vars.emplace_back(Y); + } + + element_g1( + blueprint &bp, + const typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type &P) : + component(bp) { + + // typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type Pcopy = + // P.to_affine(); + + X.assign(bp, P.X); + Y.assign(bp, P.Y); + X.evaluate(bp); + Y.evaluate(bp); + all_vars.emplace_back(X); + all_vars.emplace_back(Y); + } + + void generate_assignments( + const typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type &el) { + typename CurveType::pairing::chained_curve_type::template g1_type<>::value_type el_normalized = + el.to_affine(); + + this->bp.lc_val(X) = el_normalized.X; + this->bp.lc_val(Y) = el_normalized.Y; + } + + // (See a comment in r1cs_ppzksnark_verifier_component.hpp about why + // we mark this function noinline.) TODO: remove later + static std::size_t __attribute__((noinline)) size_in_bits() { + return 2 * scalar_field_type::modulus_bits; // This probably should be value_bits, not + // modulus_bits + } + static std::size_t num_variables() { + return 2; + } + }; + + /** + * Component that creates constraints for the validity of a G1 element. + * (if element from group G1 lies on the elliptic curve) + */ + template + class element_g1_is_well_formed : public component { + typedef typename CurveType::scalar_field_type scalar_field_type; + + public: + element_g1 P; + + blueprint_variable a; + blueprint_variable d; + + // Intermeditate variables: + blueprint_variable XX; + blueprint_variable aXX; + blueprint_variable dXX; + blueprint_variable YY; + blueprint_variable dXXYY; + blueprint_variable lhs; + blueprint_variable rhs; + + element_g1_is_well_formed(blueprint &bp, + blueprint_variable + a, + blueprint_variable + d, + const element_g1 &P) : + component(bp), + P(P), a(a), d(d) { + + XX.allocate(this->bp); + aXX.allocate(this->bp); + dXX.allocate(this->bp); + YY.allocate(this->bp); + dXXYY.allocate(this->bp); + lhs.allocate(this->bp); + rhs.allocate(this->bp); + } + void generate_gates() { + // A check, that a*X*X + Y*Y = 1 + d*X*X*Y*Y + + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P.X}, {P.X}, {XX})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P.Y}, {P.Y}, {YY})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({a}, {XX}, {aXX})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {aXX, YY}, {scalar_field_type::value_type::one()}, {lhs})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({d}, {XX}, {dXX})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({dXX}, {YY}, {dXXYY})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({dXXYY, scalar_field_type::value_type::one()}, + {scalar_field_type::value_type::one()}, + {rhs})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {lhs}, {scalar_field_type::value_type::one()}, {rhs})); + } + void generate_assignments() { + typename scalar_field_type::value_type x = this->bp.lc_val(this->P.X); + typename scalar_field_type::value_type y = this->bp.lc_val(this->P.Y); + typename scalar_field_type::value_type temp_a = this->bp.val(this->a); + typename scalar_field_type::value_type temp_d = this->bp.val(this->d); + + // this->bp.val(this->P.X) = x; + // this->bp.val(this->P.Y) = y; + // this->bp.val(this->a) = temp_a; + // this->bp.val(this->d) = temp_d; + + this->bp.val(this->XX) = x * x; + this->bp.val(this->YY) = y * y; + this->bp.val(this->aXX) = temp_a * x * x; + this->bp.val(this->lhs) = temp_a * x * x + y * y; + this->bp.val(this->dXX) = x * x * temp_d; + this->bp.val(this->dXXYY) = temp_d * x * x * y * y; + + this->bp.val(this->rhs) = temp_d * x * x * y * y + scalar_field_type::value_type::one(); + } + }; + + /** + * Component that creates constraints for the validity of a G1 element. + * (if element from group G1 lies on the elliptic curve) + */ + template + class element_g1_add : public component { + typedef typename CurveType::scalar_field_type scalar_field_type; + + public: + blueprint_variable a; + blueprint_variable d; + + element_g1 P1; + element_g1 P2; + element_g1 P1pP2; + + // std::shared_ptr> el_is_well_formed; + + // intermeditate variables + blueprint_variable X1X2; + blueprint_variable X1Y2; + blueprint_variable Y1Y2; + blueprint_variable Y1X2; + blueprint_variable X1X2Y1Y2; + blueprint_variable dX1X2Y1Y2; + blueprint_variable aX1X2; + + element_g1_add(blueprint &bp, + blueprint_variable + a, + blueprint_variable + d, + const element_g1 &P1, + const element_g1 &P2, + const element_g1 &P1pP2) : + component(bp), + P1(P1), P2(P2), P1pP2(P1pP2), a(a), d(d) { + + // el_is_well_formed.reset( + // new element_g1_is_well_formed ( + // this->bp, a, d, P1pP2)); + + X1X2.allocate(this->bp); + X1Y2.allocate(this->bp); + Y1Y2.allocate(this->bp); + Y1X2.allocate(this->bp); + X1X2Y1Y2.allocate(this->bp); + dX1X2Y1Y2.allocate(this->bp); + aX1X2.allocate(this->bp); + } + void generate_gates() { + // A check, that + // X3 = (X1*Y2 + Y1*X2) / (Fq.ONE + D*X1*X2*Y1*Y2) + // y3 = (Y1*Y2 - A*X1*X2) / (Fq.ONE - D*X1*X2*Y1*Y2) + + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P1.Y}, {P2.X}, {Y1X2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P1.X}, {P2.Y}, {X1Y2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P1.X}, {P2.X}, {X1X2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({P1.Y}, {P2.Y}, {Y1Y2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({X1X2}, {Y1Y2}, {X1X2Y1Y2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({d}, {X1X2Y1Y2}, {dX1X2Y1Y2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint({a}, {X1X2}, {aX1X2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {P1pP2.Y}, {scalar_field_type::value_type::one(), -dX1X2Y1Y2}, {Y1Y2, -aX1X2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {P1pP2.X}, {scalar_field_type::value_type::one(), dX1X2Y1Y2}, {X1Y2, Y1X2})); + } + void generate_assignments() { + + typename scalar_field_type::value_type x1 = this->bp.lc_val(this->P1.X); + typename scalar_field_type::value_type y1 = this->bp.lc_val(this->P1.Y); + typename scalar_field_type::value_type x2 = this->bp.lc_val(this->P2.X); + typename scalar_field_type::value_type y2 = this->bp.lc_val(this->P2.Y); + typename scalar_field_type::value_type temp_a = this->bp.lc_val(this->a); + typename scalar_field_type::value_type temp_d = this->bp.lc_val(this->d); + + this->bp.val(X1X2) = x1 * x2; + this->bp.val(X1Y2) = x1 * y2; + this->bp.val(Y1Y2) = y1 * y2; + this->bp.val(Y1X2) = y1 * x2; + this->bp.val(X1X2Y1Y2) = x1 * x2 * y1 * y2; + this->bp.val(dX1X2Y1Y2) = temp_d * x1 * x2 * y1 * y2; + this->bp.val(aX1X2) = temp_a * x1 * x2; + + this->bp.lc_val(P1pP2.X) = + (x1 * y2 + y1 * x2) * + ((scalar_field_type::value_type::one() + (temp_d * x1 * x2 * y1 * y2)).inversed()); + this->bp.lc_val(P1pP2.Y) = + (y1 * y2 - temp_a * x1 * x2) * + ((scalar_field_type::value_type::one() - (temp_d * x1 * x2 * y1 * y2)).inversed()); + + // el_is_well_formed->generate_assignments(); + } + }; + + /** + * Component that creates constraints for the validity of a G1 element. + */ + template + class element_g1_conditional_add : public component { + typedef typename CurveType::scalar_field_type scalar_field_type; + + public: + blueprint_variable a; + blueprint_variable d; + + element_g1 P1; + element_g1 P2; + element_g1 P1pP2; + + blueprint_variable canAdd; + + // intermeditate variables + element_g1 P_toAdd; + // blueprint_variable x_toAdd; + // blueprint_variable y_toAdd; + blueprint_variable Y_intermediate_toAdd1; + blueprint_variable Y_intermediate_toAdd2; + blueprint_variable not_canAdd; + + std::shared_ptr> el_add; + + element_g1_conditional_add(blueprint &bp, + blueprint_variable + a, + blueprint_variable + d, + const element_g1 &P1, + const element_g1 &P2, + const element_g1 &P1pP2, + blueprint_variable + canAdd) : + component(bp), + P1(P1), P2(P2), P1pP2(P1pP2), a(a), d(d), canAdd(canAdd), P_toAdd() { + + Y_intermediate_toAdd1.allocate(this->bp); + Y_intermediate_toAdd2.allocate(this->bp); + + not_canAdd.allocate(this->bp); + + el_add.reset(new element_g1_add(this->bp, a, d, P1, P_toAdd, P1pP2)); + } + + void generate_gates() { + // if coef == 1 then x_ret[i] + x_base + // x_add[i] = coef[i] * x_base; + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({P2.X}, {canAdd}, {P_toAdd.X})); + + // else do nothing. Ie add the zero point (0, 1) + // y_add[i] = coef[i] * y_base + !coef[i]; + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({P2.Y}, {canAdd}, {Y_intermediate_toAdd1})); + + // not coef + // make sure canAdd == 0 or canAdd == 1 + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(canAdd, + scalar_field_type::value_type::one() - canAdd, + scalar_field_type::value_type::zero())); + + // make sure not_canAdd == 0 or not_canAdd == 1 + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(not_canAdd, + scalar_field_type::value_type::one() - not_canAdd, + scalar_field_type::value_type::zero())); + + // make sure that the sum of canAdd, not_canAdd == 1 which means canAdd!=not_canAdd + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({not_canAdd, canAdd}, + {scalar_field_type::value_type::one()}, + {scalar_field_type::value_type::one()})); + + // because the are bool and because they are not equal we know that the inverse of one + // is the other. + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {not_canAdd}, {scalar_field_type::value_type::one()}, {Y_intermediate_toAdd2})); + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({Y_intermediate_toAdd1, Y_intermediate_toAdd2}, + {scalar_field_type::value_type::one()}, + {P_toAdd.Y})); + + // do the addition of either y1 , y1 plus x2, y2 if canAdd == true else x1 , y1 + 0 + el_add->generate_gates(); + } + void generate_assignments() { + this->bp.lc_val(P_toAdd.X) = this->bp.lc_val(this->P2.X) * this->bp.val(this->canAdd); + + this->bp.val(this->Y_intermediate_toAdd1) = + this->bp.lc_val(this->P2.Y) * this->bp.val(this->canAdd); + + if (this->bp.val(this->canAdd) == scalar_field_type::value_type::one()) { + + this->bp.val(this->not_canAdd) = scalar_field_type::value_type::zero(); + this->bp.val(this->Y_intermediate_toAdd2) = + this->bp.val(this->not_canAdd) * scalar_field_type::value_type::one(); + this->bp.lc_val(this->P_toAdd.Y) = this->bp.val(this->Y_intermediate_toAdd1); + + } else { + + this->bp.val(this->not_canAdd) = scalar_field_type::value_type::one(); + this->bp.val(this->Y_intermediate_toAdd2) = + this->bp.val(this->not_canAdd) * scalar_field_type::value_type::one(); + this->bp.lc_val(this->P_toAdd.Y) = scalar_field_type::value_type::one(); + // this->bp.lc_val(this->Y_intermediate_toAdd2)); + } + + el_add->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/montgomery/element_g1.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/montgomery/element_g1.hpp new file mode 100644 index 000000000..314e2854c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/montgomery/element_g1.hpp @@ -0,0 +1,190 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G1 components. +// +// The components verify curve arithmetic in G1 = E(F) where E/F: b * y^2 = x^3 + a * x^2 + x +// is an elliptic curve over F in Montgomery form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MONTGOMERY_G1_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MONTGOMERY_G1_COMPONENT_HPP + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /** + * @brief Component that creates constraints for the addition of two elements from G1. (if element from + * group G1 lies on the elliptic curve) + */ + template + struct element_g1_addition + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::montgomery; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + + using result_type = element_component; + + const element_component p1; + const element_component p2; + element_component result; + element_fp lambda; + + /// Auto allocation of the result + element_g1_addition(blueprint &bp, + const element_component &in_p1, + const element_component &in_p2) : + component(bp), + p1(in_p1), p2(in_p2), result(bp) { + detail::blueprint_variable lambda_var; + lambda_var.allocate(this->bp); + this->lambda = lambda_var; + } + + /// Manual allocation of the result + element_g1_addition(blueprint &bp, + const element_component &in_p1, + const element_component &in_p2, + const result_type &in_result) : + component(bp), + p1(in_p1), p2(in_p2), result(in_result) { + detail::blueprint_variable lambda_var; + lambda_var.allocate(this->bp); + this->lambda = lambda_var; + } + + void generate_gates() { + // lambda = (y' - y) / (x' - x) + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->p2.X - this->p1.X}, {this->lambda}, {this->p2.Y - this->p1.Y})); + // (lambda) * (lambda) = (A + x + x' + x'') + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->lambda}, + {this->lambda}, + {group_type::params_type::A + this->p1.X + this->p2.X + this->result.X})); + // y'' = -(y + lambda(x'' - x)) + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->p1.X - this->result.X}, this->lambda, {this->result.Y + this->p1.Y})); + } + + void generate_assignments() { + this->bp.lc_val(this->lambda) = + (this->bp.lc_val(this->p2.Y) - this->bp.lc_val(this->p1.Y)) * + (this->bp.lc_val(this->p2.X) - this->bp.lc_val(this->p1.X)).inversed(); + this->bp.lc_val(this->result.X) = this->bp.lc_val(this->lambda).squared() - + group_type::params_type::A - this->bp.lc_val(this->p1.X) - + this->bp.lc_val(this->p2.X); + this->bp.lc_val(this->result.Y) = + -(this->bp.lc_val(this->p1.Y) + + (this->bp.lc_val(this->lambda) * + (this->bp.lc_val(this->result.X) - this->bp.lc_val(this->p1.X)))); + } + }; + + /** + * Gadget to convert affine Montgomery coordinates into affine twisted Edwards coordinates. + */ + template + struct element_g1_to_twisted_edwards + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::montgomery; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + using to_element_component = + element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + using to_group_type = typename to_element_component::group_type; + + using result_type = to_element_component; + + // Input point + const element_component p; + // Output point + result_type result; + // Intermediate variables + typename field_type::value_type scale; + + /// Auto allocation of the result + element_g1_to_twisted_edwards(blueprint &bp, const element_component &in_p) : + component(bp), p(in_p), result(bp), + scale((static_cast(4) / + (static_cast(to_group_type::params_type::a) - + static_cast(to_group_type::params_type::d)) / + static_cast(group_type::params_type::B)) + .sqrt()) { + } + + /// Manual allocation of the result + element_g1_to_twisted_edwards(blueprint &bp, const element_component &in_p, + const result_type &in_result) : + component(bp), + p(in_p), result(in_result), + scale((static_cast(4) / + (static_cast(to_group_type::params_type::a) - + static_cast(to_group_type::params_type::d)) / + static_cast(group_type::params_type::B)) + .sqrt()) { + } + + void generate_gates() { + this->bp.add_r1cs_constraint(snark::r1cs_constraint({this->p.Y}, {this->result.X}, + {this->p.X * this->scale})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p.X + field_type::value_type::one()}, + {this->result.Y}, + {this->p.X - field_type::value_type::one()})); + } + + void generate_assignments() { + typename to_group_type::value_type p_to_XY = + typename group_type::value_type(this->bp.lc_val(p.X), this->bp.lc_val(p.Y)) + .to_twisted_edwards(); + this->bp.lc_val(result.X) = p_to_XY.X; + this->bp.lc_val(result.Y) = p_to_XY.Y; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MONTGOMERY_G1_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/addition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/addition.hpp new file mode 100644 index 000000000..f6eee8877 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/addition.hpp @@ -0,0 +1,114 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ADDITION_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ADDITION_COMPONENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_addition; + + template + class element_g1_addition, + CurveType, + W0, + W1, + W2, + W3, + W4, + W5, + W6> : public component { + + typedef snark::plonk_constraint_system arithmetization_type; + typedef blueprint blueprint_type; + + std::size_t i; + + public: + element_g1_addition(blueprint_type &bp) : component(bp) { + i = bp.allocate_row(); + } + + void generate_gates() { + typename blueprint_type::variable_type x_1( + W0, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type y_1( + W1, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type x_2( + W2, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type y_2( + W3, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type x_3( + W4, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type y_3( + W5, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type r(W6, + blueprint_type::variable_type::rotation_type::current); + + bp.add_gate(i, (x_2 - x_1) * (y_1 + y_3) - (y_1 - y_2) * (x_1 - x_3)); + bp.add_gate(i, (x_1 + x_2 + x_3) * (x_1 - x_3) ^ 2 - (y_1 + y_3) ^ 2); + bp.add_gate(i, (x_2 - x_1) * r - 1); + } + + void generate_assignments(typename CurveType::value_type &P1, typename CurveType::value_type &P2) { + generate_assignments(P1, P2, P1 + P2); + } + + void generate_assignments(typename CurveType::value_type &P1, + typename CurveType::value_type &P2, + typename CurveType::value_type &P3) { + bp.val(W0, i) = P1.X; + bp.val(W1, i) = P1.Y; + bp.val(W2, i) = P2.X; + bp.val(W3, i) = P2.Y; + bp.val(W4, i) = P3.X; + bp.val(W5, i) = P3.Y; + bp.val(W6, i) = ? ; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ADDITION_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/doubling.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/doubling.hpp new file mode 100644 index 000000000..eb5629678 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/doubling.hpp @@ -0,0 +1,102 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_DOUBLING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_DOUBLING_COMPONENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_doubling; + + template + class element_g1_doubling, + CurveType, + W0, + W1, + W2, + W3, + W6> : public component { + + typedef snark::plonk_constraint_system arithmetization_type; + typedef blueprint blueprint_type; + + std::size_t i; + + public: + element_g1_doubling(blueprint_type &bp) : component(bp) { + i = bp.allocate_row(); + } + + void generate_gates() { + typename blueprint_type::variable_type x_1( + W0, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type y_1( + W1, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type x_2( + W2, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type y_2( + W3, blueprint_type::variable_type::rotation_type::current); + typename blueprint_type::variable_type r(W6, + blueprint_type::variable_type::rotation_type::current); + + bp.add_gate(i, 4 * y_1 ^ 2 * (x_2 + 2 * x_1) - 9 * x_1 ^ 4); + bp.add_gate(i, 2 * y_1 * (y_2 + y_1) - 3 * x_1 ^ 2 * (x_1 - x_2)); + bp.add_gate(i, y_1 * r_1 - 1); + } + + void generate_assignments(typename CurveType::value_type &P1) { + generate_assignments(P1, P1.doubled()); + } + + void generate_assignments(typename CurveType::value_type &P1, typename CurveType::value_type &P2) { + bp.val(W0, i) = P1.X; + bp.val(W1, i) = P1.Y; + bp.val(W2, i) = P2.X; + bp.val(W3, i) = P2.Y; + bp.val(W6, i) = ? ; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_DOUBLING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/endo_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/endo_scalar.hpp new file mode 100644 index 000000000..e3d549ebf --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/endo_scalar.hpp @@ -0,0 +1,371 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class endo_scalar; + // Input: x + // Output: y + // Such as: + // mul(x, G) = endomul(y, G), for G \in E(F) + + template + struct endo_scalar_params; + + template<> + struct endo_scalar_params { + using curve_type = nil::crypto3::algebra::curves::vesta; + using scalar_field_type = typename curve_type::scalar_field_type; + using base_field_type = typename curve_type::base_field_type; + constexpr static const typename scalar_field_type::value_type endo_r = + 0x12CCCA834ACDBA712CAAD5DC57AAB1B01D1F8BD237AD31491DAD5EBDFDFE4AB9_cppui_modular255; + constexpr static const typename base_field_type::value_type endo_q = + 0x2D33357CB532458ED3552A23A8554E5005270D29D19FC7D27B7FD22F0201B547_cppui_modular255; + }; + + template<> + struct endo_scalar_params { + using curve_type = nil::crypto3::algebra::curves::pallas; + using scalar_field_type = typename curve_type::scalar_field_type; + using base_field_type = typename curve_type::base_field_type; + constexpr static const typename scalar_field_type::value_type endo_r = + 0x397E65A7D7C1AD71AEE24B27E308F0A61259527EC1D4752E619D1840AF55F1B1_cppui_modular255; + constexpr static const typename base_field_type::value_type endo_q = + 0x2D33357CB532458ED3552A23A8554E5005270D29D19FC7D27B7FD22F0201B547_cppui_modular255; + }; + + template + class endo_scalar, CurveType>: + public plonk_component { + + using endo_params = endo_scalar_params; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return endo_scalar::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 8; + } + + const std::size_t scalar_size; + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + static constexpr std::size_t gates_amount = 2; + + constexpr static const typename BlueprintFieldType::value_type endo_r = endo_params::endo_r; + constexpr static const typename CurveType::base_field_type::value_type endo_q = endo_params::endo_q; + + struct input_type { + var scalar; + + std::vector> all_vars() { + return {scalar}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const endo_scalar &component, const input_type ¶ms, std::size_t start_row_index) { + output = var(component.W(6), start_row_index + component.rows_amount - 1, + false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + endo_scalar(ContainerType witness, std::size_t scalar_size_): + component_type(witness, {}, {}, get_manifest()), + scalar_size(scalar_size_) {}; + + template + endo_scalar(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input, std::size_t scalar_size_): + component_type(witness, constant, public_input, get_manifest()), + scalar_size(scalar_size_) {}; + + endo_scalar( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t scalar_size_): + component_type(witnesses, constants, public_inputs, get_manifest()), + scalar_size(scalar_size_) {}; + + }; + + template + using plonk_endo_scalar = + endo_scalar< + crypto3::zk::snark::plonk_constraint_system, + CurveType + >; + + template + typename plonk_endo_scalar::result_type + generate_circuit( + const plonk_endo_scalar &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_endo_scalar::input_type instance_input, + const std::uint32_t start_row_index) { + + std::array selector_indices = + generate_gates(component, bp, assignment, instance_input); + + std::size_t j = start_row_index; + assignment.enable_selector(selector_indices[0], j, j + component.rows_amount - 1); + assignment.enable_selector(selector_indices[1], j + component.rows_amount - 1); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_endo_scalar::result_type(component, instance_input, start_row_index); + } + + template + typename plonk_endo_scalar::result_type + generate_assignments( + const plonk_endo_scalar &component, + assignment> &assignment, + const typename plonk_endo_scalar::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + const std::size_t crumbs_per_row = 8; + const std::size_t bits_per_crumb = 2; + const std::size_t bits_per_row = + bits_per_crumb * crumbs_per_row; // we suppose that scalar_size % bits_per_row = 0 + + typename BlueprintFieldType::value_type scalar = var_value(assignment, instance_input.scalar); + typename BlueprintFieldType::integral_type integral_scalar = + typename BlueprintFieldType::integral_type(scalar.data); + std::vector bits_msb(component.scalar_size); + { + nil::marshalling::status_type status; + assert(component.scalar_size <= BlueprintFieldType::modulus_bits); + + std::array bits_msb_all = + nil::marshalling::pack(integral_scalar, status); + + assert(status == nil::marshalling::status_type::success); + + std::copy(bits_msb_all.end() - component.scalar_size, bits_msb_all.end(), bits_msb.begin()); + + for(std::size_t i = 0; i < BlueprintFieldType::modulus_bits - component.scalar_size; ++i) { + assert(bits_msb_all[i] == false); + } + } + typename BlueprintFieldType::value_type a = 2; + typename BlueprintFieldType::value_type b = 2; + typename BlueprintFieldType::value_type n = 0; + + assert (component.scalar_size % bits_per_row == 0); + for (std::size_t chunk_start = 0; chunk_start < bits_msb.size(); chunk_start += bits_per_row) { + assignment.witness(component.W(0), row) = n; + assignment.witness(component.W(2), row) = a; + assignment.witness(component.W(3), row) = b; + + for (std::size_t j = 0; j < crumbs_per_row; j++) { + std::size_t crumb = chunk_start + j * bits_per_crumb; + typename BlueprintFieldType::value_type b0 = static_cast(bits_msb[crumb + 1]); + typename BlueprintFieldType::value_type b1 = static_cast(bits_msb[crumb + 0]); + + typename BlueprintFieldType::value_type crumb_value = b0 + b1.doubled(); + assignment.witness(component.W(7 + j), row) = crumb_value; + + a = a.doubled(); + b = b.doubled(); + + typename BlueprintFieldType::value_type s = + (b0 == BlueprintFieldType::value_type::one()) ? 1 : -1; + + if (b1 == BlueprintFieldType::value_type::zero()) { + b += s; + } else { + a += s; + } + + n = (n.doubled()).doubled(); + n += crumb_value; + } + + assignment.witness(component.W(1), row) = n; + assignment.witness(component.W(4), row) = a; + assignment.witness(component.W(5), row) = b; + row++; + } + auto res = a * component.endo_r + b; + assignment.witness(component.W(6), row - 1) = res; + return typename plonk_endo_scalar::result_type(component, instance_input, start_row_index); + } + + template + std::array generate_gates( + const plonk_endo_scalar &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_endo_scalar::input_type instance_input) { + + using F = typename BlueprintFieldType::value_type; + using var = + typename plonk_endo_scalar::var; + + auto c_f = [](var x) { + return (F(11) * F(6).inversed()) * x + (-F(5) * F(2).inversed()) * x * x + + (F(2) * F(3).inversed()) * x * x * x; + }; + + auto d_f = [](var x) { + return -F::one() + (F(29) * F(6).inversed()) * x + (-F(7) * F(2).inversed()) * x * x + + (F(2) * F(3).inversed()) * x * x * x; + }; + + auto constraint_1 = + var(component.W(7), 0) * (var(component.W(7), 0) - 1) * + (var(component.W(7), 0) - 2) * (var(component.W(7), 0) - 3); + auto constraint_2 = + var(component.W(8), 0) * + (var(component.W(8), 0) - 1) * (var(component.W(8), 0) - 2) * (var(component.W(8), 0) - 3); + auto constraint_3 = + var(component.W(9), 0) * (var(component.W(9), 0) - 1) * + (var(component.W(9), 0) - 2) * (var(component.W(9), 0) - 3); + auto constraint_4 = + var(component.W(10), 0) * (var(component.W(10), 0) - 1) * + (var(component.W(10), 0) - 2) * (var(component.W(10), 0) - 3); + auto constraint_5 = + var(component.W(11), 0) * (var(component.W(11), 0) - 1) * + (var(component.W(11), 0) - 2) * (var(component.W(11), 0) - 3); + auto constraint_6 = + var(component.W(12), 0) * (var(component.W(12), 0) - 1) * + (var(component.W(12), 0) - 2) * (var(component.W(12), 0) - 3); + auto constraint_7 = + var(component.W(13), 0) * (var(component.W(13), 0) - 1) * + (var(component.W(13), 0) - 2) * (var(component.W(13), 0) - 3); + auto constraint_8 = + var(component.W(14), 0) * (var(component.W(14), 0) - 1) * + (var(component.W(14), 0) - 2) * (var(component.W(14), 0) - 3); + auto constraint_9 = + var(component.W(4), 0) - (256 * var(component.W(2), 0) + 128 * c_f(var(component.W(7), 0)) + 64 * c_f(var(component.W(8), 0)) + + 32 * c_f(var(component.W(9), 0)) + 16 * c_f(var(component.W(10), 0)) + 8 * c_f(var(component.W(11), 0)) + + 4 * c_f(var(component.W(12), 0)) + 2 * c_f(var(component.W(13), 0)) + c_f(var(component.W(14), 0))); + auto constraint_10 = + var(component.W(5), 0) - (256 * var(component.W(3), 0) + 128 * d_f(var(component.W(7), 0)) + 64 * d_f(var(component.W(8), 0)) + + 32 * d_f(var(component.W(9), 0)) + 16 * d_f(var(component.W(10), 0)) + 8 * d_f(var(component.W(11), 0)) + + 4 * d_f(var(component.W(12), 0)) + 2 * d_f(var(component.W(13), 0)) + d_f(var(component.W(14), 0))); + auto constraint_11 = + var(component.W(1), 0) - ((1 << 16) * var(component.W(0), 0) + (1 << 14) * var(component.W(7), 0) + (1 << 12) * var(component.W(8), 0) + + (1 << 10) * var(component.W(9), 0) + (1 << 8) * var(component.W(10), 0) + (1 << 6) * var(component.W(11), 0) + + (1 << 4) * var(component.W(12), 0) + (1 << 2) * var(component.W(13), 0) + var(component.W(14), 0)); + + auto constraint_12 = var(component.W(6), 0) - + (component.endo_r * var(component.W(4), 0) + var(component.W(5), 0)); + + std::size_t selector_index_1 = bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7, constraint_8, constraint_9, constraint_10, constraint_11}); + + std::size_t selector_index_2 = bp.add_gate({constraint_12}); + + return {selector_index_1, selector_index_2}; + } + + template + void generate_copy_constraints( + const plonk_endo_scalar &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_endo_scalar::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t j = start_row_index; + + for (std::size_t z = 1; z < component.rows_amount; z++) { + bp.add_copy_constraint( + {{component.W(0), static_cast(j + z), false}, {component.W(1), static_cast(j + z - 1), false}}); + bp.add_copy_constraint( + {{component.W(2), static_cast(j + z), false}, {component.W(4), static_cast(j + z - 1), false}}); + bp.add_copy_constraint( + {{component.W(3), static_cast(j + z), false}, {component.W(5), static_cast(j + z - 1), false}}); + } + + // check that the recalculated n is equal to the input challenge + bp.add_copy_constraint({{component.W(1), static_cast(j + component.rows_amount - 1), false}, instance_input.scalar}); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_15_wires.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_15_wires.hpp new file mode 100644 index 000000000..912af771a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_15_wires.hpp @@ -0,0 +1,346 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace chips { + + template + class fixed_base_scalar_mul_phi1; + + template + class fixed_base_scalar_mul_phi1< + snark::plonk_constraint_system, CurveType> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + public: + constexpr static const std::size_t selector_seed = 0xff07; + constexpr static const std::size_t rows_amount = 0; + + static snark::plonk_constraint + generate(blueprint &bp, const var &x_1, const var &x_2, const var &x_3, + const var &x_4, std::array u) { + + return bp.add_constraint( + x_3 * (-u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2 + u[1] * x_2 * x_1 - u[1] * x_1 - + u[1] * x_2 + u[1] - u[3] * x_1 * x_2 + u[3] * x_2 - u[5] * x_1 * x_2 + u[5] * x_2 + + u[7] * x_1 * x_2) - + (x_4 - u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2)); + } + }; + + template + class fixed_base_scalar_mul_phi2; + + template + class fixed_base_scalar_mul_phi2< + snark::plonk_constraint_system, CurveType> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + public: + constexpr static const std::size_t rows_amount = 0; + + static snark::plonk_constraint + generate(blueprint &bp, const var &x_1, const var &x_2, const var &x_3, + const var &x_4, std::array v) { + + return bp.add_constraint( + x_3 * (-v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2 + v[1] * x_2 * x_1 - v[1] * x_1 - + v[1] * x_2 + v[1] - v[3] * x_1 * x_2 + v[3] * x_2 - v[5] * x_1 * x_2 + v[5] * x_2 + + v[7] * x_1 * x_2) - + (x_4 - v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2)); + } + }; + + } // namespace chips + + namespace components { + + template + class element_g1_fixed_base_scalar_mul; + + template + class element_g1_fixed_base_scalar_mul< + snark::plonk_constraint_system, CurveType, W0, W1, W2, + W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using phi1_chip = chips::fixed_base_scalar_mul_phi1; + + using phi2_chip = chips::fixed_base_scalar_mul_phi2; + + using bit_check_chip = chips::bit_check; + + public: + constexpr static const std::size_t rows_amount = 43; + + struct public_params_type { + typename CurveType::template g1_type<>::value_type B; + }; + + struct input_type { + var X; + var Y; + }; + + struct private_params_type { + typename CurveType::scalar_field_type::value_type a; + typename CurveType::scalar_field_type::value_type s; + typename CurveType::template g1_type<>::value_type P; + }; + + struct output_type { + var result; + }; + + static std::size_t allocate_rows(blueprint &bp) { + return bp.allocate_rows(rows_amount); + } + + private: + static typename CurveType::template g1_type<>::value_type + get_omega(typename CurveType::template g1_type<>::value_type B, std::size_t s, std::size_t i) { + + std::size_t coef = i * std::pow(2, 3 * s); + + return coef * B; + } + + static output_type + generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const public_params_type &init_params, + std::size_t component_start_row) { + + std::size_t j = component_start_row; + + auto bit_check_0 = bit_check_chip::generate(bp, var(W0, 0)); + auto bit_check_1 = bit_check_chip::generate(bp, var(W1, 0)); + auto bit_check_2 = bit_check_chip::generate(bp, var(W2, 0)); + auto bit_check_3 = bit_check_chip::generate(bp, var(W3, 0)); + auto bit_check_4 = bit_check_chip::generate(bp, var(W4, 0)); + auto bit_check_5 = bit_check_chip::generate(bp, var(W5, 0)); + + std::array u; + std::array v; + + // For j + 0: + { + std::size_t selector_index_j_0 = public_assignment.add_selector(j); + + for (std::size_t i = 0; i <= 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(init_params.B, 0, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + auto constraint_1 = + phi1_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W6, 0), u); + auto constraint_2 = + phi2_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W8, 0), v); + + for (std::size_t i = 0; i <= 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(init_params.B, 1, i); + u[i] = omega.X; + v[i] = omega.Y; + } + auto constraint_3 = + phi1_chip::generate(bp, var(W3, 0), var(W4, 0), var(W5, 0), var(W7, 0), u); + auto constraint_4 = + phi2_chip::generate(bp, var(W3, 0), var(W4, 0), var(W5, 0), var(W9, 0), v); + + auto acc_constraint = + bp.add_constraint(var(W14, 0) - (var(W0, 0) + var(W1, 0) * 2 + var(W2, 0) * 4 + + var(W3, 0) * 8 + var(W4, 0) * 16 + var(W5, 0) * 32)); + + auto constraint_6 = bp.add_constraint(var(W10, 0) - var(W6, 0)); + auto constraint_7 = bp.add_constraint(var(W11, 0) - var(W8, 0)); + + // auto incomplete_addition_constraint_1; + // auto incomplete_addition_constraint_2; + // TODO: add constraints for incomplete addition + + bp.add_gate(selector_index_j_0, + {bit_check_0, bit_check_1, bit_check_2, bit_check_3, bit_check_4, bit_check_5, + constraint_1, constraint_2, constraint_3, constraint_4, acc_constraint, + constraint_6, constraint_7}); + } + + // For j + z, z = 1..41: + for (std::size_t z = 1; z <= 41; z++) { + + std::size_t selector_index_j_z = public_assignment.add_selector(j + z); + + for (std::size_t i = 0; i <= 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(init_params.B, z * 2, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + auto constraint_1 = + phi1_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W6, 0), u); + auto constraint_2 = + phi2_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W8, 0), v); + + for (std::size_t i = 0; i <= 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(init_params.B, z * 2 + 1, i); + u[i] = omega.X; + v[i] = omega.Y; + } + auto constraint_3 = + phi1_chip::generate(bp, var(W3, 0), var(W4, 0), var(W5, 0), var(W7, 0), u); + auto constraint_4 = + phi2_chip::generate(bp, var(W3, 0), var(W4, 0), var(W5, 0), var(W9, 0), v); + + auto acc_constraint = bp.add_constraint( + var(W14, 0) - (var(W0, 0) + var(W1, 0) * 2 + var(W2, 0) * 4 + var(W3, 0) * 8 + + var(W4, 0) * 16 + var(W5, 0) * 32 + var(W14, -1) * 64)); + + // auto incomplete_addition_constraint_1; + // auto incomplete_addition_constraint_2; + // TODO: add constraints for incomplete addition + + bp.add_gate(selector_index_j_z, + { + bit_check_0, bit_check_1, bit_check_2, bit_check_3, bit_check_4, + bit_check_5, constraint_1, constraint_2, constraint_3, constraint_4, + acc_constraint, + // incomplete_addition_constraint_1, + // incomplete_addition_constraint_2 + }); + } + + // For j + 42: + { + + std::size_t selector_index_j_42 = public_assignment.add_selector(j + 42); + + for (std::size_t i = 0; i <= 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(init_params.B, 84, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + auto constraint_1 = + phi1_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W6, 0), u); + auto constraint_2 = + phi2_chip::generate(bp, var(W0, 0), var(W1, 0), var(W2, 0), var(W8, 0), v); + + auto acc_constraint = bp.add_constraint( + var(W14, 0) - (var(W0, 0) + var(W1, 0) * 2 + var(W2, 0) * 4 + var(W14, -1) * 8)); + + // auto complete_addition_constraint_1; + // auto complete_addition_constraint_2; + // TODO: add constraints for complete addition + + bp.add_gate( + selector_index_j_42, + { + bit_check_0, bit_check_1, bit_check_2, constraint_1, constraint_2, acc_constraint, + // complete_addition_constraint_1, + // complete_addition_constraint_2 + }); + } + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const public_params_type &init_params, + const input_type &input, + std::size_t component_start_row) { + + const std::size_t j = component_start_row; + + bp.add_copy_constraint({input.X, var(W10, j, false)}); + + bp.add_copy_constraint({input.Y, var(W11, j, false)}); + } + + public: + static output_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const public_params_type &init_params, + const input_type &input, + std::size_t component_start_row) { + + generate_copy_constraints(bp, public_assignment, init_params, input, component_start_row); + + return generate_gates(bp, public_assignment, init_params, component_start_row); + } + + static void generate_assignments( + blueprint_private_assignment_table &private_assignment, + blueprint_public_assignment_table &public_assignment, + const public_params_type &init_params, + const private_params_type ¶ms, + std::size_t component_start_row) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_5_wires.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_5_wires.hpp new file mode 100644 index 000000000..97bb54dcc --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_5_wires.hpp @@ -0,0 +1,248 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_5_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_5_WIRES_HPP + +#include + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_fixed_base_scalar_mul; + + template + class element_g1_fixed_base_scalar_mul< + snark::plonk_constraint_system, CurveType, W0, W1, W2, + W3, W4> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + public: + constexpr static const std::size_t rows_amount = 85; + + struct init_params_type { + typename CurveType::template g1_type<>::value_type B; + }; + + struct assignment_params_type { + typename CurveType::scalar_field_type::value_type a; + typename CurveType::scalar_field_type::value_type s; + typename CurveType::template g1_type<>::value_type P; + }; + + static std::size_t allocate_rows(blueprint &bp) { + return bp.allocate_rows(rows_amount); + } + + private: + static typename CurveType::template g1_type<>::value_type + get_omega(typename CurveType::template g1_type<>::value_type B, std::size_t s, std::size_t i) { + + std::size_t coef = i * std::pow(2, 3 * s); + + return coef * B; + } + + static snark::plonk_constraint + generate_phi1_constraint(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + std::array u) { + + return bp.add_constraint( + x_3 * (-u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2 + u[1] * x_2 * x_1 - u[1] * x_1 - + u[1] * x_2 + u[1] - u[3] * x_1 * x_2 + u[3] * x_2 - u[5] * x_1 * x_2 + u[5] * x_2 + + u[7] * x_1 * x_2) - + (x_4 - u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2)); + } + + static snark::plonk_constraint + generate_phi2_constraint(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + std::array v) { + + return bp.add_constraint( + x_3 * (-v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2 + v[1] * x_2 * x_1 - v[1] * x_1 - + v[1] * x_2 + v[1] - v[3] * x_1 * x_2 + v[3] * x_2 - v[5] * x_1 * x_2 + v[5] * x_2 + + v[7] * x_1 * x_2) - + (x_4 - v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2)); + } + + static snark::plonk_constraint + generate_phi3_gate(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + var x_5, var x_6) { + return bp.add_constraint( + x_1 * (1 + CurveType::template g1_type<>::params_type::b * x_3 * x_4 * x_5 * x_6) - + (x_3 * x_6 + x_4 * x_5)); + } + + static snark::plonk_constraint + generate_phi4_gate(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + var x_5, var x_6) { + return bp.add_constraint( + x_2 * (1 - CurveType::template g1_type<>::params_type::b * x_3 * x_4 * x_5 * x_6) - + (x_3 * x_5 + x_4 * x_6)); + } + + public: + static void + generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + std::size_t component_start_row) { + + std::size_t j = component_start_row; + + bp.add_bit_check({j, j + 2}, w[1][cur]); + bp.add_bit_check({j, j + 2}, w[2][cur]); + bp.add_bit_check({j, j + 1, j + 3}, w[3][cur]); + bp.add_bit_check({j + 2, j + 3}, w[4][cur]); + + // j=0 + bp.add_gate(j, w[0][cur] - (w[1][cur] * 4 + w[2][cur] * 2 + w[3][cur])); + + generate_phi3_gate(j, w[1][p1], w[2][p1], w[4][cur], w[0][p1], w[4][p1], w[3][p2]); + generate_phi4_gate(j, w[1][p1], w[2][p1], w[4][cur], w[0][p1], w[4][p1], w[3][p2]); + + // j+z, z=0 mod 5, z!=0 + for (std::size_t z = 5; z <= 84; z += 5) { + + bp.add_gate(j + z, w[0][cur] - (w[1][cur] * 4 + w[2][cur] * 2 + w[3][cur] + w[0][m1] * 8)); + + std::array u; + std::array v; + + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = get_omega(3 * z / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + generate_phi1_gate(j + z, w[1][cur], w[2][cur], w[3][cur], w[4][cur], u); + generate_phi2_gate(j + z, w[1][cur], w[2][cur], w[3][cur], w[4][p1], v); + generate_phi3_gate(j + z, w[1][p1], w[2][p1], w[1][m1], w[2][m1], w[4][p1], w[3][p2]); + generate_phi4_gate(j + z, w[1][p1], w[2][p1], w[1][m1], w[2][m1], w[4][p1], w[3][p2]); + } + + // j+z, z=2 mod 5 + for (std::size_t z = 2; z <= 84; z += 5) { + + bp.add_gate(j + z, w[0][cur] - (w[1][cur] * 4 + w[2][cur] * 2 + w[3][m1] + w[0][m2] * 8)); + + std::array u; + std::array v; + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(3 * (z - 2) / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + generate_phi1_gate(j + z, w[1][cur], w[2][cur], w[3][m1], w[4][m1], u); + generate_phi2_gate(j + z, w[1][cur], w[2][cur], w[3][m1], w[4][cur], v); + generate_phi3_gate(j + z, w[1][p1], w[2][p1], w[1][m1], w[2][m1], w[0][p1], w[3][p2]); + generate_phi4_gate(j + z, w[1][p1], w[2][p1], w[1][m1], w[2][m1], w[0][p1], w[3][p2]); + } + + // j+z, z=3 mod 5 + for (std::size_t z = 3; z <= 84; z += 5) { + + std::array u; + std::array v; + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(3 * (z - 3) / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + generate_phi1_gate(j + z, w[4][m1], w[3][cur], w[4][cur], w[0][cur], u); + generate_phi2_gate(j + z, w[4][m1], w[3][cur], w[4][cur], w[0][p1], v); + } + + // j+z, z=4 mod 5 + for (std::size_t z = 4; z <= 84; z += 5) { + + bp.add_gate(j + z - 1, w[0][p1] - (w[4][m1] * 4 + w[3][m2] * 2 + w[4][m2] + w[0][m1] * 8)); + + generate_phi3_gate(j + z, w[1][m2], w[2][cur], w[1][m1], w[2][m1], w[4][p1], w[0][p2]); + generate_phi4_gate(j + z, w[1][m2], w[2][cur], w[1][m1], w[2][m1], w[4][p1], w[0][p2]); + } + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + std::size_t component_start_row) { + } + + static void generate_assignments( + blueprint_private_assignment_table &private_assignment, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + const assignment_params_type ¶ms, + std::size_t component_start_row) { + + std::array b {}; + // = marshalling::unpack(a); + + bp.assignment(W1, j) = b[0]; + bp.assignment(W2, j) = b[1]; + bp.assignment(W3, j) = b[2]; + + bp.assignment(W1, j + 1) = params.P.X; + bp.assignment(W2, j + 1) = params.P.Y; + bp.assignment(W3, j + 1) = b[3]; + + bp.assignment(W1, j + 2) = b[4]; + bp.assignment(W2, j + 2) = b[5]; + bp.assignment(W4, j + 2) = b[6]; + + bp.assignment(W3, j + 3) = b[7]; + bp.assignment(W4, j + 3) = b[8]; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_5_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_9_wires.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_9_wires.hpp new file mode 100644 index 000000000..6c4a1a794 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/fixed_base_scalar_mul_9_wires.hpp @@ -0,0 +1,276 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_9_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_9_WIRES_HPP + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_fixed_base_scalar_mul; + + template + class element_g1_fixed_base_scalar_mul< + snark::plonk_constraint_system, CurveType, W0, W1, W2, + W3, W4, W5, W6, W7, W8> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + public: + constexpr static const std::size_t rows_amount = 85; + + struct init_params_type { + typename CurveType::template g1_type<>::value_type B; + }; + + struct assignment_params_type { + typename CurveType::scalar_field_type::value_type a; + typename CurveType::scalar_field_type::value_type s; + typename CurveType::template g1_type<>::value_type P; + }; + + static std::size_t allocate_rows(blueprint &bp) { + return bp.allocate_rows(rows_amount); + } + + private: + static typename CurveType::template g1_type<>::value_type + get_omega(typename CurveType::template g1_type<>::value_type B, std::size_t s, std::size_t i) { + + std::size_t coef = i * std::pow(2, 3 * s); + + return coef * B; + } + + static snark::plonk_constraint + generate_phi1_constraint(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + std::array u) { + + return bp.add_constraint( + x_3 * (-u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2 + u[1] * x_2 * x_1 - u[1] * x_1 - + u[1] * x_2 + u[1] - u[3] * x_1 * x_2 + u[3] * x_2 - u[5] * x_1 * x_2 + u[5] * x_2 + + u[7] * x_1 * x_2) - + (x_4 - u[0] * x_2 * x_1 + u[0] * x_1 + u[0] * x_2 - u[0] + u[2] * x_1 * x_2 - u[2] * x_2 + + u[4] * x_1 * x_2 - u[4] * x_2 - u[6] * x_1 * x_2)); + } + + static snark::plonk_constraint + generate_phi2_constraint(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + std::array v) { + + return bp.add_constraint( + x_3 * (-v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2 + v[1] * x_2 * x_1 - v[1] * x_1 - + v[1] * x_2 + v[1] - v[3] * x_1 * x_2 + v[3] * x_2 - v[5] * x_1 * x_2 + v[5] * x_2 + + v[7] * x_1 * x_2) - + (x_4 - v[0] * x_2 * x_1 + v[0] * x_1 + v[0] * x_2 - v[0] + v[2] * x_1 * x_2 - v[2] * x_2 + + v[4] * x_1 * x_2 - v[4] * x_2 - v[6] * x_1 * x_2)); + } + + static snark::plonk_constraint + generate_phi3_gate(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + var x_5, var x_6) { + return bp.add_constraint( + x_1 * (1 + CurveType::template g1_type<>::params_type::b * x_3 * x_4 * x_5 * x_6) - + (x_3 * x_6 + x_4 * x_5)); + } + + static snark::plonk_constraint + generate_phi4_gate(blueprint &bp, var x_1, var x_2, var x_3, var x_4, + var x_5, var x_6) { + return bp.add_constraint( + x_2 * (1 - CurveType::template g1_type<>::params_type::b * x_3 * x_4 * x_5 * x_6) - + (x_3 * x_5 + x_4 * x_6)); + } + + public: + static void + generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + std::size_t component_start_row) { + + std::size_t j = component_start_row; + + bp.add_bit_check({j, j + 2}, var(W1, 0)); + bp.add_bit_check({j, j + 2}, var(W2, 0)); + bp.add_bit_check({j, j + 1, j + 3}, var(W3, 0)); + bp.add_bit_check({j + 2, j + 3}, var(W4, 0)); + + // j=0 + bp.add_gate(j, var(W0, 0) - (var(W1, 0) * 4 + var(W2, 0) * 2 + var(W3, 0))); + + generate_phi3_constraint(bp, j, var(W1, +1), var(W2, +1), var(W4, 0), var(W0, +1), var(W4, +1), + var(W3, +2)); + generate_phi4_constraint(bp, j, var(W1, +1), var(W2, +1), var(W4, 0), var(W0, +1), var(W4, +1), + var(W3, +2)); + + // j+z, z=0 mod 5, z!=0 + for (std::size_t z = 5; z <= 84; z += 5) { + + std::size_t selector_index = public_assignment.add_selector(j + z); + + bp.add_gate(selector_index, + var(W0, 0) - (var(W1, 0) * 4 + var(W2, 0) * 2 + var(W3, 0) + var(W0, -1) * 8)); + + std::array u; + std::array v; + + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = get_omega(3 * z / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + generate_phi1_constraint(bp, selector_index, var(W1, 0), var(W2, 0), var(W3, 0), var(W4, 0), + u); + generate_phi2_constraint(bp, selector_index, var(W1, 0), var(W2, 0), var(W3, 0), + var(W4, +1), v); + generate_phi3_constraint(bp, selector_index, var(W1, +1), var(W2, +1), var(W1, -1), + var(W2, -1), var(W4, +1), var(W3, +2)); + generate_phi4_constraint(bp, selector_index, var(W1, +1), var(W2, +1), var(W1, -1), + var(W2, -1), var(W4, +1), var(W3, +2)); + } + + // j+z, z=2 mod 5 + for (std::size_t z = 2; z <= 84; z += 5) { + + std::size_t selector_index = public_assignment.add_selector(j + z); + + bp.add_gate(selector_index, + var(W0, 0) - (var(W1, 0) * 4 + var(W2, 0) * 2 + var(W3, -1) + var(W0, -2) * 8)); + + std::array u; + std::array v; + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(3 * (z - 2) / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + generate_phi1_constraint(bp, selector_index, var(W1, 0), var(W2, 0), var(W3, -1), + var(W4, -1), u); + generate_phi2_constraint(bp, selector_index, var(W1, 0), var(W2, 0), var(W3, -1), + var(W4, 0), v); + generate_phi3_constraint(bp, selector_index, var(W1, +1), var(W2, +1), var(W1, -1), + var(W2, -1), var(W0, +1), var(W3, +2)); + generate_phi4_constraint(bp, selector_index, var(W1, +1), var(W2, +1), var(W1, -1), + var(W2, -1), var(W0, +1), var(W3, +2)); + } + + // j+z, z=3 mod 5 + for (std::size_t z = 3; z <= 84; z += 5) { + + std::array u; + std::array v; + for (std::size_t i = 0; i < 7; i++) { + typename CurveType::template g1_type<>::value_type omega = + get_omega(3 * (z - 3) / 5, i); + u[i] = omega.X; + v[i] = omega.Y; + } + + std::size_t selector_index = public_assignment.add_selector(j + z); + generate_phi1_constraint(bp, selector_index, var(W4, -1), var(W3, 0), var(W4, 0), + var(W0, 0), u); + generate_phi2_constraint(bp, selector_index, var(W4, -1), var(W3, 0), var(W4, 0), + var(W0, +1), v); + } + + // j+z, z=4 mod 5 + for (std::size_t z = 4; z <= 84; z += 5) { + + bp.add_gate(public_assignment.add_selector(j + z - 1), + var(W0, +1) - + (var(W4, -1) * 4 + var(W3, -2) * 2 + var(W4, -2) + var(W0, -1) * 8)); + + std::size_t selector_index = public_assignment.add_selector(j + z); + generate_phi3_constraint(bp, selector_index, var(W1, -2), var(W2, 0), var(W1, -1), + var(W2, -1), var(W4, +1), var(W0, +2)); + generate_phi4_constraint(bp, selector_index, var(W1, -2), var(W2, 0), var(W1, -1), + var(W2, -1), var(W4, +1), var(W0, +2)); + } + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + std::size_t component_start_row) { + } + + static void generate_assignments( + blueprint_private_assignment_table &private_assignment, + blueprint_public_assignment_table &public_assignment, + const init_params_type &init_params, + const assignment_params_type ¶ms, + std::size_t component_start_row) { + + std::array b = + nil::marshalling::pack(params.s); + + private_assignment.witness(W1)[j] = b[0]; + private_assignment.witness(W2)[j] = b[1]; + private_assignment.witness(W3)[j] = b[2]; + + private_assignment.witness(W1)[j + 1] = params.P.X; + private_assignment.witness(W2)[j + 1] = params.P.Y; + private_assignment.witness(W3)[j + 1] = b[3]; + + private_assignment.witness(W1)[j + 2] = b[4]; + private_assignment.witness(W2)[j + 2] = b[5]; + private_assignment.witness(W4)[j + 2] = b[6]; + + private_assignment.witness(W3)[j + 3] = b[7]; + private_assignment.witness(W4)[j + 3] = b[8]; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_FIXED_BASE_SCALAR_MUL_COMPONENT_9_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/multi_scalar_mul_15_wires.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/multi_scalar_mul_15_wires.hpp new file mode 100644 index 000000000..6bb15615a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/multi_scalar_mul_15_wires.hpp @@ -0,0 +1,171 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_MULTI_SCALAR_MUL_COMPONENT_15_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_MULTI_SCALAR_MUL_COMPONENT_15_WIRES_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_multi_scalar_mul; + + template + class element_g1_multi_scalar_mul< + snark::plonk_constraint_system, CurveType, PointsAmount, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using scalar_mul_component = + zk::components::curve_element_variable_base_scalar_mul; + using add_component = + zk::components::curve_element_unified_addition; + + using var = snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + + public: + constexpr static const std::size_t selector_seed = 0x0f07; + constexpr static const std::size_t rows_amount = + PointsAmount * (scalar_mul_component::rows_amount + add_component::rows_amount); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array scalars; + std::array bases; + }; + + struct result_type { + var_ec_point output; + + result_type(const params_type ¶ms, std::size_t start_row_index) { + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array res; + for (std::size_t i = 0; i < PointsAmount; i++) { + auto multiplied = scalar_mul_component::generate_circuit( + bp, assignment, {{params.bases[i].X, params.bases[i].Y}, params.scalars[i]}, row); + row += scalar_mul_component::rows_amount; + if (i == 0) { + res[0] = multiplied.X; + res[1] = multiplied.Y; + } else { + components::generate_circuit( + bp, assignment, {{res[0], res[1]}, {multiplied.X, multiplied.Y}}, row); + typename add_component::result_type added( + {{res[0], res[1]}, {multiplied.X, multiplied.Y}}, row); + res[0] = added.X; + res[1] = added.Y; + row += add_component::rows_amount; + } + } + + auto result = result_type(params, start_row_index); + result.output.X = res[0]; + result.output.Y = res[1]; + return result; + } + + static void + generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + std::size_t component_start_row) { + } + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + std::size_t component_start_row) { + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array res; + for (std::size_t i = 0; i < PointsAmount; i++) { + auto multiplied = scalar_mul_component::generate_assignments( + assignment, {{params.bases[i].X, params.bases[i].Y}, params.scalars[i]}, row); + row += scalar_mul_component::rows_amount; + if (i == 0) { + res[0] = multiplied.X; + res[1] = multiplied.Y; + } else { + auto added = add_component::generate_assignments( + assignment, {{res[0], res[1]}, {multiplied.X, multiplied.Y}}, row); + res[0] = added.X; + res[1] = added.Y; + row += add_component::rows_amount; + } + } + + auto result = result_type(params, start_row_index); + result.output.X = res[0]; + result.output.Y = res[1]; + return result; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_MULTI_SCALAR_MUL_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/tripling.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/tripling.hpp new file mode 100644 index 000000000..a49db5058 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/tripling.hpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_TRIPLING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_TRIPLING_COMPONENT_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class element_g1_tripling; + + template + class element_g1_tripling, CurveType, W0, W1, W2, W3, + W4, W5, W6, W7> : public component { + + typedef snark::plonk_constraint_system arithmetization_type; + typedef blueprint blueprint_type; + + element_g1_doubling_plonk doubling_component; + element_g1_addition_plonk + addition_component; + + public: + element_g1_tripling(blueprint_type &bp) : + component(bp), doubling_component(bp), addition_component(bp) { + } + + void generate_gates() { + doubling_component.generate_gates(); + addition_component.generate_gates(); + } + + void generate_assignments(typename CurveType::value_type &P1) { + generate_assignments(P1, P1.doubled() + P1); + } + + void generate_assignments(typename CurveType::value_type &P1, typename CurveType::value_type &P2) { + doubling_component.generate_assignments(P1, P1.doubled()); + addition_component.generate_assignments(P1.doubled(), P1, P2); + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_TRIPLING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/types.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/types.hpp new file mode 100644 index 000000000..4ca1920d4 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/types.hpp @@ -0,0 +1,51 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_TYPES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_TYPES_HPP + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct var_ec_point { + using var = snark::plonk_variable; + var X; + var Y; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_TYPES_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/unified_addition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/unified_addition.hpp new file mode 100644 index 000000000..a44b5bede --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/unified_addition.hpp @@ -0,0 +1,324 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_UNIFIED_ADDITION_COMPONENT_11_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_UNIFIED_ADDITION_COMPONENT_11_WIRES_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: P, Q - elliptic curve points + // Output: R = P + Q + template + class unified_addition; + + template + class unified_addition, + CurveType>: + public plonk_component { + + static_assert(std::is_same::value); + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return unified_addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(11)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + static constexpr const std::size_t gates_amount = 1; + const std::string component_name = "native curve addition"; + + struct input_type { + struct var_ec_point { + var x; + var y; + }; + + var_ec_point P; + var_ec_point Q; + + std::vector> all_vars() { + return {P.x, P.y, Q.x, Q.y}; + } + }; + + struct result_type { + var X = var(0, 0, false); + var Y = var(0, 0, false); + result_type(const unified_addition &component, std::uint32_t start_row_index) { + X = var(component.W(4), start_row_index, false, var::column_type::witness); + Y = var(component.W(5), start_row_index, false, var::column_type::witness); + } + + result_type() { + } + + std::vector> all_vars() { + return {X, Y}; + } + }; + + template + explicit unified_addition(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + unified_addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + unified_addition(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + }; + + template + using plonk_native_unified_addition = + unified_addition, CurveType>; + + template + typename plonk_native_unified_addition::result_type + generate_assignments( + const plonk_native_unified_addition &component, + assignment> &assignment, + const typename plonk_native_unified_addition::input_type instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + typename BlueprintFieldType::value_type p_x = var_value(assignment, instance_input.P.x); + typename BlueprintFieldType::value_type p_y = var_value(assignment, instance_input.P.y); + typename CurveType::template g1_type::value_type P(p_x, + p_y); + + typename BlueprintFieldType::value_type q_x = var_value(assignment, instance_input.Q.x); + typename BlueprintFieldType::value_type q_y = var_value(assignment, instance_input.Q.y); + typename CurveType::template g1_type::value_type Q(q_x, + q_y); + + assignment.witness(component.W(0), j) = P.X; + assignment.witness(component.W(1), j) = P.Y; + assignment.witness(component.W(2), j) = Q.X; + assignment.witness(component.W(3), j) = Q.Y; + typename CurveType::template g1_type::value_type zero = { + 0u, 0u}; + if (P.X == zero.X && P.Y == zero.Y) { + assignment.witness(component.W(4), j) = Q.X; + assignment.witness(component.W(5), j) = Q.Y; + } else { + if (Q.X == zero.X && Q.Y == zero.Y) { + assignment.witness(component.W(4), j) = P.X; + assignment.witness(component.W(5), j) = P.Y; + } else { + if (Q.X == P.X && Q.Y == -P.Y) { + assignment.witness(component.W(4), j) = 0u; + assignment.witness(component.W(5), j) = 0u; + } else { + assignment.witness(component.W(4), j) = (P + Q).X; + assignment.witness(component.W(5), j) = (P + Q).Y; + } + } + } + if (P.X != 0u) { + assignment.witness(component.W(6), j) = P.X.inversed(); + } else { + assignment.witness(component.W(6), j) = 0u; + } + + if (Q.X != 0u) { + assignment.witness(component.W(7), j) = Q.X.inversed(); + } else { + assignment.witness(component.W(7), j) = 0u; + } + + if (P.X != Q.X) { + assignment.witness(component.W(10), j) = (Q.Y - P.Y) * (Q.X - P.X).inversed(); + + assignment.witness(component.W(9), j) = 0u; + + assignment.witness(component.W(8), j) = (Q.X - P.X).inversed(); + } else { + + if (P.Y != -Q.Y) { + assignment.witness(component.W(9), j) = (Q.Y + P.Y).inversed(); + } else { + assignment.witness(component.W(9), j) = 0u; + } + if (P.Y != 0u) { + assignment.witness(component.W(10), j) = (3u * (P.X * P.X)) * (2u * P.Y).inversed(); + } else { + assignment.witness(component.W(10), j) = 0u; + } + assignment.witness(component.W(8), j) = 0u; + } + + return typename plonk_native_unified_addition::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_native_unified_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_native_unified_addition::input_type &instance_input) { + + using var = + typename plonk_native_unified_addition::var; + + auto constraint_1 = + (var(component.W(2), 0) - var(component.W(0), 0)) * + ((var(component.W(2), 0) - var(component.W(0), 0)) * + var(component.W(10), 0) - (var(component.W(3), 0) - var(component.W(1), 0))); + auto constraint_2 = + (1u - (var(component.W(2), 0) - var(component.W(0), 0)) * var(component.W(8), 0)) * + (2u * var(component.W(1), 0) * var(component.W(10), 0) - + 3u * var(component.W(0), 0) * var(component.W(0), 0)); + + auto constraint_3 = + (var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(2), 0) - + var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(0), 0)) * + (var(component.W(10), 0) * var(component.W(10), 0) - var(component.W(0), 0) - + var(component.W(2), 0) - var(component.W(4), 0)); + auto constraint_4 = + (var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(2), 0) - + var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(0), 0)) * + (var(component.W(10), 0) * (var(component.W(0), 0) - var(component.W(4), 0)) - + var(component.W(1), 0) - var(component.W(5), 0)); + auto constraint_5 = + (var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(3), 0) + + var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(1), 0)) * + (var(component.W(10), 0) * var(component.W(10), 0) - var(component.W(0), 0) - + var(component.W(2), 0) - var(component.W(4), 0)); + auto constraint_6 = + (var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(3), 0) + + var(component.W(0), 0) * var(component.W(2), 0) * var(component.W(1), 0)) * + (var(component.W(10), 0) * (var(component.W(0), 0) - var(component.W(4), 0)) - + var(component.W(1), 0) - var(component.W(5), 0)); + auto constraint_7 = + (1u - var(component.W(0), 0) * var(component.W(6), 0)) * + (var(component.W(4), 0) - var(component.W(2), 0)); + auto constraint_8 = + (1u - var(component.W(0), 0) * var(component.W(6), 0)) * + (var(component.W(5), 0) - var(component.W(3), 0)); + auto constraint_9 = + (1u - var(component.W(2), 0) * var(component.W(7), 0)) * + (var(component.W(4), 0) - var(component.W(0), 0)); + auto constraint_10 = + (1u - var(component.W(2), 0) * var(component.W(7), 0)) * + (var(component.W(5), 0) - var(component.W(1), 0)); + auto constraint_11 = + (1u - (var(component.W(2), 0) - var(component.W(0), 0)) * var(component.W(8), 0) - + (var(component.W(3), 0) + var(component.W(1), 0)) * var(component.W(9), 0)) * + var(component.W(4), 0); + auto constraint_12 = + (1u - (var(component.W(2), 0) - var(component.W(0), 0)) * var(component.W(8), 0) - + (var(component.W(3), 0) + var(component.W(1), 0)) * var(component.W(9), 0)) * + var(component.W(5), 0); + + return bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7, constraint_8, constraint_9, constraint_10, constraint_11, constraint_12}); + } + + template + void generate_copy_constraints( + const plonk_native_unified_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_native_unified_addition::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_native_unified_addition::var; + + bp.add_copy_constraint({instance_input.P.x, var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint({instance_input.P.y, var(component.W(1), start_row_index, false)}); + bp.add_copy_constraint({instance_input.Q.x, var(component.W(2), start_row_index, false)}); + bp.add_copy_constraint({instance_input.Q.y, var(component.W(3), start_row_index, false)}); + } + + template + typename plonk_native_unified_addition::result_type + generate_circuit( + const plonk_native_unified_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_native_unified_addition::input_type &instance_input, + const std::size_t start_row_index){ + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_native_unified_addition::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_UNIFIED_ADDITION_COMPONENT_11_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_endo_scalar_mul_15_wires.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_endo_scalar_mul_15_wires.hpp new file mode 100644 index 000000000..36e71e98c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_endo_scalar_mul_15_wires.hpp @@ -0,0 +1,383 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the VARIABLE_BASE_ENDO_SCALAR_MUL component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class curve_element_variable_base_endo_scalar_mul; + + template + class curve_element_variable_base_endo_scalar_mul< + snark::plonk_constraint_system, + CurveType, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14> { + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using multiplication_component = zk::components::multiplication; + + using unified_addition_component = + zk::components::curve_element_unified_addition; + + public: + constexpr static const typename BlueprintFieldType::value_type endo = + typename BlueprintFieldType::value_type( + algebra::fields::arithmetic_params::multiplicative_generator) + .pow(typename BlueprintFieldType::integral_type( + ((BlueprintFieldType::value_type::zero() - BlueprintFieldType::value_type::one()) * + (typename BlueprintFieldType::value_type(3)).inversed()) + .data)); + constexpr static const std::size_t selector_seed = 0x0f02; + constexpr static const std::size_t rows_amount = + 33 + multiplication_component::rows_amount + unified_addition_component::rows_amount * 2; + constexpr static const std::size_t gates_amount = 1; + + struct params_type { + struct var_ec_point { + var x; + var y; + }; + + var_ec_point T; + var b; + }; + + struct result_type { + var X; + var Y; + result_type(std::size_t start_row_index) { + X = var(W4, start_row_index + rows_amount - 1, false); + Y = var(W5, start_row_index + rows_amount - 1, false); + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t j = start_row_index; + typename multiplication_component::params_type multiplication_params = { + params.T.x, var(W0, j + 1, false, var::column_type::constant)}; + auto mul_res = + multiplication_component::generate_assignments(assignment, multiplication_params, j); + j++; + + typename unified_addition_component::params_type addition_params = { + {params.T.x, params.T.y}, {mul_res.output, params.T.y}}; + auto add_res = unified_addition_component::generate_assignments(assignment, addition_params, j); + j++; + + typename unified_addition_component::params_type double_params = {{add_res.X, add_res.Y}, + {add_res.X, add_res.Y}}; + unified_addition_component::generate_assignments(assignment, double_params, j); + j++; + + typename BlueprintFieldType::value_type b = assignment.var_value(params.b); + typename BlueprintFieldType::value_type T_x = assignment.var_value(params.T.x); + typename BlueprintFieldType::value_type T_y = assignment.var_value(params.T.y); + typename CurveType::template g1_type::value_type T(T_x, + T_y); + + typename CurveType::template g1_type::value_type P; + + typename CurveType::template g1_type::value_type R; + typename CurveType::template g1_type::value_type Q; + typename CurveType::scalar_field_type::integral_type integral_b = + typename CurveType::scalar_field_type::integral_type(b.data); + + std::array bits = {false}; + { + nil::marshalling::status_type status; + std::array bits_all = + nil::marshalling::pack(integral_b, status); + std::copy(bits_all.end() - 128, bits_all.end(), bits.begin()); + } + + typename ArithmetizationType::field_type::value_type n = 0; + typename ArithmetizationType::field_type::value_type n_next = 0; + typename ArithmetizationType::field_type::value_type s1 = 0; + typename ArithmetizationType::field_type::value_type s3 = 0; + for (std::size_t i = j; i < j + 32; i++) { + assignment.witness(W0)[i] = T.X; + assignment.witness(W1)[i] = T.Y; + if (i == j) { + Q.X = endo * T.X; + Q.Y = T.Y; + P = T + (T + Q) + Q; + assignment.witness(W4)[i] = P.X; + assignment.witness(W5)[i] = P.Y; + assignment.witness(W6)[i] = n; + } else { + Q.X = (1 + (endo - 1) * bits[(i - j) * 4 - 2]) * T.X; + Q.Y = (2 * bits[(i - j) * 4 - 1] - 1) * T.Y; + /*s4 = 2 * R.Y * (2*R.X + Q.X - s3 * s3).inversed() - s3; + P.X = Q.X + s4*s4 - s3*s3; + P.Y = (R.X - P.X)*s4 -R.Y;*/ + P = 2 * R + Q; + assignment.witness(W4)[i] = P.X; + assignment.witness(W5)[i] = P.Y; + n_next = n * 16 + bits[(i - j) * 4 - 4] * 8 + bits[(i - j) * 4 - 3] * 4 + + bits[(i - j) * 4 - 2] * 2 + bits[(i - j) * 4 - 1]; + assignment.witness(W6)[i] = n_next; + n = n_next; + } + assignment.witness(W11)[i] = bits[(i - j) * 4]; + assignment.witness(W12)[i] = bits[(i - j) * 4 + 1]; + assignment.witness(W13)[i] = bits[(i - j) * 4 + 2]; + assignment.witness(W14)[i] = bits[(i - j) * 4 + 3]; + Q.X = (1 + (endo - 1) * bits[(i - j) * 4]) * T.X; + Q.Y = (2 * bits[(i - j) * 4 + 1] - 1) * T.Y; + s1 = (Q.Y - P.Y) * (Q.X - P.X).inversed(); + // s2 = 2 * P.Y * (2*P.X + Q.X - s1 * s1).inversed() - s1; + + assignment.witness(W9)[i] = s1; + /*R.X = Q.X + s2*s2 - s1*s1; + R.Y = (P.X - R.X)*s2 -P.Y;*/ + R = 2 * P + Q; + s3 = ((2 * bits[(i - j) * 4 + 3] - 1) * T.Y - R.Y) * + ((1 + (endo - 1) * bits[(i - j) * 4 + 2]) * T.X - R.X).inversed(); + assignment.witness(W10)[i] = s3; + assignment.witness(W7)[i] = R.X; + assignment.witness(W8)[i] = R.Y; + } + + Q.X = (1 + (endo - 1) * bits[126]) * T.X; + Q.Y = (2 * bits[127] - 1) * T.Y; + /*s4 = 2 * R.Y * (2*R.X + Q.X - s3 * s3).inversed() - s3; + P.X = Q.X + s4*s4 - s3*s3; + P.Y = (R.X - P.X)*s4 -R.Y; */ + P = R + Q + R; + assignment.witness(W4)[j + 32] = P.X; + assignment.witness(W5)[j + 32] = P.Y; + n_next = n * 16 + bits[124] * 8 + bits[125] * 4 + bits[126] * 2 + bits[127]; + assignment.witness(W6)[j + 32] = n_next; + } + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(bp, assignment, params, start_row_index); + + auto selector_iterator = assignment.find_selector(selector_seed); + std::size_t first_selector_index; + if (selector_iterator == assignment.selectors_end()) { + first_selector_index = assignment.allocate_selector(selector_seed, gates_amount); + generate_gates(bp, assignment, params, first_selector_index); + } else { + first_selector_index = selector_iterator->second; + } + std::size_t j = start_row_index; + typename multiplication_component::params_type multiplication_params = { + params.T.x, var(W0, j + 1, false, var::column_type::constant)}; + zk::components::generate_circuit( + bp, assignment, multiplication_params, start_row_index); + typename multiplication_component::result_type mul_res(multiplication_params, j); + j++; + + typename unified_addition_component::params_type addition_params = { + {params.T.x, params.T.y}, {mul_res.output, params.T.y}}; + zk::components::generate_circuit( + bp, assignment, addition_params, j); + typename unified_addition_component::result_type add_res(addition_params, j); + j++; + + typename unified_addition_component::params_type double_params = {{add_res.X, add_res.Y}, + {add_res.X, add_res.Y}}; + zk::components::generate_circuit(bp, assignment, double_params, j); + j++; + + assignment.enable_selector(first_selector_index, j, j + 31); + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + auto bit_check_1 = bp.add_bit_check(var(W11, 0)); + auto bit_check_2 = bp.add_bit_check(var(W12, 0)); + auto bit_check_3 = bp.add_bit_check(var(W13, 0)); + auto bit_check_4 = bp.add_bit_check(var(W14, 0)); + + auto constraint_1 = + bp.add_constraint(((1 + (endo - 1) * var(W11, 0)) * var(W0, 0) - var(W4, 0)) * var(W9, 0) - + 2 * var(W12, 0) * var(W1, 0) + var(W1, 0) + var(W5, 0)); + auto constraint_2 = bp.add_constraint( + (2 * var(W4, 0) - var(W9, 0) * var(W9, 0) + (1 + (endo - 1) * var(W11, 0)) * var(W0, 0)) * + ((var(W4, 0) - var(W7, 0)) * var(W9, 0) + var(W8, 0) + var(W5, 0)) - + ((var(W4, 0) - var(W7, 0)) * 2 * var(W5, 0))); + auto constraint_3 = bp.add_constraint( + (var(W8, 0) + var(W5, 0)) * (var(W8, 0) + var(W5, 0)) - + ((var(W4, 0) - var(W7, 0)) * (var(W4, 0) - var(W7, 0)) * + (var(W9, 0) * var(W9, 0) - (1 + (endo - 1) * var(W11, 0)) * var(W0, 0) + var(W7, 0)))); + auto constraint_4 = + bp.add_constraint(((1 + (endo - 1) * var(W13, 0)) * var(W0, 0) - var(W7, 0)) * var(W10, 0) - + 2 * var(W14, 0) * var(W1, 0) + var(W1, 0) + var(W8, 0)); + auto constraint_5 = bp.add_constraint( + (2 * var(W7, 0) - var(W10, 0) * var(W10, 0) + (1 + (endo - 1) * var(W13, 0)) * var(W0, 0)) * + ((var(W7, 0) - var(W4, +1)) * var(W10, 0) + var(W5, +1) + var(W8, 0)) - + ((var(W7, 0) - var(W4, +1)) * 2 * var(W8, 0))); + auto constraint_6 = bp.add_constraint( + (var(W5, +1) + var(W8, 0)) * (var(W5, +1) + var(W8, 0)) - + ((var(W7, 0) - var(W4, +1)) * (var(W7, 0) - var(W4, +1)) * + (var(W10, 0) * var(W10, 0) - (1 + (endo - 1) * var(W13, 0)) * var(W0, 0) + var(W4, +1)))); + auto constraint_7 = + bp.add_constraint(var(W6, +1) - (16 * var(W6, 0) + 8 * var(W11, 0) + 4 * var(W12, 0) + + 2 * var(W13, 0) + var(W14, 0))); + + bp.add_gate(first_selector_index, + {bit_check_1, bit_check_2, bit_check_3, bit_check_4, constraint_1, constraint_2, + constraint_3, constraint_4, constraint_5, constraint_6, constraint_7}); + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t j = start_row_index; + + typename multiplication_component::params_type multiplication_params = { + params.T.x, var(0, j, false, var::column_type::constant)}; + typename multiplication_component::result_type mul_res(multiplication_params, start_row_index); + j++; + + typename unified_addition_component::params_type addition_params = { + {params.T.x, params.T.y}, {mul_res.output, params.T.y}}; + typename unified_addition_component::result_type add_res(addition_params, j); + j++; + + typename unified_addition_component::params_type double_params = {{add_res.X, add_res.Y}, + {add_res.X, add_res.Y}}; + typename unified_addition_component::result_type double_res(double_params, j); + j++; + + bp.add_copy_constraint({{W4, (std::int32_t)(j), false}, double_res.X}); + bp.add_copy_constraint({{W5, (std::int32_t)(j), false}, double_res.Y}); + + for (int z = 0; z < 31; z++) { + bp.add_copy_constraint( + {{W0, (std::int32_t)(j + z), false}, {W0, (std::int32_t)(j + z + 1), false}}); + bp.add_copy_constraint( + {{W1, (std::int32_t)(j + z), false}, {W1, (std::int32_t)(j + z + 1), false}}); + } + bp.add_copy_constraint( + {{W6, (std::int32_t)(j + 0), false}, + {0, (std::int32_t)(start_row_index + 1), false, var::column_type::constant}}); + + // TODO link to params.b + + bp.add_copy_constraint({{W6, (std::int32_t)(j + 32), false}, params.b}); + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + + assignment.constant(0)[row] = ArithmetizationType::field_type::value_type::zero(); + assignment.constant(0)[row + 1] = endo; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_scalar_mul.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_scalar_mul.hpp new file mode 100644 index 000000000..57fa7a402 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/pasta/plonk/variable_base_scalar_mul.hpp @@ -0,0 +1,944 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_HPP + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Using results from https://arxiv.org/pdf/math/0208038.pdf + // Vesta curve: + // Input: x \in F_p, P \in E(F_p) + // Output: y * P, where x = (y - 2^255 - 1) / 2 (if x is not -1, 0, 1) + // Output: y * P, where x = (y - 2^255) (if x is -1, 0, 1) + // Pallas curve: + // Input: x, x_high_bit \in F_p, P \in E(F_p) + // Output: y * P, where x + 2^254 * x_high_bit = (y - 2^255 - 1) / 2 (if (x + 2^254 * x_high_bit) is not -1, 0, 1) + // Output: y * P, where x + 2^254 * x_high_bit = (y - 2^255) (if (x + 2^254 * x_high_bit) is -1, 0, 1) + + // clang-format off +// _____________________________________________________________________________________________________________________________________________________ +// | | W0 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 | W14 | +// |‾row‾0‾‾|‾‾ calculating 2T ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| +// | row 1 | T.X | T.Y | P[0].X | p[0].Y | n | n_next | | P[1].X | P[1].Y | P[2].X | P[2].Y | P[3].X | P[3].Y | P[4].X | P[4].Y | +// | row 2 | P[5].X | P[5].Y | bits[0] | bits[1] | bits[2] | bits[3] | bits[4] | s0 | s1 | s2 | s3 | s4 | | | | +// | row 3 | T.X | T.Y | P[0].X | p[0].Y | n | n_next | | P[1].X | P[1].Y | P[2].X | P[2].Y | P[3].X | P[3].Y | P[4].X | P[4].Y | +// | row 4 | P[5].X | P[5].Y | bits[5] | bits[6] | bits[7] | bits[8] | bits[9] | s0 | s1 | s2 | s3 | s4 | | | | +// | | ... | +// | | ... | +// | row 59 | T.X | T.Y | P[0].X | p[0].Y | n | n_next | u | P[1].X | P[1].Y | P[2].X | P[2].Y | P[3].X | P[3].Y | P[4].X | P[4].Y | +// | row 60 | P[5].X | P[5].Y | bits[5] | bits[6] | bits[7] | bits[8] | bits[9] | s0 | s1 | s2 | s3 | s4 | u0 | u1 | u_next | +// | | ... | +// | | ... | +// | row 99 | T.X | T.Y | P[0].X | p[0].Y | n | n_next | u | P[1].X | P[1].Y | P[2].X | P[2].Y | P[3].X | P[3].Y | P[4].X | P[4].Y | +// | row 100| P[5].X | P[5].Y | bits[5] | bits[6] | bits[7] | bits[8] | bits[9] | s0 | s1 | s2 | s3 | s4 | u0 | u1 | u_next | +// | row 101| T.X | T.Y | P[0].X | p[0].Y | n | n_next | u | P[1].X | P[1].Y | P[2].X | P[2].Y | P[3].X | P[3].Y | P[4].X | P[4].Y | +// | row 102| P[5].X | P[5].Y | bits[5] | bits[6] | bits[7] | bits[8] | bits[9] | s0 | s1 | s2 | s3 | s4 | u0 | u1 | u_next | +// | row 103| x | y | t0 | t1 | t2 | n_next | T.X | T.Y | m | e1 | e2 | b | aux | | | +// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + // clang-format on + + //////////////////////////////// + template + struct variable_base_scalar_mul_shifted_consts; + + template<> + struct variable_base_scalar_mul_shifted_consts { + using FieldType = nil::crypto3::algebra::fields::pallas_base_field; + + constexpr static const typename FieldType::value_type shifted_minus_one = 0x224698fc0994a8dd8c46eb2100000000_cppui_modular255; + constexpr static const typename FieldType::value_type shifted_zero = 0x200000000000000000000000000000003369e57a0e5efd4c526a60b180000001_cppui_modular255; + constexpr static const typename FieldType::value_type shifted_one = 0x224698fc0994a8dd8c46eb2100000001_cppui_modular255; + }; + + template<> + struct variable_base_scalar_mul_shifted_consts { + using FieldType = nil::crypto3::algebra::fields::vesta_base_field; + + constexpr static const typename FieldType::value_type shifted_minus_one = 0x448d31f81299f237325a61da00000001_cppui_modular255; + constexpr static const typename FieldType::value_type shifted_zero = 0x448d31f81299f237325a61da00000002_cppui_modular255; + constexpr static const typename FieldType::value_type shifted_one = 0x448d31f81299f237325a61da00000003_cppui_modular255; + }; + //////////////////////////////// + + template + class curve_element_variable_base_scalar_mul; + + template + class curve_element_variable_base_scalar_mul< + crypto3::zk::snark::plonk_constraint_system, + CurveType + >: public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using add_component = + nil::blueprint::components::unified_addition, CurveType>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return curve_element_variable_base_scalar_mul::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(add_component::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + true + ).merge_with(add_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount; + } + + constexpr static const std::size_t mul_rows_amount = 102; + constexpr static const std::size_t add_component_rows_amount = add_component::get_rows_amount(11); + constexpr static const std::size_t rows_amount = add_component_rows_amount + mul_rows_amount + 1; + constexpr static const std::size_t gates_amount = 3; + const std::string component_name = "native curve multiplication by shifted const (https://arxiv.org/pdf/math/0208038.pdf)"; + + constexpr static const std::size_t aux_bits_rows_amount = 44; + constexpr static const std::size_t aux_bits_start_row = rows_amount - aux_bits_rows_amount - 1; // = 59 + + constexpr static const typename BlueprintFieldType::value_type shifted_minus_one = variable_base_scalar_mul_shifted_consts::shifted_minus_one; + constexpr static const typename BlueprintFieldType::value_type shifted_zero = variable_base_scalar_mul_shifted_consts::shifted_zero; + constexpr static const typename BlueprintFieldType::value_type shifted_one = variable_base_scalar_mul_shifted_consts::shifted_one; + + constexpr static const typename BlueprintFieldType::value_type t_q = 0x224698fc0994a8dd8c46eb2100000001_cppui_modular255; // q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001_cppui_modular255 = 2**254 + t_q + constexpr static const typename BlueprintFieldType::value_type t_p = 0x224698fc094cf91b992d30ed00000001_cppui_modular255; // p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001_cppui_modular255 = 2**254 + t_p (q > p) + constexpr static const typename BlueprintFieldType::value_type two = 2; + + struct input_type { + struct var_ec_point { + var x; + var y; + }; + + var_ec_point T; + var b; + var b_high; + input_type(var_ec_point _T, var _b): T(_T), b(_b) {}; + input_type(var_ec_point _T, var _b, var _b_high): T(_T), b(_b), b_high(_b_high) {}; + + std::vector> all_vars() { + if (std::is_same::value) { + return {T.x, T.y, b, b_high}; + } else { + return {T.x, T.y, b}; + } + } + }; + + struct result_type { + var X; + var Y; + result_type(const curve_element_variable_base_scalar_mul &component, std::size_t start_row_index) { + X = var(component.W(0), start_row_index + component.rows_amount - 1, false, var::column_type::witness); + Y = var(component.W(1), start_row_index + component.rows_amount - 1, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {X, Y}; + } + }; + + template + curve_element_variable_base_scalar_mul(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + curve_element_variable_base_scalar_mul(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + curve_element_variable_base_scalar_mul(std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + }; + + template + using plonk_curve_element_variable_base_scalar_mul = + curve_element_variable_base_scalar_mul< + crypto3::zk::snark::plonk_constraint_system, + CurveType + >; + + template + typename plonk_curve_element_variable_base_scalar_mul::result_type + generate_assignments( + const plonk_curve_element_variable_base_scalar_mul &component, + assignment> &assignment, + const typename plonk_curve_element_variable_base_scalar_mul::input_type instance_input, + const std::uint32_t start_row_index) { + + using add_component = typename plonk_curve_element_variable_base_scalar_mul< + BlueprintFieldType, CurveType>::add_component; + + typename BlueprintFieldType::value_type b = var_value(assignment, instance_input.b); + typename BlueprintFieldType::value_type b_high; + if (std::is_same::value) { + b_high = var_value(assignment, instance_input.b_high); + } else { + b_high = 0; + } + typename BlueprintFieldType::value_type T_x = var_value(assignment, instance_input.T.x); + typename BlueprintFieldType::value_type T_y = var_value(assignment, instance_input.T.y); + typename CurveType::template g1_type::value_type T(T_x, + T_y); + + typedef typename CurveType::template g1_type::value_type g1_value_type; + std::array P; + g1_value_type Q; + + typename CurveType::scalar_field_type::integral_type integral_b = + typename CurveType::scalar_field_type::integral_type(b.data); + const std::size_t scalar_size = 255; + nil::marshalling::status_type status; + std::array bits = + nil::marshalling::pack(integral_b, status); + + typename BlueprintFieldType::value_type z_n2; + typename BlueprintFieldType::value_type aux; + if (std::is_same::value) { + z_n2 = integral_b; + aux = z_n2 - component.t_q + component.two.pow(130); + typename BlueprintFieldType::integral_type intehral_b_high = typename BlueprintFieldType::integral_type(b_high.data); + if (intehral_b_high == 1) { + bits[0] = 1; + } + } else { + z_n2 = integral_b - bits[0] * component.two.pow(254); + aux = z_n2 - component.t_p + component.two.pow(130); + } + typename CurveType::scalar_field_type::integral_type integral_aux = + typename CurveType::scalar_field_type::integral_type(aux.data); + const std::size_t base_size = 255; + std::array aux_bits = + nil::marshalling::pack(integral_aux, status); + + typename BlueprintFieldType::value_type n = 0; + typename BlueprintFieldType::value_type n_next = 0; + + add_component unified_addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8), component.W(9), + component.W(10)},{},{}); + + typename add_component::input_type addition_input = {{instance_input.T.x, instance_input.T.y}, + {instance_input.T.x, instance_input.T.y}}; + + typename add_component::result_type addition_res = + generate_assignments(unified_addition_instance, assignment, addition_input, start_row_index); + + + g1_value_type T_doubled(var_value(assignment, addition_res.X), var_value(assignment, addition_res.Y)); + + std::size_t j = start_row_index + component.add_component_rows_amount; + + for (std::size_t i = j; i < j + component.mul_rows_amount; i = i + 2) { + assignment.witness(component.W(0), i) = T.X; + assignment.witness(component.W(1), i) = T.Y; + if (i == j) { + P[0] = T_doubled; + } else { + P[0] = P[5]; + n = n_next; + } + assignment.witness(component.W(2), i) = P[0].X; + assignment.witness(component.W(3), i) = P[0].Y; + assignment.witness(component.W(4), i) = n; + n_next = 32 * n + 16 * bits[((i - j) / 2) * 5] + 8 * bits[((i - j) / 2) * 5 + 1] + + 4 * bits[((i - j) / 2) * 5 + 2] + 2 * bits[((i - j) / 2) * 5 + 3] + + bits[((i - j) / 2) * 5 + 4]; + assignment.witness(component.W(5), i) = n_next; + Q.X = T.X; + Q.Y = (2 * bits[((i - j) / 2) * 5] - 1) * T.Y; + + P[1] = (P[0] + Q) + P[0]; + assignment.witness(component.W(7), i) = P[1].X; + assignment.witness(component.W(8), i) = P[1].Y; + typename BlueprintFieldType::value_type diff0 = (P[0].X - Q.X); + assignment.witness(component.W(7), i + 1) = (P[0].Y - Q.Y) * (diff0.is_zero() ? 0: diff0.inversed()); + Q.Y = (2 * bits[((i - j) / 2) * 5 + 1] - 1) * T.Y; + + P[2] = (P[1] + Q) + P[1]; + assignment.witness(component.W(9), i) = P[2].X; + assignment.witness(component.W(10), i) = P[2].Y; + typename BlueprintFieldType::value_type diff1 = (P[1].X - Q.X); + assignment.witness(component.W(8), i + 1) = (P[1].Y - Q.Y) * (diff1.is_zero() ? 0: diff1.inversed()); + Q.Y = (2 * bits[((i - j) / 2) * 5 + 2] - 1) * T.Y; + + P[3] = (P[2] + Q) + P[2]; + assignment.witness(component.W(11), i) = P[3].X; + assignment.witness(component.W(12), i) = P[3].Y; + typename BlueprintFieldType::value_type diff2 = (P[2].X - Q.X); + assignment.witness(component.W(9), i + 1) = (P[2].Y - Q.Y) * (diff2.is_zero() ? 0: diff2.inversed()); + Q.Y = (2 * bits[((i - j) / 2) * 5 + 3] - 1) * T.Y; + + P[4] = (P[3] + Q) + P[3]; + assignment.witness(component.W(13), i) = P[4].X; + assignment.witness(component.W(14), i) = P[4].Y; + typename BlueprintFieldType::value_type diff3 = (P[3].X - Q.X); + assignment.witness(component.W(10), i + 1) = (P[3].Y - Q.Y) * (diff3.is_zero() ? 0: diff3.inversed()); + Q.Y = (2 * bits[((i - j) / 2) * 5 + 4] - 1) * T.Y; + + P[5] = (P[4] + Q) + P[4]; + assignment.witness(component.W(0), i + 1) = P[5].X; + assignment.witness(component.W(1), i + 1) = P[5].Y; + typename BlueprintFieldType::value_type diff4 = (P[4].X - Q.X); + assignment.witness(component.W(11), i + 1) = (P[4].Y - Q.Y) * (diff4.is_zero() ? 0: diff4.inversed()); + + assignment.witness(component.W(2), i + 1) = bits[((i - j) / 2) * 5]; + assignment.witness(component.W(3), i + 1) = bits[((i - j) / 2) * 5 + 1]; + assignment.witness(component.W(4), i + 1) = bits[((i - j) / 2) * 5 + 2]; + assignment.witness(component.W(5), i + 1) = bits[((i - j) / 2) * 5 + 3]; + assignment.witness(component.W(6), i + 1) = bits[((i - j) / 2) * 5 + 4]; + } + + // assign additional bits of aux for the range check (integral_b < q) or (b_high * 2^254 + integral_b < q) + typename BlueprintFieldType::value_type u_next = 0; + typename BlueprintFieldType::value_type u0, u1; + for (std::size_t i = start_row_index + component.aux_bits_start_row; i <= start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 3; i = i + 2) { + assignment.witness(component.W(6), i) = u_next; + const std::size_t ind = 125 + ((i - component.aux_bits_start_row - start_row_index) / 2) * 6; + u0 = 4 * aux_bits[ind] + 2 * aux_bits[ind+1] + aux_bits[ind+2]; + u1 = 4 * aux_bits[ind+3] + 2 * aux_bits[ind+4] + aux_bits[ind+5]; + u_next = 64 * u_next + 8 * u0 + u1; + assignment.witness(component.W(12), i+1) = u0; + assignment.witness(component.W(13), i+1) = u1; + assignment.witness(component.W(14), i+1) = u_next; + } + assignment.witness(component.W(6), start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 2) = u_next; + const std::size_t ind = 125 + (component.aux_bits_rows_amount / 2 - 1) * 6; + u0 = 4 * aux_bits[ind] + 2 * aux_bits[ind+1] + aux_bits[ind+2]; + u1 = aux_bits[ind+3]; + u_next = 16 * u_next + 2 * u0 + u1; + assignment.witness(component.W(12), start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 1) = u0; + assignment.witness(component.W(13), start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 1) = u1; + assignment.witness(component.W(14), start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 1) = u_next; + + assignment.witness(component.W(9), start_row_index + component.rows_amount - 1) = bits[0]; + typename BlueprintFieldType::value_type e2 = 0; + typename BlueprintFieldType::value_type cur_pow = 1; + for (std::size_t l = 130; l <= 254; l = l + 1) { + e2 += + bits[254-l] * cur_pow; + cur_pow = cur_pow * 2; + } + assignment.witness(component.W(10), start_row_index + component.rows_amount - 1) = e2; + assignment.witness(component.W(11), start_row_index + component.rows_amount - 1) = integral_b; + assignment.witness(component.W(12), start_row_index + component.rows_amount - 1) = aux; + + // assign last 3 rows + typename BlueprintFieldType::value_type m = ((n_next - component.shifted_minus_one)* + (n_next - component.shifted_zero)*(n_next - component.shifted_one)); + typename BlueprintFieldType::value_type t0 = ( m == 0 ? 0 : m.inversed()); + typename BlueprintFieldType::value_type t1 = ((n_next - component.shifted_minus_one) == 0) ? 0 : (n_next - component.shifted_minus_one).inversed(); + typename BlueprintFieldType::value_type t2 = ((n_next - component.shifted_one) == 0) ? 0 : (n_next - component.shifted_one).inversed(); + typename BlueprintFieldType::value_type x; + typename BlueprintFieldType::value_type y; + if (n_next == component.shifted_minus_one) { + x = T.X; + y = -T.Y; + } else { + if (n_next == component.shifted_zero) { + x = 0; + y = 0; + } else { + if (n_next == component.shifted_one) { + x = T.X; + y = T.Y; + } else { + x = P[5].X; + y = P[5].Y; + } + } + } + assignment.witness(component.W(2), start_row_index + component.rows_amount - 1) = t0; + assignment.witness(component.W(3), start_row_index + component.rows_amount - 1) = t1; + assignment.witness(component.W(4), start_row_index + component.rows_amount - 1) = t2; + assignment.witness(component.W(5), start_row_index + component.rows_amount - 1) = n_next; + assignment.witness(component.W(6), start_row_index + component.rows_amount - 1) = T.X; + assignment.witness(component.W(7), start_row_index + component.rows_amount - 1) = T.Y; + assignment.witness(component.W(8), start_row_index + component.rows_amount - 1) = m; + assignment.witness(component.W(0), start_row_index + component.rows_amount - 1) = x; + assignment.witness(component.W(1), start_row_index + component.rows_amount - 1) = y; + + return typename plonk_curve_element_variable_base_scalar_mul::result_type(component, start_row_index); + } + + template + typename plonk_curve_element_variable_base_scalar_mul::result_type + generate_circuit( + const plonk_curve_element_variable_base_scalar_mul &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_curve_element_variable_base_scalar_mul::input_type &instance_input, + const std::uint32_t start_row_index) { + + using add_component = typename plonk_curve_element_variable_base_scalar_mul< + BlueprintFieldType, CurveType>::add_component; + + generate_assignments_constants(component, bp, assignment, instance_input, start_row_index); + + auto selectors = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selectors[0], start_row_index + component.add_component_rows_amount, + start_row_index + component.rows_amount - 4, 2); + assignment.enable_selector(selectors[1], start_row_index + component.rows_amount - 2); + assignment.enable_selector(selectors[2], start_row_index + component.aux_bits_start_row, + start_row_index + component.aux_bits_start_row + component.aux_bits_rows_amount - 4, 2); + + typename add_component::input_type addition_input = {{instance_input.T.x, instance_input.T.y}, + {instance_input.T.x, instance_input.T.y}}; + + add_component unified_addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8), component.W(9), + component.W(10)}, {}, {}); + + generate_circuit(unified_addition_instance, bp, assignment, addition_input, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + return typename plonk_curve_element_variable_base_scalar_mul::result_type(component, start_row_index); + } + + template + std::array generate_gates( + const plonk_curve_element_variable_base_scalar_mul &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_curve_element_variable_base_scalar_mul::input_type instance_input) { + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = typename curve_element_variable_base_scalar_mul::var; + + auto bit_check_1 = var(component.W(2), +1) * (1 - var(component.W(2), +1)); + auto bit_check_2 = var(component.W(3), +1) * (1 - var(component.W(3), +1)); + auto bit_check_3 = var(component.W(4), +1) * (1 - var(component.W(4), +1)); + auto bit_check_4 = var(component.W(5), +1) * (1 - var(component.W(5), +1)); + auto bit_check_5 = var(component.W(6), +1) * (1 - var(component.W(6), +1)); + + auto constraint_1 = + (var(component.W(2), 0) - var(component.W(0), 0)) * var(component.W(7), +1) - + (var(component.W(3), 0) - (2 * var(component.W(2), +1) - 1) * var(component.W(1), 0)); + auto constraint_2 = + (var(component.W(7), 0) - var(component.W(0), 0)) * var(component.W(8), +1) - + (var(component.W(8), 0) - (2 * var(component.W(3), +1) - 1) * var(component.W(1), 0)); + auto constraint_3 = + (var(component.W(9), 0) - var(component.W(0), 0)) * var(component.W(9), +1) - + (var(component.W(10), 0) - (2 * var(component.W(4), +1) - 1) * var(component.W(1), 0)); + auto constraint_4 = + (var(component.W(11), 0) - var(component.W(0), 0)) * var(component.W(10), +1) - + (var(component.W(12), 0) - (2 * var(component.W(5), +1) - 1) * var(component.W(1), 0)); + auto constraint_5 = + (var(component.W(13), 0) - var(component.W(0), 0)) * var(component.W(11), +1) - + (var(component.W(14), 0) - (2 * var(component.W(6), +1) - 1) * var(component.W(1), 0)); + + auto constraint_6 = + (2 * var(component.W(3), 0) - var(component.W(7), 1) * (2 * var(component.W(2), 0) - + var(component.W(7), 1).pow(2) + var(component.W(0), 0))) * + (2 * var(component.W(3), 0) - var(component.W(7), 1) * (2 * var(component.W(2), 0) - + var(component.W(7), 1).pow(2) + var(component.W(0), 0))) - + ((2 * var(component.W(2), 0) - var(component.W(7), 1).pow(2) + var(component.W(0), 0)) * + (2 * var(component.W(2), 0) - var(component.W(7), 1).pow(2) + var(component.W(0), 0)) * + (var(component.W(7), 0) - var(component.W(0), 0) + var(component.W(7), 1).pow(2))); + auto constraint_7 = + (2 * var(component.W(8), 0) - var(component.W(8), 1) * (2 * var(component.W(7), 0) - + var(component.W(8), 1).pow(2) + var(component.W(0), 0))) * + (2 * var(component.W(8), 0) - var(component.W(8), 1) * (2 * var(component.W(7), 0) - + var(component.W(8), 1).pow(2) + var(component.W(0), 0))) - + ((2 * var(component.W(7), 0) - var(component.W(8), 1).pow(2) + var(component.W(0), 0)) * + (2 * var(component.W(7), 0) - var(component.W(8), 1).pow(2) + var(component.W(0), 0)) * + (var(component.W(9), 0) - var(component.W(0), 0) + var(component.W(8), 1).pow(2))); + auto constraint_8 = + (2 * var(component.W(10), 0) - var(component.W(9), 1) * (2 * var(component.W(9), 0) - + var(component.W(9), 1).pow(2) + var(component.W(0), 0))) * + (2 * var(component.W(10), 0) - var(component.W(9), 1) * (2 * var(component.W(9), 0) - + var(component.W(9), 1).pow(2) + var(component.W(0), 0))) - + ((2 * var(component.W(9), 0) - var(component.W(9), 1).pow(2) + var(component.W(0), 0)) * + (2 * var(component.W(9), 0) - var(component.W(9), 1).pow(2) + var(component.W(0), 0)) * + (var(component.W(11), 0) - var(component.W(0), 0) + var(component.W(9), 1).pow(2))); + auto constraint_9 = + (2 * var(component.W(12), 0) - var(component.W(10), +1) * (2 * var(component.W(11), 0) - + var(component.W(10), +1).pow(2) + var(component.W(0), 0))) * + (2 * var(component.W(12), 0) - var(component.W(10), +1) * (2 * var(component.W(11), 0) - + var(component.W(10), +1).pow(2) + var(component.W(0), 0))) - + ((2 * var(component.W(11), 0) - var(component.W(10), +1).pow(2) + var(component.W(0), 0)) * + (2 * var(component.W(11), 0) - var(component.W(10), +1).pow(2) + var(component.W(0), 0)) * + (var(component.W(13), 0) - var(component.W(0), 0) + var(component.W(10), +1).pow(2))); + auto constraint_10 = + (2 * var(component.W(14), 0) - var(component.W(11), +1) * (2 * var(component.W(13), 0) - + var(component.W(11), +1).pow(2) + var(component.W(0), 0))) * + (2 * var(component.W(14), 0) - var(component.W(11), +1) * (2 * var(component.W(13), 0) - + var(component.W(11), +1).pow(2) + var(component.W(0), 0))) - + ((2 * var(component.W(13), 0) - var(component.W(11), +1).pow(2) + var(component.W(0), 0)) * + (2 * var(component.W(13), 0) - var(component.W(11), +1).pow(2) + var(component.W(0), 0)) * + (var(component.W(0), 1) - var(component.W(0), 0) + var(component.W(11), +1).pow(2))); + + auto constraint_11 = + (var(component.W(8), 0) + var(component.W(3), 0)) * (2 * var(component.W(2), 0) - var(component.W(7), +1).pow(2) + var(component.W(0), 0)) - + ((var(component.W(2), 0) - var(component.W(7), 0)) * + (2 * var(component.W(3), 0) - var(component.W(7), +1) * (2 * var(component.W(2), 0) - + var(component.W(7), +1).pow(2) + var(component.W(0), 0)))); + auto constraint_12 = + (var(component.W(10), 0) + var(component.W(8), 0)) * (2 * var(component.W(7), 0) - + var(component.W(8), +1).pow(2) + var(component.W(0), 0)) - + ((var(component.W(7), 0) - var(component.W(9), 0)) * + (2 * var(component.W(8), 0) - var(component.W(8), +1) * + (2 * var(component.W(7), 0) - var(component.W(8), +1).pow(2) + var(component.W(0), 0)))); + auto constraint_13 = + (var(component.W(12), 0) + var(component.W(10), 0)) * (2 * var(component.W(9), 0) - + var(component.W(9), +1).pow(2) + var(component.W(0), 0)) - + ((var(component.W(9), 0) - var(component.W(11), 0)) * + (2 * var(component.W(10), 0) - var(component.W(9), +1) * (2 * var(component.W(9), 0) - + var(component.W(9), +1).pow(2) + var(component.W(0), 0)))); + auto constraint_14 = + (var(component.W(14), 0) + var(component.W(12), 0)) * (2 * var(component.W(11), 0) - + var(component.W(10), +1).pow(2) + var(component.W(0), 0)) - + ((var(component.W(11), 0) - var(component.W(13), 0)) * + (2 * var(component.W(12), 0) - var(component.W(10), +1) * + (2 * var(component.W(11), 0) - var(component.W(10), +1).pow(2) + var(component.W(0), 0)))); + auto constraint_15 = + (var(component.W(1), +1) + var(component.W(14), 0)) * (2 * var(component.W(13), 0) - + var(component.W(11), +1).pow(2) + var(component.W(0), 0)) - + ((var(component.W(13), 0) - var(component.W(0), +1)) * + (2 * var(component.W(14), 0) - var(component.W(11), +1) * (2 * var(component.W(13), 0) - + var(component.W(11), +1).pow(2) + var(component.W(0), 0)))); + + auto constraint_16 = + var(component.W(5), 0) - + (32 * (var(component.W(4), 0)) + 16 * var(component.W(2), +1) + 8 * var(component.W(3), +1) + + 4 * var(component.W(4), +1) + 2 * var(component.W(5), +1) + var(component.W(6), +1)); + + std::size_t selector_index_1 = bp.add_gate( + {bit_check_1, bit_check_2, bit_check_3, bit_check_4, bit_check_5, + constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, + constraint_6, constraint_7, constraint_8, constraint_9, constraint_10, + constraint_11, constraint_12, constraint_13, constraint_14, constraint_15, + constraint_16}); + + bit_check_1 = var(component.W(2), 0) * (1 - var(component.W(2), 0)); + bit_check_2 = var(component.W(3), 0) * (1 - var(component.W(3), 0)); + bit_check_3 = var(component.W(4), 0) * (1 - var(component.W(4), 0)); + bit_check_4 = var(component.W(5), 0) * (1 - var(component.W(5), 0)); + bit_check_5 = var(component.W(6), 0) * (1 - var(component.W(6), 0)); + + constraint_1 = + (var(component.W(2), -1) - var(component.W(0), -1)) * var(component.W(7), 0) - + (var(component.W(3), -1) - (2 * var(component.W(2), 0) - 1) * var(component.W(1), -1)); + constraint_2 = + (var(component.W(7), -1) - var(component.W(0), -1)) * var(component.W(8), 0) - + (var(component.W(8), -1) - (2 * var(component.W(3), 0) - 1) * var(component.W(1), -1)); + constraint_3 = + (var(component.W(9), -1) - var(component.W(0), -1)) * var(component.W(9), 0) - + (var(component.W(10), -1) - (2 * var(component.W(4), 0) - 1) * var(component.W(1), -1)); + constraint_4 = + (var(component.W(11), -1) - var(component.W(0), -1)) * var(component.W(10), 0) - + (var(component.W(12), -1) - (2 * var(component.W(5), 0) - 1) * var(component.W(1), -1)); + constraint_5 = + (var(component.W(13), -1) - var(component.W(0), -1)) * var(component.W(11), 0) - + (var(component.W(14), -1) - (2 * var(component.W(6), 0) - 1) * var(component.W(1), -1)); + + constraint_6 = + (2 * var(component.W(3), -1) - var(component.W(7), 0) * (2 * var(component.W(2), -1) - + var(component.W(7), 0).pow(2) + var(component.W(0), -1))) * + (2 * var(component.W(3), -1) - var(component.W(7), 0) * (2 * var(component.W(2), -1) - + var(component.W(7), 0).pow(2) + var(component.W(0), -1))) - + ((2 * var(component.W(2), -1) - var(component.W(7), 0).pow(2) + var(component.W(0), -1)) * + (2 * var(component.W(2), -1) - var(component.W(7), 0).pow(2) + var(component.W(0), -1)) * + (var(component.W(7), -1) - var(component.W(0), -1) + var(component.W(7), 0).pow(2))); + constraint_7 = + (2 * var(component.W(8), -1) - var(component.W(8), 0) * (2 * var(component.W(7), -1) - + var(component.W(8), 0).pow(2) + var(component.W(0), -1))) * + (2 * var(component.W(8), -1) - var(component.W(8), 0) * (2 * var(component.W(7), -1) - + var(component.W(8), 0).pow(2) + var(component.W(0), -1))) - + ((2 * var(component.W(7), -1) - var(component.W(8), 0).pow(2) + var(component.W(0), -1)) * + (2 * var(component.W(7), -1) - var(component.W(8), 0).pow(2) + var(component.W(0), -1)) * + (var(component.W(9), -1) - var(component.W(0), -1) + var(component.W(8), 0).pow(2))); + constraint_8 = + (2 * var(component.W(10), -1) - var(component.W(9), 0) * (2 * var(component.W(9), -1) - + var(component.W(9), 0).pow(2) + var(component.W(0), -1))) * + (2 * var(component.W(10), -1) - var(component.W(9), 0) * (2 * var(component.W(9), -1) - + var(component.W(9), 0).pow(2) + var(component.W(0), -1))) - + ((2 * var(component.W(9), -1) - var(component.W(9), 0).pow(2) + var(component.W(0), -1)) * + (2 * var(component.W(9), -1) - var(component.W(9), 0).pow(2) + var(component.W(0), -1)) * + (var(component.W(11), -1) - var(component.W(0), -1) + var(component.W(9), 0).pow(2))); + constraint_9 = + ((2 * var(component.W(12), -1) - var(component.W(10), 0) * (2 * var(component.W(11), -1) - + var(component.W(10), 0).pow(2) + var(component.W(0), -1))) * + (2 * var(component.W(12), -1) - var(component.W(10), 0) * (2 * var(component.W(11), -1) - + var(component.W(10), 0).pow(2) + var(component.W(0), -1))) - + ((2 * var(component.W(11), -1) - var(component.W(10), 0).pow(2) + var(component.W(0), -1)) * + (2 * var(component.W(11), -1) - var(component.W(10), 0).pow(2) + var(component.W(0), -1)) * + (var(component.W(13), -1) - var(component.W(0), -1) + var(component.W(10), 0).pow(2)))) * + var(component.W(8), +1) * var(component.W(2), +1); + constraint_10 = + ((2 * var(component.W(14), -1) - var(component.W(11), 0) * (2 * var(component.W(13), -1) - + var(component.W(11), 0).pow(2) + var(component.W(0), -1))) * + (2 * var(component.W(14), -1) - var(component.W(11), 0) * (2 * var(component.W(13), -1) - + var(component.W(11), 0).pow(2) + var(component.W(0), -1))) - + ((2 * var(component.W(13), -1) - var(component.W(11), 0).pow(2) + var(component.W(0), -1)) * + (2 * var(component.W(13), -1) - var(component.W(11), 0).pow(2) + var(component.W(0), -1)) * + (var(component.W(0), 0) - var(component.W(0), -1) + var(component.W(11), 0).pow(2)))) * + var(component.W(8), +1) * var(component.W(2), +1); + + constraint_11 = + (var(component.W(8), -1) + var(component.W(3), -1)) * + (2 * var(component.W(2), -1) - var(component.W(7), 0).pow(2) + var(component.W(0), -1)) - + ((var(component.W(2), -1) - var(component.W(7), -1)) * + (2 * var(component.W(3), -1) - var(component.W(7), 0) * (2 * var(component.W(2), -1) - + var(component.W(7), 0).pow(2) + var(component.W(0), -1)))); + constraint_12 = + (var(component.W(10), -1) + var(component.W(8), -1)) * (2 * var(component.W(7), -1) - + var(component.W(8), 0).pow(2) + var(component.W(0), -1)) - + ((var(component.W(7), -1) - var(component.W(9), -1)) * + (2 * var(component.W(8), -1) - var(component.W(8), 0) * (2 * var(component.W(7), -1) - + var(component.W(8), 0).pow(2) + var(component.W(0), -1)))); + constraint_13 = + (var(component.W(12), -1) + var(component.W(10), -1)) * (2 * var(component.W(9), -1) - + var(component.W(9), 0).pow(2) + var(component.W(0), -1)) - + ((var(component.W(9), -1) - var(component.W(11), -1)) * + (2 * var(component.W(10), -1) - var(component.W(9), 0) * (2 * var(component.W(9), -1) - + var(component.W(9), 0).pow(2) + var(component.W(0), -1)))); + constraint_14 = + ((var(component.W(14), -1) + var(component.W(12), -1)) * (2 * var(component.W(11), -1) - + var(component.W(10), 0).pow(2) + var(component.W(0), -1)) - + ((var(component.W(11), -1) - var(component.W(13), -1)) * + (2 * var(component.W(12), -1) - + var(component.W(10), 0) * (2 * var(component.W(11), -1) - var(component.W(10), 0).pow(2) + var(component.W(0), -1))))) * + var(component.W(8), +1) * var(component.W(2), +1); + constraint_15 = + ((var(component.W(1), 0) + var(component.W(14), -1)) * (2 * var(component.W(13), -1) - var(component.W(11), 0).pow(2) + var(component.W(0), -1)) - + ((var(component.W(13), -1) - var(component.W(0), 0)) * + (2 * var(component.W(14), -1) - + var(component.W(11), 0) * (2 * var(component.W(13), -1) - var(component.W(11), 0).pow(2) + var(component.W(0), -1))))) * + var(component.W(8), +1) * var(component.W(2), +1); + + constraint_16 = + var(component.W(5), -1) - (32 * (var(component.W(4), -1)) + 16 * var(component.W(2), 0) + 8 * var(component.W(3), 0) + + 4 * var(component.W(4), 0) + 2 * var(component.W(5), 0) + var(component.W(6), 0)); + + auto constraint_17 = (var(component.W(8), +1)*var(component.W(2), +1) - 1) * var(component.W(8), +1); + auto constraint_18 = ((var(component.W(5), +1) - component.shifted_minus_one) + *var(component.W(3), +1) - 1) * (var(component.W(5), +1) - component.shifted_minus_one); + auto constraint_19 = ((var(component.W(5), +1) - component.shifted_one) + *var(component.W(4), +1) - 1) * (var(component.W(5), +1) - component.shifted_one); + auto constraint_20 = (var(component.W(8), +1)*var(component.W(2), +1)*var(component.W(0), 0)) + + ((var(component.W(5), +1) - component.shifted_minus_one) + *var(component.W(3), +1) - (var(component.W(5), +1) - component.shifted_one) + *var(component.W(4), +1))* ((var(component.W(5), +1) - component.shifted_minus_one) + *var(component.W(3), +1) - (var(component.W(5), +1) - component.shifted_one) + *var(component.W(4), +1)) * var(component.W(6), +1) - var(component.W(0), +1); + auto constraint_21 = (var(component.W(8), +1)*var(component.W(2), +1)*var(component.W(1), 0)) + + ((var(component.W(5), +1) - component.shifted_minus_one) + *var(component.W(3), +1) - (var(component.W(5), +1) - component.shifted_one) + *var(component.W(4), +1)) * var(component.W(7), +1) - var(component.W(1), +1); + auto constraint_22 = var(component.W(8), +1) - ((var(component.W(5), +1) - component.shifted_minus_one) + *(var(component.W(5), +1) - component.shifted_zero)* + (var(component.W(5), +1) - component.shifted_one)); + + // additional range-check constraints: + // check u_0 = 3-bit chunk of aux + auto constraint_23 = + var(component.W(12), 0) * (var(component.W(12), 0) - 1) * (var(component.W(12), 0) - 2) * (var(component.W(12), 0) - 3) + * (var(component.W(12), 0) - 4) * (var(component.W(12), 0) - 5) * (var(component.W(12), 0) - 6) * (var(component.W(12), 0) - 7); + // check u_1 = 1-bit chunk of aux + auto constraint_24 = var(component.W(13), 0) * (var(component.W(13), 0) - 1); + // check accumalator(u_i) + auto constraint_25 = + var(component.W(14), 0) - 16 * var(component.W(6), -1) - 2 * var(component.W(12), 0) - + var(component.W(13), 0); + // check aux = z_{n-2} - t_p + 2^130 + auto constraint_28 = var(component.W(9), 0) - (var(component.W(9), 0)); + if (std::is_same::value) { + constraint_28 = + var(component.W(12), +1) - var(component.W(11), +1) + component.t_q - component.two.pow(130); + } else { + constraint_28 = + var(component.W(12), +1) - var(component.W(11), +1) + var(component.W(9), +1) * component.two.pow(254) + component.t_p - component.two.pow(130); + } + // check (bits[0] = 1) => accumalator(u_i) = aux + auto constraint_26 = var(component.W(9), +1) * (var(component.W(14), 0) - var(component.W(12), +1)); + // check (bits[0] = 1) => V_130 = 2^124 + auto constraint_27 = var(component.W(9), +1) * (var(component.W(10), +1) - component.two.pow(124)); + + // check b_high * 2^254 + b = accamulator(b_i) (mod p) + // (b_high = 1) => b < 2^254 + auto constraint_29 = var(component.W(9), 0) - var(component.W(9), 0); + if (std::is_same::value) { + constraint_29 = + var(component.W(5), -1) - var(component.W(11), +1) - + var(component.W(9), +1) * component.two.pow(254); + } + + std::size_t selector_index_2 = bp.add_gate( + {bit_check_1, bit_check_2, bit_check_3, bit_check_4, bit_check_5, + constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, + constraint_6, constraint_7, constraint_8, constraint_9, constraint_10, + constraint_11, constraint_12, constraint_13, constraint_14, constraint_15, + constraint_16, constraint_17, constraint_18, constraint_19, constraint_20, + constraint_21, constraint_22, + constraint_23, constraint_24, constraint_25, constraint_26, constraint_27, + constraint_28, constraint_29}); + + // check u_0 = 3-bit chunk of aux + constraint_1 = + var(component.W(12), +1) * (var(component.W(12), +1) - 1) * (var(component.W(12), +1) - 2) * (var(component.W(12), +1) - 3) + * (var(component.W(12), +1) - 4) * (var(component.W(12), +1) - 5) * (var(component.W(12), +1) - 6) * (var(component.W(12), +1) - 7); + // check u_1 = 3-bit chunk of aux + constraint_2 = + var(component.W(13), +1) * (var(component.W(13), +1) - 1) * (var(component.W(13), +1) - 2) * (var(component.W(13), +1) - 3) + * (var(component.W(13), +1) - 4) * (var(component.W(13), +1) - 5) * (var(component.W(13), +1) - 6) * (var(component.W(13), +1) - 7); + // check u_next = intermediate accumalator(u_i) + constraint_3 = + var(component.W(14), +1) - 64 * var(component.W(6), 0) - 8 * var(component.W(12), +1) - var(component.W(13), +1); + std::size_t selector_index_3 = bp.add_gate({constraint_1, constraint_2, constraint_3}); + + return {selector_index_1, selector_index_2, selector_index_3}; + } + + template + void generate_copy_constraints( + const plonk_curve_element_variable_base_scalar_mul &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_curve_element_variable_base_scalar_mul::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t j = start_row_index + component.add_component_rows_amount; + using var = typename plonk_curve_element_variable_base_scalar_mul::var; + using add_component = typename plonk_curve_element_variable_base_scalar_mul< + BlueprintFieldType, CurveType>::add_component; + + add_component unified_addition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8), component.W(9), + component.W(10)},{},{}); + + typename add_component::result_type addition_res(unified_addition_instance, start_row_index); + + bp.add_copy_constraint({{component.W(2), (std::int32_t)(j), false}, addition_res.X}); + bp.add_copy_constraint({{component.W(3), (std::int32_t)(j), false}, addition_res.Y}); + + // main algorithm + + for (int z = 0; z < component.mul_rows_amount - 2; z += 2) { + bp.add_copy_constraint( + {{component.W(0), (std::int32_t)(j + z), false}, {component.W(0), (std::int32_t)(j + z + 2), false}}); + bp.add_copy_constraint( + {{component.W(1), (std::int32_t)(j + z), false}, {component.W(1), (std::int32_t)(j + z + 2), false}}); + } + + for (int z = 2; z < component.mul_rows_amount; z += 2) { + bp.add_copy_constraint( + {{component.W(2), (std::int32_t)(j + z), false}, {component.W(0), (std::int32_t)(j + z - 1), false}}); + bp.add_copy_constraint( + {{component.W(3), (std::int32_t)(j + z), false}, {component.W(1), (std::int32_t)(j + z - 1), false}}); + } + + for (int z = 2; z < component.mul_rows_amount; z += 2) { + bp.add_copy_constraint( + {{component.W(4), (std::int32_t)(j + z), false}, {component.W(5), (std::int32_t)(j + z - 2), false}}); + } + bp.add_copy_constraint({{component.W(5), (std::int32_t)(start_row_index + component.rows_amount - 1), false}, + {component.W(5), (std::int32_t)(start_row_index + component.rows_amount - 3), false}}); + bp.add_copy_constraint({{component.W(6), (std::int32_t)(start_row_index + component.rows_amount - 1), false}, + {component.W(0), (std::int32_t)(start_row_index + component.rows_amount - 3), false}}); + bp.add_copy_constraint({{component.W(7), (std::int32_t)(start_row_index + component.rows_amount - 1), false}, + {component.W(1), (std::int32_t)(start_row_index + component.rows_amount - 3), false}}); + + bp.add_copy_constraint({{component.W(4), (std::int32_t)(j), false}, + {component.W(0), (std::int32_t)(j), false, var::column_type::constant}}); + + // bp.add_copy_constraint( + // {instance_input.b, {component.W(5), (std::int32_t)(j + component.rows_amount - 4), false}}); // scalar value check + + // additional range-checks copy constraints + if (std::is_same::value) { + bp.add_copy_constraint( + {instance_input.b_high, {component.W(2), (std::int32_t)(j + 1), false}}); + } else { + bp.add_copy_constraint( + {instance_input.b, {component.W(5), (std::int32_t)(j + component.rows_amount - 4), false}}); // scalar value check + } + bp.add_copy_constraint( + {{component.C(0), (std::int32_t)(j), false, var::column_type::constant}, {component.W(6), (std::int32_t)(j + 58), false}}); + for (int z = 0; z < 40; z += 2) { + bp.add_copy_constraint( + {{component.W(14), (std::int32_t)(j + 58 + z + 1), false}, {component.W(6), (std::int32_t)(j + 58 + z + 2), false}}); + } + bp.add_copy_constraint( + {{component.W(2), (std::int32_t)(j + 1), false}, {component.W(9), (std::int32_t)(j + 102), false}}); + bp.add_copy_constraint( + {{component.W(5), (std::int32_t)(j + 48), false}, {component.W(10), (std::int32_t)(j + 102), false}}); + bp.add_copy_constraint( + {instance_input.b, {component.W(11), (std::int32_t)(j + 102), false}}); + + } + + template + void generate_assignments_constants( + const plonk_curve_element_variable_base_scalar_mul &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_curve_element_variable_base_scalar_mul::input_type instance_input, + const std::uint32_t start_row_index) { + std::size_t row = start_row_index + component.add_component_rows_amount; + + assignment.constant(component.C(0), row) = BlueprintFieldType::value_type::zero(); + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter< + plonk_curve_element_variable_base_scalar_mul> { + + using component_type = + plonk_curve_element_variable_base_scalar_mul; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + tmp_assignment.public_input(0, 0) = var_value(assignment, input.T.x); + tmp_assignment.public_input(0, 1) = var_value(assignment, input.T.y); + tmp_assignment.public_input(0, 2) = var_value(assignment, input.b); + if (std::is_same::value) { + tmp_assignment.public_input(0, 3) = var_value(assignment, input.b_high); + } + + if (std::is_same::value) { + input_type new_input( + {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input)}, + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input) + ); + return new_input; + } else { + input_type new_input( + {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input)}, + var(0, 2, false, var::column_type::public_input) + ); + return new_input; + } + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + var new_var; + switch (variable.rotation) { + case 0: + new_var = input.T.x; + break; + case 1: + new_var = input.T.y; + break; + case 2: + new_var = input.b; + break; + case 3: + new_var = input.b_high; + break; + default: + BOOST_ASSERT_MSG(false, "Incorrect variable passed to deconvert_var"); + } + return new_var; + } + }; + + template + class result_type_converter< + plonk_curve_element_variable_base_scalar_mul> { + + using component_type = + plonk_curve_element_variable_base_scalar_mul; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + new_result.X = component.move_var( + old_result.X, + start_row_index + component.line_mapping[old_result.X.rotation], + instance_input); + new_result.Y = component.move_var( + old_result.Y, + start_row_index + component.line_mapping[old_result.Y.rotation], + instance_input); + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_CURVE_ELEMENT_VARIABLE_BASE_SCALAR_MUL_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/twisted_edwards/element_g1.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/twisted_edwards/element_g1.hpp new file mode 100644 index 000000000..799ed1022 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/twisted_edwards/element_g1.hpp @@ -0,0 +1,438 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G1 components. +// +// The components verify curve arithmetic in G1 = E(F) where E/F: a * x^2 + y^2 = 1 + d * x^2 * y^2 +// is an elliptic curve over F in Twisted Edwards form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /** + * @brief Component that creates constraints for the addition of two elements from G1. + */ + template + struct element_g1_addition + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::twisted_edwards; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + + using result_type = element_component; + + const element_component p1; + const element_component p2; + result_type result; + + // Intermediate variables + element_fp X1X2; + element_fp X1Y2; + element_fp Y1Y2; + element_fp Y1X2; + element_fp X1X2Y1Y2; + element_fp dX1X2Y1Y2; + element_fp aX1X2; + + private: + void init() { + detail::blueprint_variable X1X2_var, X1Y2_var, Y1Y2_var, Y1X2_var, X1X2Y1Y2_var, + dX1X2Y1Y2_var, aX1X2_var; + + X1X2_var.allocate(this->bp); + X1Y2_var.allocate(this->bp); + Y1Y2_var.allocate(this->bp); + Y1X2_var.allocate(this->bp); + X1X2Y1Y2_var.allocate(this->bp); + dX1X2Y1Y2_var.allocate(this->bp); + aX1X2_var.allocate(this->bp); + + this->X1X2 = X1X2_var; + this->X1Y2 = X1Y2_var; + this->Y1Y2 = Y1Y2_var; + this->Y1X2 = Y1X2_var; + this->X1X2Y1Y2 = X1X2Y1Y2_var; + this->dX1X2Y1Y2 = dX1X2Y1Y2_var; + this->aX1X2 = aX1X2_var; + } + + public: + /// Auto allocation of the result + element_g1_addition(blueprint &bp, + const element_component &in_p1, + const element_component &in_p2) : + component(bp), + p1(in_p1), p2(in_p2), result(bp) { + init(); + } + + /// Manual allocation of the result + element_g1_addition(blueprint &bp, + const element_component &in_p1, + const element_component &in_p2, + const result_type &in_result) : + component(bp), + p1(in_p1), p2(in_p2), result(in_result) { + init(); + } + + void generate_gates() { + // X3 = (X1*Y2 + Y1*X2) / (Fq.ONE + D*X1*X2*Y1*Y2) + // y3 = (Y1*Y2 - A*X1*X2) / (Fq.ONE - D*X1*X2*Y1*Y2) + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p1.Y}, {this->p2.X}, {this->Y1X2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p1.X}, {this->p2.Y}, {this->X1Y2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p1.X}, {this->p2.X}, {this->X1X2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p1.Y}, {this->p2.Y}, {this->Y1Y2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->X1X2}, {this->Y1Y2}, {this->X1X2Y1Y2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {group_type::params_type::d}, {this->X1X2Y1Y2}, {this->dX1X2Y1Y2})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {group_type::params_type::a}, {this->X1X2}, {this->aX1X2})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->result.Y}, + {field_type::value_type::one(), -(this->dX1X2Y1Y2)}, + {this->Y1Y2, -(this->aX1X2)})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->result.X}, + {field_type::value_type::one(), this->dX1X2Y1Y2}, + {this->X1Y2, this->Y1X2})); + } + + void generate_assignments() { + const typename field_type::value_type &x1 = this->bp.lc_val(this->p1.X); + const typename field_type::value_type &y1 = this->bp.lc_val(this->p1.Y); + const typename field_type::value_type &x2 = this->bp.lc_val(this->p2.X); + const typename field_type::value_type &y2 = this->bp.lc_val(this->p2.Y); + + this->bp.lc_val(X1X2) = x1 * x2; + this->bp.lc_val(X1Y2) = x1 * y2; + this->bp.lc_val(Y1Y2) = y1 * y2; + this->bp.lc_val(Y1X2) = y1 * x2; + this->bp.lc_val(X1X2Y1Y2) = this->bp.lc_val(X1X2) * this->bp.lc_val(Y1Y2); + this->bp.lc_val(dX1X2Y1Y2) = + static_cast(group_type::params_type::d) * + this->bp.lc_val(X1X2Y1Y2); + this->bp.lc_val(aX1X2) = + static_cast(group_type::params_type::a) * + this->bp.lc_val(X1X2); + this->bp.lc_val(this->result.X) = + (this->bp.lc_val(X1Y2) + this->bp.lc_val(Y1X2)) * + (field_type::value_type::one() + this->bp.lc_val(dX1X2Y1Y2)).inversed(); + this->bp.lc_val(this->result.Y) = + (this->bp.lc_val(Y1Y2) - this->bp.lc_val(aX1X2)) * + (field_type::value_type::one() - this->bp.lc_val(dX1X2Y1Y2)).inversed(); + } + }; + + /** + * @brief Component that creates constraints for the validity of a G1 element. (if element from group G1 + * lies on the elliptic curve) + */ + template + struct element_g1_is_well_formed + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::twisted_edwards; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + + const element_component p; + + // Intermediate variables + element_fp XX; + element_fp aXX; + element_fp dXX; + element_fp YY; + element_fp dXXYY; + element_fp lhs; + element_fp rhs; + + element_g1_is_well_formed(blueprint &bp, const element_component &in_p) : + component(bp), p(in_p) { + detail::blueprint_variable XX_var, aXX_var, dXX_var, YY_var, dXXYY_var, lhs_var, rhs_var; + + XX_var.allocate(this->bp); + aXX_var.allocate(this->bp); + dXX_var.allocate(this->bp); + YY_var.allocate(this->bp); + dXXYY_var.allocate(this->bp); + lhs_var.allocate(this->bp); + rhs_var.allocate(this->bp); + + this->XX = XX_var; + this->aXX = aXX_var; + this->dXX = dXX_var; + this->YY = YY_var; + this->dXXYY = dXXYY_var; + this->lhs = lhs_var; + this->rhs = rhs_var; + } + + void generate_gates() { + // a*X*X + Y*Y = 1 + d*X*X*Y*Y + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p.X}, {this->p.X}, {this->XX})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p.Y}, {this->p.Y}, {this->YY})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({group_type::params_type::a}, {this->XX}, {this->aXX})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->aXX, this->YY}, {field_type::value_type::one()}, {this->lhs})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({group_type::params_type::d}, {this->XX}, {this->dXX})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->dXX}, {this->YY}, {this->dXXYY})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->dXXYY, field_type::value_type::one()}, + {field_type::value_type::one()}, + {this->rhs})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->lhs}, {field_type::value_type::one()}, {this->rhs})); + } + + void generate_assignments() { + const typename field_type::value_type &x = this->bp.lc_val(this->p.X); + const typename field_type::value_type &y = this->bp.lc_val(this->p.Y); + + this->bp.lc_val(this->XX) = x * x; + this->bp.lc_val(this->YY) = y * y; + this->bp.lc_val(this->aXX) = + static_cast(group_type::params_type::a) * + this->bp.lc_val(this->XX); + this->bp.lc_val(this->lhs) = this->bp.lc_val(this->aXX) + this->bp.lc_val(this->YY); + this->bp.lc_val(this->dXX) = + static_cast(group_type::params_type::d) * + this->bp.lc_val(this->XX); + this->bp.lc_val(this->dXXYY) = this->bp.lc_val(this->dXX) * this->bp.lc_val(this->YY); + this->bp.lc_val(this->rhs) = this->bp.lc_val(this->dXXYY) + field_type::value_type::one(); + } + }; + + /** + * @brief Component that creates constraints for the point serialization into the bit sequence + * according to https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub + */ + template + struct element_g1_to_bits + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::twisted_edwards; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + + using field_to_bits_component = field_to_bits_strict; + using result_type = typename field_to_bits_component::result_type; + + field_to_bits_component field_to_bits_converter; + result_type &result; + + /// Auto allocation of the result + element_g1_to_bits(blueprint &bp, const element_component &in_p) : + component(bp), field_to_bits_converter(bp, in_p.X), + result(field_to_bits_converter.result) { + } + + /// Manual allocation of the result + element_g1_to_bits(blueprint &bp, + const element_component &in_p, + const result_type &in_result) : + component(bp), + field_to_bits_converter(bp, in_p.X, in_result), result(field_to_bits_converter.result) { + } + + void generate_gates() { + this->field_to_bits_converter.generate_gates(); + } + + void generate_assignments() { + this->field_to_bits_converter.generate_assignments(); + } + }; + + /** + * @brief Component that creates constraints for the addition of two elements from G1. + */ + // TODO: fixme + template + struct element_g1_conditional_addition + : public component::field_type> { + using curve_type = Curve; + using form = algebra::curves::forms::twisted_edwards; + using coordinates = algebra::curves::coordinates::affine; + + using element_component = element_g1; + + using field_type = typename element_component::field_type; + using group_type = typename element_component::group_type; + + const element_component p1; + const element_component p2; + element_component result; + + const detail::blueprint_variable can_add; + + // intermediate variables + element_component p_to_add; + element_fp Y_intermediate_to_add1; + element_fp Y_intermediate_to_add2; + detail::blueprint_variable cannot_add; + + // TODO: refactor + // std::shared_ptr> el_add; + + element_g1_conditional_addition(blueprint &bp, + const element_component &in_p1, + const element_component &in_p2, + const detail::blueprint_variable &in_can_add, + const element_component &in_result) : + component(bp), + p1(in_p1), p2(in_p2), can_add(in_can_add), p_to_add(bp), result(in_result) { + detail::blueprint_variable Y_intermediate_to_add1_var, Y_intermediate_to_add2_var; + + Y_intermediate_to_add1_var.allocate(this->bp); + Y_intermediate_to_add2_var.allocate(this->bp); + cannot_add.allocate(this->bp); + + this->Y_intermediate_to_add1 = Y_intermediate_to_add1_var; + this->Y_intermediate_to_add2 = Y_intermediate_to_add2_var; + + // TODO: refactor + // el_add.reset(new element_g1_add(this->bp, a, d, p1, P_toAdd, p1pp2)); + } + + void generate_gates() { + // if coef == 1 then x_ret[i] + x_base + // x_add[i] = coef[i] * x_base; + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->p2.X}, {this->can_add}, {this->p_to_add.X})); + + // else do nothing. Ie add the zero point (0, 1) + // y_add[i] = coef[i] * y_base + !coef[i]; + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->p2.Y}, {this->can_add}, {this->Y_intermediate_to_add1})); + + // not coef + // make sure canAdd == 0 or canAdd == 1 + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(this->can_add, + field_type::value_type::one() - this->can_add, + field_type::value_type::zero())); + + // make sure not_canAdd == 0 or not_canAdd == 1 + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(this->cannot_add, + field_type::value_type::one() - this->cannot_add, + field_type::value_type::zero())); + + // make sure that the sum of canAdd, not_canAdd == 1 which means canAdd!=not_canAdd + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({this->cannot_add, this->can_add}, + {field_type::value_type::one()}, + {field_type::value_type::one()})); + + // because the are bool and because they are not equal we know that the inverse of one is the + // other. + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->cannot_add}, {field_type::value_type::one()}, {this->Y_intermediate_to_add2})); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {this->Y_intermediate_to_add1, this->Y_intermediate_to_add2}, + {field_type::value_type::one()}, + {this->p_to_add.Y})); + + // TODO: refactor + // do the addition of either y1 , y1 plus x2, y2 if canAdd == true else x1 , y1 + 0 + // el_add->generate_gates(); + } + + void generate_assignments() { + this->bp.lc_val(this->p_to_add.X) = this->bp.lc_val(this->p2.X) * this->bp.val(this->can_add); + this->bp.lc_val(this->Y_intermediate_to_add1) = + this->bp.lc_val(this->p2.Y) * this->bp.val(this->can_add); + + if (this->bp.val(this->can_add) == field_type::value_type::one()) { + this->bp.val(this->cannot_add) = field_type::value_type::zero(); + this->bp.lc_val(this->Y_intermediate_to_add2) = + this->bp.val(this->cannot_add) * field_type::value_type::one(); + this->bp.lc_val(this->p_to_add.Y) = this->bp.lc_val(this->Y_intermediate_to_add1); + } else { + this->bp.val(this->cannot_add) = field_type::value_type::one(); + this->bp.lc_val(this->Y_intermediate_to_add2) = + this->bp.val(this->cannot_add) * field_type::value_type::one(); + this->bp.lc_val(this->p_to_add.Y) = field_type::value_type::one(); + } + + // TODO: refactor + // el_add->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_TWISTED_EDWARDS_G1_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g1.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g1.hpp new file mode 100644 index 000000000..2c177f7e5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g1.hpp @@ -0,0 +1,264 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G1 components. +// +// The components verify curve arithmetic in G1 = E(F) where E/F: y^2 = x^3 + A * X + B +// is an elliptic curve over F in short Weierstrass form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /** + * Component that represents a G1 element. + */ + template + class element_g1 : public component { + + using underlying_field_type = typename CurveType::scalar_field_type; + using underlying_element_type = element_fp; + + public: + underlying_element_type X; + underlying_element_type Y; + + ::nil::crypto3::zk::detail::blueprint_linear_combination_vector all_vars; + + element_g1(blueprint &bp) : component(bp) { + + ::nil::crypto3::zk::detail::blueprint_variable X_var, Y_var; + + X_var.allocate(bp); + Y_var.allocate(bp); + + X = underlying_element_type(X_var); + Y = underlying_element_type(Y_var); + + all_vars.emplace_back(X); + all_vars.emplace_back(Y); + } + + element_g1(blueprint &bp, + const typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type &P) : + component(bp) { + typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type Pcopy = + P.to_affine(); + + X.assign(bp, Pcopy.X); + Y.assign(bp, Pcopy.Y); + X.evaluate(bp); + Y.evaluate(bp); + all_vars.emplace_back(X); + all_vars.emplace_back(Y); + } + + void generate_assignments( + const typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type &el) { + typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type el_normalized = + el.to_affine(); + + this->bp.lc_val(X) = el_normalized.X; + this->bp.lc_val(Y) = el_normalized.Y; + } + + // (See a comment in r1cs_ppzksnark_verifier_component.hpp about why + // we mark this function noinline.) TODO: remove later + static std::size_t __attribute__((noinline)) size_in_bits() { + return 2 * underlying_field_type::modulus_bits; + } + static std::size_t num_variables() { + return 2; + } + }; + + /** + * Component that creates constraints for the validity of a G1 element. + */ + template + class element_g1_is_well_formed : public component { + + using underlying_field_type = typename CurveType::scalar_field_type; + + public: + element_g1 P; + ::nil::crypto3::zk::detail::blueprint_variable P_X_squared; + ::nil::crypto3::zk::detail::blueprint_variable P_Y_squared; + + element_g1_is_well_formed(blueprint &bp, const element_g1 &P) : + component(bp), P(P) { + P_X_squared.allocate(bp); + P_Y_squared.allocate(bp); + } + void generate_gates() { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({P.X}, {P.X}, {P_X_squared})); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({P.Y}, {P.Y}, {P_Y_squared})); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {P.X}, + {P_X_squared, ::nil::crypto3::zk::detail::blueprint_variable(0) * + CurveType::pairing::pair_curve_type::a}, + {P_Y_squared, ::nil::crypto3::zk::detail::blueprint_variable(0) * + (-CurveType::pairing::pair_curve_type::b)})); + } + void generate_assignments() { + this->bp.val(P_X_squared) = this->bp.lc_val(P.X).squared(); + this->bp.val(P_Y_squared) = this->bp.lc_val(P.Y).squared(); + } + }; + + /** + * Component that creates constraints for G1 addition. + */ + template + class element_g1_add : public component { + + using underlying_field_type = typename CurveType::scalar_field_type; + + public: + ::nil::crypto3::zk::detail::blueprint_variable lambda; + ::nil::crypto3::zk::detail::blueprint_variable inv; + + element_g1 A; + element_g1 B; + element_g1 C; + + element_g1_add(blueprint &bp, + const element_g1 &A, + const element_g1 &B, + const element_g1 &C) : + component(bp), + A(A), B(B), C(C) { + /* + lambda = (B.y - A.y)/(B.x - A.x) + C.x = lambda^2 - A.x - B.x + C.y = lambda(A.x - C.x) - A.y + + Special cases: + + doubling: if B.y = A.y and B.x = A.x then lambda is unbound and + C = (lambda^2, lambda^3) + + addition of negative point: if B.y = -A.y and B.x = A.x then no + lambda can satisfy the first equation unless B.y - A.y = 0. But + then this reduces to doubling. + + So we need to check that A.x - B.x != 0, which can be done by + enforcing I * (B.x - A.x) = 1 + */ + lambda.allocate(bp); + inv.allocate(bp); + } + void generate_gates() { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {lambda}, {B.X, A.X * (-1)}, {B.Y, A.Y * (-1)})); + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({lambda}, {lambda}, {C.X, A.X, B.X})); + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({lambda}, {A.X, C.X * (-1)}, {C.Y, A.Y})); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {inv}, + {B.X, A.X * (-1)}, + {::nil::crypto3::zk::detail::blueprint_variable(0)})); + } + void generate_assignments() { + this->bp.val(inv) = (this->bp.lc_val(B.X) - this->bp.lc_val(A.X)).inversed(); + this->bp.val(lambda) = (this->bp.lc_val(B.Y) - this->bp.lc_val(A.Y)) * this->bp.val(inv); + this->bp.lc_val(C.X) = + this->bp.val(lambda).squared() - this->bp.lc_val(A.X) - this->bp.lc_val(B.X); + this->bp.lc_val(C.Y) = + this->bp.val(lambda) * (this->bp.lc_val(A.X) - this->bp.lc_val(C.X)) - this->bp.lc_val(A.Y); + } + }; + + /** + * Component that creates constraints for G1 doubling. + */ + template + class element_g1_doubled : public component { + + using underlying_field_type = typename CurveType::scalar_field_type; + + public: + ::nil::crypto3::zk::detail::blueprint_variable Xsquared; + ::nil::crypto3::zk::detail::blueprint_variable lambda; + + element_g1 A; + element_g1 B; + + element_g1_doubled(blueprint &bp, + const element_g1 &A, + const element_g1 &B) : + component(bp), + A(A), B(B) { + Xsquared.allocate(bp); + lambda.allocate(bp); + } + void generate_gates() { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({A.X}, {A.X}, {Xsquared})); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {lambda * 2}, + {A.Y}, + {Xsquared * 3, ::nil::crypto3::zk::detail::blueprint_variable(0x00) * + CurveType::pairing::pair_curve_type::a})); + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({lambda}, {lambda}, {B.X, A.X * 2})); + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({lambda}, {A.X, B.X * (-1)}, {B.Y, A.Y})); + } + void generate_assignments() { + this->bp.val(Xsquared) = this->bp.lc_val(A.X).squared(); + this->bp.val(lambda) = + (typename underlying_field_type::value_type(0x03) * this->bp.val(Xsquared) + + CurveType::pairing::pair_curve_type::a) * + (typename underlying_field_type::value_type(0x02) * this->bp.lc_val(A.Y)).inversed(); + this->bp.lc_val(B.X) = this->bp.val(lambda).squared() - + typename underlying_field_type::value_type(0x02) * this->bp.lc_val(A.X); + this->bp.lc_val(B.Y) = + this->bp.val(lambda) * (this->bp.lc_val(A.X) - this->bp.lc_val(B.X)) - this->bp.lc_val(A.Y); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G1_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g2.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g2.hpp new file mode 100644 index 000000000..b78eed9b6 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/curves/weierstrass/r1cs/element_g2.hpp @@ -0,0 +1,165 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for G2 components. +// +// The components verify curve arithmetic in G2 = E'(F) where E'/F^e: y^2 = x^3 + A' * X + B' +// is an elliptic curve over F^e in short Weierstrass form. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G2_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G2_COMPONENT_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + using namespace nil::crypto3::algebra::pairing; + + /** + * Component that represents a G2 element. + */ + template + class element_g2 : public component { + + using underlying_field_type = typename CurveType::scalar_field_type; + + using field_type = typename CurveType::pairing::fp_type; + + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + using component_policy = basic_curve_component_policy; + + public: + std::shared_ptr X; + std::shared_ptr Y; + + ::nil::crypto3::zk::detail::blueprint_linear_combination_vector all_vars; + + element_g2(blueprint &bp) : component(bp) { + X.reset(new typename component_policy::Fqe_variable_type(bp)); + Y.reset(new typename component_policy::Fqe_variable_type(bp)); + + all_vars.insert(all_vars.end(), X->all_vars.begin(), X->all_vars.end()); + all_vars.insert(all_vars.end(), Y->all_vars.begin(), Y->all_vars.end()); + } + element_g2(blueprint &bp, + const typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type &Q) : + component(bp) { + typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type Q_copy = + Q.to_affine(); + + X.reset(new typename component_policy::Fqe_variable_type(bp, Q_copy.X)); + Y.reset(new typename component_policy::Fqe_variable_type(bp, Q_copy.Y)); + + all_vars.insert(all_vars.end(), X->all_vars.begin(), X->all_vars.end()); + all_vars.insert(all_vars.end(), Y->all_vars.begin(), Y->all_vars.end()); + } + + void generate_assignments( + const typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type &Q) { + typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type Qcopy = + Q.to_affine(); + + X->generate_assignments(Qcopy.X); + Y->generate_assignments(Qcopy.Y); + } + + // (See a comment in r1cs_ppzksnark_verifier_component.hpp about why + // we mark this function noinline.) TODO: remove later + static std::size_t __attribute__((noinline)) size_in_bits() { + return 2 * typename component_policy::Fqe_variable_type::size_in_bits(); + } + static std::size_t num_variables() { + return 2 * typename component_policy::Fqe_variable_type::num_variables(); + } + }; + + /** + * Component that creates constraints for the validity of a G2 element. + */ + template + class element_g2_is_well_formed : public component { + typedef typename CurveType::pairing::fp_type field_type; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + using component_policy = basic_curve_component_policy; + + public: + element_g2 Q; + + std::shared_ptr Xsquared; + std::shared_ptr Ysquared; + std::shared_ptr Xsquared_plus_a; + std::shared_ptr Ysquared_minus_b; + + std::shared_ptr compute_Xsquared; + std::shared_ptr compute_Ysquared; + std::shared_ptr curve_equation; + + element_g2_is_well_formed(blueprint &bp, const element_g2 &Q) : + component(bp), Q(Q) { + Xsquared.reset(new typename component_policy::Fqe_variable_type(bp)); + Ysquared.reset(new typename component_policy::Fqe_variable_type(bp)); + + compute_Xsquared.reset( + new typename component_policy::Fqe_sqr_component_type(bp, *(Q.X), *Xsquared)); + compute_Ysquared.reset( + new typename component_policy::Fqe_sqr_component_type(bp, *(Q.Y), *Ysquared)); + + Xsquared_plus_a.reset(new typename component_policy::Fqe_variable_type( + (*Xsquared) + CurveType::pairing::pair_curve_type::a)); + Ysquared_minus_b.reset(new typename component_policy::Fqe_variable_type( + (*Ysquared) + (-CurveType::pairing::pair_curve_type::b))); + + curve_equation.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(Q.X), *Xsquared_plus_a, *Ysquared_minus_b)); + } + + void generate_gates() { + compute_Xsquared->generate_gates(); + compute_Ysquared->generate_gates(); + curve_equation->generate_gates(); + } + void generate_assignments() { + compute_Xsquared->generate_assignments(); + compute_Ysquared->generate_assignments(); + Xsquared_plus_a->evaluate(); + curve_equation->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_G2_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/addition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/addition.hpp new file mode 100644 index 000000000..f322daa19 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/addition.hpp @@ -0,0 +1,237 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_PLONK_FIELD_ADDITION_HPP +#define CRYPTO3_BLUEPRINT_PLONK_FIELD_ADDITION_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in F_p + // Output: z = x + y, z \in F_p + template + class addition; + + template + class addition, + BlueprintFieldType, NonNativePolicyType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "native field addition"; + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + + result_type(const addition &component, std::size_t start_row_index) { + output = var(component.W(2), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit addition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + addition(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + return x + y; + } + }; + + template + using plonk_native_addition = + addition, + BlueprintFieldType, basic_non_native_policy>; + + template + typename plonk_native_addition::result_type + generate_assignments( + const plonk_native_addition &component, + assignment> + &assignment, + const typename plonk_native_addition::input_type + instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = var_value(assignment, instance_input.y); + assignment.witness(component.W(2), j) = + var_value(assignment, instance_input.x) + var_value(assignment, instance_input.y); + return typename plonk_native_addition::result_type( + component, start_row_index); + } + + template + typename plonk_native_addition::result_type + generate_empty_assignments( + const plonk_native_addition &component, + assignment> + &assignment, + const typename plonk_native_addition::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_native_addition; + assignment.witness(component.W(2), start_row_index) = component_type::calculate( + var_value(assignment, instance_input.x), var_value(assignment, instance_input.y)); + return typename plonk_native_addition::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_native_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_native_addition::input_type + &instance_input) { + + using var = typename plonk_native_addition::var; + + auto constraint_1 = var(component.W(0), 0) + var(component.W(1), 0) - var(component.W(2), 0); + + return bp.add_gate(constraint_1); + } + + template + void generate_copy_constraints( + const plonk_native_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_native_addition::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_native_addition::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + bp.add_copy_constraint({component_y, instance_input.y}); + } + + template + typename plonk_native_addition::result_type generate_circuit( + const plonk_native_addition &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_native_addition::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_native_addition::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_PLONK_FIELD_ADDITION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/bit_shift_constant.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/bit_shift_constant.hpp new file mode 100644 index 000000000..abc02e143 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/bit_shift_constant.hpp @@ -0,0 +1,314 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BIT_SHIFT_CONSTANT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BIT_SHIFT_CONSTANT_HPP + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + enum bit_shift_mode { + LEFT, + RIGHT, + }; + + /* + Shits an element < 2^{bits_amount} by a constant amount of bits. + Input has to fit into [bits_amount < BlueprintFieldType::modulus_bits - 1] bits (this is checked). + This is implemented as decomposition + composition. + Left shift is done modulo 2^{bits_amount}. + */ + template + class bit_shift_constant; + + + template + class bit_shift_constant< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount, + std::size_t shift, bit_shift_mode mode) { + return decomposition_component_type::get_rows_amount(witness_amount, bits_amount, bit_composition_mode::LSB) + + composition_component_type::get_rows_amount(witness_amount, + calculate_composition_bits_amount(bits_amount, shift, mode), false, bit_composition_mode::LSB); + } + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using decomposition_component_type = + bit_decomposition>; + + using composition_component_type = + bit_composition>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 0; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + std::size_t shift, + bit_shift_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(decomposition_component_type::get_gate_manifest( + witness_amount, bits_amount, bit_composition_mode::LSB)) + .merge_with(composition_component_type::get_gate_manifest( + witness_amount, + calculate_composition_bits_amount(bits_amount, shift, mode), + false, bit_composition_mode::LSB)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, std::size_t shift, bit_shift_mode mode) { + manifest_type manifest = + decomposition_component_type::get_manifest( + bits_amount, bit_composition_mode::LSB + ).merge_with( + composition_component_type::get_manifest( + calculate_composition_bits_amount(bits_amount, shift, mode), + false, bit_composition_mode::LSB)); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, + std::size_t shift, bit_shift_mode mode) { + return rows_amount_internal(witness_amount, bits_amount, shift, mode); + } + + constexpr static const std::size_t rows(const decomposition_component_type& decomposition_subcomponent, + const composition_component_type& composition_subcomponent) { + return decomposition_subcomponent.rows_amount + composition_subcomponent.rows_amount; + } + + constexpr static const std::uint32_t calculate_composition_bits_amount(std::uint32_t bits_amount, + std::uint32_t shift, + bit_shift_mode mode) { + return mode == bit_shift_mode::RIGHT ? bits_amount - shift + : bits_amount; + } + + + decomposition_component_type decomposition_subcomponent; + composition_component_type composition_subcomponent; + + const std::size_t bits_amount; + const std::uint32_t shift; + const bit_shift_mode mode; + + // Technically, this component uses two gates. + // But both of them are inside subcomponents. + static constexpr const std::size_t gates_amount = 0; + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount, shift, mode); + const std::string component_name = "bit shift (constant)"; + + struct input_type { + var input; + + std::vector> all_vars() { + return {input}; + } + }; + + struct result_type { + var output; + + result_type(const bit_shift_constant &component, std::uint32_t start_row_index) { + std::uint32_t row = start_row_index; + row += component.decomposition_subcomponent.rows_amount; + output = typename composition_component_type::result_type( + component.composition_subcomponent, row).output; + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit bit_shift_constant(ContainerType witness, std::uint32_t bits_amount_, std::uint32_t shift_, + bit_shift_mode mode_) : + component_type(witness, {}, {}, get_manifest(bits_amount_, shift_, mode_)), + decomposition_subcomponent(witness, bits_amount_, bit_composition_mode::MSB), + composition_subcomponent(witness, + calculate_composition_bits_amount(bits_amount_, shift_, mode_), + false, bit_composition_mode::MSB), + bits_amount(bits_amount_), + shift(shift_), + mode(mode_) {}; + + template + bit_shift_constant(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::uint32_t bits_amount_, + std::uint32_t shift_, bit_shift_mode mode_) : + component_type(witness, constant, public_input, get_manifest(bits_amount_, shift_, mode_)), + decomposition_subcomponent(witness, constant, public_input, + bits_amount_, bit_composition_mode::MSB), + composition_subcomponent(witness, constant, public_input, + calculate_composition_bits_amount(bits_amount_, shift_, mode_), + false, bit_composition_mode::MSB), + bits_amount(bits_amount_), + shift(shift_), + mode(mode_) {}; + + bit_shift_constant( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::uint32_t bits_amount_, std::uint32_t shift_, bit_shift_mode mode_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_, shift_, mode_)), + decomposition_subcomponent(witnesses, constants, public_inputs, + bits_amount_, bit_composition_mode::MSB), + composition_subcomponent(witnesses, constants, public_inputs, + calculate_composition_bits_amount(bits_amount_, shift_, mode_), + false, bit_composition_mode::MSB), + bits_amount(bits_amount_), + shift(shift_), + mode(mode_) {}; + }; + + template + using plonk_bit_shift_constant = bit_shift_constant< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_bit_shift_constant::result_type + generate_assignments( + const plonk_bit_shift_constant + &component, + assignment> + &assignment, + const typename plonk_bit_shift_constant::input_type + &instance_input, + const std::uint32_t start_row_index) { + std::uint32_t row = start_row_index; + + using var = typename plonk_bit_shift_constant::var; + using decomposition_component_type = + typename plonk_bit_shift_constant::decomposition_component_type; + using composition_component_type = + typename plonk_bit_shift_constant::composition_component_type; + + typename decomposition_component_type::result_type decomposition = + generate_assignments(component.decomposition_subcomponent, assignment, + {instance_input.input}, row); + row += component.decomposition_subcomponent.rows_amount; + + typename composition_component_type::input_type composition_input; + composition_input.bits.resize(component.composition_subcomponent.bits_amount); + if (component.mode == bit_shift_mode::LEFT) { + var zero(0, start_row_index, false, var::column_type::constant); + std::fill(composition_input.bits.begin(), composition_input.bits.end(), zero); + + std::move(decomposition.output.begin() + component.shift, decomposition.output.end(), + composition_input.bits.begin()); + } else if (component.mode == bit_shift_mode::RIGHT) { + std::move(decomposition.output.begin(), decomposition.output.end() - component.shift, + composition_input.bits.begin()); + } + generate_assignments(component.composition_subcomponent, assignment, composition_input, row); + row += component.composition_subcomponent.rows_amount; + + assert(row == start_row_index + component.rows_amount); + return typename plonk_bit_shift_constant::result_type( + component, start_row_index); + } + + template + typename plonk_bit_shift_constant::result_type + generate_circuit( + const plonk_bit_shift_constant + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_bit_shift_constant::input_type + &instance_input, + const std::size_t start_row_index) { + std::uint32_t row = start_row_index; + + using var = typename plonk_bit_shift_constant::var; + using decomposition_component_type = + typename plonk_bit_shift_constant::decomposition_component_type; + using composition_component_type = + typename plonk_bit_shift_constant::composition_component_type; + + typename decomposition_component_type::result_type decomposition = + generate_circuit(component.decomposition_subcomponent, bp, assignment, {instance_input.input}, + row); + row += component.decomposition_subcomponent.rows_amount; + + typename composition_component_type::input_type composition_input; + composition_input.bits.resize(component.composition_subcomponent.bits_amount); + if (component.mode == bit_shift_mode::LEFT) { + // We do not need to set this constant, as it is set by the decomposition component + var zero(0, start_row_index, false, var::column_type::constant); + std::fill(composition_input.bits.begin(), composition_input.bits.end(), zero); + + std::move(decomposition.output.begin() + component.shift, decomposition.output.end(), + composition_input.bits.begin()); + } else if (component.mode == bit_shift_mode::RIGHT) { + std::move(decomposition.output.begin(), decomposition.output.end() - component.shift, + composition_input.bits.begin()); + } + generate_circuit(component.composition_subcomponent, bp, assignment, composition_input, row); + row += component.composition_subcomponent.rows_amount; + + assert(row == start_row_index + component.rows_amount); + return typename plonk_bit_shift_constant::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BIT_SHIFT_CONSTANT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/combined_inner_product.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/combined_inner_product.hpp new file mode 100644 index 000000000..b2fb8b0d3 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/combined_inner_product.hpp @@ -0,0 +1,219 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_COMBINED_INNER_PRODUCT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_COMBINED_INNER_PRODUCT_HPP + +#include + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class combined_inner_product; + + template + class combined_inner_product, + k, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + constexpr static const std::size_t selector_seed = 0xff70; + + constexpr static const std::size_t witness_per_row = 5; + + constexpr static const std::size_t main_rows = + (k + ((witness_per_row - (k % witness_per_row)) % witness_per_row)) / witness_per_row; + + public: + constexpr static const std::size_t rows_amount = 1 + main_rows; + constexpr static const std::size_t gates_amount = 1; + + struct params_type { + std::array f_zeta1; + std::array f_zeta2; + var xi; + var r; + }; + + struct result_type { + var output; + + result_type(const params_type ¶ms, std::size_t component_start_row) { + output = var(W2, component_start_row + rows_amount - 1, false); + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + generate_assignments_constant(bp, assignment, params, start_row_index); + + auto selector_iterator = assignment.find_selector(selector_seed); + std::size_t first_selector_index; + + if (selector_iterator == assignment.selectors_end()) { + first_selector_index = assignment.allocate_selector(selector_seed, gates_amount); + generate_gates(bp, assignment, params, first_selector_index); + } else { + first_selector_index = selector_iterator->second; + } + + assignment.enable_selector(first_selector_index, start_row_index, + start_row_index + rows_amount - 2); + + generate_copy_constraints(bp, assignment, params, start_row_index); + + return result_type(params, start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type xi = assignment.var_value(params.xi); + typename BlueprintFieldType::value_type r = assignment.var_value(params.r); + constexpr static const std::size_t rem = k % witness_per_row; + constexpr static const std::size_t k_size = k + ((witness_per_row - rem) % witness_per_row); + std::array f_zeta1; + std::array f_zeta2; + for (std::size_t i = 0; i < k_size; i++) { + if (i < k) { + f_zeta1[i] = assignment.var_value(params.f_zeta1[i]); + f_zeta2[i] = assignment.var_value(params.f_zeta2[i]); + } else { + f_zeta1[i] = 0; + f_zeta2[i] = 0; + } + } + typename BlueprintFieldType::value_type s = 0; + typename BlueprintFieldType::value_type acc_xi = 1; + + for (std::size_t i = row; i < row + rows_amount - 1; i++) { + assignment.witness(W0)[i] = r; + assignment.witness(W1)[i] = acc_xi; + assignment.witness(W2)[i] = s; + for (std::size_t j = 0; j < witness_per_row; j++) { + s += acc_xi * (f_zeta1[(i - row) * witness_per_row + j] + + r * f_zeta2[(i - row) * witness_per_row + j]); + acc_xi *= xi; + assignment.witness(3 + j * 2)[i] = f_zeta1[(i - row) * witness_per_row + j]; + assignment.witness(4 + j * 2)[i] = f_zeta2[(i - row) * witness_per_row + j]; + } + assignment.witness(W13)[i] = xi; + } + assignment.witness(W0)[row + rows_amount - 1] = r; + assignment.witness(W1)[row + rows_amount - 1] = acc_xi; + assignment.witness(W2)[row + rows_amount - 1] = s; + assignment.witness(W13)[row + rows_amount - 1] = xi; + + return result_type(params, start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + auto constraint_1 = bp.add_constraint(var(W0, 0) - var(W0, +1)); + auto constraint_2 = bp.add_constraint(var(W13, 0) - var(W13, +1)); + snark::plonk_constraint xi_deg = var(W13, +1); + for (int i = 0; i < witness_per_row - 1; i++) { + xi_deg = xi_deg * var(W13, +1); + } + auto constraint_3 = bp.add_constraint(var(W1, +1) - var(W1, 0) * xi_deg); + snark::plonk_constraint s = var(W2, 0); + snark::plonk_constraint acc_xi = var(W1, 0); + for (std::size_t j = 0; j < witness_per_row; j++) { + s = s + acc_xi * (var(3 + j * 2, 0) + var(W0, 0) * var(4 + j * 2, 0)); + acc_xi = acc_xi * var(W13, 0); + } + auto constraint_4 = bp.add_constraint(var(W2, +1) - s); + + bp.add_gate(first_selector_index, {constraint_1, constraint_2, constraint_3, constraint_4}); + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + bp.add_copy_constraint({{W0, static_cast(component_start_row), false}, params.r}); + bp.add_copy_constraint({{W13, static_cast(component_start_row), false}, params.xi}); + bp.add_copy_constraint({{W1, static_cast(component_start_row), false}, + {0, static_cast(component_start_row + 1), false, var::column_type::constant}}); + bp.add_copy_constraint({{W2, static_cast(component_start_row), false}, + {0, static_cast(component_start_row), false, var::column_type::constant}}); + + for (std::size_t i = 0; i < k; i++) { + bp.add_copy_constraint( + {{3 + (2 * i) % 10, static_cast(component_start_row + (i / 5)), false}, + params.f_zeta1[i]}); + bp.add_copy_constraint( + {{4 + (2 * i) % 10, static_cast(component_start_row + (i / 5)), false}, + params.f_zeta2[i]}); + } + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + + assignment.constant(0)[row] = 0; + assignment.constant(0)[row + 1] = 1; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_COMBINED_INNER_PRODUCT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division.hpp new file mode 100644 index 000000000..eed81c090 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division.hpp @@ -0,0 +1,236 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element division component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_PLONK_FIELD_DIVISION_HPP +#define CRYPTO3_BLUEPRINT_PLONK_FIELD_DIVISION_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in F_p, y != 0 + // Output: z = x / y, z \in F_p + template + class division; + + template + class division, + BlueprintFieldType, NonNativePolicyType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return division::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(4)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "native field division"; + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const division &component, std::uint32_t start_row_index) { + output = var(component.W(2), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit division(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + division(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + division(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + return x / y; + } + }; + + template + using plonk_division = + division, + BlueprintFieldType, basic_non_native_policy>; + + template + typename plonk_division::result_type generate_assignments( + const plonk_division &component, + assignment> + &assignment, + const typename plonk_division::input_type instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = var_value(assignment, instance_input.y); + assignment.witness(component.W(2), j) = + var_value(assignment, instance_input.x) / var_value(assignment, instance_input.y); + assignment.witness(component.W(3), j) = var_value(assignment, instance_input.y).inversed(); + + return typename plonk_division::result_type( + component, start_row_index); + } + + template + typename plonk_division::result_type generate_empty_assignments( + const plonk_division &component, + assignment> + &assignment, + const typename plonk_division::input_type instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_division; + const std::size_t j = start_row_index; + assignment.witness(component.W(2), j) = component_type::calculate( + var_value(assignment, instance_input.x), var_value(assignment, instance_input.y)); + + return typename plonk_division::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_division &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_division::input_type &instance_input) { + + using var = typename plonk_division::var; + + auto constraint_1 = var(component.W(1), 0) * var(component.W(2), 0) - var(component.W(0), 0); + auto constraint_2 = var(component.W(1), 0) * var(component.W(3), 0) - 1; + + return bp.add_gate({constraint_1, constraint_2}); + } + + template + void generate_copy_constraints( + const plonk_division &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_division::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_division::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + bp.add_copy_constraint({component_y, instance_input.y}); + } + + template + typename plonk_division::result_type generate_circuit( + const plonk_division &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_division::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_division::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_PLONK_FIELD_DIVISION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division_or_zero.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division_or_zero.hpp new file mode 100644 index 000000000..458aa03ca --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/division_or_zero.hpp @@ -0,0 +1,238 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element division component. +// If divider is zero, component's result is zero either. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_DIVISION_OR_ZERO_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_DIVISION_OR_ZERO_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in Fp + // Output: z = x / y, if y != 0, else 0 z \in F_p + template + class division_or_zero; + + template + class division_or_zero, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return division_or_zero::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "native field division or zero"; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(5)), + false + ); + return manifest; + } + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const division_or_zero &component, std::uint32_t start_row_index) { + output = var(component.W(2), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + division_or_zero(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + division_or_zero(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + division_or_zero(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + return (y == BlueprintFieldType::value_type::zero()) ? BlueprintFieldType::value_type::zero() : x * y.inversed(); + } + }; + + template + using plonk_division_or_zero = + division_or_zero, + BlueprintFieldType>; + + template + typename plonk_division_or_zero::result_type + generate_assignments( + const plonk_division_or_zero &component, + assignment> &assignment, + const typename plonk_division_or_zero::input_type instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = var_value(assignment, instance_input.y); + if (var_value(assignment, instance_input.y) != BlueprintFieldType::value_type::zero()) { + assignment.witness(component.W(2), j) = var_value(assignment, instance_input.x) * + var_value(assignment, instance_input.y).inversed(); + } else { + assignment.witness(component.W(2), j) = BlueprintFieldType::value_type::zero(); + } + assignment.witness(component.W(3), j) = (var_value(assignment, instance_input.y) == BlueprintFieldType::value_type::zero()) ? + BlueprintFieldType::value_type::zero() : var_value(assignment, instance_input.y).inversed(); + assignment.witness(component.W(4), j) = var_value(assignment, instance_input.y) * assignment.witness(component.W(3), j); + + return typename plonk_division_or_zero::result_type(component, start_row_index); + } + + template + typename plonk_division_or_zero::result_type + generate_empty_assignments( + const plonk_division_or_zero &component, + assignment> &assignment, + const typename plonk_division_or_zero::input_type instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_division_or_zero; + assignment.witness(component.W(2), start_row_index) = component_type::calculate( + var_value(assignment, instance_input.x), var_value(assignment, instance_input.y)); + + return typename plonk_division_or_zero::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_division_or_zero &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_division_or_zero::input_type &instance_input) { + + using var = typename plonk_division_or_zero::var; + + auto constraint_1 = var(component.W(1), 0) * var(component.W(3), 0) - var(component.W(4), 0); + auto constraint_2 = var(component.W(4), 0) * (var(component.W(4), 0) - 1u); + auto constraint_3 = (var(component.W(3), 0) - var(component.W(1), 0)) * (var(component.W(4), 0) - 1u); + auto constraint_4 = var(component.W(0), 0) * var(component.W(3), 0) - var(component.W(2), 0); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + } + + template + void generate_copy_constraints( + const plonk_division_or_zero &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_division_or_zero::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_division_or_zero::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + bp.add_copy_constraint({component_y, instance_input.y}); + } + + template + typename plonk_division_or_zero::result_type + generate_circuit( + const plonk_division_or_zero &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_division_or_zero::input_type &instance_input, + const std::size_t start_row_index){ + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_division_or_zero::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_DIVISION_OR_ZERO_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/element_powers.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/element_powers.hpp new file mode 100644 index 000000000..d379536b9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/element_powers.hpp @@ -0,0 +1,191 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_ELEMENT_POWERS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_ELEMENT_POWERS_HPP + +#include + +#include + +#include +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // for (base, n) calculates [base^0, base^1, ..., base^n] + template + class element_powers; + + template< + typename CurveType, + std::size_t n, + std::size_t W0, + std::size_t W1, + std::size_t W2, + std::size_t W3, + std::size_t W4, + std::size_t W5, + std::size_t W6, + std::size_t W7, + std::size_t W8, + std::size_t W9, + std::size_t W10, + std::size_t W11, + std::size_t W12, + std::size_t W13, + std::size_t W14> + class element_powers< + snark::plonk_constraint_system, + CurveType, + n, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + + constexpr static const std::size_t selector_seed = 0x0fff; + + public: + constexpr static const std::size_t rows_amount = n * mul_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var base; + var n; + var one; + }; + + struct result_type { + std::array output; + + result_type(std::size_t component_start_row) { + if (n > 0) { + output[0] = var(W0, component_start_row, false); + } + if (n > 1) { + output[1] = var(W1, component_start_row, false); + } + for (std::size_t i = 2; i < n; i++) { + output[i] = mul_component::result_type(component_start_row + i).output; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::cout << "ELEMENT POWERS COMPONENT IS NOT IMPLEMENTED" << std::endl; + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(params, start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::vector res(n); + if (n < 2) { + res.resize(2); + } + assignment.witness(W0)[row] = 1; + res[0] = var(0, row, false); + typename BlueprintFieldType::value_type base_value = assignment.var_value(x); + assignment.witness(W0 + 1)[row] = base_value; + res[1] = var(W0 + 1, row, false); + typename BlueprintFieldType::value_type prev_value = base_value; + std::size_t column_idx = 2; + + for (std::size_t i = 2; i < n; i++) { + // we need to copy any power of the element + // so we place them only on copy-constrainted columns + if (column_idx >= zk::snark::kimchi_constant::PERMUTES) { + column_idx = 0; + row++; + } + typename BlueprintFieldType::value_type new_value = prev_value * base_value; + assignment.witness(W0 + column_idx)[row] = new_value; + res[i] = var(W0 + i, row, false); + prev_value = new_value; + } + + return res; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(bblueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + bp.add_copy_constraint({{W0, static_cast(component_start_row), false}, params.one}); + bp.add_copy_constraint({{W1, static_cast(component_start_row), false}, params.base}); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_ELEMENT_POWERS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/exponentiation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/exponentiation.hpp new file mode 100644 index 000000000..35584b499 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/exponentiation.hpp @@ -0,0 +1,361 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_EXPONENTIATION_HPP + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: exponent, base \in Fp + // Output: base**exponent + template + class exponentiation; + + // clang-format off + // res = base.pow(exponent) +// _______________________________________________________________________________________________________________________________________________ +// | W0 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 | W14 | +// | base | n = [b0...b7] | base^[b0b1] | base^[b0b1b2b3]| base^[b0...b5]|base^[b0...b7]| - | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | +// | base | n = [b8...b15] | base^[b0...b9] | base^[b0...b11]| ... | ... | - | b15 | b14 | b13 | b12 | b11 | b10 | b9 | b8 | +// | ... | +// | ... | ... | ... | ... | ... | ... | res | ... | ... | ... | ... | ... | ... | ... | ... | +// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + // clang-format on + + template + class exponentiation< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType, + ExponentSize + >: + public plonk_component { + + constexpr static const std::size_t reserved_witnesses = 2; // base, accumulated_n + + static std::size_t intermediate_results_per_row_intenal(std::size_t witness_amount) { + return (witness_amount - reserved_witnesses) / (bits_per_intermediate_result + 1); + } + + static std::size_t bits_per_row_internal(std::size_t witness_amount) { + return intermediate_results_per_row_intenal(witness_amount) * bits_per_intermediate_result; + } + + static std::size_t main_rows_amount_internal(std::size_t witness_amount) { + return (ExponentSize + bits_per_row_internal(witness_amount) - 1) / + bits_per_row_internal(witness_amount); + } + + static std::size_t padded_exponent_size_internal(std::size_t witness_amount) { + return main_rows_amount_internal(witness_amount) * bits_per_row_internal(witness_amount); + } + + static std::size_t rows_amount_internal(std::size_t witness_amount) { + return main_rows_amount_internal(witness_amount) + 1; + } + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return exponentiation::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + + constexpr static const std::size_t intermediate_start = 0 + reserved_witnesses; + constexpr static const std::size_t bits_per_intermediate_result = + 2; // defines + // max degree of the constraints + // 2 ** bits_per_intermediate_result + const std::size_t intermediate_results_per_row = + intermediate_results_per_row_intenal(this->witness_amount()); + const std::size_t bits_per_row = + bits_per_row_internal(this->witness_amount()); + const std::size_t main_rows = main_rows_amount_internal(this->witness_amount()); + const std::size_t padded_exponent_size = padded_exponent_size_internal(this->witness_amount()); + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + struct input_type { + var base; + var exponent; + + std::vector> all_vars() { + return {base, exponent}; + } + }; + + struct result_type { + var output = var(0, 0); + + result_type(const exponentiation &component, std::size_t start_row_index) { + output = var(component.W(intermediate_start + component.intermediate_results_per_row - 1), + start_row_index + component.rows_amount - 1, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + exponentiation(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + exponentiation( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + }; + + template + using plonk_exponentiation = + exponentiation< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType, + ExponentSize + >; + + template + typename plonk_exponentiation::result_type + generate_circuit( + const plonk_exponentiation &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_exponentiation::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index + 1, start_row_index + 1 + component.main_rows - 1); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constants(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_exponentiation::result_type(component, start_row_index); + } + + template + typename plonk_exponentiation::result_type + generate_assignments( + const plonk_exponentiation &component, + assignment> &assignment, + const typename plonk_exponentiation::input_type &instance_input, + const std::uint32_t start_row_index) { + + typename BlueprintFieldType::value_type base = var_value(assignment, instance_input.base); + typename BlueprintFieldType::value_type exponent = var_value(assignment, instance_input.exponent); + + std::vector bits(component.padded_exponent_size, false); + { + std::vector bbb; + auto data = exponent.data; + while (data != 0u) { + bbb.push_back((data - (data >> 1u << 1u)) != 0u); + data = data >> 1u; + } + for (std::uint32_t i = 1; i < component.padded_exponent_size - bbb.size(); ++i) { + bits[i] = false; + } + for (std::uint32_t i = 0; i < bbb.size(); ++i) { + bits[component.padded_exponent_size - 1 - i] = bbb[i]; + } + } + + typename BlueprintFieldType::value_type accumulated_n = 0u; + typename BlueprintFieldType::value_type acc1 = 1u; + + // we use first empty row to unify first row gate with others + assignment.witness(component.W(1), start_row_index) = 0u; + assignment.witness(component.intermediate_start + component.intermediate_results_per_row - 1, start_row_index) = 1u; + std::size_t start_row_padded = start_row_index + 1; + + std::size_t current_bit = 0; + for (std::size_t row = start_row_padded; row < start_row_padded + component.main_rows; row++) { + assignment.witness(component.W(0), row) = base; + + for (std::size_t j = 0; j < component.intermediate_results_per_row; j++) { + typename BlueprintFieldType::value_type intermediate_exponent = 0u; + for (std::size_t bit_column = 0; bit_column < component.bits_per_intermediate_result; + bit_column++) { + std::size_t column_idx = 14 - j * (component.bits_per_intermediate_result)-bit_column; + assignment.witness(component.W(column_idx), row) = bits[current_bit] ? 1u : 0u; + // wierd stuff is here for oracles scalar + // std::cout<<"column_idx "<::result_type(component, start_row_index); + + } + + template + std::size_t generate_gates( + const plonk_exponentiation &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_exponentiation::input_type instance_input) { + + using var = typename plonk_exponentiation::var; + + typename BlueprintFieldType::value_type exponent_shift = 2u; + exponent_shift = power(exponent_shift, component.bits_per_row); + + std::vector> constraints; + + nil::crypto3::zk::snark::plonk_constraint accumulated_n_constraint; + for (std::size_t j = 0; j < component.intermediate_results_per_row; j++) { + nil::crypto3::zk::snark::plonk_constraint intermediate_result_constraint = + j == 0 ? var(component.W(component.intermediate_start + component.intermediate_results_per_row - 1), -1) : + var(component.W(component.intermediate_start + j - 1), 0); + + for (std::size_t bit_column = 0; bit_column < component.bits_per_intermediate_result; bit_column++) { + std::size_t column_idx = 14 - j * (component.bits_per_intermediate_result)-bit_column; + constraints.emplace_back( + var(component.W(column_idx), 0) * + (1u - var(component.W(column_idx), 0))); // fail on oracles scalar + + nil::crypto3::zk::snark::plonk_constraint bit_res = + var(component.W(0), 0) * var(component.W(column_idx), 0); + if (j == 0 && bit_column == 0) { + accumulated_n_constraint = var(component.W(column_idx), 0); + } else { + accumulated_n_constraint = 2u * accumulated_n_constraint + var(component.W(column_idx), 0); + } + intermediate_result_constraint = intermediate_result_constraint * + intermediate_result_constraint * + (bit_res + (1u - var(component.W(column_idx), 0))); + } + + intermediate_result_constraint = + intermediate_result_constraint - var(component.W(component.intermediate_start + j), 0); + constraints.push_back(intermediate_result_constraint); // fail on oracles scalar + } + + accumulated_n_constraint = accumulated_n_constraint + exponent_shift * var(component.W(1), -1) - var(component.W(1), 0); + + constraints.push_back(accumulated_n_constraint); + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_exponentiation &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_exponentiation::input_type &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_exponentiation::var; + + var zero(component.W(0), start_row_index, false, var::column_type::constant); + var one(component.W(0), start_row_index + 1, false, var::column_type::constant); + + for (std::size_t row = start_row_index + 1; row < start_row_index + component.rows_amount; row++) { + bp.add_copy_constraint({{component.W(0), static_cast(row), false}, instance_input.base}); + } + bp.add_copy_constraint({{component.W(1), static_cast(start_row_index), false}, zero}); + bp.add_copy_constraint({{component.W(component.intermediate_start + component.intermediate_results_per_row - 1), + static_cast(start_row_index), false}, + one}); + // check that the recalculated n is equal to the input challenge + bp.add_copy_constraint( + {{component.W(1), static_cast(start_row_index + component.rows_amount - 1), false}, instance_input.exponent}); // fail on oracles scalar + } + + template + void generate_assignments_constants( + const plonk_exponentiation &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_exponentiation::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + assignment.constant(component.C(0), row) = 0u; + row++; + assignment.constant(component.C(0), row) = 1u; + row++; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_FIELD_EXPONENTIATION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/linear_interpolation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/linear_interpolation.hpp new file mode 100644 index 000000000..17ab507eb --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/linear_interpolation.hpp @@ -0,0 +1,223 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for linear interpolation coefficients component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LINEAR_INTER_COEFS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LINEAR_INTER_COEFS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // linear interpolation with points (x0,z0), (x1,z1) + // Input: x0, z0, x1, z1 + // Output: a0, a1, such that the line z = a0 + a1*x passes through both points + // checks that x0 != x1 (otherwise a constraint is violated) + template + class linear_inter_coefs; + + template + class linear_inter_coefs, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return linear_inter_coefs::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(7)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + var x0, z0, x1, z1; + + std::vector> all_vars() { + return {x0, z0, x1, z1}; + } + }; + + struct result_type { + std::array output; + + result_type(const linear_inter_coefs &component, std::uint32_t start_row_index) { + output = { var(component.W(4), start_row_index, false, var::column_type::witness), + var(component.W(5), start_row_index, false, var::column_type::witness)}; + } + + std::vector> all_vars() { + return {output[0], output[1]}; + } + }; + + template + explicit linear_inter_coefs(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + linear_inter_coefs(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + linear_inter_coefs( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_linear_inter_coefs = + linear_inter_coefs< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; + + template + typename plonk_linear_inter_coefs::result_type generate_assignments( + const plonk_linear_inter_coefs &component, + assignment> + &assignment, + const typename plonk_linear_inter_coefs::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + value_type x0 = var_value(assignment, instance_input.x0), + z0 = var_value(assignment, instance_input.z0), + x1 = var_value(assignment, instance_input.x1), + z1 = var_value(assignment, instance_input.z1); + + assignment.witness(component.W(0), start_row_index) = x0; + assignment.witness(component.W(1), start_row_index) = z0; + assignment.witness(component.W(2), start_row_index) = x1; + assignment.witness(component.W(3), start_row_index) = z1; + if (x0 != x1) { // normal case + auto diff_inversed = (x1-x0).inversed(); + assignment.witness(component.W(4), start_row_index) = (x1*z0 - x0*z1) * diff_inversed; + assignment.witness(component.W(5), start_row_index) = (z1-z0) * diff_inversed; + assignment.witness(component.W(6), start_row_index) = diff_inversed; + } else { // just make some assignments that will fail + assignment.witness(component.W(4), start_row_index) = 0; + assignment.witness(component.W(5), start_row_index) = 0; + assignment.witness(component.W(6), start_row_index) = 0; + } + return typename plonk_linear_inter_coefs::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_linear_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_linear_inter_coefs::input_type + &instance_input) { + + using var = typename plonk_linear_inter_coefs::var; + var X0 = var(component.W(0), 0, true), + Z0 = var(component.W(1), 0, true), + X1 = var(component.W(2), 0, true), + Z1 = var(component.W(3), 0, true), + A0 = var(component.W(4), 0, true), + A1 = var(component.W(5), 0, true), + I = var(component.W(6), 0, true); + + return bp.add_gate({A1*X0 + A0 - Z0, A1*X1 + A0 - Z1, (X1 - X0)*I - 1}); + } + + template + void generate_copy_constraints( + const plonk_linear_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_linear_inter_coefs::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_linear_inter_coefs::var; + + bp.add_copy_constraint({var(component.W(0), start_row_index, false), instance_input.x0}); + bp.add_copy_constraint({var(component.W(1), start_row_index, false), instance_input.z0}); + bp.add_copy_constraint({var(component.W(2), start_row_index, false), instance_input.x1}); + bp.add_copy_constraint({var(component.W(3), start_row_index, false), instance_input.z1}); + } + + template + typename plonk_linear_inter_coefs::result_type generate_circuit( + const plonk_linear_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_linear_inter_coefs::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, start_row_index); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_linear_inter_coefs::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LINEAR_INTER_COEFS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_and_flag.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_and_flag.hpp new file mode 100644 index 000000000..4a22ee9d9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_and_flag.hpp @@ -0,0 +1,315 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_AND_FLAG_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_AND_FLAG_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + /** && component + * Input: x, y + * Output: f = 0 if xy=0, f=1 otherwise + * + * Constraints: + * p = xy + * pv = f + * f(f-1) = 0 + * (v-p)(f-1) = 0 + * Let p = xy; Then there exists v such that vp=f. + * If p=0, then v=0, so f. Otherwise, v = p.inverse() and f = 1 + * */ + template + class logic_and_flag; + + template + class logic_and_flag> + : public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return witness_amount == 2 ? 3 : (witness_amount < 5 ? 2 : 1); + } + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + static const constexpr std::size_t clamp_val = 5; + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) + : witness_amount(std::min(witness_amount_, clamp_val)) {} + + std::uint32_t gates_amount() const override { + return logic_and_flag::gates_amount; + } + + bool operator<(const component_gate_manifest *other) const override { + return witness_amount < dynamic_cast(other)->witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(2, 6)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = rows_amount_internal(component_type::witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + + struct input_type { + var x; + var y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output; + + result_type(const logic_and_flag> + &component, + std::uint32_t start_row_index) { + output = + var(component.W(component.witness_amount() - 1), + start_row_index + component.rows_amount - 1, false); + } + result_type(const logic_and_flag> + &component, + std::uint32_t start_row_index, bool skip) { + output = var(component.W(0), start_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit logic_and_flag(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + logic_and_flag(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_and_flag(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + + std::array t; + t[0] = x; + t[1] = y; + t[2] = t[0] * t[1]; // p + t[3] = t[2].is_zero() ? t[2] : t[2].inversed(); // v + t[4] = t[3] * t[2]; // f + + return t[4]; + } + }; + + template + using plonk_logic_and_flag_component = + logic_and_flag>; + + template + typename plonk_logic_and_flag_component::result_type + generate_assignments( + const plonk_logic_and_flag_component + &component, + assignment> + &assignment, + const typename plonk_logic_and_flag_component::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::array t; + t[0] = var_value(assignment, instance_input.x); + t[1] = var_value(assignment, instance_input.y); + t[2] = t[0] * t[1]; // p + t[3] = t[2].is_zero() ? t[2] : t[2].inversed(); // v + t[4] = t[3] * t[2]; // f + + std::size_t _idx; + for (std::size_t i = 0; i < component.rows_amount; i++) { + for (std::size_t j = 0; j < witness_amount; j++) { + _idx = i * witness_amount + j; + if (_idx < 5) { + assignment.witness(component.W(j), row + i) = t[_idx]; + } + } + } + // store the output in last column, last row + assignment.witness(component.W(witness_amount - 1), row + component.rows_amount - 1) = t[4]; + + return + typename plonk_logic_and_flag_component::result_type + (component, start_row_index); + } + + template + typename plonk_logic_and_flag_component::result_type + generate_empty_assignments( + const plonk_logic_and_flag_component + &component, + assignment> + &assignment, + const typename plonk_logic_and_flag_component::input_type &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_logic_and_flag_component; + + assignment.witness(component.W(0), start_row_index) = component_type::calculate(var_value(assignment, instance_input.x), + var_value(assignment, instance_input.y)); + + return + typename plonk_logic_and_flag_component::result_type + (component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_logic_and_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_and_flag_component::input_type &instance_input) { + + using var = typename plonk_logic_and_flag_component::var; + + std::size_t offset = component.rows_amount == 3 ? -1 : 0; + std::size_t witness_amount = component.witness_amount(); + + std::array, 4> wl; + + int _idx; + for (std::size_t i = 0; i < component.rows_amount; i++) { + for (std::size_t j = 0; j < witness_amount; j++) { + _idx = i * witness_amount + j; + if (_idx < 4) { + wl[_idx] = std::make_pair(j, i + offset); + } + } + } + + auto _x = var(component.W(wl[0].first), wl[0].second); + auto _y = var(component.W(wl[1].first), wl[1].second); + auto _p = var(component.W(wl[2].first), wl[2].second); + auto _v = var(component.W(wl[3].first), wl[3].second); + auto _f = var(component.W(witness_amount - 1), offset + component.rows_amount - 1); + + auto constraint_1 = _p - _x * _y; // p =x*y + auto constraint_2 = _f * (_f - 1); // f(f-1)=0 + auto constraint_3 = _f - _p * _v; // f = pv + auto constraint_4 = (_v - _p) * (_f - 1); // (v-p)(f-1)=0 + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + } + + template + void generate_copy_constraints( + const plonk_logic_and_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_and_flag_component::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + using var = typename plonk_logic_and_flag_component::var; + + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.x}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.y}); + } + + template + typename plonk_logic_and_flag_component::result_type + generate_circuit( + const plonk_logic_and_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_and_flag_component::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index + (component.rows_amount == 3 ? 1 : 0)); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return + typename plonk_logic_and_flag_component::result_type + (component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_AND_FLAG_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_or_flag.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_or_flag.hpp new file mode 100644 index 000000000..9ef1efe53 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/logic_or_flag.hpp @@ -0,0 +1,356 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_OR_FLAG_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_OR_FLAG_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + /** || component + * Input: x, y + * Output: f = 0 if x=y=0, f=1 otherwise + * + * Constraints: + * x*v_x = f_x + * f_x(f_x-1) = 0 + * (v_x-x)(f_x-1) = 0 + * y*v_y = f_y + * f_y(f_y-1) = 0 + * (v_y-y)(f_y-1) = 0 + * f_x + f_y - f_x * f_y = f + * + * First convert each input to 0 or 1, then apply usual boolean || operator + * */ + + template + class logic_or_flag; + + template + class logic_or_flag> + : public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return witness_amount <= 4 ? 6 - witness_amount : (witness_amount < 7 ? 2 : 1); + } + + constexpr static std::size_t gates_amount_internal(std::size_t witness_amount) { + return 1 + 1 * (witness_amount == 2); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + static const constexpr std::size_t clamp_val = 6; + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) + : witness_amount(std::min(witness_amount_, clamp_val)) {} + + std::uint32_t gates_amount() const override { + return logic_or_flag::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + return witness_amount < dynamic_cast(other)->witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(2, 7)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + const std::size_t gates_amount = gates_amount_internal(this->witness_amount()); + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + + struct input_type { + var x; + var y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output; + + result_type(const logic_or_flag> + &component, + std::uint32_t start_row_index) { + output = + var(component.W(component.witness_amount() - 1), + start_row_index + component.rows_amount - 1, false); + } + result_type(const logic_or_flag> + &component, + std::uint32_t start_row_index, bool skip) { + output = var(component.W(0), start_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit logic_or_flag(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + logic_or_flag(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_or_flag(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + std::array t; + + t[0] = x; + t[1] = y; + t[2] = t[0].is_zero() ? t[0] : t[0].inversed(); + t[3] = t[1].is_zero() ? t[1] : t[1].inversed(); + t[4] = t[0] * t[2]; + t[5] = t[1] * t[3]; + t[6] = t[4] + t[5] - t[4] * t[5]; + + return t[6]; + } + }; + + template + using plonk_logic_or_flag_component = + logic_or_flag>; + + template + typename plonk_logic_or_flag_component::result_type + generate_assignments( + const plonk_logic_or_flag_component + &component, + assignment> + &assignment, + const typename plonk_logic_or_flag_component::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::array t; + + t[0] = var_value(assignment, instance_input.x); + t[1] = var_value(assignment, instance_input.y); + t[2] = t[0].is_zero() ? t[0] : t[0].inversed(); + t[3] = t[1].is_zero() ? t[1] : t[1].inversed(); + t[4] = t[0] * t[2]; + t[5] = t[1] * t[3]; + t[6] = t[4] + t[5] - t[4] * t[5]; + + std::size_t _idx; + for (std::size_t i = 0; i < component.rows_amount; i++) { + for (std::size_t j = 0; j < witness_amount; j++) { + _idx = i * witness_amount + j; + assignment.witness(component.W(j), row + i) = t[_idx % 7]; + } + } + // store the output in last column, last row + assignment.witness(component.W(witness_amount - 1), row + component.rows_amount - 1) = t[6]; + + return typename plonk_logic_or_flag_component::result_type + (component, start_row_index); + } + + template + typename plonk_logic_or_flag_component::result_type + generate_empty_assignments( + const plonk_logic_or_flag_component + &component, + assignment> + &assignment, + const typename plonk_logic_or_flag_component::input_type + &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_logic_or_flag_component; + + assignment.witness(component.W(0), start_row_index) = component_type::calculate( + var_value(assignment, instance_input.x), + var_value(assignment, instance_input.y)); + + return typename plonk_logic_or_flag_component::result_type + (component, start_row_index, true); + } + + template + std::vector generate_gates( + const plonk_logic_or_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_or_flag_component::input_type + &instance_input) { + + using var = typename plonk_logic_or_flag_component::var; + + std::vector selector_indices; + + const int offset = component.rows_amount >= 3 ? -1 : 0; + const std::size_t witness_amount = component.witness_amount(); + + std::array, 6> wl; +//ww + int _idx; + for (std::uint32_t i = 0; i < component.rows_amount; i++) { + for (std::uint32_t j = 0; j < witness_amount; j++) { + _idx = i * witness_amount + j; + if (_idx < 6) { + wl[_idx] = std::make_pair(j, i + offset); + } + } + } + + auto _x = var(component.W(wl[0].first), wl[0].second), _y = var(component.W(wl[1].first), wl[1].second), + _vx = var(component.W(wl[2].first), wl[2].second), + _vy = var(component.W(wl[3].first), wl[3].second), + _fx = var(component.W(wl[4].first), wl[4].second), + _fy = var(component.W(wl[5].first), wl[5].second), + _f = var(component.W(witness_amount - 1), offset + component.rows_amount - 1); + + auto constraint_1 = _fx - _x * _vx; // fx =x*vx + auto constraint_2 = _fy - _y * _vy; // fy =y*vy + + auto constraint_3 = _fx * (_fx - 1); // fx(fx-1)=0 + auto constraint_4 = _fy * (_fy - 1); // fy(fy-1)=0 + + auto constraint_5 = (_vx - _x) * (_fx - 1); // (vx-x)(fx-1)=0 + auto constraint_6 = (_vy - _y) * (_fy - 1); // (vy-y)(fy-1)=0 + + if (witness_amount == 2) { + _fx = var(component.W(wl[4].first), 0), _fy = var(component.W(wl[5].first), 0), + _f = var(component.W(witness_amount - 1), +1); + auto constraint_7 = _f - _fx - _fy + _fx * _fy; // f = f_x + f_y - f_x*f_y + selector_indices.push_back(bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6})); + selector_indices.push_back(bp.add_gate({constraint_7})); + } else { + auto constraint_7 = _f - _fx - _fy + _fx * _fy; // f = f_x + f_y - f_x*f_y + selector_indices.push_back(bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7})); + } + return selector_indices; + } + + template + void generate_copy_constraints( + const plonk_logic_or_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_or_flag_component::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + using var = typename plonk_logic_or_flag_component::var; + + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.x}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.y}); + } + + template + typename plonk_logic_or_flag_component::result_type + generate_circuit( + const plonk_logic_or_flag_component + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_logic_or_flag_component::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector selector_indices = + generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_indices[0], + start_row_index + (component.rows_amount >= 3 ? 1 : 0)); + if (component.witness_amount() == 2) { + if (selector_indices.size() != 2) { + std::cerr << "Internal error: logic_or_flag component returned the wrong selector amount." + << std::endl; + } + assignment.enable_selector(selector_indices[1], start_row_index + 2); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_logic_or_flag_component::result_type + (component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELDS_LOGIC_OR_FLAG_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication.hpp new file mode 100644 index 000000000..e1b66628c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication.hpp @@ -0,0 +1,241 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element multiplication component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in F_p + // Output: z = x * y, z \in F_p + template + class multiplication; + + template + class multiplication, + BlueprintFieldType, NonNativePolicyType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return multiplication::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "native field multiplication"; + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const multiplication &component, std::uint32_t start_row_index) { + output = var(component.W(2), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit multiplication(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + multiplication(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + multiplication(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + return x * y; + } + }; + + template + using plonk_multiplication = + multiplication, + BlueprintFieldType, basic_non_native_policy>; + + template + typename plonk_multiplication::result_type + generate_assignments( + const plonk_multiplication &component, + assignment> + &assignment, + const typename plonk_multiplication::input_type + instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = var_value(assignment, instance_input.y); + assignment.witness(component.W(2), j) = + var_value(assignment, instance_input.x) * var_value(assignment, instance_input.y); + return typename plonk_multiplication::result_type( + component, start_row_index); + } + + template + typename plonk_multiplication::result_type + generate_empty_assignments( + const plonk_multiplication &component, + assignment> + &assignment, + const typename plonk_multiplication::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_multiplication; + const std::size_t j = start_row_index; + assignment.witness(component.W(2), j) = component_type::calculate( + var_value(assignment, instance_input.x), var_value(assignment, instance_input.y)); + return typename plonk_multiplication::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_multiplication::input_type + &instance_input) { + + using var = typename plonk_multiplication::var; + + auto constraint_1 = var(component.W(0), 0) * var(component.W(1), 0) - var(component.W(2), 0); + + return bp.add_gate(constraint_1); + } + + template + void generate_copy_constraints( + const plonk_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_multiplication::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_multiplication::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + if( instance_input.x != component_x ) bp.add_copy_constraint({instance_input.x, component_x}); + if( instance_input.y != component_y ) bp.add_copy_constraint({component_y, instance_input.y}); + } + + template + typename plonk_multiplication::result_type generate_circuit( + const plonk_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_multiplication::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_multiplication::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication_by_constant.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication_by_constant.hpp new file mode 100644 index 000000000..1d1855cd3 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/multiplication_by_constant.hpp @@ -0,0 +1,240 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element multiplication by constant component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_BY_CONSTANT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_BY_CONSTANT_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, c \in F_p, c is fixed public parameter + // Output: z = c * y, z \in F_p + template + class mul_by_constant; + + template + class mul_by_constant, + BlueprintFieldType>: + public plonk_component { + + public: + using component_type = plonk_component; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return mul_by_constant::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static constexpr const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using manifest_type = plonk_component_manifest; + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(2)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + value_type constant; + + struct input_type { + var x = var(0, 0, false); + + std::vector> all_vars() { + return {x}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const mul_by_constant &component, std::uint32_t start_row_index) { + output = var(component.W(1), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + mul_by_constant(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, value_type constant_): + component_type(witness, constant, public_input, get_manifest()), + constant(constant_) {}; + + mul_by_constant(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs, + value_type constant_): + component_type(witnesses, constants, public_inputs, get_manifest()), + constant(constant_) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type constant) { + return x * constant; + } + }; + + template + using plonk_mul_by_constant = + mul_by_constant, + BlueprintFieldType>; + + template + typename plonk_mul_by_constant::result_type + generate_assignments( + const plonk_mul_by_constant &component, + assignment> &assignment, + const typename plonk_mul_by_constant::input_type instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = component.constant * + var_value(assignment, instance_input.x); + + return typename plonk_mul_by_constant::result_type(component, start_row_index); + } + + template + typename plonk_mul_by_constant::result_type + generate_empty_assignments( + const plonk_mul_by_constant &component, + assignment> &assignment, + const typename plonk_mul_by_constant::input_type instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_mul_by_constant; + assignment.witness(component.W(1), start_row_index) = component_type::calculate(var_value(assignment, instance_input.x), component.constant); + + return typename plonk_mul_by_constant::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_mul_by_constant &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mul_by_constant::input_type &instance_input) { + + using var = typename plonk_mul_by_constant::var; + + auto constraint_1 = + var(component.W(0), 0) * var(0, 0, true, var::column_type::constant) - var(component.W(1), 0); + + return bp.add_gate(constraint_1); + } + + template + void generate_copy_constraints( + const plonk_mul_by_constant &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mul_by_constant::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_mul_by_constant::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + } + + template + typename plonk_mul_by_constant::result_type + generate_circuit( + const plonk_mul_by_constant &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mul_by_constant::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + return typename plonk_mul_by_constant::result_type(component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_mul_by_constant + &component, + assignment> + &assignment, + const typename plonk_mul_by_constant::input_type + &instance_input, + const std::size_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = component.constant; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_MULTIPLICATION_BY_CONSTANT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/addition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/addition.hpp new file mode 100644 index 000000000..5ceeaf84b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/addition.hpp @@ -0,0 +1,492 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_ADDITION_COMPONENT_9_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_ADDITION_COMPONENT_9_WIRES_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + /* + 1 non_native range for q + 2 q + 3 non-native range for r + 4 + 5 a0 a1 a2 a3 b0 b1 b2 b3 q0 + 6 q1 q2 q3 r0 r1 r2 r3 v0 v1 + 7 v00 v01 v02 v03 v10 v11 v12 v13 + + */ + template + class addition; + + template + class addition, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy> + : public plonk_component { + + using operating_field_type = crypto3::algebra::fields::curve25519_base_field; + using non_native_policy_type = basic_non_native_policy; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return 2 + range_type::get_rows_amount(witness_amount); + } + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using range_type = range, + typename crypto3::algebra::fields::curve25519_base_field, + non_native_policy_type>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()).merge_with( + range_type::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(range_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t T = 257; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + constexpr static const std::size_t gates_amount = 1; + const std::string component_name = "non-native field addition"; + + struct input_type { + typename non_native_policy_type::template field::non_native_var_type A; + typename non_native_policy_type::template field::non_native_var_type B; + + std::vector> all_vars() { + return {A[0], A[1], A[2], A[3], B[0], B[1], B[2], B[3]}; + } + }; + + struct result_type { + typename non_native_policy_type::template field::non_native_var_type output; + + result_type(const addition &component, std::uint32_t start_row_index) { + output = {var(component.W(0), start_row_index + 2, false), + var(component.W(1), start_row_index + 2, false), + var(component.W(2), start_row_index + 2, false), + var(component.W(3), start_row_index + 2, false)}; + } + result_type(const addition &component, std::uint32_t start_row_index, bool skip) { + output = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3]}; + } + }; + + template + explicit addition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + addition(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static std::array + calculate(std::array a, + std::array b) { + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + typename ed25519_field_type::integral_type base = 1; + typename BlueprintFieldType::integral_type pasta_base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + typename ed25519_field_type::value_type eddsa_a = + typename ed25519_field_type::integral_type(a[0].data) + + typename ed25519_field_type::integral_type(a[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(a[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(a[3].data) * (base << 198); + + typename ed25519_field_type::value_type eddsa_b = + typename ed25519_field_type::integral_type(b[0].data) + + typename ed25519_field_type::integral_type(b[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(b[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(b[3].data) * (base << 198); + + typename ed25519_field_type::value_type eddsa_r = eddsa_a + eddsa_b; + typename ed25519_field_type::integral_type integral_eddsa_r = + typename ed25519_field_type::integral_type(eddsa_r.data); + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + + // On the following like values like eddsa_a.data are of modular type. We do not allow converting them to + // integral types of different precision, so we first need to convert them to integral type, then extend. + typename ed25519_field_type::extended_integral_type integral_eddsa_q = + (typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_a.data)) + + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_b.data)) - + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_r.data))) / + eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + typename BlueprintFieldType::integral_type mask = (pasta_base << 66) - 1; + typename BlueprintFieldType::extended_integral_type extended_mask = mask; + r[0] = integral_eddsa_r & mask; + q[0] = integral_eddsa_q & extended_mask; + p[0] = minus_eddsa_p & extended_mask; + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & (mask); + } + typename BlueprintFieldType::value_type t = a[0] + b[0] + p[0] * q[0]; + + typename BlueprintFieldType::value_type u0 = t - r[0]; + + typename BlueprintFieldType::integral_type u0_integral = + typename BlueprintFieldType::integral_type(u0.data) >> 66; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & (1); + + return {r[0], r[1], r[2], r[3]}; + } + }; + + template + using plonk_ed25519_addition = + addition, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy>; + + template + typename plonk_ed25519_addition::result_type + generate_assignments( + const plonk_ed25519_addition &component, + assignment> + &assignment, + const typename plonk_ed25519_addition::input_type + instance_input, + const std::uint32_t start_row_index) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + using var = typename plonk_ed25519_addition::var; + + std::size_t row = start_row_index; + + typename ed25519_field_type::integral_type base = 1; + typename BlueprintFieldType::integral_type pasta_base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + std::array a = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[3]).data)}; + typename ed25519_field_type::value_type eddsa_a = + typename ed25519_field_type::integral_type(a[0].data) + + typename ed25519_field_type::integral_type(a[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(a[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(a[3].data) * (base << 198); + + std::array b = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[3]).data)}; + typename ed25519_field_type::value_type eddsa_b = + typename ed25519_field_type::integral_type(b[0].data) + + typename ed25519_field_type::integral_type(b[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(b[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(b[3].data) * (base << 198); + + typename ed25519_field_type::value_type eddsa_r = eddsa_a + eddsa_b; + typename ed25519_field_type::integral_type integral_eddsa_r = + typename ed25519_field_type::integral_type(eddsa_r.data); + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + + // On the following like values like eddsa_a.data are of modular type. We do not allow converting them to + // integral types of different precision, so we first need to convert them to integral type, then extend. + typename ed25519_field_type::extended_integral_type integral_eddsa_q = + (typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_a.data)) + + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_b.data)) - + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_r.data))) / + eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + typename BlueprintFieldType::integral_type mask = (pasta_base << 66) - 1; + typename BlueprintFieldType::extended_integral_type extended_mask = (pasta_base << 66) - 1; + r[0] = integral_eddsa_r & mask; + q[0] = integral_eddsa_q & extended_mask; + p[0] = minus_eddsa_p & extended_mask; + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & (mask); + } + typename BlueprintFieldType::value_type t = a[0] + b[0] + p[0] * q[0]; + + typename BlueprintFieldType::value_type u0 = t - r[0]; + + typename BlueprintFieldType::integral_type u0_integral = + typename BlueprintFieldType::integral_type(u0.data) >> 66; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & (1); + + assignment.witness(component.W(0), row + 1) = a[0]; + assignment.witness(component.W(1), row + 1) = b[0]; + assignment.witness(component.W(2), row + 1) = integral_eddsa_q; + assignment.witness(component.W(3), row + 1) = a[1]; + assignment.witness(component.W(4), row + 1) = a[2]; + assignment.witness(component.W(5), row + 1) = a[3]; + assignment.witness(component.W(6), row + 1) = b[1]; + assignment.witness(component.W(7), row + 1) = b[2]; + assignment.witness(component.W(8), row + 1) = b[3]; + assignment.witness(component.W(3), row) = u0_chunks[0]; + assignment.witness(component.W(4), row) = u0_chunks[1]; + assignment.witness(component.W(5), row) = u0_chunks[2]; + assignment.witness(component.W(6), row) = u0_chunks[3]; + assignment.witness(component.W(7), row) = typename BlueprintFieldType::value_type(u0_integral); + assignment.witness(component.W(0), row + 2) = r[0]; + assignment.witness(component.W(1), row + 2) = r[1]; + assignment.witness(component.W(2), row + 2) = r[2]; + assignment.witness(component.W(3), row + 2) = r[3]; + + using range_type = typename plonk_ed25519_addition::range_type; + + typename range_type::input_type range_input_r = { + var(0, row + 2, false), var(1, row + 2, false), var(2, row + 2, false), var(3, row + 2, false)}; + + range_type range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + generate_assignments(range_component_instance, assignment, range_input_r, row + 2); + + return typename plonk_ed25519_addition::result_type( + component, start_row_index); + } + + template + typename plonk_ed25519_addition::result_type + generate_empty_assignments( + const plonk_ed25519_addition &component, + assignment> + &assignment, + const typename plonk_ed25519_addition::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_ed25519_addition; + + std::array a = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[3]).data)}; + + std::array b = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[3]).data)}; + + auto r = component_type::calculate(a, b); + + assignment.witness(component.W(0), start_row_index) = r[0]; + assignment.witness(component.W(1), start_row_index) = r[1]; + assignment.witness(component.W(2), start_row_index) = r[2]; + assignment.witness(component.W(3), start_row_index) = r[3]; + + return typename plonk_ed25519_addition::result_type( + component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_ed25519_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_addition::input_type + &instance_input) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + using var = typename plonk_ed25519_addition::var; + + typename BlueprintFieldType::integral_type base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + typename BlueprintFieldType::value_type pasta_eddsa_p = eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + std::array p; + typename BlueprintFieldType::extended_integral_type mask = (base << 66) - 1; + p[0] = minus_eddsa_p & mask; + + auto t = var(component.W(0), 0) + var(component.W(1), 0) + p[0] * var(component.W(2), 0); + auto constraint_1 = var(component.W(7), -1) * (base << 66) - (t - var(component.W(0), +1)); + + auto constraint_2 = var(component.W(2), 0) * (var(component.W(2), 0) - 1); + + auto constraint_3 = var(component.W(7), -1) - + (var(component.W(3), -1) + var(component.W(4), -1) * (1 << 22) + + var(component.W(5), -1) * (base << 44) + var(component.W(6), -1) * (base << 66)); + + auto constraint_4 = + (var(component.W(0), 0) + var(component.W(3), 0) * (base << 66) + + var(component.W(4), 0) * (base << 132) + var(component.W(5), 0) * (base << 198)) + + (var(component.W(1), 0) + var(component.W(6), 0) * (base << 66) + + var(component.W(7), 0) * (base << 132) + var(component.W(8), 0) * (base << 198)) - + (var(component.W(2), 0) * pasta_eddsa_p + + (var(component.W(0), +1) + var(component.W(1), +1) * (base << 66) + + var(component.W(2), +1) * (base << 132) + var(component.W(3), +1) * (base << 198))); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + } + + template + void generate_copy_constraints( + const plonk_ed25519_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_addition::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_ed25519_addition::var; + + std::size_t row = start_row_index; + + bp.add_copy_constraint({var(component.W(0), row + 1, false), instance_input.A[0]}); + bp.add_copy_constraint({var(component.W(1), row + 1, false), instance_input.B[0]}); + bp.add_copy_constraint({var(component.W(3), row + 1, false), instance_input.A[1]}); + bp.add_copy_constraint({var(component.W(4), row + 1, false), instance_input.A[2]}); + bp.add_copy_constraint({var(component.W(5), row + 1, false), instance_input.A[3]}); + bp.add_copy_constraint({var(component.W(6), row + 1, false), instance_input.B[1]}); + bp.add_copy_constraint({var(component.W(7), row + 1, false), instance_input.B[2]}); + bp.add_copy_constraint({var(component.W(8), row + 1, false), instance_input.B[3]}); + } + + template + typename plonk_ed25519_addition::result_type generate_circuit( + const plonk_ed25519_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_addition::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + std::size_t j = start_row_index; + assignment.enable_selector(selector_index, j + 1); + + generate_copy_constraints(component, bp, assignment, instance_input, j); + + using ArithmetizationType = + crypto3::zk::snark::plonk_constraint_system; + using var = typename plonk_ed25519_addition::var; + + typename range>::input_type non_range_input_r = { + var(component.W(0), j + 2, false), var(component.W(1), j + 2, false), + var(component.W(2), j + 2, false), var(component.W(3), j + 2, false)}; + + range> + range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + + generate_circuit(range_component_instance, bp, assignment, non_range_input_r, j + 2); + + return typename plonk_ed25519_addition::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_SUM_MULTIPLICATION_COMPONENT_9_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_composition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_composition.hpp new file mode 100644 index 000000000..7e047f6e1 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_composition.hpp @@ -0,0 +1,323 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BIT_COMPOSITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BIT_COMPOSITION_HPP + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +using nil::blueprint::components::detail::bit_builder_component; +using nil::blueprint::components::bit_composition_mode; + +namespace nil { + namespace blueprint { + namespace components { + + /* + Makes a single field element from bits_amount bits. + If CheckInput is true, performs a check that input values are actually in {0, 1}. + Otherwise, assumes that input values are already in {0, 1}. + Only the case of bits_amount < BlueprintFieldType::modulus_bits is supported. + Bits can be passed LSB-first or MSB-first, depending on the value of Mode parameter. + + A schematic representation of this component can be found in bit_builder_component.hpp. + */ + template + class bit_composition; + + template + class bit_composition< + crypto3::zk::snark::plonk_constraint_system> + : public + bit_builder_component> { + + void check_params(std::size_t bits_amount, bit_composition_mode mode) const { + BLUEPRINT_RELEASE_ASSERT(bits_amount > 0 && bits_amount < BlueprintFieldType::modulus_bits); + BLUEPRINT_RELEASE_ASSERT(mode == bit_composition_mode::LSB || mode == bit_composition_mode::MSB); + } + public: + using component_type = + bit_builder_component< + crypto3::zk::snark::plonk_constraint_system>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 0; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + bool check_input, bit_composition_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(component_type::get_gate_manifest(witness_amount, bits_amount, check_input, mode)); + return manifest; + } + + static manifest_type get_manifest( + std::uint32_t bits_amount, bool check_input, + bit_composition_mode mode) { + return component_type::get_manifest(bits_amount, check_input, mode); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, bool check_input, bit_composition_mode mode) { + return component_type::get_rows_amount(witness_amount, bits_amount, check_input); + } + + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + const bit_composition_mode mode; + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "bit_composition"; + + struct input_type { + std::vector bits; + + std::vector> all_vars() { + std::vector> res; + for (auto& it : bits) { + res.push_back(it); + } + return res; + } + }; + + struct result_type { + var output; + result_type(const bit_composition &component, std::uint32_t start_row_index) { + auto pos = component.sum_bit_position(start_row_index, component.sum_bits_amount() - 1); + output = var(component.W(pos.second), pos.first, false); + } + result_type(const bit_composition &component, std::uint32_t start_row_index, bool skip) { + output = var(component.W(0), start_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit bit_composition(ContainerType witness, std::uint32_t bits_amount, bool check_input, + bit_composition_mode mode_) : + component_type(witness, {}, {}, get_manifest(bits_amount, check_input, mode_), + bits_amount, check_input), mode(mode_) { + + check_params(bits_amount, mode); + }; + + template + bit_composition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::uint32_t bits_amount, bool check_input, bit_composition_mode mode_) : + component_type(witness, constant, public_input, get_manifest(bits_amount, check_input, mode_), + bits_amount, check_input), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + bit_composition( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::uint32_t bits_amount, bool check_input, bit_composition_mode mode_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount, check_input, mode_), + bits_amount, check_input), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + static typename BlueprintFieldType::value_type calculate(std::vector input_bits, + bit_composition_mode mode = bit_composition_mode::MSB) { + using field_value_type = typename BlueprintFieldType::value_type; + + std::vector true_input_bits(input_bits.size()); + auto bit_index = [&mode, &input_bits](std::size_t i) { + return mode == bit_composition_mode::MSB ? i : input_bits.size() - i - 1; + }; + + for (std::uint32_t i = 0; i < input_bits.size(); ++i) { + true_input_bits[i] = input_bits[bit_index(i)] != 0 ? true : false; + } + + field_value_type sum = 0; + for (std::size_t i = 0; i < true_input_bits.size(); i++) { + sum = 2 * sum + static_cast(true_input_bits[i]); + } + return sum; + } + }; + + template + using plonk_bit_composition = bit_composition>; + + template + typename plonk_bit_composition::result_type + generate_assignments( + const plonk_bit_composition + &component, + assignment> + &assignment, + const typename plonk_bit_composition::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector input_bits(component.bits_amount); + + auto bit_index = [&component](std::size_t i) { + return component.mode == bit_composition_mode::MSB ? i : component.bits_amount - i - 1; + }; + + for (std::uint32_t i = 0; i < component.bits_amount; ++i) { + input_bits[i] = var_value(assignment, instance_input.bits[bit_index(i)]) != 0 ? true : false; + } + // calling bit_builder_component's generate_assignments + generate_assignments( + component, assignment, input_bits, start_row_index); + + return typename plonk_bit_composition::result_type( + component, start_row_index); + } + + template + typename plonk_bit_composition::result_type + generate_empty_assignments( + const plonk_bit_composition + &component, + assignment> + &assignment, + const typename plonk_bit_composition::input_type + &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_bit_composition; + + std::vector input_bits(component.bits_amount); + + for (std::uint32_t i = 0; i < component.bits_amount; ++i) { + input_bits[i] = var_value(assignment, instance_input.bits[i]) != 0 ? true : false; + } + + assignment.witness(component.W(0), start_row_index) = component_type::calculate(input_bits, component.mode); + + return typename plonk_bit_composition::result_type( + component, start_row_index, true); + } + + template + void generate_copy_constraints( + const plonk_bit_composition + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_bit_composition::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_bit_composition::var; + + std::size_t row = start_row_index; + + auto bit_index = [&component](std::size_t i) { + return component.mode == bit_composition_mode::MSB ? i : component.bits_amount - i - 1; + }; + + var zero(0, row, false, var::column_type::constant); + std::size_t padding = 0; + for (; padding < component.padding_bits_amount(); padding++) { + auto bit_pos = component.bit_position(row, padding); + bp.add_copy_constraint({zero, + var(component.W(bit_pos.second), bit_pos.first, false)}); + } + + for (std::size_t i = 0; i < component.bits_amount; i++) { + auto bit_pos = component.bit_position(row, padding + i); + bp.add_copy_constraint({instance_input.bits[bit_index(i)], + var(component.W(bit_pos.second), bit_pos.first, false)}); + } + + for (std::size_t i = 0; i < component.sum_bits_amount() - 1; i += 2) { + auto sum_bit_pos_1 = component.sum_bit_position(row, i); + auto sum_bit_pos_2 = component.sum_bit_position(row, i + 1); + bp.add_copy_constraint( + {var(component.W(sum_bit_pos_1.second), sum_bit_pos_1.first, false), + var(component.W(sum_bit_pos_2.second), sum_bit_pos_2.first, false)}); + } + } + + template + typename plonk_bit_composition::result_type + generate_circuit( + const plonk_bit_composition + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_bit_composition::input_type + &instance_input, + const std::size_t start_row_index) { + + // calling bit_builder_component's generate_circuit + generate_circuit(component, bp, assignment, start_row_index); + // copy constraints are specific to this component + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_bit_composition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BIT_COMPOSITION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_decomposition.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_decomposition.hpp new file mode 100644 index 000000000..544ad56a1 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/bit_decomposition.hpp @@ -0,0 +1,327 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BIT_DECOMPOSITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BIT_DECOMPOSITION_HPP + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using nil::blueprint::components::detail::bit_builder_component; +using nil::blueprint::components::bit_composition_mode; + +namespace nil { + namespace blueprint { + namespace components { + /* + Decomposes a single field element into bits_amount bits. + Output bits can be ordered LSB-first or MSB-first, depending on the value of mode parameter. + + A schematic representation of this component can be found in bit_builder_component.hpp. + */ + template + class bit_decomposition; + + template + class bit_decomposition< + crypto3::zk::snark::plonk_constraint_system> + : public bit_builder_component> { + + void check_params(std::size_t bits_amount, bit_composition_mode mode) const { + BLUEPRINT_RELEASE_ASSERT(bits_amount > 0 && bits_amount < BlueprintFieldType::modulus_bits); + BLUEPRINT_RELEASE_ASSERT(mode == bit_composition_mode::LSB || mode == bit_composition_mode::MSB); + } + public: + using component_type = + bit_builder_component< + crypto3::zk::snark::plonk_constraint_system>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 0; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, bit_composition_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(component_type::get_gate_manifest(witness_amount, bits_amount, true, mode)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, bit_composition_mode mode) { + return component_type::get_manifest(bits_amount, true, mode); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, bit_composition_mode mode) { + return component_type::get_rows_amount(witness_amount, bits_amount, true); + } + + constexpr static std::size_t get_empty_rows_amount(std::size_t bits_amount, bit_composition_mode mode) { + return bits_amount / 9 + (bits_amount % 9 != 0); + } + + const bit_composition_mode mode; + const std::size_t empty_rows_amount = get_empty_rows_amount(this->bits_amount, this->mode); + const std::string component_name = "bit_decomposition"; + + struct input_type { + var input; + + std::vector> all_vars() { + return {input}; + } + }; + + struct result_type { + std::vector output; + result_type(const bit_decomposition &component, std::uint32_t start_row_index) { + output.resize(component.bits_amount); + auto padded_bit_index = [&component](std::size_t i) { + return component.padding_bits_amount() + + (component.mode == bit_composition_mode::MSB ? + i + : component.bits_amount - i - 1); + }; + + for (std::size_t i = 0; i < component.bits_amount; i++) { + auto pos = component.bit_position(start_row_index, padded_bit_index(i)); + output[i] = var(component.W(pos.second), pos.first, false); + } + } + result_type(const bit_decomposition &component, std::uint32_t start_row_index, bool skip) { + output.resize(component.bits_amount); + + for (std::size_t i = 0; i < component.bits_amount; i++) { + output[i] = var(component.W(i % 9), start_row_index + i / 9, false); + } + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(output.size()); + std::copy(output.begin(), output.end(), std::back_inserter(result)); + return result; + } + }; + + template + explicit bit_decomposition(ContainerType witness, std::uint32_t bits_amount, + bit_composition_mode mode_) : + component_type(witness, get_manifest(bits_amount, mode_), bits_amount, true), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + template + bit_decomposition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::uint32_t bits_amount, + bit_composition_mode mode_) : + component_type(witness, constant, public_input, + get_manifest(bits_amount, mode_), bits_amount, true), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + bit_decomposition( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::uint32_t bits_amount, bit_composition_mode mode_) : + component_type(witnesses, constants, public_inputs, + get_manifest(bits_amount, mode_), bits_amount, true), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + static std::vector calculate(typename BlueprintFieldType::value_type input, + std::uint32_t bits_amount, bit_composition_mode mode) { + auto bit_index = [&mode, &bits_amount](std::size_t i) { + return mode == bit_composition_mode::MSB ? i : bits_amount - i - 1; + }; + std::vector bits(bits_amount); + { + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack(input, status); + std::copy(bytes_all.end() - bits_amount, bytes_all.end(), bits.begin()); + assert(status == nil::marshalling::status_type::success); + } + std::vector true_bits(bits_amount); + for (std::size_t i = 0; i < bits_amount; i++) { + true_bits[i] = bits[bit_index(i)]; + } + return true_bits; + } + }; + + template + using plonk_bit_decomposition = bit_decomposition< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_bit_decomposition::result_type + generate_assignments( + const plonk_bit_decomposition + &component, + assignment> + &assignment, + const typename plonk_bit_decomposition::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector input_bits(component.bits_amount); + { + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack( + var_value(assignment, instance_input.input), status); + std::copy(bytes_all.end() - component.bits_amount, bytes_all.end(), input_bits.begin()); + assert(status == nil::marshalling::status_type::success); + } + // calling bit_builder_component's generate_assignments + generate_assignments( + component, assignment, input_bits, start_row_index); + + return typename plonk_bit_decomposition::result_type( + component, start_row_index); + } + + template + typename plonk_bit_decomposition::result_type + generate_empty_assignments( + const plonk_bit_decomposition + &component, + assignment> + &assignment, + const typename plonk_bit_decomposition::input_type + &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_bit_decomposition; + using value_type = typename BlueprintFieldType::value_type; + + value_type input_data = var_value(assignment, instance_input.input); + + std::vector bits = component_type::calculate(input_data, component.bits_amount, component.mode); + + for (std::size_t i = 0; i < component.bits_amount; i++) { + assignment.witness(component.W(i % 9), start_row_index + i / 9) = value_type(bits[i] ? 1 : 0); + } + + return typename plonk_bit_decomposition::result_type( + component, start_row_index, true); + } + + template + void generate_copy_constraints( + const plonk_bit_decomposition + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_bit_decomposition::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_bit_decomposition::var; + + std::size_t row = start_row_index; + + var zero(0, row, false, var::column_type::constant); + std::size_t padding = 0; + for (; padding < component.padding_bits_amount(); padding++) { + auto bit_pos = component.bit_position(row, padding); + bp.add_copy_constraint({zero, var(component.W(bit_pos.second), bit_pos.first, false)}); + } + + for (std::size_t i = 0; i < component.sum_bits_amount() - 1; i += 2) { + auto sum_bit_pos_1 = component.sum_bit_position(row, i); + auto sum_bit_pos_2 = component.sum_bit_position(row, i + 1); + bp.add_copy_constraint( + {var(component.W(sum_bit_pos_1.second), sum_bit_pos_1.first, false), + var(component.W(sum_bit_pos_2.second), sum_bit_pos_2.first, false)}); + } + + auto sum_pos = component.sum_bit_position(row, component.sum_bits_amount() - 1); + bp.add_copy_constraint({instance_input.input, + var(component.W(sum_pos.second), sum_pos.first, false)}); + } + + template + typename plonk_bit_decomposition::result_type + generate_circuit( + const plonk_bit_decomposition + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_bit_decomposition::input_type + &instance_input, + const std::size_t start_row_index) { + + // calling bit_builder_component's generate_circuit + generate_circuit( + component, bp, assignment, start_row_index); + // copy constraints are specific to this component + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_bit_decomposition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_checked.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_checked.hpp new file mode 100644 index 000000000..2fa2b6a9f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_checked.hpp @@ -0,0 +1,586 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_NON_NATIVE_COMPARISON_CHECKED_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_NON_NATIVE_COMPARISON_CHECKED_HPP + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class comparison_checked; + + /* + Compare x and y, failing if the comparsion is not satisfied. + Both x and y have to fit in bits_amount bits; this condition is checked. See comparsion_unchecked + Additionally, bits_amount has to satisfy: bits_amount < modulus_bits - 1. + Takes one gate less for bits_amount divisible by chunk_size. + + For less, we check that both x and x - y are less than 2^{bits_amount}. + The check is done by splitting x (x-y) into bit chunks and checking that their weighted sum is + equal to x (x-y). After that, we add a constraint checking for non-zero x - y. + Other comparsion modes are implemented similarly. + + The component is multiple copies of the following gate (illustrated for WitnessesAmount = 15): + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |x|d|p|p|p|p|p|p|p|p|p|p|p|p|p| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |p|o|o|o|o|o|o|o|o|o|o|o|o|o|o| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |x|d| | | | | | | | | | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Where x and d are weighted sum of bit chunks for x and y - x respectively, and o/p are the bit chunks + of x and y - x respectively. Empty spaces are not constrained. + Starting sums for x and d are constrained to be zero. + We use the third cell in the final row to store y, and use it to check that the difference is correct. + + See comparison_flag if you want to access the result of the comparison instead. + */ + template + class comparison_checked> : + public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + static std::size_t chunk_amount_internal(std::size_t bits_amount) { + return (bits_amount + chunk_size - 1) / chunk_size; + } + // We need to pad each of x, y - x up to the nearest multiple of WitnessAmount - 1. + static std::size_t padded_chunks_internal(std::size_t witness_amount, std::size_t bits_amount) { + return (chunk_amount_internal(bits_amount) + witness_amount - 2) / + (witness_amount - 1) * (witness_amount - 1); + } + + static std::size_t padding_size_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padded_chunks_internal(witness_amount, bits_amount) - chunk_amount_internal(bits_amount); + } + + static std::size_t padding_bits_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padded_chunks_internal(witness_amount, bits_amount) * chunk_size - bits_amount; + } + + static bool needs_bonus_row_internal(std::size_t witness_amount, comparison_mode mode) { + return witness_amount <= 3 && + (mode == comparison_mode::LESS_THAN || + mode == comparison_mode::GREATER_THAN); + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount, + comparison_mode mode) { + return 1 + 2 * padded_chunks_internal(witness_amount, bits_amount) / (witness_amount - 1) + + needs_bonus_row_internal(witness_amount, mode); + } + + static bool needs_first_chunk_constraint_internal(std::size_t bits_amount) { + return (bits_amount % chunk_size) && + (bits_amount + ((chunk_size - bits_amount % chunk_size) % chunk_size) >= + BlueprintFieldType::modulus_bits - 1); + } + + static std::size_t gates_amount_internal(std::size_t bits_amount, comparison_mode mode) { + return 2 + needs_first_chunk_constraint_internal(bits_amount); + } + + static std::size_t chunks_per_row_internal(std::size_t witness_amount) { + return witness_amount - 1; + } + + static std::size_t bits_per_row_internal(std::size_t witness_amount, std::size_t bits_amount) { + return chunks_per_row_internal(witness_amount) * chunk_size; + } + + void check_params(std::size_t bits_amount, comparison_mode mode) const { + BLUEPRINT_RELEASE_ASSERT(bits_amount > 0 && bits_amount < BlueprintFieldType::modulus_bits - 1); + BLUEPRINT_RELEASE_ASSERT(mode == comparison_mode::LESS_THAN || + mode == comparison_mode::GREATER_THAN || + mode == comparison_mode::LESS_EQUAL || + mode == comparison_mode::GREATER_EQUAL); + } + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + std::size_t bits_amount; + comparison_mode mode; + + gate_manifest_type(std::size_t witness_amount_, std::size_t bits_amount_, comparison_mode mode_) + : witness_amount(witness_amount_), bits_amount(bits_amount_), mode(mode_) {} + + std::uint32_t gates_amount() const override { + return comparison_checked::gates_amount_internal(bits_amount, mode); + } + + bool operator<(gate_manifest_type const& other) const { + return witness_amount < other.witness_amount || + (witness_amount == other.witness_amount && bits_amount < other.bits_amount) || + (witness_amount == other.witness_amount && + bits_amount == other.bits_amount && mode < other.mode); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, bits_amount, mode)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, + comparison_mode mode) { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param( + 3, std::max(4, (bits_amount + chunk_size - 1) / chunk_size + 1))), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + return rows_amount_internal(witness_amount, bits_amount, mode); + } + + /* + It's CRITICAL that these three variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + const comparison_mode mode; + constexpr static const std::size_t chunk_size = 2; + /* Do NOT move the above variables! */ + + const std::size_t chunk_amount = chunk_amount_internal(bits_amount); + // Techincally, this is average chunks per row after first. + const std::size_t chunks_per_row = chunks_per_row_internal(this->witness_amount()); + const std::size_t bits_per_row = bits_per_row_internal(this->witness_amount(), bits_amount); + + const std::size_t padded_chunks = padded_chunks_internal(this->witness_amount(), bits_amount); + const std::size_t padding_size = padding_size_internal(this->witness_amount(), bits_amount); + const std::size_t padding_bits = padding_bits_internal(this->witness_amount(), bits_amount); + + const bool needs_bonus_row = needs_bonus_row_internal(this->witness_amount(), mode); + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount, mode); + const bool needs_first_chunk_constraint = needs_first_chunk_constraint_internal(bits_amount); + const std::size_t gates_amount = gates_amount_internal(bits_amount, mode); + + struct input_type { + var x, y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + result_type(const comparison_checked &component, std::size_t start_row_index) {} + + std::vector> all_vars() { + return {}; + } + }; + + template + comparison_checked(ContainerType witness, std::size_t bits_amount_, comparison_mode mode_): + component_type(witness, {}, {}, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + template + comparison_checked(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t bits_amount_, comparison_mode mode_): + component_type(witness, constant, public_input, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + comparison_checked( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t bits_amount_, comparison_mode mode_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) { + + check_params(bits_amount, mode); + }; + }; + + template + using plonk_comparison_checked = + comparison_checked>; + + template + std::vector generate_gates( + const plonk_comparison_checked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_checked::input_type + &instance_input) { + + using var = typename plonk_comparison_checked::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector selector_indices; + + typename BlueprintFieldType::value_type base_two = 2; + std::vector constraints; + constraints.reserve(component.witness_amount() * 2 - 2); + + auto generate_chunk_size_constraint = [](var v, std::size_t size) { + constraint_type constraint = v; + for (std::size_t i = 1; i < std::size_t(1 << size); i++) { + constraint = constraint * (v - i); + } + return constraint; + }; + + // Assert chunk size. + for (std::size_t row_idx = 0; row_idx < 2; row_idx++) { + for (std::size_t i = 2 * (1 - row_idx); i < component.witness_amount(); i++) { + constraint_type chunk_range_constraint = + generate_chunk_size_constraint(var(component.W(i), int(row_idx) - 1, true), + component.chunk_size); + + constraints.push_back(chunk_range_constraint); + } + } + // Assert sums. var_idx = 0 is x, var_idx = 1 is diff=y-x. + for (int var_idx = 0; var_idx < 2; var_idx++) { + constraint_type sum_constraint = var(component.W(1 + var_idx), -var_idx, true); + for (std::size_t i = 2 + var_idx; i < component.witness_amount(); i++) { + sum_constraint = base_two.pow(component.chunk_size) * sum_constraint + + var(component.W(i), -var_idx, true); + } + if (var_idx == 1) { + sum_constraint = base_two.pow(component.chunk_size) * sum_constraint + + var(component.W(0), 0, true); + } + sum_constraint = sum_constraint + + base_two.pow(component.chunk_size * component.chunks_per_row) * + var(component.W(var_idx), -1, true) - + var(component.W(var_idx), 1, true); + constraints.push_back(sum_constraint); + } + + selector_indices.push_back(bp.add_gate(constraints)); + + std::vector correctness_constraints; + constraint_type diff_constraint = var(component.W(2), 0, true) - var(component.W(0), 0, true) - + var(component.W(1), 0, true), + non_zero_constraint; + correctness_constraints.push_back(diff_constraint); + switch (component.mode) { + case comparison_mode::GREATER_EQUAL: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::LESS_THAN: + case comparison_mode::GREATER_THAN: + if (!component.needs_bonus_row) { + non_zero_constraint = var(component.W(1), 0, true) * var(component.W(3), 0, true) - 1; + } else { + non_zero_constraint = var(component.W(1), 0, true) * var(component.W(0), 1, true) - 1; + } + correctness_constraints.push_back(non_zero_constraint); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + + selector_indices.push_back(bp.add_gate(correctness_constraints)); + + if (!component.needs_first_chunk_constraint) return selector_indices; + // If bits_amount is not divisible by chunk size, the first chunk of both x/y - x should be constrained + // to be less than 2^{bits_amount % component.chunk_size}. + // We actually only need this constraint when y - x can do an unsafe overflow. + // Otherwise the constraint on y - x takes care of this. + std::vector first_chunk_range_constraints; + + var size_constraint_var = component.padding_size != component.witness_amount() - 2 ? + var(component.W(2 + component.padding_size), 0, true) + : var(component.W(0), 1, true); + constraint_type first_chunk_range_constraint = generate_chunk_size_constraint( + size_constraint_var, component.bits_amount % component.chunk_size); + first_chunk_range_constraints.push_back(first_chunk_range_constraint); + + size_constraint_var = var(component.W(1 + component.padding_size), 1, true); + first_chunk_range_constraint = + generate_chunk_size_constraint(size_constraint_var, component.bits_amount % component.chunk_size); + first_chunk_range_constraints.push_back(first_chunk_range_constraint); + + selector_indices.push_back(bp.add_gate(first_chunk_range_constraints)); + return selector_indices; + } + + template + void generate_copy_constraints( + const plonk_comparison_checked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_checked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_comparison_checked::var; + std::uint32_t row = start_row_index; + var zero(0, start_row_index, false, var::column_type::constant); + + bp.add_copy_constraint({zero, var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint({zero, var(component.W(1), start_row_index, false)}); + + // Padding constraints for x + for (std::size_t i = 0; i < component.padding_size; i++) { + bp.add_copy_constraint({zero, var(component.W(i + 1), start_row_index + 1, false)}); + } + // Padding constraints for difference + for (std::size_t i = 0; i < component.padding_size; i++) { + bp.add_copy_constraint({zero, var(component.W(i + 2), start_row_index, false)}); + } + + row += component.rows_amount - 1 - component.needs_bonus_row; + var x_var = var(component.W(0), row, false), + y_var = var(component.W(2), row, false); + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::GREATER_THAN: + case comparison_mode::GREATER_EQUAL: + std::swap(x_var, y_var); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + bp.add_copy_constraint({instance_input.x, x_var}); + bp.add_copy_constraint({instance_input.y, y_var}); + } + + template + typename plonk_comparison_checked::result_type + generate_circuit( + const plonk_comparison_checked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_checked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector selector_indices = + generate_gates(component, bp, assignment, instance_input); + + std::size_t final_gate_mid_row = start_row_index + component.rows_amount - 2 - + component.needs_bonus_row; + + assignment.enable_selector(selector_indices[0], start_row_index + 1, + final_gate_mid_row, 2); + assignment.enable_selector(selector_indices[1], final_gate_mid_row + 1); + + if (component.needs_first_chunk_constraint) { + if (selector_indices.size() != 3) { + std::cerr << "Internal error: comparison_checked component returned the wrong selector amount." + << std::endl; + std::abort(); + } + assignment.enable_selector(selector_indices[2], start_row_index); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constants(component, assignment, instance_input, start_row_index); + + return typename plonk_comparison_checked::result_type( + component, start_row_index); + } + + template + typename plonk_comparison_checked::result_type + generate_assignments( + const plonk_comparison_checked + &component, + assignment> + &assignment, + const typename plonk_comparison_checked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_comparison_checked; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using chunk_type = std::uint8_t; + BOOST_ASSERT(component.chunk_size <= 8); + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::GREATER_THAN: + case comparison_mode::GREATER_EQUAL: + std::swap(x, y); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + value_type diff = y - x; + + std::array integrals = {integral_type(x.data), integral_type(diff.data)}; + + std::array, 2> bits; + for (std::size_t i = 0; i < 2; i++) { + bits[i].resize(component.bits_amount + component.padding_bits); + std::fill(bits[i].begin(), bits[i].end(), false); + + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack(integrals[i], status); + std::copy(bytes_all.end() - component.bits_amount, bytes_all.end(), + bits[i].begin() + component.padding_bits); + assert(status == nil::marshalling::status_type::success); + } + + std::array, 2> chunks; + for (std::size_t i = 0; i < 2; i++) { + chunks[i].resize(component.padded_chunks); + for (std::size_t j = 0; j < component.padded_chunks; j++) { + chunk_type chunk_value = 0; + for (std::size_t k = 0; k < component.chunk_size; k++) { + chunk_value <<= 1; + chunk_value |= bits[i][j * component.chunk_size + k]; + } + chunks[i][j] = chunk_value; + } + } + + assignment.witness(component.W(0), row) = assignment.witness(component.W(1), row) = 0; + + std::array sum = {0, 0}; + for (std::size_t i = 0; i < (component.rows_amount - 1) / 2; i++) { + // Filling the first row. + for (std::size_t j = 0; j < component.chunks_per_row - 1; j++) { + assignment.witness(component.W(j + 2), row) = + chunks[1][i * component.chunks_per_row + j]; + sum[1] *= (1 << component.chunk_size); + sum[1] += chunks[1][i * component.chunks_per_row + j]; + } + row++; + // Filling the second row. + assignment.witness(component.W(0), row) = chunks[1][i * component.chunks_per_row + + component.chunks_per_row - 1]; + sum[1] *= (1 << component.chunk_size); + sum[1] += chunks[1][i * component.chunks_per_row + component.chunks_per_row - 1]; + + for (std::size_t j = 0; j < component.chunks_per_row; j++) { + assignment.witness(component.W(j + 1), row) = + chunks[0][i * component.chunks_per_row + j]; + sum[0] *= (1 << component.chunk_size); + sum[0] += chunks[0][i * component.chunks_per_row + j]; + } + row++; + // Filling the sums + assignment.witness(component.W(0), row) = sum[0]; + assignment.witness(component.W(1), row) = sum[1]; + } + assignment.witness(component.W(2), row) = y; + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::GREATER_THAN: + if (!component.needs_bonus_row) { + assignment.witness(component.W(3), row) = diff != 0 ? diff.inversed() : 0; + } else { + row++; + assignment.witness(component.W(0), row) = diff != 0 ? diff.inversed() : 0; + } + break; + case comparison_mode::LESS_EQUAL: + case comparison_mode::GREATER_EQUAL: + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + row++; + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + void generate_assignments_constants( + const plonk_comparison_checked + &component, + assignment> + &assignment, + const typename plonk_comparison_checked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = 0; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_NON_NATIVE_COMPARISON_CHECKED_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_flag.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_flag.hpp new file mode 100644 index 000000000..44dee4f73 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_flag.hpp @@ -0,0 +1,833 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_COMPARISON_FLAG_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_COMPARISON_FLAG_HPP + +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class comparison_flag; + + /* + Compares two field elements, which are both less than 2^{bits_amount}. This condition is checked. + Outputs a flag value, depending on the comparison result. + If you do not require a flag, use a more efficient comparison_fail component. + Takes one gate less if bits_amount is divisible by chunk_size. + + bits_amount should be less than BlueprintFieldType::modulus_bits. + This component can be used in multiple modes: + a) Outputs a flag, depending on comparison result: + 1 if x > y. + 0 if x = y, + -1 if x < y. + b) Outputs 0 if the comparison is false, 1 otherwise. + + If we desire a flag, the comparison is performed chunkwise. + Schematic representation of the component's primary gate for WitnessesAmount = 3: + + +--+--+--+ + |x |y |f0| + +--+--+--+ + |c |d |t | + +--+--+--+ + |x |y |f1| + +--+--+--+ + + x and y are chunk sums for the respective inputs, starting from 0. + The top x, y are previous chunk sums, bottom are the current ones. + f are the comparison bit flags, t are temporary variables, which are used to calculate f. + c and d denote the chunks for x and y respectively. + This gate is repeated as often as needed to compare all chunks. + + For bigger WitnessesAmount we can fit more 4-cell comparison chunks. An example for + WitnessesAmount = 15: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |x |y |f0|t1|f1|t2|f2|t3|f3|t4|f4|t5|f5|t6|f6| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |c7|d7|t7|c1|d1|c2|d2|c3|d3|c4|d4|c5|d5|c6|d6| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |x |y |f7| | | | | | | | | | | | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + Numbers here denote the chunk number, from most significant bits to least significant bits. + Essentially, each comparison but the last (which is knight move shaped) is a 4-cell chunk + (plus the previous f value). + + If WitnessesAmount divides 2, we leave a column free to the right, as we are unable to fit + an additional comparison. + */ + template + class comparison_flag>: + public plonk_component { + + static std::size_t comparisons_per_gate_instance_internal(std::size_t witness_amount) { + return 1 + (witness_amount - 3) / 2; + } + + static std::size_t bits_per_gate_instance_internal(std::size_t witness_amount) { + return comparisons_per_gate_instance_internal(witness_amount) * chunk_size; + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount) { + return (bits_amount + bits_per_gate_instance_internal(witness_amount) - 1) / + bits_per_gate_instance_internal(witness_amount) * 2 + + 1 + needs_bonus_row_internal(witness_amount); + } + + static std::size_t gate_instances_internal(std::size_t witness_amount, std::size_t bits_amount) { + return (rows_amount_internal(witness_amount, bits_amount) - 1) / 2; + } + + static std::size_t padded_chunks_internal(std::size_t witness_amount, std::size_t bits_amount) { + return gate_instances_internal(witness_amount, bits_amount) * + comparisons_per_gate_instance_internal(witness_amount); + } + + static std::size_t padding_bits_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padded_chunks_internal(witness_amount, bits_amount) * chunk_size - bits_amount; + } + + static std::size_t padding_size_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padding_bits_internal(witness_amount, bits_amount) / chunk_size; + } + + static std::size_t gates_amount_internal(std::size_t bits_amount) { + return 2 + (bits_amount % chunk_size > 0); + } + + static std::size_t needs_bonus_row_internal(std::size_t witness_amount) { + return witness_amount <= 3; + } + + void check_params(std::size_t bits_amount, comparison_mode mode) const { + BLUEPRINT_RELEASE_ASSERT(bits_amount > 0 && bits_amount < BlueprintFieldType::modulus_bits ); + BLUEPRINT_RELEASE_ASSERT(mode == comparison_mode::LESS_THAN || + mode == comparison_mode::GREATER_THAN || + mode == comparison_mode::LESS_EQUAL || + mode == comparison_mode::GREATER_EQUAL || + mode == comparison_mode::FLAG); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + std::size_t bits_amount; + comparison_mode mode; + + gate_manifest_type(std::size_t witness_amount_, std::size_t bits_amount_, + comparison_mode mode_) + : witness_amount(witness_amount_), bits_amount(bits_amount_), mode(mode_) {} + + std::uint32_t gates_amount() const override { + return comparison_flag::gates_amount_internal(bits_amount); + } + + bool operator<(const component_gate_manifest* other) const override{ + const gate_manifest_type* other_casted = dynamic_cast(other); + return witness_amount < other_casted->witness_amount || + (witness_amount == other_casted->witness_amount && + bits_amount < other_casted->bits_amount) || + (witness_amount == other_casted->witness_amount && + bits_amount == other_casted->bits_amount && + mode < other_casted->mode); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount, bits_amount, mode)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, comparison_mode mode) { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param(3, std::max(4, (bits_amount + 28 - 1) / 28 + 1))), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + return rows_amount_internal(witness_amount, bits_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + /* + It's CRITICAL that these three variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + const comparison_mode mode; + constexpr static const std::size_t chunk_size = 2; + /* Do NOT move the above variables! */ + + const std::size_t comparisons_per_gate_instance = + comparisons_per_gate_instance_internal(this->witness_amount()); + const std::size_t bits_per_gate_instance = + bits_per_gate_instance_internal(this->witness_amount()); + const bool needs_bonus_row = needs_bonus_row_internal(this->witness_amount()); + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + + const std::size_t gate_instances = gate_instances_internal(this->witness_amount(), bits_amount); + const std::size_t padded_chunks = padded_chunks_internal(this->witness_amount(), bits_amount); + const std::size_t padding_bits = padding_bits_internal(this->witness_amount(), bits_amount); + const std::size_t padding_size = padding_size_internal(this->witness_amount(), bits_amount); + + const std::size_t gates_amount = gates_amount_internal(bits_amount); + const std::string component_name = "comparison (==, !=)"; + + struct input_type { + var x, y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var flag; + result_type(const comparison_flag &component, std::size_t start_row_index) { + std::size_t outuput_w = component.needs_bonus_row ? 0 : 3; + flag = var(component.W(outuput_w), start_row_index + component.rows_amount - 1, false); + } + result_type(const comparison_flag &component, std::size_t start_row_index, bool skip) { + flag = var(component.W(0), start_row_index, false); + } + + std::vector> all_vars() { + return {flag}; + } + }; + + template + explicit comparison_flag(ContainerType witness, std::size_t bits_amount_, comparison_mode mode_): + component_type(witness, {}, {}, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) {}; + + template + comparison_flag(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t bits_amount_, comparison_mode mode_): + component_type(witness, constant, public_input, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + comparison_flag( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t bits_amount_, comparison_mode mode_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_) { + + check_params(bits_amount, mode); + }; + + static typename BlueprintFieldType::value_type calculate(std::size_t witness_amount, + typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y, + std::size_t arg_bits_amount, comparison_mode arg_mode) { + + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using chunk_type = std::uint8_t; + + auto chunk_size = 2; + auto padding_bits = padding_bits_internal(witness_amount, arg_bits_amount); + auto padded_chunks = padded_chunks_internal(witness_amount, arg_bits_amount); + auto comparisons_per_gate_instance = comparisons_per_gate_instance_internal(witness_amount); + auto gate_instances = gate_instances_internal(witness_amount, arg_bits_amount); + + BOOST_ASSERT(chunk_size <= 8); + + std::array integrals = {integral_type(x.data), integral_type(y.data)}; + + std::array, 2> bits; + for (std::size_t i = 0; i < 2; i++) { + std::fill(bits[i].begin(), bits[i].end(), false); + bits[i].resize(arg_bits_amount + padding_bits); + + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack(integrals[i], status); + std::copy(bytes_all.end() - arg_bits_amount, bytes_all.end(), + bits[i].begin() + padding_bits); + assert(status == nil::marshalling::status_type::success); + } + + BOOST_ASSERT(padded_chunks * chunk_size == + arg_bits_amount + padding_bits); + std::array, 2> chunks; + for (std::size_t i = 0; i < 2; i++) { + chunks[i].resize(padded_chunks); + for (std::size_t j = 0; j < padded_chunks; j++) { + chunk_type chunk_value = 0; + for (std::size_t k = 0; k < std::size_t(chunk_size); k++) { + chunk_value <<= 1; + chunk_value |= bits[i][j * chunk_size + k]; + } + chunks[i][j] = chunk_value; + } + } + + value_type greater_val = - value_type(2).pow(chunk_size), + last_flag = 0; + std::array sum = {0, 0}; + + for (std::size_t i = 0; i < gate_instances; i++) { + std::array current_chunk = {0, 0}; + std::size_t base_idx, chunk_idx; + + // I basically used lambdas instead of macros to cut down on code reuse. + // Note that the captures are by reference! + auto calculate_flag = [¤t_chunk, &greater_val](value_type last_flag) { + return last_flag != 0 ? last_flag + : (current_chunk[0] > current_chunk[1] ? 1 + : current_chunk[0] == current_chunk[1] ? 0 : greater_val); + }; + // WARNING: this one is impure! But the code after it gets to look nicer. + auto place_chunk_pair = [¤t_chunk, &chunks, &sum, &chunk_size]( + std::size_t base_idx, std::size_t chunk_idx) { + for (std::size_t k = 0; k < 2; k++) { + current_chunk[k] = chunks[k][chunk_idx]; + sum[k] *= (1 << chunk_size); + sum[k] += current_chunk[k]; + } + }; + + for (std::size_t j = 0; j < comparisons_per_gate_instance - 1; j++) { + base_idx = 3 + j * 2; + chunk_idx = i * comparisons_per_gate_instance + j; + + place_chunk_pair(base_idx, chunk_idx); + last_flag = calculate_flag(last_flag); + } + // Last chunk + base_idx = 0; + chunk_idx = i * comparisons_per_gate_instance + + comparisons_per_gate_instance - 1; + + place_chunk_pair(base_idx, chunk_idx); + last_flag = calculate_flag(last_flag); + } + value_type output; + switch (arg_mode) { + case comparison_mode::FLAG: + output = last_flag != greater_val ? last_flag : -1; + break; + case comparison_mode::LESS_THAN: + output = last_flag == greater_val; + break; + case comparison_mode::LESS_EQUAL: + output = (last_flag == greater_val) || (last_flag == 0); + break; + case comparison_mode::GREATER_THAN: + output = last_flag == 1; + break; + case comparison_mode::GREATER_EQUAL: + output = (last_flag == 1) || (last_flag == 0); + break; + } + + return output; + } + }; + + template + using plonk_comparison_flag = + comparison_flag>; + + template + typename plonk_comparison_flag::result_type + generate_circuit( + const plonk_comparison_flag + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector selector_indices = + generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_indices[0], start_row_index + 1, + start_row_index + component.rows_amount - 2 - component.needs_bonus_row, 2); + + assignment.enable_selector(selector_indices[1], start_row_index + component.rows_amount - 1); + + if (component.bits_amount % component.chunk_size != 0) { + if (selector_indices.size() != 3) { + std::cerr << "Internal error: comparison_flag component returned the wrong selector amount." + << std::endl; + std::abort(); + } + assignment.enable_selector(selector_indices[2], start_row_index + 1); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constants(component, assignment, instance_input, start_row_index); + + return typename plonk_comparison_flag::result_type( + component, start_row_index); + } + + template + typename plonk_comparison_flag::result_type + generate_assignments( + const plonk_comparison_flag + &component, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_comparison_flag; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using chunk_type = std::uint8_t; + BOOST_ASSERT(component.chunk_size <= 8); + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + + std::array integrals = {integral_type(x.data), integral_type(y.data)}; + + std::array, 2> bits; + for (std::size_t i = 0; i < 2; i++) { + std::fill(bits[i].begin(), bits[i].end(), false); + bits[i].resize(component.bits_amount + component.padding_bits); + + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack(integrals[i], status); + std::copy(bytes_all.end() - component.bits_amount, bytes_all.end(), + bits[i].begin() + component.padding_bits); + assert(status == nil::marshalling::status_type::success); + } + + BOOST_ASSERT(component.padded_chunks * component.chunk_size == + component.bits_amount + component.padding_bits); + std::array, 2> chunks; + for (std::size_t i = 0; i < 2; i++) { + chunks[i].resize(component.padded_chunks); + for (std::size_t j = 0; j < component.padded_chunks; j++) { + chunk_type chunk_value = 0; + for (std::size_t k = 0; k < component.chunk_size; k++) { + chunk_value <<= 1; + chunk_value |= bits[i][j * component.chunk_size + k]; + } + chunks[i][j] = chunk_value; + } + } + + assignment.witness(component.W(0), row) = assignment.witness(component.W(1), row) + = assignment.witness(component.W(2), row) = 0; + + value_type greater_val = - value_type(2).pow(component.chunk_size), + last_flag = 0; + std::array sum = {0, 0}; + + for (std::size_t i = 0; i < component.gate_instances; i++) { + std::array current_chunk = {0, 0}; + std::size_t base_idx, chunk_idx; + + // I basically used lambdas instead of macros to cut down on code reuse. + // Note that the captures are by reference! + auto calculate_flag = [¤t_chunk, &greater_val](value_type last_flag) { + return last_flag != 0 ? last_flag + : (current_chunk[0] > current_chunk[1] ? 1 + : current_chunk[0] == current_chunk[1] ? 0 : greater_val); + }; + auto calculate_temp = [¤t_chunk](value_type last_flag) { + return last_flag != 0 ? last_flag : current_chunk[0] - current_chunk[1]; + }; + // WARNING: this one is impure! But the code after it gets to look nicer. + auto place_chunk_pair = [¤t_chunk, &chunks, &sum, &component, &row, &assignment]( + std::size_t base_idx, std::size_t chunk_idx) { + for (std::size_t k = 0; k < 2; k++) { + current_chunk[k] = chunks[k][chunk_idx]; + + assignment.witness(component.W(base_idx + k), row + 1) = current_chunk[k]; + sum[k] *= (1 << component.chunk_size); + sum[k] += current_chunk[k]; + } + }; + + for (std::size_t j = 0; j < component.comparisons_per_gate_instance - 1; j++) { + base_idx = 3 + j * 2; + chunk_idx = i * component.comparisons_per_gate_instance + j; + + place_chunk_pair(base_idx, chunk_idx); + assignment.witness(component.W(base_idx), row) = calculate_temp(last_flag); + assignment.witness(component.W(base_idx + 1), row) = last_flag = calculate_flag(last_flag); + } + // Last chunk + base_idx = 0; + chunk_idx = i * component.comparisons_per_gate_instance + + component.comparisons_per_gate_instance - 1; + + place_chunk_pair(base_idx, chunk_idx); + + assignment.witness(component.W(2), row + 1) = calculate_temp(last_flag); + assignment.witness(component.W(2), row + 2) = last_flag = calculate_flag(last_flag); + row += 2; + assignment.witness(component.W(0), row) = sum[0]; + assignment.witness(component.W(1), row) = sum[1]; + } + value_type output; + switch (component.mode) { + case comparison_mode::FLAG: + output = last_flag != greater_val ? last_flag : -1; + break; + case comparison_mode::LESS_THAN: + output = last_flag == greater_val; + break; + case comparison_mode::LESS_EQUAL: + output = (last_flag == greater_val) || (last_flag == 0); + break; + case comparison_mode::GREATER_THAN: + output = last_flag == 1; + break; + case comparison_mode::GREATER_EQUAL: + output = (last_flag == 1) || (last_flag == 0); + break; + } + if (!component.needs_bonus_row) { + assignment.witness(component.W(3), row) = output; + } else { + row++; + assignment.witness(component.W(0), row) = output; + } + row++; + + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_comparison_flag::result_type + generate_empty_assignments( + const plonk_comparison_flag + &component, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_comparison_flag; + using value_type = typename BlueprintFieldType::value_type; + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + + assignment.witness(component.W(0), start_row_index) = + component_type::calculate(component.witness_amount(), x, y, component.bits_amount, component.mode); + + return typename component_type::result_type(component, start_row_index, true); + } + + template + std::vector generate_gates( + const plonk_comparison_flag + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input) { + + using var = typename plonk_comparison_flag::var; + using value_type = typename BlueprintFieldType::value_type; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector selector_indices; + + value_type base_two = 2, + greater_val = -base_two.pow(component.chunk_size), + sum_shift = base_two.pow(component.chunk_size); + std::vector constraints; + + auto generate_chunk_size_constraint = [](var v, std::size_t size) { + constraint_type constraint = v; + for (std::size_t i = 1; i < std::size_t(1 << size); i++) { + constraint = constraint * (v - i); + } + return constraint; + }; + auto generate_flag_values_constraint = [&greater_val](var v) { + constraint_type constraint = v * (v - 1) * (v - greater_val); + return constraint; + }; + auto generate_t_update_rule = [&greater_val](var t, var f, var c, var d) { + constraint_type constraint = t - ((c - d) * (1 - f) * (f - greater_val) * + (-greater_val.inversed()) + f); + return constraint; + }; + auto generate_t_f_constraint = [&greater_val](var t, var f) { + constraint_type constraint = t * (f - 1) * (f - greater_val); + return constraint; + }; + auto generate_difference_constraint = [](var t, var f, std::size_t size) { + constraint_type constraint = t - f; + for (std::size_t i = 1; i < std::size_t(1 << size); i++) { + constraint = constraint * (t - f - i); + } + return constraint; + }; + + // Assert chunk size. + for (std::size_t i = 0; i < component.comparisons_per_gate_instance; i++) { + constraint_type chunk_range_constraint = + generate_chunk_size_constraint(var(component.W(2 * i + (i != 0)), 0, true), + component.chunk_size); + constraints.push_back(chunk_range_constraint); + + chunk_range_constraint = + generate_chunk_size_constraint(var(component.W(2 * i + (i != 0) + 1), 0, true), + component.chunk_size); + constraints.push_back(chunk_range_constraint); + } + // Assert flag values. + for (std::size_t i = 1; i < component.comparisons_per_gate_instance; i++) { + constraint_type flag_value_constraint = + generate_flag_values_constraint(var(component.W(2 + 2 * i), -1, true)); + constraints.push_back(flag_value_constraint); + } + constraint_type last_flag_value_constraint = + generate_flag_values_constraint(var(component.W(2), 1, true)); + constraints.push_back(last_flag_value_constraint); + // Assert temp and flag values update logic. + for (std::size_t i = 0; i < component.comparisons_per_gate_instance - 1; i++) { + var f_prev = var(component.W(2 + 2 * i), -1, true), + f_cur = var(component.W(3 + 2 * i + 1), -1, true), + t = var(component.W(3 + 2 * i), -1, true), + c = var(component.W(3 + 2 * i), 0, true), + d = var(component.W(3 + 2 * i + 1), 0, true); + constraint_type t_update_rule = generate_t_update_rule(t, f_prev, c, d); + constraints.push_back(t_update_rule); + + constraint_type t_f_constraint = generate_t_f_constraint(t, f_cur); + constraints.push_back(t_f_constraint); + + constraint_type difference_constraint = + generate_difference_constraint(t, f_cur, component.chunk_size); + constraints.push_back(difference_constraint); + } + var last_f_prev = var(component.W(2 + 2 * (component.comparisons_per_gate_instance - 1)), -1, true), + last_f_cur = var(component.W(2), 1, true), + last_t = var(component.W(2), 0, true), + last_c = var(component.W(0), 0, true), + last_d = var(component.W(1), 0, true); + constraint_type last_t_update_rule = generate_t_update_rule(last_t, last_f_prev, last_c, last_d); + constraints.push_back(last_t_update_rule); + + constraint_type last_t_f_constraint = generate_t_f_constraint(last_t, last_f_cur); + constraints.push_back(last_t_f_constraint); + + constraint_type last_difference_constraint = + generate_difference_constraint(last_t, last_f_cur, component.chunk_size); + constraints.push_back(last_difference_constraint); + + // Assert chunk sums. + std::array sum_constraints; + for (std::size_t i = 0; i < 2; i++) { + sum_constraints[i] = var(component.W(i), -1, true); + } + for (std::size_t i = 0; i < component.comparisons_per_gate_instance - 1; i++) { + for (std::size_t j = 0; j < 2; j++) { + sum_constraints[j] = sum_shift * sum_constraints[j] + + var(component.W(3 + 2 * i + j), 0, true); + } + } + for (std::size_t j = 0; j < 2; j++) { + sum_constraints[j] = sum_shift * sum_constraints[j] + var(component.W(j), 0, true); + sum_constraints[j] = var(component.W(j), 1, true) - sum_constraints[j]; + + constraints.push_back(sum_constraints[j]); + } + + selector_indices.push_back(bp.add_gate(constraints)); + + constraint_type comparison_constraint; + var flag_var, output_var; + value_type g = greater_val, + g_m_1 = greater_val - 1, + g_g_m_1 = greater_val * (greater_val - 1); + // All constraints below are the appropriate Lagrange interpolation polynomials. + if (!component.needs_bonus_row) { + flag_var = var(component.W(2), 0, true); + output_var = var(component.W(3), 0, true); + } else { + flag_var = var(component.W(2), -1, true); + output_var = var(component.W(0), 0, true); + } + switch (component.mode) { + case comparison_mode::FLAG: + // This converts flag {greater_val, 0, 1} to {-1, 0, 1}. + comparison_constraint = output_var - + ((- 2 * g_g_m_1.inversed() - g.inversed()) * flag_var * flag_var + + (2 * g_g_m_1.inversed() + g.inversed() + 1) * flag_var); + + break; + case comparison_mode::GREATER_THAN: + // This converts flag {greater_val, 0, 1} to {0, 0, 1}. + comparison_constraint = output_var + flag_var * (flag_var - g) * g_m_1.inversed(); + break; + case comparison_mode::GREATER_EQUAL: + // This converts flag {greater_val, 0, 1} to {0, 1, 1}. + comparison_constraint = output_var + + (flag_var - g) * (flag_var - (1 - g)) * g_g_m_1.inversed(); + break; + case comparison_mode::LESS_THAN: + // This converts flag {greater_val, 0, 1} to {1, 0, 0}. + comparison_constraint = output_var - flag_var * (flag_var - 1) * g_g_m_1.inversed(); + break; + case comparison_mode::LESS_EQUAL: + // This converts flag {greater_val, 0, 1} to {1, 1, 0}. + comparison_constraint = output_var - (1 - flag_var * (flag_var - g) * (-g_m_1).inversed()); + break; + } + selector_indices.push_back(bp.add_gate(comparison_constraint)); + + if (component.bits_amount % component.chunk_size == 0) return selector_indices; + // If bits_amount is not divisible by chunk size, the first chunk of x/y should be constrained to + // be less than 2^{bits_amount % component.chunk_size} + // These constraints cannot be skipped: otherwise, + // we don't check that x and y fit into 2^{bits_amount}. + std::vector first_chunk_range_constraints; + + var size_constraint_var = var(component.W(3 + 2 * component.padding_size), 0, true); + constraint_type first_chunk_range_constraint = generate_chunk_size_constraint( + size_constraint_var, component.bits_amount % component.chunk_size); + first_chunk_range_constraints.push_back(first_chunk_range_constraint); + + size_constraint_var = var(component.W(3 + 2 * component.padding_size + 1), 0, true); + first_chunk_range_constraint = + generate_chunk_size_constraint(size_constraint_var, + component.bits_amount % component.chunk_size); + first_chunk_range_constraints.push_back(first_chunk_range_constraint); + + selector_indices.push_back(bp.add_gate(first_chunk_range_constraints)); + return selector_indices; + } + + template + void generate_copy_constraints( + const plonk_comparison_flag + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_comparison_flag::var; + + std::size_t row = start_row_index; + var zero(0, start_row_index, false, var::column_type::constant); + for (std::size_t i = 0; i < 3; i++) { + bp.add_copy_constraint({zero, var(component.W(i), row, false)}); + } + row++; + for (std::size_t i = 0; i < component.padding_size; i++) { + bp.add_copy_constraint({zero, var(component.W(3 + 2 * i), row, false)}); + bp.add_copy_constraint({zero, var(component.W(3 + 2 * i + 1), row, false)}); + } + row = start_row_index + component.rows_amount - 1 - component.needs_bonus_row; + bp.add_copy_constraint({instance_input.x, var(component.W(0), row, false)}); + bp.add_copy_constraint({instance_input.y, var(component.W(1), row, false)}); + } + + template + void generate_assignments_constants( + const plonk_comparison_flag + &component, + assignment> + &assignment, + const typename plonk_comparison_flag::input_type + &instance_input, + const std::uint32_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = 0; + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_COMPARISON_FLAG_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_mode.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_mode.hpp new file mode 100644 index 000000000..0ae7bd522 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_mode.hpp @@ -0,0 +1,44 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_COMPARISON_MODE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_COMPARISON_MODE_HPP + +namespace nil { + namespace blueprint { + namespace components { + // Ordering is important here: the logic differs for FLAG and non-FLAG modes. + // We use comparison with comparison_mode::FLAG in order to distinguish between the two cases. + enum comparison_mode { + FLAG, + LESS_THAN, + LESS_EQUAL, + GREATER_THAN, + GREATER_EQUAL, + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_DETAIL_COMPARISON_MODE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_unchecked.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_unchecked.hpp new file mode 100644 index 000000000..b0ea5fbf5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/comparison_unchecked.hpp @@ -0,0 +1,379 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_ALGEBRA_FIELDS_PLONK_NON_NATIVE_COMPARISON_UNCHECKED_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_ALGEBRA_FIELDS_PLONK_NON_NATIVE_COMPARISON_UNCHECKED_HPP + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class comparison_unchecked; + + /* + Compare x and y, failing if the comparsion is not satisfied. + x and y should fit into bits_amount bits. This isn't checked. + Additionally, bits_amount has to satisfy: bits_amount < modulus_bits - 1. + + In the less case this is implemented by just calling the range_check component for y - x, and checking + that y - x is not zero. Other cases are similar. + + Takes one gate less for bits_amount divisible by range_check's chunk_size -- because range_check takes + one gate less in this case. + + See comparison_flag if you want to access the result of the comparison instead. + */ + template + class comparison_unchecked> : + public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + static bool needs_bonus_row_internal(std::size_t witness_amount, comparison_mode mode) { + return witness_amount <= 3 && + (mode == comparison_mode::LESS_THAN || + mode == comparison_mode::GREATER_THAN); + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount, + comparison_mode mode) { + return range_check_component_type::get_rows_amount(witness_amount, bits_amount) + + 1 + needs_bonus_row_internal(witness_amount, mode); + } + + void check_params(std::size_t bits_amount, comparison_mode mode) const { + BLUEPRINT_RELEASE_ASSERT(bits_amount > 0 && bits_amount < BlueprintFieldType::modulus_bits - 1); + BLUEPRINT_RELEASE_ASSERT(mode == comparison_mode::LESS_THAN || + mode == comparison_mode::GREATER_THAN || + mode == comparison_mode::LESS_EQUAL || + mode == comparison_mode::GREATER_EQUAL); + } + public: + using component_type = plonk_component; + + using var = typename component_type::var; + + using range_check_component_type = + range_check>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + static const constexpr std::size_t clamp = 4; + std::size_t witness_amount; + comparison_mode mode; + + gate_manifest_type(std::size_t witness_amount_, comparison_mode mode_) + : witness_amount(std::min(witness_amount_, clamp)), mode(mode_) {} + + std::uint32_t gates_amount() const override { + return comparison_unchecked::gates_amount; + } + + bool operator<(const component_gate_manifest* other) const override { + const gate_manifest_type* casted_other = + dynamic_cast(other); + return (witness_amount < casted_other->witness_amount) || + (witness_amount == casted_other->witness_amount && mode < casted_other->mode); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount, mode)) + .merge_with( + range_check_component_type::get_gate_manifest(witness_amount, bits_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, comparison_mode mode) { + manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param(3, 5)), + false + ).merge_with(range_check_component_type::get_manifest(bits_amount)); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, + comparison_mode mode) { + return rows_amount_internal(witness_amount, bits_amount, mode); + } + + /* + It's CRITICAL that these two variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + const comparison_mode mode; + /* Do NOT move the above variables! */ + + range_check_component_type range_check_subcomponent; + + const bool needs_bonus_row = needs_bonus_row_internal(this->witness_amount(), mode); + + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), bits_amount, mode); + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + var x, y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + result_type(const comparison_unchecked &component, std::size_t start_row_index) {} + + std::vector> all_vars() { + return {}; + } + }; + + template + comparison_unchecked(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t bits_amount_, comparison_mode mode_): + component_type(witness, constant, public_input, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_), + range_check_subcomponent(witness, constant, public_input, bits_amount_ + ) { + check_params(bits_amount, mode); + }; + + comparison_unchecked( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t bits_amount_, comparison_mode mode_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_, mode_)), + bits_amount(bits_amount_), + mode(mode_), + range_check_subcomponent(witnesses, constants, public_inputs, bits_amount_) { + + check_params(bits_amount, mode); + }; + }; + + template + using plonk_comparison_unchecked = + comparison_unchecked>; + + template + std::size_t generate_gates( + const plonk_comparison_unchecked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_unchecked::input_type + &instance_input) { + + using var = typename plonk_comparison_unchecked::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector correctness_constraints; + constraint_type diff_constraint = var(component.W(2), 0, true) - var(component.W(1), 0, true) + + var(component.W(0), 0, true), + non_zero_constraint; + correctness_constraints.push_back(diff_constraint); + switch (component.mode) { + case comparison_mode::GREATER_EQUAL: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::LESS_THAN: + case comparison_mode::GREATER_THAN: + if (!component.needs_bonus_row) { + non_zero_constraint = var(component.W(2), 0, true) * var(component.W(3), 0, true) - 1; + } else { + non_zero_constraint = var(component.W(2), 0, true) * var(component.W(0), 1, true) - 1; + } + correctness_constraints.push_back(non_zero_constraint); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + + return bp.add_gate(correctness_constraints); + } + + template + void generate_copy_constraints( + const plonk_comparison_unchecked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_unchecked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_comparison_unchecked::var; + std::uint32_t row = start_row_index; + + row += component.rows_amount - 1 - component.needs_bonus_row; + var x_var = var(component.W(0), row, false), + y_var = var(component.W(1), row, false); + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::GREATER_THAN: + case comparison_mode::GREATER_EQUAL: + std::swap(x_var, y_var); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + bp.add_copy_constraint({instance_input.x, x_var}); + bp.add_copy_constraint({instance_input.y, y_var}); + } + + template + typename plonk_comparison_unchecked::result_type + generate_circuit( + const plonk_comparison_unchecked + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_comparison_unchecked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + + using var = typename plonk_comparison_unchecked::var; + generate_circuit(component.range_check_subcomponent, bp, assignment, + {var(component.W(2), start_row_index + component.range_check_subcomponent.rows_amount, false)}, + start_row_index); + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + std::size_t final_first_row = start_row_index + component.rows_amount - 1 - + component.needs_bonus_row; + assignment.enable_selector(selector_index, final_first_row); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_comparison_unchecked::result_type( + component, start_row_index); + } + + template + typename plonk_comparison_unchecked::result_type + generate_assignments( + const plonk_comparison_unchecked + &component, + assignment> + &assignment, + const typename plonk_comparison_unchecked::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_comparison_unchecked; + using value_type = typename BlueprintFieldType::value_type; + using var = typename component_type::var; + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::LESS_EQUAL: + break; + case comparison_mode::GREATER_THAN: + case comparison_mode::GREATER_EQUAL: + std::swap(x, y); + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + value_type diff = y - x; + + row += component.range_check_subcomponent.rows_amount; + + assignment.witness(component.W(0), row) = x; + assignment.witness(component.W(1), row) = y; + assignment.witness(component.W(2), row) = diff; + // Note that we fill rows below the current value of row! + generate_assignments(component.range_check_subcomponent, assignment, + {var(component.W(2), row)}, + start_row_index); + + switch (component.mode) { + case comparison_mode::LESS_THAN: + case comparison_mode::GREATER_THAN: + if (!component.needs_bonus_row) { + assignment.witness(component.W(3), row) = diff != 0 ? diff.inversed() : 0; + } else { + row++; + assignment.witness(component.W(0), row) = diff != 0 ? diff.inversed() : 0; + } + break; + case comparison_mode::LESS_EQUAL: + case comparison_mode::GREATER_EQUAL: + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + row++; + + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_ALGEBRA_FIELDS_PLONK_NON_NATIVE_COMPARISON_UNCHECKED_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/bit_builder_component.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/bit_builder_component.hpp new file mode 100644 index 000000000..71ca02a8c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/bit_builder_component.hpp @@ -0,0 +1,437 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BIT_BUILDER_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BIT_BUILDER_COMPONENT_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + enum bit_composition_mode { + LSB, + MSB, + }; + namespace detail { + /* + This is a component base, which is used for both bit_decomposition and + bit_composition components, as they are similar. + + Only the case of bits_amount < BlueprintFieldType::modulus_bits is supported. + + The composition part does not perfom checks that the inputs are actually bits, + unless the check is specifically enabled. + Bits should be passed here MSB-first; for LSB first, the derived component + should reverse the order of the bits. + + A schematic representation of the component gate. 'o' signifies an input bit. + 'x' signifies one of the sum bits. + '0' signifies padding with zeros. + Input bits are packed MSB first. + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |x|o|o|o|o|o|o|o|o|o|o|o|o|o|o| ] + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | -- The first 'x' is the previous sum. + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|o| | The second 'x' is constrained to be equal to the + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | (weighted) sum of 'o' bits and the first 'x'. + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|x| ] + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The first bit in the component is always padded '0' for check_bits = true, and input/padding bit + for check_bits = false. + + This requires padding up to nearest value of + k * (3 * WitnessesAmount - 2) for check_bits = true, + 3 * WitnessesAmount - 1 + k * (3 * WitnessesAmount - 2) for check_bits = false. + + An example for bits_amount = 80 (90 cells: 3 sum bits, 80 input bits, 7 padding bits): + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0|0|0|0|0|0|0|o|o|o|o|o|o|o|o| ] -- Note that here there is no difference between different + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | valuese of check_bits. + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|o| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|x| ] + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |x|o|o|o|o|o|o|o|o|o|o|o|o|o|o| ] -- The top left 'x' needs to be constrained to be equal to + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | the bottom right 'x' in the previous constraint block. + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|o| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + |o|o|o|o|o|o|o|o|o|o|o|o|o|o|x| ] + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + template + class bit_builder_component; + + template + class bit_builder_component< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + static std::size_t rows_amount_internal(std::size_t witness_amount, + std::size_t bits_amount, bool check_bits) { + std::size_t total_bits = bits_amount + + sum_bits_amount_internal(witness_amount, bits_amount, check_bits) + + padding_bits_amount_internal(witness_amount, bits_amount, check_bits); + return total_bits / witness_amount; + } + + /* + Returns bit position inside the packing, if the packing were done by filling each row in order, + without skipping any cells. + */ + std::pair straight_bit_position( + std::size_t start_row_index, std::size_t bit_num) const{ + + std::size_t row = start_row_index + bit_num / this->witness_amount(); + std::size_t col = bit_num % this->witness_amount(); + + return std::make_pair(row, col); + }; + + static std::size_t padding_bits_amount_internal(std::size_t witness_amount, + std::size_t bits_amount, bool check_bits) { + std::size_t bits_per_gate = bits_per_gate_internal(witness_amount); + std::size_t bits_per_first_gate = bits_per_first_gate_internal(witness_amount, check_bits); + if (check_bits) { + // in this case, first bit always has to be padded '0' + if (bits_amount > bits_per_gate) { + return 1 + (bits_per_gate - bits_amount % bits_per_gate) % bits_per_gate; + } else { + return bits_per_gate + 1 - bits_amount; + } + } else { + // in this case, first bit of component is a normal input bit + if (bits_amount > bits_per_first_gate) { + return (bits_per_gate - + (bits_amount - bits_per_first_gate) % bits_per_gate) % bits_per_gate; + } else { + return bits_per_first_gate - bits_amount; + } + } + } + + static std::size_t sum_bits_amount_internal(std::size_t witness_amount, + std::size_t bits_amount, bool check_bits) { + return 1 + (bits_amount + check_bits >= 2 ? bits_amount + check_bits - 2 : 0) / + bits_per_gate_internal(witness_amount) * 2; + } + + static std::size_t bits_per_gate_internal(std::size_t witness_amount) { + return 3 * witness_amount - 2; + } + + static std::size_t bits_per_first_gate_internal(std::size_t witness_amount, bool check_bits) { + std::size_t bits_per_gate = bits_per_gate_internal(witness_amount); + return check_bits ? bits_per_gate : bits_per_gate + 1; + } + + static std::size_t last_bit_gate_pos_internal(std::size_t witness_amount) { + return 3 * witness_amount - 1; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t bits_amount; + bool check_bits; + public: + gate_manifest_type(std::size_t bits_amount_, bool check_bits_) + : bits_amount(bits_amount_), check_bits(check_bits_) {} + + std::uint32_t gates_amount() const override { + return bit_builder_component::gates_amount; + } + + bool operator<(const component_gate_manifest *other) const override { + const gate_manifest_type* other_casted = dynamic_cast(other); + return bits_amount < other_casted->bits_amount || + (bits_amount == other_casted->bits_amount && check_bits < other_casted->check_bits); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + bool check_bits, + bit_composition_mode mode) { + gate_manifest manifest = gate_manifest(gate_manifest_type(bits_amount, check_bits)); + return manifest; + } + + static manifest_type get_manifest( + std::size_t bits_amount, bool check_bits, bit_composition_mode mode) { + manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param( + 3, std::max(4, bits_amount / 3 + 2))), + false + ); + return manifest; + } + + /* + It's CRITICAL that these two variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + const bool check_bits; + /* Do NOT move the above variables! */ + + const std::size_t bits_per_gate = bits_per_gate_internal(this->witness_amount()); + const std::size_t bits_per_first_gate = bits_per_first_gate_internal(this->witness_amount(), + check_bits); + const std::size_t last_bit_gate_pos = last_bit_gate_pos_internal(this->witness_amount()); + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount, check_bits); + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, bool check_bits) { + return rows_amount_internal(witness_amount, bits_amount, check_bits); + } + + template + explicit bit_builder_component(ContainerType witness, manifest_type manifest, + std::uint32_t bits_amount_, bool check_bits_) : + component_type(witness, std::array(), std::array(), + manifest), + bits_amount(bits_amount_), + check_bits(check_bits_) {}; + + template + bit_builder_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + manifest_type manifest, + std::uint32_t bits_amount_, bool check_bits_) : + component_type(witness, constant, public_input, manifest), + bits_amount(bits_amount_), + check_bits(check_bits_) {}; + + bit_builder_component( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + manifest_type manifest, + std::uint32_t bits_amount_, bool check_bits_) : + component_type(witnesses, constants, public_inputs, manifest), + bits_amount(bits_amount_), + check_bits(check_bits_) {}; + + + std::size_t padding_bits_amount() const { + return padding_bits_amount_internal(this->witness_amount(), bits_amount, check_bits); + } + + /* + Returns row and column pair for each input bit. + Packing is done MSB first; code in generate_assignments is responsible for reversing the order if necessary. + */ + std::pair bit_position( + std::size_t start_row_index, std::size_t bit_num) const { + std::size_t sum_bits = 0; + + sum_bits = (bit_num >= last_bit_gate_pos) * + (2 + (bit_num - last_bit_gate_pos) / bits_per_gate * 2); + + return straight_bit_position(start_row_index, bit_num + sum_bits); + } + + /* + Returns the amount of auxillary sum bits in the component. + */ + std::size_t sum_bits_amount() const { + return sum_bits_amount_internal(this->witness_amount(), bits_amount, check_bits); + } + + /* + Returns row and column pair for each auxillary sum bit. + */ + std::pair sum_bit_position( + std::size_t start_row_index, std::size_t sum_bit_num) const { + assert(sum_bit_num < sum_bits_amount()); + std::size_t bit_pos = 0; + + bit_pos = last_bit_gate_pos + (sum_bit_num / 2) * (3 * this->witness_amount()) + + (sum_bit_num % 2); + + return straight_bit_position(start_row_index, bit_pos); + } + }; + + template + using plonk_bit_builder = bit_builder_component>; + + template + void generate_assignments( + const plonk_bit_builder + &component, + assignment> + &assignment, + const std::vector &input_bits, + const std::uint32_t start_row_index) { + + assert(input_bits.size() == component.bits_amount); + + using field_value_type = typename BlueprintFieldType::value_type; + + std::size_t padding = 0; + for (; padding < component.padding_bits_amount(); padding++) { + auto bit_pos = component.bit_position(start_row_index, padding); + assignment.witness(component.W(bit_pos.second), bit_pos.first) = 0; + } + + for (std::size_t i = 0; i < input_bits.size(); i++) { + auto bit_pos = component.bit_position(start_row_index, padding + i); + assignment.witness(component.W(bit_pos.second), bit_pos.first) = static_cast(input_bits[i]); + } + + field_value_type sum = 0; + std::size_t bit_num = 0; + for (std::size_t i = 0; i < component.sum_bits_amount(); i += 2) { + + auto sum_bit_pos = component.sum_bit_position(start_row_index, i); + std::size_t max_bit_num = + component.last_bit_gate_pos - padding + (i / 2) * component.bits_per_gate; + + for (; bit_num < max_bit_num; bit_num++) { + sum = 2 * sum + static_cast(input_bits[bit_num]); + } + + assignment.witness(component.W(sum_bit_pos.second), sum_bit_pos.first) = sum; + if (i != component.sum_bits_amount() - 1) { + auto sum_bit_pos = component.sum_bit_position(start_row_index, i + 1); + assignment.witness(component.W(sum_bit_pos.second), sum_bit_pos.first) = sum; + } + } + } + + /* + The check_bits parameter should always be true for bit_decomposition: + we need to check that the output is actually bits. + It is optional for bit_composition: the input might have already been checked. + */ + template + std::size_t generate_gates( + const plonk_bit_builder + &component, + circuit> + &bp, + assignment> + &assignment) { + + using var = typename plonk_bit_builder::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + int row_idx = -1; + std::size_t col_idx = 1; + std::vector constraints; + constraint_type sum_constraint; + + sum_constraint = var(component.W(0), -1); + for (std::size_t bit_num = 1; bit_num < component.last_bit_gate_pos; bit_num++) { + + sum_constraint = 2 * sum_constraint + var(component.W(col_idx), row_idx); + col_idx++; + if (col_idx % component.witness_amount() == 0) { + row_idx++; + col_idx = 0; + } + } + + sum_constraint = sum_constraint - var(component.W(col_idx), row_idx); + constraints.push_back(sum_constraint); + + if (component.check_bits) { + row_idx = -1; + col_idx = 1; + + for (std::size_t bit_num = 1; bit_num < component.last_bit_gate_pos; bit_num++) { + + constraints.push_back(var(component.W(col_idx), row_idx) * + (1 - var(component.W(col_idx), row_idx))); + col_idx++; + if (col_idx % component.witness_amount() == 0) { + row_idx++; + col_idx = 0; + } + } + } + return bp.add_gate(constraints); + } + + template + void generate_assignments_constant( + const plonk_bit_builder + &component, + assignment> + &assignment, + const std::size_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = 0; + } + + template + void generate_circuit( + const plonk_bit_builder + &component, + circuit> + &bp, + assignment> + &assignment, + const std::size_t start_row_index) { + + std::size_t selector_index = + generate_gates(component, bp, assignment); + + std::size_t end_row_index = start_row_index + component.rows_amount - 2; + + assignment.enable_selector(selector_index, start_row_index + 1, end_row_index, 3); + + // copy constraints are specific to either bit_composition or bit_decomposition + // they are created in generate_circuit for corresponding classes + generate_assignments_constant( + component, assignment, start_row_index); + } + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BIT_BUILDER_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_lookup_op_component.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_lookup_op_component.hpp new file mode 100644 index 000000000..82c3343af --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_lookup_op_component.hpp @@ -0,0 +1,238 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_LOOKUP_OP_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_LOOKUP_OP_COMPONENT_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + /* + This is a generalized boolean operation component. + It abstracts boolean functions with 2 variables, when: + a) 2 + 1 < WitnessesAmount + b) The function is implemented as a single constraint. + No checks that arguments are boolean are performed. + */ + template + class boolean_lookup_op_component; + + template + class boolean_lookup_op_component> + : public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(2 + 1)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + + virtual crypto3::zk::snark::plonk_lookup_constraint op_lookup_constraint( + const std::array &witnesses, + circuit> + &bp + ) const = 0; + + virtual value_type result_assignment(const std::array &input_values) const = 0; + + struct input_type { + std::array input; + + input_type() = default; + input_type(std::initializer_list input) : input(input) {}; + + std::vector> all_vars() { + std::vector> result; + result.insert(result.end(), input.begin(), input.end()); + return result; + } + }; + + struct result_type { + var output; + + result_type(const boolean_lookup_op_component> + &component, + const std::uint32_t start_row_index) { + output = var(component.W(2), start_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit boolean_lookup_op_component(ContainerType witness, manifest_type manifest) : + component_type(witness, std::array(), std::array(), + manifest) {}; + + template + boolean_lookup_op_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, manifest_type manifest) : + component_type(witness, constant, public_input, manifest) {}; + + boolean_lookup_op_component(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + manifest_type manifest) : + component_type(witnesses, constants, public_inputs, manifest) {}; + }; + } // namespace detail + + using detail::boolean_lookup_op_component; + + template + using plonk_boolean_lookup_op_component = + boolean_lookup_op_component>; + + template + typename plonk_boolean_lookup_op_component::result_type + generate_assignments( + const plonk_boolean_lookup_op_component + &component, + assignment> + &assignment, + const typename plonk_boolean_lookup_op_component::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_boolean_lookup_op_component; + using value_type = typename BlueprintFieldType::value_type; + + std::uint32_t col_idx = 0; + std::array input_vals; + for (; col_idx < 2; col_idx++) { + assignment.witness(component.W(col_idx), start_row_index) = + input_vals[col_idx] = var_value(assignment, instance_input.input[col_idx]); + } + assignment.witness(component.W(col_idx), start_row_index) = component.result_assignment(input_vals); + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_boolean_lookup_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_lookup_op_component::input_type + &instance_input + ) { + + using var = typename plonk_boolean_lookup_op_component::var; + + std::array witnesses; + for (std::size_t col_idx = 0; col_idx < witnesses.size(); col_idx++) { + witnesses[col_idx] = var(component.W(col_idx), 0); + } + auto constraint = component.op_lookup_constraint(witnesses, bp); + auto selector_id = bp.add_lookup_gate({constraint}); + return selector_id; + } + + template + void generate_copy_constraints( + const plonk_boolean_lookup_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_lookup_op_component::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_boolean_lookup_op_component::var; + std::size_t row = start_row_index; + + for (std::size_t col_idx = 0; col_idx < 2; col_idx++) { + bp.add_copy_constraint({instance_input.input[col_idx], var(component.W(col_idx), (std::int32_t)(row), false)}); + } + } + + template + typename plonk_boolean_lookup_op_component::result_type + generate_circuit( + const plonk_boolean_lookup_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_lookup_op_component::input_type + &instance_input, + const std::size_t start_row_index + ) { + using component_type = plonk_boolean_lookup_op_component; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_LOOKUP_OP_COMPONENT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_op_component.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_op_component.hpp new file mode 100644 index 000000000..3faba9fc1 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/boolean_op_component.hpp @@ -0,0 +1,246 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_OP_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_OP_COMPONENT_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + /* + This is a generalized boolean operation component. + It abstracts boolean functions with ArgNum variables, when: + a) ArgNum + 1 < WitnessesAmount + b) The function is implemented as a single constraint. + No checks that arguments are boolean are performed. + */ + template + class boolean_op_component; + + template + class boolean_op_component, + ArgNum> + : public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + virtual ~boolean_op_component() = default; + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(ArgNum + 1)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const = 0; + + virtual value_type result_assignment(const std::array &input_values) const = 0; + + struct input_type { + std::array input; + + input_type() = default; + input_type(std::initializer_list input) : input(input) {}; + + std::vector> all_vars() { + std::vector> result; + result.insert(result.end(), input.begin(), input.end()); + return result; + } + }; + + struct result_type { + var output; + + result_type(const boolean_op_component, + ArgNum> &component, + const std::uint32_t start_row_index) { + output = var(component.W(ArgNum), start_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit boolean_op_component(ContainerType witness, manifest_type manifest) : + component_type(witness, std::array(), std::array(), + manifest) {}; + + template + boolean_op_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, manifest_type manifest) : + component_type(witness, constant, public_input, manifest) {}; + + boolean_op_component(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + manifest_type manifest) : + component_type(witnesses, constants, public_inputs, manifest) {}; + }; + } // namespace detail + + using detail::boolean_op_component; + + template + using plonk_boolean_op_component = + boolean_op_component, ArgNum>; + + template + typename plonk_boolean_op_component::result_type + generate_assignments( + const plonk_boolean_op_component + &component, + assignment> + &assignment, + const typename plonk_boolean_op_component::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_boolean_op_component; + using value_type = typename BlueprintFieldType::value_type; + + std::uint32_t col_idx = 0; + std::array input_vals; + for (; col_idx < ArgNum; col_idx++) { + assignment.witness(component.W(col_idx), start_row_index) = + input_vals[col_idx] = var_value(assignment, instance_input.input[col_idx]); + } + assignment.witness(component.W(col_idx), start_row_index) = component.result_assignment(input_vals); + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_boolean_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_op_component::input_type + &instance_input) { + + using var = typename plonk_boolean_op_component::var; + + std::array witnesses; + for (std::size_t col_idx = 0; col_idx < witnesses.size(); col_idx++) { + witnesses[col_idx] = var(component.W(col_idx), 0); + } + auto constraint = component.op_constraint(witnesses); + return bp.add_gate({constraint}); + } + + template + void generate_copy_constraints( + const plonk_boolean_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_op_component::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = + typename plonk_boolean_op_component::var; + + std::size_t row = start_row_index; + + for (std::size_t col_idx = 0; col_idx < ArgNum; col_idx++) { + bp.add_copy_constraint({instance_input.input[col_idx], + var(component.W(col_idx), (std::int32_t)(row), false)}); + } + } + + template + typename plonk_boolean_op_component::result_type + generate_circuit( + const plonk_boolean_op_component + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_boolean_op_component::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_boolean_op_component; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_BOOLEAN_OP_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/division_remainder.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/division_remainder.hpp new file mode 100644 index 000000000..c940d3d1c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/division_remainder.hpp @@ -0,0 +1,442 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_DIVISION_REMAINDER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_DIVISION_REMAINDER_HPP + +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + class division_remainder; + + /* + For x, y < 2^{bits_amount} bits, where bits_amount < modulus_bits / 2, we divide x by y: + x = qy + r, r < y, + outputting q and r. + If check_inputs = true, this checks that x and y satisfy x, y < 2^{bits_amount}. + */ + template + class division_remainder> : + public plonk_component { + + using value_type = typename BlueprintFieldType::value_type; + + static std::size_t range_check_amount_internal(bool check_inputs) { + return 2 + 2 * check_inputs; + } + + static bool needs_bonus_row_internal(std::size_t witness_amount) { + return witness_amount < 5; + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount, + bool check_inputs) { + return range_check_amount_internal(check_inputs) * + range_check_component_type::get_rows_amount(witness_amount, bits_amount) + + 1 + needs_bonus_row_internal(witness_amount); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + + using range_check_component_type = + range_check>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + static const constexpr std::size_t clamp = 5; + std::uint32_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) + : witness_amount(std::min(witness_amount_, clamp)) {} + + std::uint32_t gates_amount() const override { + return division_remainder::gates_amount; + } + + bool operator<(const component_gate_manifest *other) { + const gate_manifest_type *other_casted = + dynamic_cast(other); + return witness_amount < other_casted->witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount, + bool check_inputs) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)) + .merge_with(range_check_component_type::get_gate_manifest(witness_amount, bits_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount, bool check_inputs) { + manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param(3, 6)), + true + ).merge_with(range_check_component_type::get_manifest(bits_amount)); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount, + bool check_inputs) { + return rows_amount_internal(witness_amount, bits_amount, check_inputs); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + /* + It's CRITICAL that these two variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + const bool check_inputs; + /* Do NOT move the above variables! */ + + const std::size_t range_check_amount = range_check_amount_internal(check_inputs); + + std::vector range_checks; + + const bool needs_bonus_row = needs_bonus_row_internal(this->witness_amount()); + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount, check_inputs); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + constexpr static const std::size_t gates_amount = 1; + const std::string component_name = "native integer division remainder"; + + enum var_address { + X, Y, Q, R_, Y_MINUS_R + }; + + std::pair get_var_address( + var_address var_ad, std::size_t start_row_index) const{ + std::size_t row = start_row_index + var_ad / this->witness_amount(), + column = var_ad % this->witness_amount(); + return std::make_pair(row, column); + } + + var get_var_for_gate(var_address var_ad) const { + auto address = get_var_address(var_ad, 0); + return var(this->W(address.second), address.first, true); + } + + struct input_type { + var x, y; + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var quotient, remainder; + + result_type(const division_remainder &component, std::size_t start_row_index) { + std::pair + r_address = component.get_var_address(var_address::R_, start_row_index), + q_address = component.get_var_address(var_address::Q, start_row_index); + + quotient = var(component.W(q_address.second), q_address.first); + remainder = var(component.W(r_address.second), r_address.first); + } + + result_type(const division_remainder &component, std::size_t start_row_index, bool skip) { + quotient = var(component.W(0), start_row_index); + remainder = var(component.W(1), start_row_index); + } + + std::vector> all_vars() { + return {quotient, remainder}; + } + }; + + template + division_remainder(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t bits_amount_, bool check_inputs_): + component_type(witness, constant, public_input, get_manifest(bits_amount_, check_inputs_)), + bits_amount(bits_amount_), + check_inputs(check_inputs_), + range_checks(range_check_amount, range_check_component_type(witness, constant, + public_input, bits_amount_)) + {}; + + division_remainder( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t bits_amount_, bool check_inputs_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_, check_inputs_)), + bits_amount(bits_amount_), + check_inputs(check_inputs_), + range_checks(range_check_amount, range_check_component_type(witnesses, constants, + public_inputs, bits_amount_)) + {}; + + static std::array calculate( + std::array input) { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + integral_type x_integral = integral_type(input[0].data), + y_integral = integral_type(input[1].data); + integral_type q_integral = y_integral != 0 ? x_integral / y_integral : 0, + r_integral = y_integral != 0 ? x_integral % y_integral : 0; + return {value_type(q_integral), value_type(r_integral)}; + } + }; + + template + using plonk_division_remainder = + division_remainder>; + + template + std::size_t generate_gates( + const plonk_division_remainder + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_division_remainder::input_type + &instance_input) { + + using component_type = plonk_division_remainder; + using var = typename component_type::var; + using var_address = typename component_type::var_address; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + var x = component.get_var_for_gate(var_address::X), + y = component.get_var_for_gate(var_address::Y), + r = component.get_var_for_gate(var_address::R_), + q = component.get_var_for_gate(var_address::Q), + y_minus_r = component.get_var_for_gate(var_address::Y_MINUS_R); + + std::vector constraints; + constraint_type division_constraint = x - y * q - r; + constraints.push_back(division_constraint); + constraint_type y_minus_r_constraint = y - r - y_minus_r; + constraints.push_back(y_minus_r_constraint); + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_division_remainder + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_division_remainder::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_division_remainder; + using var = typename component_type::var; + using var_address = typename component_type::var_address; + + std::pair + x_address = component.get_var_address(var_address::X, start_row_index), + y_address = component.get_var_address(var_address::Y, start_row_index); + + bp.add_copy_constraint({instance_input.x, var(component.W(x_address.second), x_address.first, false)}); + bp.add_copy_constraint({instance_input.y, var(component.W(y_address.second), y_address.first, false)}); + } + + template + typename plonk_division_remainder::result_type + generate_circuit( + const plonk_division_remainder + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_division_remainder::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_division_remainder; + using var = typename component_type::var; + using var_address = typename component_type::var_address; + + std::pair + x_address = component.get_var_address(var_address::X, start_row_index), + y_address = component.get_var_address(var_address::Y, start_row_index), + q_address = component.get_var_address(var_address::Q, start_row_index), + y_minus_r_address = component.get_var_address(var_address::Y_MINUS_R, start_row_index); + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + row += 1 + component.needs_bonus_row; + + generate_circuit(component.range_checks[0], bp, assignment, + {var(component.W(y_minus_r_address.second), y_minus_r_address.first, false)}, row); + row += component.range_checks[0].rows_amount; + + generate_circuit(component.range_checks[1], bp, assignment, + {var(component.W(q_address.second), q_address.first, false)}, row); + row += component.range_checks[1].rows_amount; + + if (component.check_inputs) { + generate_circuit(component.range_checks[2], bp, assignment, + {var(component.W(x_address.second), x_address.first, false)}, row); + row += component.range_checks[2].rows_amount; + + generate_circuit(component.range_checks[3], bp, assignment, + {var(component.W(y_address.second), y_address.first, false)}, row); + row += component.range_checks[3].rows_amount; + } + + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_division_remainder::result_type + generate_assignments( + const plonk_division_remainder + &component, + assignment> + &assignment, + const typename plonk_division_remainder::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_division_remainder; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using var = typename component_type::var; + using var_address = typename component_type::var_address; + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + integral_type x_integral = integral_type(x.data), + y_integral = integral_type(y.data); + integral_type q_integral = y_integral != 0 ? x_integral / y_integral : 0, + r_integral = y_integral != 0 ? x_integral % y_integral : 0; + value_type q = value_type(q_integral), + r = value_type(r_integral); + + std::pair + x_address = component.get_var_address(var_address::X, start_row_index), + y_address = component.get_var_address(var_address::Y, start_row_index), + r_address = component.get_var_address(var_address::R_, start_row_index), + q_address = component.get_var_address(var_address::Q, start_row_index), + y_minus_r_address = component.get_var_address(var_address::Y_MINUS_R, start_row_index); + + assignment.witness(component.W(x_address.second), x_address.first) = x; + assignment.witness(component.W(y_address.second), y_address.first) = y; + assignment.witness(component.W(r_address.second), r_address.first) = r; + assignment.witness(component.W(q_address.second), q_address.first) = q; + assignment.witness(component.W(y_minus_r_address.second), y_minus_r_address.first) = y - r; + row += 1 + component.needs_bonus_row; + + generate_assignments(component.range_checks[0], assignment, + {var(component.W(y_minus_r_address.second), y_minus_r_address.first, false)}, row); + row += component.range_checks[0].rows_amount; + + generate_assignments(component.range_checks[1], assignment, + {var(component.W(q_address.second), q_address.first, false)}, row); + row += component.range_checks[1].rows_amount; + + if (component.check_inputs) { + generate_assignments(component.range_checks[2], assignment, + {var(component.W(x_address.second), x_address.first, false)}, row); + row += component.range_checks[2].rows_amount; + + generate_assignments(component.range_checks[3], assignment, + {var(component.W(y_address.second), y_address.first, false)}, row); + row += component.range_checks[3].rows_amount; + } + + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_division_remainder::result_type + generate_empty_assignments( + const plonk_division_remainder + &component, + assignment> + &assignment, + const typename plonk_division_remainder::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_division_remainder; + using value_type = typename BlueprintFieldType::value_type; + + value_type x = var_value(assignment, instance_input.x), + y = var_value(assignment, instance_input.y); + auto res = component_type::calculate({x, y}); + + assignment.witness(component.W(0), start_row_index) = res[0]; + assignment.witness(component.W(1), start_row_index) = res[1]; + + return typename component_type::result_type(component, start_row_index, true); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_NON_NATIVE_DIVISION_REMAINDER_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/equality_flag.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/equality_flag.hpp new file mode 100644 index 000000000..dfaa5305c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/equality_flag.hpp @@ -0,0 +1,236 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_EQUALITY_FLAG_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_EQUALITY_FLAG_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in Fp + // Output: 1 iff x == y, 0 otherwise + // Output is reversed if inequality = true. + // Basically runs a slightly optimized copy of division-or-zero component on [x - y]. + template + class equality_flag; + + template + class equality_flag, + BlueprintFieldType>: + public plonk_component { + + public: + using component_type = plonk_component; + + class gate_manifest_type : public component_gate_manifest { + public: + bool inequality; + + gate_manifest_type(bool inequality_) :inequality(inequality_) {} + + std::uint32_t gates_amount() const override { + return equality_flag::gates_amount; + } + + bool operator<(gate_manifest_type const& other) const { + return inequality < other.inequality; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + bool inequality) { + gate_manifest manifest = gate_manifest(gate_manifest_type(inequality)); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + bool inequality) { + return 1; + } + + bool inequality; + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), inequality); + const std::string component_name = "equaluty flag (returns 1 if x==y and 0 otherwise)"; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + static manifest_type get_manifest(bool inequality) { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(4)), + false + ); + return manifest; + } + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const equality_flag &component, std::uint32_t start_row_index) { + output = var(component.W(3), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + + template + equality_flag(ContainerType witness, bool inequality_): + component_type(witness, {}, {}, get_manifest(inequality_)), + inequality(inequality_) + {}; + + template + equality_flag(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, bool inequality_): + component_type(witness, constant, public_input, get_manifest(inequality_)), + inequality(inequality_) + {}; + + equality_flag(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs, + bool inequality_): + component_type(witnesses, constants, public_inputs, get_manifest(inequality_)), + inequality(inequality_) + {}; + }; + + template + using plonk_equality_flag = + equality_flag, + BlueprintFieldType>; + + template + typename plonk_equality_flag::result_type + generate_assignments( + const plonk_equality_flag &component, + assignment> &assignment, + const typename plonk_equality_flag::input_type instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + auto x_val = var_value(assignment, instance_input.x); + auto y_val = var_value(assignment, instance_input.y); + assignment.witness(component.W(0), j) = x_val; + assignment.witness(component.W(1), j) = y_val; + if (x_val == y_val) { + assignment.witness(component.W(2), j) = 0; + } else { + assignment.witness(component.W(2), j) = (x_val - y_val).inversed(); + } + assignment.witness(component.W(3), j) = x_val == y_val ? !component.inequality : component.inequality; + + return typename plonk_equality_flag::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_equality_flag &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_equality_flag::input_type &instance_input) { + + using var = typename plonk_equality_flag::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + var x_var = var(component.W(0), 0), + y_var = var(component.W(1), 0), + inv_or_zero = var(component.W(2), 0), + result = var(component.W(3), 0); + + auto constraint_1 = result * (result - 1); + auto constraint_2 = (x_var - y_var) * inv_or_zero + + (component.inequality ? constraint_type(-result) : constraint_type(result - 1)); + auto constraint_3 = (inv_or_zero - (x_var - y_var)) * + (component.inequality ? constraint_type(result - 1) : constraint_type(result)); + + return bp.add_gate({constraint_1, constraint_2, constraint_3}); + } + + template + void generate_copy_constraints( + const plonk_equality_flag &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_equality_flag::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_equality_flag::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + bp.add_copy_constraint({instance_input.y, component_y}); + } + + template + typename plonk_equality_flag::result_type + generate_circuit( + const plonk_equality_flag &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_equality_flag::input_type &instance_input, + const std::size_t start_row_index){ + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_equality_flag::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_EQUALITY_FLAG_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/logic_ops.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/logic_ops.hpp new file mode 100644 index 000000000..9e3d15d65 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/logic_ops.hpp @@ -0,0 +1,525 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP + +#include + +#include +#include +#include +#include + +#include + +using nil::blueprint::components::detail::boolean_op_component; + +namespace nil { + namespace blueprint { + namespace components { + + /* + The following logical operations do NOT perform any checks on the input values. + */ + + template + class logic_not; + + template + class logic_not> + : public boolean_op_component, + 1> { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 1>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_not::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_not"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return 1 - witnesses[0] - witnesses[1]; + } + + virtual value_type result_assignment(const std::array &input_values) const { + return 1 - input_values[0]; + } + + template + explicit logic_not(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_not(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_not(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + + template + class logic_and; + + template + class logic_and> + : public boolean_op_component, + 2> { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 2>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_and::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_and"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[2] - witnesses[0] * witnesses[1]; + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] * input_values[1]; + } + + template + explicit logic_and(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_and(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_and(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + + template + class logic_or; + + template + class logic_or> + : public boolean_op_component, 2> + { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 2>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_or::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_or"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[2] - (witnesses[0] + witnesses[1] - witnesses[0] * witnesses[1]); + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] + input_values[1] - input_values[0] * input_values[1]; + } + + template + explicit logic_or(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_or(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_or(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + + template + class logic_xor; + + template + class logic_xor> + : public boolean_op_component, 2> + { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 2>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_xor::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_xor"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[2] - (witnesses[0] + witnesses[1] - 2 * witnesses[0] * witnesses[1]); + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] + input_values[1] - 2 * input_values[0] * input_values[1]; + } + + template + explicit logic_xor(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_xor(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_xor(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + + template + class logic_nand; + + template + class logic_nand> + : public boolean_op_component, 2> + { + + constexpr static const std::uint32_t WitnessesAmount = 3; + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 2>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_nand::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_nand"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[2] - (1 - witnesses[0] * witnesses[1]); + } + + virtual value_type result_assignment(const std::array &input_values) const { + return 1 - input_values[0] * input_values[1]; + } + + template + explicit logic_nand(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_nand(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_nand(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + class logic_nor; + + template + class logic_nor> + : public boolean_op_component, 2> + { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 2>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return logic_nor::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "logic_nor"; + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[2] - (1 - (witnesses[0] + witnesses[1] - witnesses[0] * witnesses[1])); + } + + virtual value_type result_assignment(const std::array &input_values) const { + return 1 - (input_values[0] + input_values[1] - input_values[0] * input_values[1]); + } + + template + explicit logic_nor(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + logic_nor(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + logic_nor(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + // if (cond) then (a) else (b) + // expects cond to be a boolean + template + class select; + + template + class select> + : public boolean_op_component, + 3> { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_op_component, 3>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return select::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + + virtual crypto3::zk::snark::plonk_constraint + op_constraint(const std::array &witnesses) const { + return witnesses[3] - (witnesses[0] * witnesses[1] + (1 - witnesses[0]) * witnesses[2]); + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] == 0 ? input_values[2] : input_values[1]; + } + + template + explicit select(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + select(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + select(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/lookup_logic_ops.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/lookup_logic_ops.hpp new file mode 100644 index 000000000..0fb8aac8a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/lookup_logic_ops.hpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP + +#include + +#include +#include +#include +#include + +#include + +using nil::blueprint::components::detail::boolean_lookup_op_component; + +namespace nil { + namespace blueprint { + namespace components { + + /* + The following logical operations perform checks on that input values are boolean. + */ + + + template + class lookup_logic_and; + + template + class lookup_logic_and> + : public boolean_lookup_op_component> { + + using value_type = typename BlueprintFieldType::value_type; + public: + using component_type = + boolean_lookup_op_component>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return lookup_logic_and::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + + virtual crypto3::zk::snark::plonk_lookup_constraint op_lookup_constraint( + const std::array &witnesses, + circuit> + &bp + ) const { + crypto3::zk::snark::plonk_lookup_constraint result; + bp.reserve_table("binary_and_table/full"); + result.table_id = bp.get_reserved_indices().at("binary_and_table/full"); + result.lookup_input = {witnesses[0], witnesses[1], witnesses[2]}; + return result; + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] * input_values[1]; + } + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["binary_and_table/full"] = 0; // REQUIRED_TABLE + return lookup_tables; + } + + template + explicit lookup_logic_and(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + lookup_logic_and(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + lookup_logic_and(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + class lookup_logic_xor; + + template + class lookup_logic_xor> + : public boolean_lookup_op_component> { + + using value_type = typename BlueprintFieldType::value_type; + + public: + using component_type = + boolean_lookup_op_component>; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return lookup_logic_xor::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + return component_type::get_manifest(); + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return component_type::get_rows_amount(witness_amount); + } + + virtual crypto3::zk::snark::plonk_lookup_constraint op_lookup_constraint( + const std::array &witnesses, + circuit> + &bp + ) const { + crypto3::zk::snark::plonk_lookup_constraint result; + bp.reserve_table("binary_xor_table/full"); + result.table_id = bp.get_reserved_indices().at("binary_xor_table/full"); + result.lookup_input = {witnesses[0], witnesses[1], witnesses[2]}; + return result; + } + + virtual value_type result_assignment(const std::array &input_values) const { + return input_values[0] + input_values[1] - 2 * input_values[0] * input_values[1]; + } + + template + explicit lookup_logic_xor(ContainerType witness) : component_type(witness, get_manifest()) {}; + + template + lookup_logic_xor(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + lookup_logic_xor(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["binary_xor_table/full"] = 0; // REQUIRED_TABLE + return lookup_tables; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_logic_OPS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/multiplication.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/multiplication.hpp new file mode 100644 index 000000000..a36ecda9e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/multiplication.hpp @@ -0,0 +1,655 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_MULTIPLICATION_COMPONENT_9_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_MULTIPLICATION_COMPONENT_9_WIRES_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + /* + 1 non_native range for q + 2 q + 3 non-native range for r + 4 + 5 a0 a1 a2 a3 b0 b1 b2 b3 q0 + 6 q1 q2 q3 r0 r1 r2 r3 v0 v1 + 7 v00 v01 v02 v03 v10 v11 v12 v13 + + */ + template + class multiplication; + + template + class multiplication, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy> + : public plonk_component { + + using operating_field_type = crypto3::algebra::fields::curve25519_base_field; + using non_native_policy_type = basic_non_native_policy; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return 3 + 2 * range_type::get_rows_amount(witness_amount); + } + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using range_type = range, + typename crypto3::algebra::fields::curve25519_base_field, + non_native_policy_type>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return multiplication::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()).merge_with( + range_type::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(range_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t T = 257; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + static constexpr const std::size_t gates_amount = 1; + const std::string component_name = "non-native field multiplication"; + + struct input_type { + typename non_native_policy_type::template field::non_native_var_type A; + typename non_native_policy_type::template field::non_native_var_type B; + + std::vector> all_vars() { + return {A[0], A[1], A[2], A[3], B[0], B[1], B[2], B[3]}; + } + }; + + struct result_type { + typename non_native_policy_type::template field::non_native_var_type output; + + result_type(const multiplication &component, std::uint32_t start_row_index) { + output = {var(component.W(3), start_row_index + component.rows_amount - 2, false), + var(component.W(4), start_row_index + component.rows_amount - 2, false), + var(component.W(5), start_row_index + component.rows_amount - 2, false), + var(component.W(6), start_row_index + component.rows_amount - 2, false)}; + } + result_type(const multiplication &component, std::uint32_t start_row_index, bool skip) { + output = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3]}; + } + }; + + template + explicit multiplication(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + multiplication(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + multiplication(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static std::array + calculate(std::array a, + std::array b) { + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + using native_value_type = typename BlueprintFieldType::value_type; + using native_integral_type = typename BlueprintFieldType::integral_type; + using foreign_value_type = typename ed25519_field_type::value_type; + using foreign_integral_type = typename ed25519_field_type::integral_type; + using foreign_extended_integral_type = typename ed25519_field_type::extended_integral_type; + + foreign_integral_type base = 1; + native_integral_type pasta_base = 1; + foreign_extended_integral_type extended_base = 1; + foreign_value_type eddsa_a = + foreign_integral_type(a[0].data) + + foreign_integral_type(a[1].data) * (base << 66) + + foreign_integral_type(a[2].data) * (base << 132) + + foreign_integral_type(a[3].data) * (base << 198); + foreign_value_type eddsa_b = + foreign_integral_type(b[0].data) + + foreign_integral_type(b[1].data) * (base << 66) + + foreign_integral_type(b[2].data) * (base << 132) + + foreign_integral_type(b[3].data) * (base << 198); + foreign_value_type eddsa_r = eddsa_a * eddsa_b; + foreign_integral_type integral_eddsa_r = + foreign_integral_type(eddsa_r.data); + foreign_extended_integral_type eddsa_p = ed25519_field_type::modulus; + foreign_extended_integral_type integral_eddsa_q = + (foreign_extended_integral_type(foreign_integral_type(eddsa_a.data)) * + foreign_extended_integral_type(foreign_integral_type(eddsa_b.data)) - + foreign_extended_integral_type(foreign_integral_type(eddsa_r.data))) / + eddsa_p; + foreign_extended_integral_type pow = extended_base << 257; + foreign_extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + foreign_integral_type mask = (pasta_base << 66) - 1; + foreign_extended_integral_type extended_mask = mask; + r[0] = integral_eddsa_r & mask; + q[0] = integral_eddsa_q & extended_mask; + p[0] = minus_eddsa_p & extended_mask; + p[1] = (minus_eddsa_p >> 66) & extended_mask; + p[2] = (minus_eddsa_p >> 132) & extended_mask; + p[3] = (minus_eddsa_p >> 198) & extended_mask; + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & extended_mask; + q[i] = (integral_eddsa_q >> (66 * i)) & extended_mask; + } + std::array t; + t[0] = a[0] * b[0] + p[0] * q[0]; + t[1] = a[1] * b[0] + a[0] * b[1] + p[0] * q[1] + p[1] * q[0]; + t[2] = a[2] * b[0] + a[0] * b[2] + a[1] * b[1] + p[2] * q[0] + q[2] * p[0] + p[1] * q[1]; + t[3] = a[3] * b[0] + b[3] * a[0] + a[1] * b[2] + b[1] * a[2] + p[3] * q[0] + q[3] * p[0] + p[1] * q[2] + + q[1] * p[2]; + + native_value_type u0 = + t[0] - r[0] + t[1] * (pasta_base << 66) - r[1] * (pasta_base << 66); + + native_integral_type u0_integral = + native_integral_type(u0.data) >> 132; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & ((1 << 4) - 1); + + native_value_type u1 = t[2] - r[2] + t[3] * (pasta_base << 66) - + r[3] * (pasta_base << 66) + + native_value_type(u0_integral); + + native_integral_type u1_integral = + native_integral_type(u1.data) >> 125; + std::array u1_chunks; + u1_chunks[0] = u1_integral & ((1 << 22) - 1); + u1_chunks[1] = (u1_integral >> 22) & ((1 << 22) - 1); + u1_chunks[2] = (u1_integral >> 44) & ((1 << 22) - 1); + u1_chunks[3] = (u1_integral >> 66) & ((1 << 9) - 1); + + return {r[0], r[1], r[2], r[3]}; + } + }; + + template + using plonk_ed25519_multiplication = + multiplication, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy>; + + template + typename plonk_ed25519_multiplication::result_type + generate_assignments( + const plonk_ed25519_multiplication &component, + assignment> + &assignment, + const typename plonk_ed25519_multiplication::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + using var = typename plonk_ed25519_multiplication::var; + using component_type = plonk_ed25519_multiplication; + using range_type = typename component_type::range_type; + + using native_value_type = typename BlueprintFieldType::value_type; + using native_integral_type = typename BlueprintFieldType::integral_type; + using foreign_value_type = typename ed25519_field_type::value_type; + using foreign_integral_type = typename ed25519_field_type::integral_type; + using foreign_extended_integral_type = typename ed25519_field_type::extended_integral_type; + + + std::size_t row = start_row_index; + foreign_integral_type base = 1; + native_integral_type pasta_base = 1; + foreign_extended_integral_type extended_base = 1; + std::array a = { + native_integral_type(var_value(assignment, instance_input.A[0]).data), + native_integral_type(var_value(assignment, instance_input.A[1]).data), + native_integral_type(var_value(assignment, instance_input.A[2]).data), + native_integral_type(var_value(assignment, instance_input.A[3]).data)}; + foreign_value_type eddsa_a = + foreign_integral_type(a[0].data) + + foreign_integral_type(a[1].data) * (base << 66) + + foreign_integral_type(a[2].data) * (base << 132) + + foreign_integral_type(a[3].data) * (base << 198); + std::array b = { + native_integral_type(var_value(assignment, instance_input.B[0]).data), + native_integral_type(var_value(assignment, instance_input.B[1]).data), + native_integral_type(var_value(assignment, instance_input.B[2]).data), + native_integral_type(var_value(assignment, instance_input.B[3]).data)}; + foreign_value_type eddsa_b = + foreign_integral_type(b[0].data) + + foreign_integral_type(b[1].data) * (base << 66) + + foreign_integral_type(b[2].data) * (base << 132) + + foreign_integral_type(b[3].data) * (base << 198); + foreign_value_type eddsa_r = eddsa_a * eddsa_b; + foreign_integral_type integral_eddsa_r = + foreign_integral_type(eddsa_r.data); + foreign_extended_integral_type eddsa_p = ed25519_field_type::modulus; + foreign_extended_integral_type integral_eddsa_q = + (foreign_extended_integral_type(foreign_integral_type(eddsa_a.data)) * + foreign_extended_integral_type(foreign_integral_type(eddsa_b.data)) - + foreign_extended_integral_type(foreign_integral_type(eddsa_r.data))) / + eddsa_p; + foreign_extended_integral_type pow = extended_base << 257; + foreign_extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + foreign_integral_type mask = (pasta_base << 66) - 1; + foreign_extended_integral_type extended_mask = mask; + r[0] = integral_eddsa_r & mask; + q[0] = integral_eddsa_q & extended_mask; + p[0] = minus_eddsa_p & extended_mask; + p[1] = minus_eddsa_p >> 66 & extended_mask; + p[2] = minus_eddsa_p >> 132 & extended_mask; + p[3] = minus_eddsa_p >> 198 & extended_mask; + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & (mask); + q[i] = (integral_eddsa_q >> (66 * i)) & (extended_mask); + } + std::array t; + t[0] = a[0] * b[0] + p[0] * q[0]; + t[1] = a[1] * b[0] + a[0] * b[1] + p[0] * q[1] + p[1] * q[0]; + t[2] = a[2] * b[0] + a[0] * b[2] + a[1] * b[1] + p[2] * q[0] + q[2] * p[0] + p[1] * q[1]; + t[3] = a[3] * b[0] + b[3] * a[0] + a[1] * b[2] + b[1] * a[2] + p[3] * q[0] + q[3] * p[0] + p[1] * q[2] + + q[1] * p[2]; + + native_value_type u0 = + t[0] - r[0] + t[1] * (pasta_base << 66) - r[1] * (pasta_base << 66); + + native_integral_type u0_integral = + native_integral_type(u0.data) >> 132; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & ((1 << 4) - 1); + + native_value_type u1 = t[2] - r[2] + t[3] * (pasta_base << 66) - + r[3] * (pasta_base << 66) + + native_value_type(u0_integral); + + native_integral_type u1_integral = + native_integral_type(u1.data) >> 125; + std::array u1_chunks; + u1_chunks[0] = u1_integral & ((1 << 22) - 1); + u1_chunks[1] = (u1_integral >> 22) & ((1 << 22) - 1); + u1_chunks[2] = (u1_integral >> 44) & ((1 << 22) - 1); + u1_chunks[3] = (u1_integral >> 66) & ((1 << 9) - 1); + + assignment.witness(component.W(0), row + 4) = a[0]; + assignment.witness(component.W(1), row + 4) = a[1]; + assignment.witness(component.W(2), row + 4) = a[2]; + assignment.witness(component.W(3), row + 4) = a[3]; + assignment.witness(component.W(4), row + 4) = b[0]; + assignment.witness(component.W(5), row + 4) = b[1]; + assignment.witness(component.W(6), row + 4) = b[2]; + assignment.witness(component.W(7), row + 4) = b[3]; + assignment.witness(component.W(8), row + 4) = q[0]; + assignment.witness(component.W(0), row + 5) = q[1]; + assignment.witness(component.W(1), row + 5) = q[2]; + assignment.witness(component.W(2), row + 5) = q[3]; + assignment.witness(component.W(3), row + 5) = r[0]; + assignment.witness(component.W(4), row + 5) = r[1]; + assignment.witness(component.W(5), row + 5) = r[2]; + assignment.witness(component.W(6), row + 5) = r[3]; + assignment.witness(component.W(7), row + 5) = native_value_type(u0_integral); + assignment.witness(component.W(8), row + 5) = native_value_type(u1_integral); + assignment.witness(component.W(0), row + 6) = u0_chunks[0]; + assignment.witness(component.W(1), row + 6) = u0_chunks[1]; + assignment.witness(component.W(2), row + 6) = u0_chunks[2]; + assignment.witness(component.W(3), row + 6) = u0_chunks[3]; + assignment.witness(component.W(4), row + 6) = u1_chunks[0]; + assignment.witness(component.W(5), row + 6) = u1_chunks[1]; + assignment.witness(component.W(6), row + 6) = u1_chunks[2]; + assignment.witness(component.W(7), row + 6) = u1_chunks[3]; + + range_type range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + + typename range_type::input_type non_range_input_q = { + var(component.W(8), row + 4, false), var(component.W(0), row + 5, false), + var(component.W(1), row + 5, false), var(component.W(2), row + 5, false)}; + + generate_assignments(range_component_instance, assignment, non_range_input_q, row); + + typename range_type::input_type non_range_input_r = { + var(component.W(3), row + 5, false), var(component.W(4), row + 5, false), + var(component.W(5), row + 5, false), var(component.W(6), row + 5, false)}; + + generate_assignments(range_component_instance, assignment, non_range_input_r, row + 2); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_ed25519_multiplication::result_type + generate_empty_assignments( + const plonk_ed25519_multiplication &component, + assignment> + &assignment, + const typename plonk_ed25519_multiplication::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_ed25519_multiplication; + + using native_value_type = typename BlueprintFieldType::value_type; + using native_integral_type = typename BlueprintFieldType::integral_type; + + std::array a = { + native_integral_type(var_value(assignment, instance_input.A[0]).data), + native_integral_type(var_value(assignment, instance_input.A[1]).data), + native_integral_type(var_value(assignment, instance_input.A[2]).data), + native_integral_type(var_value(assignment, instance_input.A[3]).data)}; + std::array b = { + native_integral_type(var_value(assignment, instance_input.B[0]).data), + native_integral_type(var_value(assignment, instance_input.B[1]).data), + native_integral_type(var_value(assignment, instance_input.B[2]).data), + native_integral_type(var_value(assignment, instance_input.B[3]).data)}; + + auto r = component_type::calculate(a, b); + assignment.witness(component.W(0), start_row_index) = r[0]; + assignment.witness(component.W(1), start_row_index) = r[1]; + assignment.witness(component.W(2), start_row_index) = r[2]; + assignment.witness(component.W(3), start_row_index) = r[3]; + + return typename component_type::result_type(component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_ed25519_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_multiplication::input_type + &instance_input) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + using var = typename plonk_ed25519_multiplication::var; + + using native_value_type = typename BlueprintFieldType::value_type; + using native_integral_type = typename BlueprintFieldType::integral_type; + using foreign_integral_type = typename ed25519_field_type::integral_type; + using foreign_extended_integral_type = typename ed25519_field_type::extended_integral_type; + + native_integral_type base = 1; + foreign_extended_integral_type extended_base = 1; + foreign_extended_integral_type eddsa_p = ed25519_field_type::modulus; + native_value_type pasta_eddsa_p = eddsa_p; + foreign_extended_integral_type pow = extended_base << 257; + foreign_extended_integral_type minus_eddsa_p = pow - eddsa_p; + std::array p; + foreign_integral_type mask = (base << 66) - 1; + foreign_extended_integral_type extended_mask = mask; + p[0] = minus_eddsa_p & extended_mask; + p[1] = minus_eddsa_p >> 66 & extended_mask; + p[2] = minus_eddsa_p >> 132 & extended_mask; + p[3] = minus_eddsa_p >> 198 & extended_mask; + + std::array, 5> t; + t[0] = var(component.W(0), -1) * var(component.W(4), -1) + p[0] * var(component.W(8), -1); + t[1] = var(component.W(1), -1) * var(component.W(4), -1) + + var(component.W(0), -1) * var(component.W(5), -1) + p[0] * var(component.W(0), 0) + + p[1] * var(component.W(8), -1); + t[2] = var(component.W(2), -1) * var(component.W(4), -1) + + var(component.W(0), -1) * var(component.W(6), -1) + + var(component.W(1), -1) * var(component.W(5), -1) + p[2] * var(component.W(8), -1) + + var(component.W(1), 0) * p[0] + p[1] * var(component.W(0), 0); + t[3] = var(component.W(3), -1) * var(component.W(4), -1) + + var(component.W(7), -1) * var(component.W(0), -1) + + var(component.W(1), -1) * var(component.W(6), -1) + + var(component.W(5), -1) * var(component.W(2), -1) + p[3] * var(component.W(8), -1) + + var(component.W(2), 0) * p[0] + p[1] * var(component.W(1), 0) + var(component.W(0), 0) * p[2]; + auto constraint_1 = + var(component.W(7), 0) * (base << 132) - + (t[0] - var(component.W(3), 0) + t[1] * (base << 66) - var(component.W(4), 0) * (base << 66)); + auto constraint_2 = var(component.W(8), 0) * (base << 125) - + (t[2] - var(component.W(5), 0) + t[3] * (base << 66) - + var(component.W(6), 0) * (base << 66) + var(component.W(7), 0)); + auto constraint_3 = var(component.W(7), 0) - + (var(component.W(0), +1) + var(component.W(1), +1) * (1 << 22) + + var(component.W(2), +1) * (base << 44) + + var(component.W(3), +1) * (base << 66)); + auto constraint_4 = var(component.W(8), 0) - + (var(component.W(4), +1) + var(component.W(5), +1) * (1 << 22) + + var(component.W(6), +1) * (base << 44) + + var(component.W(7), +1) * (base << 66)); + auto constraint_5 = + (var(component.W(0), -1) + var(component.W(1), -1) * (base << 66) + + var(component.W(2), -1) * (base << 132) + var(component.W(3), -1) * (base << 198)) * + (var(component.W(4), -1) + var(component.W(5), -1) * (base << 66) + + var(component.W(6), -1) * (base << 132) + var(component.W(7), -1) * (base << 198)) - + ((var(component.W(8), -1) + var(component.W(0), 0) * (base << 66) + + var(component.W(1), 0) * (base << 132) + var(component.W(2), 0) * (base << 198)) * + pasta_eddsa_p + + (var(component.W(3), 0) + var(component.W(4), 0) * (base << 66) + + var(component.W(5), 0) * (base << 132) + var(component.W(6), 0) * (base << 198))); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4, constraint_5}); + } + + template + void generate_copy_constraints( + const plonk_ed25519_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_multiplication::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_ed25519_multiplication::var; + + std::size_t row = start_row_index; + + bp.add_copy_constraint({var(component.W(0), row + 4, false), instance_input.A[0]}); + bp.add_copy_constraint({var(component.W(1), row + 4, false), instance_input.A[1]}); + bp.add_copy_constraint({var(component.W(2), row + 4, false), instance_input.A[2]}); + bp.add_copy_constraint({var(component.W(3), row + 4, false), instance_input.A[3]}); + bp.add_copy_constraint({var(component.W(4), row + 4, false), instance_input.B[0]}); + bp.add_copy_constraint({var(component.W(5), row + 4, false), instance_input.B[1]}); + bp.add_copy_constraint({var(component.W(6), row + 4, false), instance_input.B[2]}); + bp.add_copy_constraint({var(component.W(7), row + 4, false), instance_input.B[3]}); + } + + template + typename plonk_ed25519_multiplication::result_type + generate_circuit( + const plonk_ed25519_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_multiplication::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_ed25519_multiplication; + using range_type = typename component_type::range_type; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::size_t j = start_row_index; + assignment.enable_selector(selector_index, j + 5); + + generate_copy_constraints(component, bp, assignment, instance_input, j); + + using var = typename plonk_ed25519_multiplication::var; + + range_type range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + + typename range_type::input_type non_range_input_q = { + var(component.W(8), j + 4, false), var(component.W(0), j + 5, false), + var(component.W(1), j + 5, false), var(component.W(2), j + 5, false)}; + + generate_circuit(range_component_instance, bp, assignment, non_range_input_q, j); + + typename range_type::input_type non_range_input_r = { + var(component.W(3), j + 5, false), var(component.W(4), j + 5, false), + var(component.W(5), j + 5, false), var(component.W(6), j + 5, false)}; + + generate_circuit(range_component_instance, bp, assignment, non_range_input_r, j + 2); + + return typename component_type::result_type(component, start_row_index); + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter> { + + using component_type = plonk_ed25519_multiplication; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + for (std::size_t i = 0; i < input.A.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.A[i]); + new_input.A[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.B.size(); i++) { + std::size_t new_idx = input.A.size() + i; + tmp_assignment.public_input(0, new_idx) = var_value(assignment, input.B[i]); + new_input.B[i] = var(0, new_idx, false, var::column_type::public_input); + } + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.A.size()) { + return input.A[variable.rotation]; + } else { + return input.B[variable.rotation - input.A.size()]; + } + } + }; + + template + class result_type_converter> { + + using component_type = plonk_ed25519_multiplication; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < new_result.output.size(); i++) { + new_result.output[i] = component.move_var( + old_result.output[i], + start_row_index + component.line_mapping[old_result.output[i].rotation], + instance_input); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_MULTIPLICATION_COMPONENT_9_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/range.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/range.hpp new file mode 100644 index 000000000..c49e4f724 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/range.hpp @@ -0,0 +1,297 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the RANGE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_RANGE_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_RANGE_EDWARD25519_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + /* a0 a1 a2 a3 a'0 a'1 a'2 a'3 xi + a'4 a'5 a'6 a'7 a'8 a'9 a'10 a'11 c + */ + template + class range; + + template + class range, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy> + : public plonk_component { + + using operating_field_type = crypto3::algebra::fields::curve25519_base_field; + using non_native_policy_type = basic_non_native_policy; + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return range::gates_amount; + } + }; + + static gate_manifest& get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 2; + } + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + typename non_native_policy_type::template field::non_native_var_type + input; // 66,66,66,57 bits + + std::vector> all_vars() { + return {input[0], input[1], input[2], input[3]}; + } + }; + + struct result_type { + result_type(const range &component, std::uint32_t start_row_index) {} + + std::vector> all_vars() { + return {}; + } + }; + + template + explicit range(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + range(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + range(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_ed25519_range = + range, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy>; + + template + typename plonk_ed25519_range::result_type + generate_assignments( + const plonk_ed25519_range &component, + assignment> + &assignment, + const typename plonk_ed25519_range::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + typename BlueprintFieldType::integral_type base = 1; + std::array ed25519_value = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.input[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.input[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.input[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.input[3]).data)}; + assignment.witness(component.W(0), row) = ed25519_value[0]; + assignment.witness(component.W(1), row) = ed25519_value[1]; + assignment.witness(component.W(2), row) = ed25519_value[2]; + assignment.witness(component.W(3), row) = ed25519_value[3]; + std::array range_chunks; + typename BlueprintFieldType::integral_type mask = 0; + typename BlueprintFieldType::value_type xi = 0; + for (std::size_t i = 0; i < 4; i++) { + for (std::size_t j = 0; j < 3; j++) { + if (i == 3) { + if (j == 2) { + mask = (base << 15) - 1; + range_chunks[9 + j] = (ed25519_value[i] >> (21 * j)) & mask; + xi += range_chunks[i * 3 + j] - (base << 15) + 1; + } else { + mask = (base << 21) - 1; + range_chunks[9 + j] = (ed25519_value[i] >> (21 * j)) & mask; + xi += range_chunks[i * 3 + j] - (base << 21) + 1; + } + } else { + mask = (1 << 22) - 1; + range_chunks[i * 3 + j] = (ed25519_value[i] >> (22 * j)) & mask; + if (i + j != 0) { + xi += range_chunks[i * 3 + j] - (base << 22) + 1; + } + } + } + } + if (xi != 0) { + xi = xi.inversed(); + } else { + xi = 0; + } + assignment.witness(component.W(4), row) = range_chunks[0]; + assignment.witness(component.W(5), row) = range_chunks[1]; + assignment.witness(component.W(6), row) = range_chunks[2]; + assignment.witness(component.W(7), row) = range_chunks[3]; + assignment.witness(component.W(8), row) = xi; + row++; + assignment.witness(component.W(0), row) = range_chunks[4]; + assignment.witness(component.W(1), row) = range_chunks[5]; + assignment.witness(component.W(2), row) = range_chunks[6]; + assignment.witness(component.W(3), row) = range_chunks[7]; + assignment.witness(component.W(4), row) = range_chunks[8]; + assignment.witness(component.W(5), row) = range_chunks[9]; + assignment.witness(component.W(6), row) = range_chunks[10]; + assignment.witness(component.W(7), row) = range_chunks[11]; + bool c = 1; + if (range_chunks[0] > (base << 22) - 20) { + c = 0; + } + assignment.witness(component.W(8), row) = c; + return typename plonk_ed25519_range::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_ed25519_range &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_range::input_type + &instance_input) { + + using var = typename plonk_ed25519_range::var; + + typename BlueprintFieldType::integral_type base = 1; + auto constraint_1 = + var(component.W(0), 0) - (var(component.W(4), 0) + var(component.W(5), 0) * (base << 22) + + var(component.W(6), 0) * (base << 44)); + auto constraint_2 = + var(component.W(1), 0) - (var(component.W(7), 0) + var(component.W(0), +1) * (base << 22) + + var(component.W(1), +1) * (base << 44)); + auto constraint_3 = + var(component.W(2), 0) - (var(component.W(2), +1) + var(component.W(3), +1) * (base << 22) + + var(component.W(4), +1) * (base << 44)); + auto constraint_4 = + var(component.W(3), 0) - (var(component.W(5), +1) + var(component.W(6), +1) * (base << 21) + + var(component.W(7), +1) * (base << 42)); + + crypto3::zk::snark::plonk_constraint sum = + var(component.W(5), 0) + var(component.W(6), 0) + var(component.W(7), 0) + var(component.W(0), +1) + + var(component.W(1), +1) + var(component.W(2), +1) + var(component.W(3), +1) + + var(component.W(4), +1) + var(component.W(5), +1) + var(component.W(6), +1) + + var(component.W(7), +1) - 2 * (base << 21) - 8 * (base << 22) - (base << 15) + 11; + auto constraint_5 = sum * (var(component.W(8), 0) * sum - 1); + auto constraint_6 = + var(component.W(8), 0) * sum + (1 - var(component.W(8), 0) * sum) * var(component.W(8), +1) - 1; + + return bp.add_gate({ + constraint_1, + constraint_2, + constraint_3, + constraint_4, + constraint_5, + constraint_6, + }); + } + + template + void generate_copy_constraints( + const plonk_ed25519_range &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_range::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_ed25519_range::var; + + std::size_t row = start_row_index; + if(var(component.W(0), static_cast(row), false) != instance_input.input[0]) + bp.add_copy_constraint({var(component.W(0), static_cast(row), false), instance_input.input[0]}); + if(var(component.W(1), static_cast(row), false) != instance_input.input[1]) + bp.add_copy_constraint({var(component.W(1), static_cast(row), false), instance_input.input[1]}); + if(var(component.W(2), static_cast(row), false) != instance_input.input[2]) + bp.add_copy_constraint({var(component.W(2), static_cast(row), false), instance_input.input[2]}); + if(var(component.W(3), static_cast(row), false) != instance_input.input[3]) + bp.add_copy_constraint({var(component.W(3), static_cast(row), false), instance_input.input[3]}); + } + + template + typename plonk_ed25519_range::result_type generate_circuit( + const plonk_ed25519_range &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_range::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::size_t j = start_row_index; + assignment.enable_selector(selector_index, j); + generate_copy_constraints(component, bp, assignment, instance_input, j); + return typename plonk_ed25519_range::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_REDUCTION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/reduction.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/reduction.hpp new file mode 100644 index 000000000..0f0659148 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/reduction.hpp @@ -0,0 +1,382 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the DECOMPOSITION component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_DECOMPOSITION_EDWARD25519_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_DECOMPOSITION_EDWARD25519_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class reduction; + + template + class reduction, + BlueprintFieldType, + basic_non_native_policy> + : public plonk_component { + + using operating_field_type = crypto3::algebra::fields::curve25519_scalar_field; + using non_native_policy_type = basic_non_native_policy; + + public: + using component_type = plonk_component; + + using var = typename component_type::var;using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return reduction::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 4; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + static const std::size_t gates_amount = 2; + const std::string component_name = "sha512 input preparation component"; + + struct input_type { + std::array k; + + std::vector> all_vars() { + return {k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7]}; + } + }; + + struct result_type { + typename non_native_policy_type::template field::non_native_var_type output; + + result_type(const reduction &component, std::uint32_t start_row_index) { + output = var(component.W(4), start_row_index + component.rows_amount - 3, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit reduction(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + reduction(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + reduction(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_reduction = + reduction, + BlueprintFieldType, + basic_non_native_policy>; + + template + typename plonk_reduction::result_type generate_assignments( + const plonk_reduction &component, + assignment> + &assignment, + const typename plonk_reduction::input_type instance_input, + const std::uint32_t start_row_index) { + + using ArithmetizationType = + crypto3::zk::snark::plonk_constraint_system; + + std::size_t row = start_row_index; + std::array data = { + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[0]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[1]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[2]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[3]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[4]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[5]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[6]).data), + typename ArithmetizationType::field_type::integral_type( + var_value(assignment, instance_input.k[7]).data)}; + + auto L = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular512; + auto k = 0x00_cppui_modular512; + auto shft = 0x01_cppui_modular512; + + for (std::size_t i = 0; i < 8; i++) { + assignment.witness(component.W(i), row + 3) = data[i]; + k = k + data[i] * (shft % L); + shft *= 0x10000000000000000_cppui_modular512; + } + + auto r = k % L; + auto q = (k / L); + + assignment.witness(component.W(3), row + 2) = q & 127; + assignment.witness(component.W(2), row + 2) = (q >> 7) & ((1 << (20)) - 1); + assignment.witness(component.W(1), row + 2) = (q >> 27) & ((1 << (20)) - 1); + assignment.witness(component.W(0), row + 2) = (q >> 47) & ((1 << (20)) - 1); + assignment.witness(component.W(4), row + 1) = r; + + assignment.witness(component.W(3), row + 1) = + typename ArithmetizationType::field_type::value_type((r) & ((1 << (13)) - 1)); + assignment.witness(component.W(2), row + 1) = + typename ArithmetizationType::field_type::value_type((r >> 13) & ((1 << (20)) - 1)); + assignment.witness(component.W(1), row + 1) = + typename ArithmetizationType::field_type::value_type((r >> 33) & ((1 << (20)) - 1)); + assignment.witness(component.W(0), row + 1) = + typename ArithmetizationType::field_type::value_type((r >> 53) & ((1 << (20)) - 1)); + assignment.witness(component.W(8), row) = + typename ArithmetizationType::field_type::value_type((r >> 73) & ((1 << (20)) - 1)); + assignment.witness(component.W(7), row) = + typename ArithmetizationType::field_type::value_type((r >> 93) & ((1 << (20)) - 1)); + assignment.witness(component.W(6), row) = + typename ArithmetizationType::field_type::value_type((r >> 113) & ((1 << (20)) - 1)); + assignment.witness(component.W(5), row) = + typename ArithmetizationType::field_type::value_type((r >> 133) & ((1 << (20)) - 1)); + assignment.witness(component.W(4), row) = + typename ArithmetizationType::field_type::value_type((r >> 153) & ((1 << (20)) - 1)); + assignment.witness(component.W(3), row) = + typename ArithmetizationType::field_type::value_type((r >> 173) & ((1 << (20)) - 1)); + assignment.witness(component.W(2), row) = + typename ArithmetizationType::field_type::value_type((r >> 193) & ((1 << (20)) - 1)); + assignment.witness(component.W(1), row) = + typename ArithmetizationType::field_type::value_type((r >> 213) & ((1 << (20)) - 1)); + assignment.witness(component.W(0), row) = + typename ArithmetizationType::field_type::value_type((r >> 233)); + + typename ArithmetizationType::field_type::value_type s_r = assignment.witness(component.W(0), row); + for (size_t i = 1; i < 9; i++) { + s_r += assignment.witness(component.W(i), row); + } + s_r += assignment.witness(component.W(0), row + 1) + assignment.witness(component.W(1), row + 1) + + assignment.witness(component.W(2), row + 1); + s_r -= 12 * ((1 << (20)) - 1); + crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type one = 1; + assignment.witness(component.W(5), row + 1) = s_r.inversed(); + + // if ((r) & ((1 << (13)) - 1) (L - (one << 252))) { \\TO-DO + assignment.witness(component.W(6), row + 1) = 1; + //} else { + //} + + auto c = data[0] + data[1] * ((one << 64)) + data[3] * (((one << 192) % L) & ((one << 73) - 1)) + + data[4] * (((one << 256) % L) & ((one << 73) - 1)) + + data[5] * (((one << 320) % L) & ((one << 73) - 1)) + + data[6] * (((one << 384) % L) & ((one << 73) - 1)) + + data[7] * (((one << 448) % L) & ((one << 73) - 1)) + q * ((one << 73) - + (crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type(L) % (one << 73))); + crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type r_extended = r; + auto d = (r_extended) & ((1 << (13)) - 1) + ((r_extended >> 13) & ((1 << (20)) - 1)) * (one << 13) + + ((r_extended >> 33) & ((1 << (20)) - 1)) * (one << 33) + + ((r_extended >> 53) & ((1 << (20)) - 1)) * (one << 53); + auto v = (c - d) >> 69; + + assignment.witness(component.W(8), row + 3) = v; + assignment.witness(component.W(4), row + 2) = v >> 56; + assignment.witness(component.W(5), row + 2) = (v >> 34) & ((1 << (22)) - 1); + assignment.witness(component.W(6), row + 2) = (v >> 12) & ((1 << (22)) - 1); + assignment.witness(component.W(7), row + 2) = v & 4095; + + return typename plonk_reduction::result_type( + component, start_row_index); + } + + template + std::array generate_gates( + const plonk_reduction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_reduction::input_type + &instance_input) { + + using var = typename plonk_reduction::var; + + auto L = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular512; + + auto constraint_1 = + var(component.W(0), +1) * 0x01_cppui_modular512 + var(component.W(1), +1) * 0x10000000000000000_cppui_modular512 + + var(component.W(2), +1) * 0x100000000000000000000000000000000_cppui_modular512 + + var(component.W(3), +1) * 0x1000000000000000000000000000000000000000000000000_cppui_modular512 + + var(component.W(4), +1) * + 0xffffffffffffffffffffffffffffffec6ef5bf4737dcf70d6ec31748d98951d_cppui_modular512 + + var(component.W(5), +1) * + 0xffffffffffffffeb2106215d086329a93b8c838d39a5e065812631a5cf5d3ed_cppui_modular512 + + var(component.W(6), +1) * + 0x2106215d086329a7ed9ce5a30a2c131b64a7f435e4fdd9539822129a02a6271_cppui_modular512 + + var(component.W(7), +1) * + 0xed9ce5a30a2c131b399411b7c309a3de24babbe38d1d7a979daf520a00acb65_cppui_modular512 - + var(component.W(4), -1) - + (var(component.W(0), 0) * 0x800000000000_cppui_modular512 + var(component.W(1), 0) * 0x8000000_cppui_modular512 + + var(component.W(2), 0) * 0x80_cppui_modular512 + var(component.W(3), 0)) * + L; + + auto s_r = var(component.W(0), -1) + var(component.W(1), -1) + var(component.W(2), -1) + + var(component.W(3), -1) + var(component.W(4), -1) + var(component.W(5), -1) + + var(component.W(6), -1) + var(component.W(7), -1) + var(component.W(8), -1) + + var(component.W(0), 0) + var(component.W(1), 0) + var(component.W(2), 0) - + 12 * ((1 << (20)) - 1); + + auto constraint_2 = + var(component.W(4), 0) - + (var(component.W(3), 0) + var(component.W(2), 0) * 0x2000_cppui_modular255 + + var(component.W(1), 0) * 0x200000000_cppui_modular255 + + var(component.W(0), 0) * 0x20000000000000_cppui_modular255 + + var(component.W(8), -1) * 0x2000000000000000000_cppui_modular255 + + var(component.W(7), -1) * 0x200000000000000000000000_cppui_modular255 + + var(component.W(6), -1) * 0x20000000000000000000000000000_cppui_modular255 + + var(component.W(5), -1) * 0x2000000000000000000000000000000000_cppui_modular255 + + var(component.W(4), -1) * 0x200000000000000000000000000000000000000_cppui_modular255 + + var(component.W(3), -1) * 0x20000000000000000000000000000000000000000000_cppui_modular255 + + var(component.W(2), -1) * 0x2000000000000000000000000000000000000000000000000_cppui_modular255 + + var(component.W(1), -1) * 0x200000000000000000000000000000000000000000000000000000_cppui_modular255 + + var(component.W(0), -1) * 0x20000000000000000000000000000000000000000000000000000000000_cppui_modular255); + + auto constraint_3 = (s_r) * ((s_r)*var(component.W(5), 0) - 1); + + auto constraint_4 = + (s_r) * var(component.W(5), 0) + + (1 - (s_r)*var(component.W(5), 0)) * var(component.W(6), 0) - 1; + crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type one = 1; + std::array m = { + ((one << 192) % L), ((one << 256) % L), ((one << 320) % L), ((one << 384) % L), ((one << 448) % L)}; + auto constraint_5 = + var(component.W(0), +1) + var(component.W(1), +1) * (one << 64) + + var(component.W(3), +1) * (m[0] & ((one << 73) - 1)) + + var(component.W(4), +1) * (m[1] & ((one << 73) - 1)) + + var(component.W(5), +1) * (m[2] & ((one << 73) - 1)) + + var(component.W(6), +1) * (m[3] & ((one << 73) - 1)) + + var(component.W(7), +1) * (m[4] & ((one << 73) - 1)) + + (var(component.W(0), 0) * 0x800000000000_cppui_modular512 + var(component.W(1), 0) * 0x8000000_cppui_modular512 + + var(component.W(2), 0) * 0x80_cppui_modular512 + var(component.W(3), 0)) * + ((one << 73) - (crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type(L) % (one << 73))) - + (var(component.W(3), -1) + var(component.W(2), -1) * (one << 13) + + var(component.W(1), -1) * (one << 33) + var(component.W(0), -1) * (one << 53)) - + var(component.W(8), +1) * (one << 69); + + auto constraint_6 = + var(component.W(8), +1) - + (var(component.W(4), 0) * (one << 56) + var(component.W(5), 0) * (one << 34) + + var(component.W(6), 0) * (one << 12) + var(component.W(7), 0)); + + std::size_t selector_index_1 = bp.add_gate({constraint_2, constraint_3, constraint_4}); + + std::size_t selector_index_2 = bp.add_gate({constraint_1, constraint_5, constraint_6}); + + return {selector_index_1, selector_index_2}; + } + + template + void generate_copy_constraints( + const plonk_reduction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_reduction::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_reduction::var; + + std::size_t row = start_row_index; + + bp.add_copy_constraint({var(component.W(0), row + 3, false), instance_input.k[0]}); + bp.add_copy_constraint({var(component.W(1), row + 3, false), instance_input.k[1]}); + bp.add_copy_constraint({var(component.W(2), row + 3, false), instance_input.k[2]}); + bp.add_copy_constraint({var(component.W(3), row + 3, false), instance_input.k[3]}); + bp.add_copy_constraint({var(component.W(4), row + 3, false), instance_input.k[4]}); + bp.add_copy_constraint({var(component.W(5), row + 3, false), instance_input.k[5]}); + bp.add_copy_constraint({var(component.W(6), row + 3, false), instance_input.k[6]}); + bp.add_copy_constraint({var(component.W(7), row + 3, false), instance_input.k[7]}); + } + + template + typename plonk_reduction::result_type generate_circuit( + const plonk_reduction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_reduction::input_type + &instance_input, + const std::size_t start_row_index) { + + std::array selector_indices = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_indices[0], start_row_index + 1); + assignment.enable_selector(selector_indices[1], start_row_index + 2); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_reduction::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_REDUCTION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/subtraction.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/subtraction.hpp new file mode 100644 index 000000000..78fa30877 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/non_native/subtraction.hpp @@ -0,0 +1,498 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_SUBTRACTION_COMPONENT_9_WIRES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_SUBTRACTION_COMPONENT_9_WIRES_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + /* + 1 non_native range for q + 2 q + 3 non-native range for r + 4 + 5 a0 a1 a2 a3 b0 b1 b2 b3 q0 + 6 q1 q2 q3 r0 r1 r2 r3 v0 v1 + 7 v00 v01 v02 v03 v10 v11 v12 v13 + + */ + template + class subtraction; + + template + class subtraction, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy> + : public plonk_component { + + using operating_field_type = crypto3::algebra::fields::curve25519_base_field; + using non_native_policy_type = basic_non_native_policy; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount) { + return 2 + range_type::get_rows_amount(witness_amount); + } + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using range_type = range, + typename crypto3::algebra::fields::curve25519_base_field, + non_native_policy_type>; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return subtraction::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(range_type::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(9)), + false + ).merge_with(range_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows_amount_internal(witness_amount); + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t T = 257; + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + constexpr static const std::size_t gates_amount = 1; + const std::string component_name = "non_native field subtraction"; + + + struct input_type { + typename non_native_policy_type::template field::non_native_var_type A; + typename non_native_policy_type::template field::non_native_var_type B; + + std::vector> all_vars() { + return {A[0], A[1], A[2], A[3], B[0], B[1], B[2], B[3]}; + } + }; + + struct result_type { + typename non_native_policy_type::template field::non_native_var_type output; + + result_type(const subtraction &component, std::uint32_t start_row_index) { + output = {var(component.W(0), start_row_index + 2, false), + var(component.W(1), start_row_index + 2, false), + var(component.W(2), start_row_index + 2, false), + var(component.W(3), start_row_index + 2, false)}; + } + result_type(const subtraction &component, std::uint32_t start_row_index, bool skip) { + output = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3]}; + } + }; + + template + explicit subtraction(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + subtraction(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + subtraction(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static std::array + calculate(std::array a, + std::array b) { + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + typename ed25519_field_type::integral_type base = 1; + typename BlueprintFieldType::integral_type pasta_base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + typename ed25519_field_type::value_type eddsa_a = + typename ed25519_field_type::integral_type(a[0].data) + + typename ed25519_field_type::integral_type(a[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(a[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(a[3].data) * (base << 198); + + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + typename ed25519_field_type::value_type eddsa_b = + (typename ed25519_field_type::integral_type(b[0].data) + + typename ed25519_field_type::integral_type(b[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(b[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(b[3].data) * (base << 198)); + + typename ed25519_field_type::value_type eddsa_r = eddsa_a - eddsa_b; + typename ed25519_field_type::integral_type integral_eddsa_r = + typename ed25519_field_type::integral_type(eddsa_r.data); + typename ed25519_field_type::extended_integral_type integral_eddsa_q = + (typename ed25519_field_type::extended_integral_type(ed25519_field_type::integral_type(eddsa_a.data)) + eddsa_p - + typename ed25519_field_type::extended_integral_type(ed25519_field_type::integral_type(eddsa_b.data)) - + typename ed25519_field_type::extended_integral_type(ed25519_field_type::integral_type(eddsa_r.data))) / + eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + + // We need to convert mask to ed25519_field_type::extended_integral_type, + // because you cannot use operator& for numbers of different sizes. + typename ed25519_field_type::integral_type mask = (pasta_base << 66) - 1; + typename ed25519_field_type::extended_integral_type extended_mask = mask; + r[0] = (integral_eddsa_r) & (mask); + q[0] = (integral_eddsa_q) & (extended_mask); + p[0] = (minus_eddsa_p) & (extended_mask); + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & (mask); + } + typename ed25519_field_type::extended_integral_type eddsa_p0 = + eddsa_p & extended_mask; + typename BlueprintFieldType::value_type t = a[0] + eddsa_p0 - b[0] + p[0] * q[0]; + + typename BlueprintFieldType::value_type u0 = t - r[0]; + + typename BlueprintFieldType::integral_type u0_integral = + typename BlueprintFieldType::integral_type(u0.data) >> 66; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & ((1 << 2) - 1); + + return {r[0], r[1], r[2], r[3]}; + } + }; + + template + using plonk_ed25519_subtraction = + subtraction, + typename crypto3::algebra::fields::curve25519_base_field, + basic_non_native_policy>; + + template + typename plonk_ed25519_subtraction::result_type + generate_assignments( + const plonk_ed25519_subtraction &component, + assignment> + &assignment, + const typename plonk_ed25519_subtraction::input_type + instance_input, + const std::uint32_t start_row_index) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + + using var = typename plonk_ed25519_subtraction::var; + + std::size_t row = start_row_index; + typename ed25519_field_type::integral_type base = 1; + typename BlueprintFieldType::integral_type pasta_base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + std::array a = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[3]).data)}; + typename ed25519_field_type::value_type eddsa_a = + typename ed25519_field_type::integral_type(a[0].data) + + typename ed25519_field_type::integral_type(a[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(a[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(a[3].data) * (base << 198); + + std::array b = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[3]).data)}; + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + typename ed25519_field_type::value_type eddsa_b = + (typename ed25519_field_type::integral_type(b[0].data) + + typename ed25519_field_type::integral_type(b[1].data) * (base << 66) + + typename ed25519_field_type::integral_type(b[2].data) * (base << 132) + + typename ed25519_field_type::integral_type(b[3].data) * (base << 198)); + + typename ed25519_field_type::value_type eddsa_r = eddsa_a - eddsa_b; + typename ed25519_field_type::integral_type integral_eddsa_r = + typename ed25519_field_type::integral_type(eddsa_r.data); + typename ed25519_field_type::extended_integral_type integral_eddsa_q = + (typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_a.data)) + eddsa_p - + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_b.data)) - + typename ed25519_field_type::extended_integral_type(typename ed25519_field_type::integral_type(eddsa_r.data))) / + eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + + std::array r; + std::array q; + std::array p; + + // We need to convert mask to ed25519_field_type::extended_integral_type, + // because you cannot use operator& for numbers of different sizes. + typename ed25519_field_type::integral_type mask = (pasta_base << 66) - 1; + typename ed25519_field_type::extended_integral_type extended_mask = mask; + r[0] = (integral_eddsa_r) & (mask); + q[0] = (integral_eddsa_q) & (extended_mask); + p[0] = (minus_eddsa_p) & (extended_mask); + for (std::size_t i = 1; i < 4; i++) { + r[i] = (integral_eddsa_r >> (66 * i)) & (mask); + } + typename ed25519_field_type::extended_integral_type eddsa_p0 = + eddsa_p & extended_mask; + typename BlueprintFieldType::value_type t = a[0] + eddsa_p0 - b[0] + p[0] * q[0]; + + typename BlueprintFieldType::value_type u0 = t - r[0]; + + typename BlueprintFieldType::integral_type u0_integral = + typename BlueprintFieldType::integral_type(u0.data) >> 66; + std::array u0_chunks; + + u0_chunks[0] = u0_integral & ((1 << 22) - 1); + u0_chunks[1] = (u0_integral >> 22) & ((1 << 22) - 1); + u0_chunks[2] = (u0_integral >> 44) & ((1 << 22) - 1); + u0_chunks[3] = (u0_integral >> 66) & ((1 << 2) - 1); + + assignment.witness(component.W(0), row + 1) = a[0]; + assignment.witness(component.W(1), row + 1) = b[0]; + assignment.witness(component.W(2), row + 1) = integral_eddsa_q; + assignment.witness(component.W(3), row + 1) = a[1]; + assignment.witness(component.W(4), row + 1) = a[2]; + assignment.witness(component.W(5), row + 1) = a[3]; + assignment.witness(component.W(6), row + 1) = b[1]; + assignment.witness(component.W(7), row + 1) = b[2]; + assignment.witness(component.W(8), row + 1) = b[3]; + assignment.witness(component.W(3), row) = u0_chunks[0]; + assignment.witness(component.W(4), row) = u0_chunks[1]; + assignment.witness(component.W(5), row) = u0_chunks[2]; + assignment.witness(component.W(6), row) = u0_chunks[3]; + assignment.witness(component.W(7), row) = typename BlueprintFieldType::value_type(u0_integral); + assignment.witness(component.W(0), row + 2) = r[0]; + assignment.witness(component.W(1), row + 2) = r[1]; + assignment.witness(component.W(2), row + 2) = r[2]; + assignment.witness(component.W(3), row + 2) = r[3]; + + using range_type = typename plonk_ed25519_subtraction::range_type; + + typename range_type::input_type range_input_r = { + var(0, row + 2, false), var(1, row + 2, false), var(2, row + 2, false), var(3, row + 2, false)}; + + range_type range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + generate_assignments(range_component_instance, assignment, range_input_r, row + 2); + + return typename plonk_ed25519_subtraction::result_type( + component, start_row_index); + } + + template + typename plonk_ed25519_subtraction::result_type + generate_empty_assignments( + const plonk_ed25519_subtraction &component, + assignment> + &assignment, + const typename plonk_ed25519_subtraction::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_ed25519_subtraction; + + std::array a = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A[3]).data)}; + + std::array b = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.B[3]).data)}; + + auto r = component_type::calculate(a, b); + assignment.witness(component.W(0), start_row_index) = r[0]; + assignment.witness(component.W(1), start_row_index) = r[1]; + assignment.witness(component.W(2), start_row_index) = r[2]; + assignment.witness(component.W(3), start_row_index) = r[3]; + + return typename plonk_ed25519_subtraction::result_type( + component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_ed25519_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_subtraction::input_type + &instance_input) { + + using ed25519_field_type = crypto3::algebra::fields::curve25519_base_field; + using var = typename plonk_ed25519_subtraction::var; + + typename BlueprintFieldType::integral_type base = 1; + typename ed25519_field_type::extended_integral_type extended_base = 1; + typename ed25519_field_type::extended_integral_type eddsa_p = ed25519_field_type::modulus; + typename BlueprintFieldType::value_type pasta_eddsa_p = eddsa_p; + typename ed25519_field_type::extended_integral_type pow = extended_base << 257; + typename ed25519_field_type::extended_integral_type minus_eddsa_p = pow - eddsa_p; + std::array p; + + // We need to convert mask to ed25519_field_type::extended_integral_type, + // because you cannot use operator& for numbers of different sizes. + typename ed25519_field_type::extended_integral_type mask = (base << 66) - 1; + typename ed25519_field_type::extended_integral_type eddsa_p0 = eddsa_p & mask; + p[0] = minus_eddsa_p & mask; + + auto t = var(component.W(0), 0) + p[0] * var(component.W(2), 0); + auto constraint_1 = + var(component.W(7), -1) * (base << 66) - + (t + eddsa_p0 - var(component.W(1), 0) - var(component.W(0), +1)); + auto constraint_2 = var(component.W(2), 0) * (var(component.W(2), 0) - 1); + auto constraint_3 = + var(component.W(7), -1) - + (var(component.W(3), -1) + var(component.W(4), -1) * (1 << 22) + + var(component.W(5), -1) * (base << 44) + + var(component.W(6), -1) * (base << 66)); + + auto constraint_4 = + (var(component.W(0), 0) + var(component.W(3), 0) * (base << 66) + + var(component.W(4), 0) * (base << 132) + var(component.W(5), 0) * (base << 198)) + + pasta_eddsa_p - + (var(component.W(1), 0) + var(component.W(6), 0) * (base << 66) + + var(component.W(7), 0) * (base << 132) + var(component.W(8), 0) * (base << 198)) - + pasta_eddsa_p * var(component.W(2), 0) - + (var(component.W(0), +1) + var(component.W(1), +1) * (base << 66) + + var(component.W(2), +1) * (base << 132) + var(component.W(3), +1) * (base << 198)); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + } + + template + void generate_copy_constraints( + const plonk_ed25519_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_subtraction::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_ed25519_subtraction::var; + + std::size_t row = start_row_index; + + bp.add_copy_constraint({var(component.W(0), row + 1, false), instance_input.A[0]}); + bp.add_copy_constraint({var(component.W(1), row + 1, false), instance_input.B[0]}); + bp.add_copy_constraint({var(component.W(3), row + 1, false), instance_input.A[1]}); + bp.add_copy_constraint({var(component.W(4), row + 1, false), instance_input.A[2]}); + bp.add_copy_constraint({var(component.W(5), row + 1, false), instance_input.A[3]}); + bp.add_copy_constraint({var(component.W(6), row + 1, false), instance_input.B[1]}); + bp.add_copy_constraint({var(component.W(7), row + 1, false), instance_input.B[2]}); + bp.add_copy_constraint({var(component.W(8), row + 1, false), instance_input.B[3]}); + } + + template + typename plonk_ed25519_subtraction::result_type + generate_circuit( + const plonk_ed25519_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_ed25519_subtraction::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::size_t j = start_row_index; + assignment.enable_selector(selector_index, j + 1); + + generate_copy_constraints(component, bp, assignment, instance_input, j); + + using var = typename plonk_ed25519_subtraction::var; + using range_type = typename plonk_ed25519_subtraction::range_type; + + typename range_type::input_type non_range_input_r = { + var(component.W(0), j + 2, false), var(component.W(1), j + 2, false), + var(component.W(2), j + 2, false), var(component.W(3), j + 2, false)}; + + range_type range_component_instance({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8)}, + {}, {}); + + generate_circuit(range_component_instance, bp, assignment, non_range_input_r, j + 2); + + return typename plonk_ed25519_subtraction::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_NON_NATIVE_FIELDS_EDDSA_SUM_MULTIPLICATION_COMPONENT_9_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/quadratic_interpolation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/quadratic_interpolation.hpp new file mode 100644 index 000000000..97cd21807 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/quadratic_interpolation.hpp @@ -0,0 +1,258 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for quadratic interpolation coefficients component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_QUADRATIC_INTER_COEFS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_QUADRATIC_INTER_COEFS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + // compute the determinant of a 3x3 matrix + template + T det3(std::array a) { + return a[0]*a[4]*a[8] + a[2]*a[3]*a[7] + a[1]*a[5]*a[6] + -a[2]*a[4]*a[6] - a[0]*a[5]*a[7] - a[1]*a[3]*a[8]; + } + } // namespace detail + + // quadratic interpolation with points (x0,z0), (x1,z1), (x2,z2) + // Input: x0, z0, x1, z1, x2, z2 + // Output: a0, a1, a2, such that the curve z = a0 + a1*x + a2*x^2 passes through all points + // checks that x0,x1,x2 are all distinct (otherwise a constraint is violated) + + using detail::det3; + + template + class quadratic_inter_coefs; + + template + class quadratic_inter_coefs, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return quadratic_inter_coefs::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(10)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + var x0, z0, x1, z1, x2, z2; + + std::vector> all_vars() { + return {x0, z0, x1, z1, x2, z2}; + } + }; + + struct result_type { + std::array output; + + result_type(const quadratic_inter_coefs &component, std::uint32_t start_row_index) { + output = { var(component.W(6), start_row_index, false, var::column_type::witness), + var(component.W(7), start_row_index, false, var::column_type::witness), + var(component.W(8), start_row_index, false, var::column_type::witness) }; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2]}; + } + }; + + template + explicit quadratic_inter_coefs(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + quadratic_inter_coefs(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + quadratic_inter_coefs( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_quadratic_inter_coefs = + quadratic_inter_coefs< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; + + template + typename plonk_quadratic_inter_coefs::result_type generate_assignments( + const plonk_quadratic_inter_coefs &component, + assignment> + &assignment, + const typename plonk_quadratic_inter_coefs::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + value_type x0 = var_value(assignment, instance_input.x0), + z0 = var_value(assignment, instance_input.z0), + x1 = var_value(assignment, instance_input.x1), + z1 = var_value(assignment, instance_input.z1), + x2 = var_value(assignment, instance_input.x2), + z2 = var_value(assignment, instance_input.z2); + value_type d = (x1 - x0) * (x2 - x0) * (x2 - x1); + const value_type one = 1; + + assignment.witness(component.W(0), start_row_index) = x0; + assignment.witness(component.W(1), start_row_index) = z0; + assignment.witness(component.W(2), start_row_index) = x1; + assignment.witness(component.W(3), start_row_index) = z1; + assignment.witness(component.W(4), start_row_index) = x2; + assignment.witness(component.W(5), start_row_index) = z2; + if (d != 0) { // normal case + auto d_inversed = d.inversed(); + assignment.witness(component.W(6), start_row_index) = det3(std::array{ z0, x0, x0*x0, + z1, x1, x1*x1, + z2, x2, x2*x2 }) * d_inversed; + assignment.witness(component.W(7), start_row_index) = det3(std::array{ one, z0, x0*x0, + one, z1, x1*x1, + one, z2, x2*x2 }) * d_inversed; + assignment.witness(component.W(8), start_row_index) = det3(std::array{ one, x0, z0, + one, x1, z1, + one, x2, z2 }) * d_inversed; + assignment.witness(component.W(9), start_row_index) = d_inversed; + } else { // just make some assignments that will fail + assignment.witness(component.W(6), start_row_index) = 0; + assignment.witness(component.W(7), start_row_index) = 0; + assignment.witness(component.W(8), start_row_index) = 0; + assignment.witness(component.W(9), start_row_index) = 0; + } + return typename plonk_quadratic_inter_coefs::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_quadratic_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_quadratic_inter_coefs::input_type + &instance_input) { + + using var = typename plonk_quadratic_inter_coefs::var; + var X0 = var(component.W(0), 0, true), + Z0 = var(component.W(1), 0, true), + X1 = var(component.W(2), 0, true), + Z1 = var(component.W(3), 0, true), + X2 = var(component.W(4), 0, true), + Z2 = var(component.W(5), 0, true), + A0 = var(component.W(6), 0, true), + A1 = var(component.W(7), 0, true), + A2 = var(component.W(8), 0, true), + I = var(component.W(9), 0, true); + + return bp.add_gate({A2*X0*X0 + A1*X0 + A0 - Z0, + A2*X1*X1 + A1*X1 + A0 - Z1, + A2*X2*X2 + A1*X2 + A0 - Z2, + (X1 - X0)*(X2 - X0)*(X2 - X1)*I - 1}); + } + + template + void generate_copy_constraints( + const plonk_quadratic_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_quadratic_inter_coefs::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_quadratic_inter_coefs::var; + + bp.add_copy_constraint({var(component.W(0), start_row_index, false), instance_input.x0}); + bp.add_copy_constraint({var(component.W(1), start_row_index, false), instance_input.z0}); + bp.add_copy_constraint({var(component.W(2), start_row_index, false), instance_input.x1}); + bp.add_copy_constraint({var(component.W(3), start_row_index, false), instance_input.z1}); + bp.add_copy_constraint({var(component.W(4), start_row_index, false), instance_input.x2}); + bp.add_copy_constraint({var(component.W(5), start_row_index, false), instance_input.z2}); + } + + template + typename plonk_quadratic_inter_coefs::result_type generate_circuit( + const plonk_quadratic_inter_coefs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_quadratic_inter_coefs::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, start_row_index); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_quadratic_inter_coefs::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_QUADRATIC_INTER_COEFS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/range_check.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/range_check.hpp new file mode 100644 index 000000000..6bf2e2289 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/range_check.hpp @@ -0,0 +1,407 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_FIELD_RANGE_CHECK_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_FIELD_RANGE_CHECK_HPP + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Constraint that x < 2**bits_amount. + // Works when bits_amount < modulus_bits. + // Input: x \in Fp + // Takes one gate less for bits_amount divisible by chunk_size. + template + class range_check; + + // The idea is split x in ConstraintDegree-bit chunks. + // Then, for each chunk x_i, we constraint that x_i < 2**ConstraintDegree. + // Thus, we get bits_amount/ConstraintDegree chunks that is proved to be less than 2**ConstraintDegree. + // We can aggreate them into one value < 2**bits_amount. + // Layout: + // W0 | W1 | ... | W14 + // 0 | ... | ... | ... + // sum | c_0 | ... | c_13 + // sum | c_14 | ... | c_27 + // ... + // The last sum = x + template + class range_check> : + public plonk_component { + + static std::size_t chunks_per_row_internal(std::size_t witness_amount) { + return witness_amount - reserved_columns; + } + + static std::size_t bits_per_row_internal(std::size_t witness_amount) { + return chunks_per_row_internal(witness_amount) * chunk_size; + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t bits_amount) { + // 1 + ceil(bits_amount / bits_per_row) + return 1 + (bits_amount + bits_per_row_internal(witness_amount) - 1) / + bits_per_row_internal(witness_amount); + } + + static std::size_t padded_chunks_internal(std::size_t witness_amount, std::size_t bits_amount) { + return (rows_amount_internal(witness_amount, bits_amount) - 1) * + chunks_per_row_internal(witness_amount); + } + + static std::size_t padding_size_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padded_chunks_internal(witness_amount, bits_amount) - + (bits_amount + chunk_size - 1) / chunk_size; + } + + static std::size_t padding_bits_internal(std::size_t witness_amount, std::size_t bits_amount) { + return padded_chunks_internal(witness_amount, bits_amount) * chunk_size - bits_amount; + } + + static std::size_t gates_amount_internal(std::size_t bits_amount) { + return 1 + (bits_amount % chunk_size == 0 ? 0 : 1); + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + std::size_t bits_amount; + + gate_manifest_type(std::size_t witness_amount_, std::size_t bits_amount_) + : witness_amount(witness_amount_), bits_amount(bits_amount_) {} + + std::uint32_t gates_amount() const override { + return range_check::gates_amount_internal(bits_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + std::size_t other_witness_amount = + dynamic_cast(other)->witness_amount; + return + (witness_amount < other_witness_amount) || + (witness_amount == other_witness_amount && + bits_amount < dynamic_cast(other)->bits_amount); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t bits_amount) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, bits_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t bits_amount) { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param(2, std::max(3, bits_amount / chunk_size + 2))), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t bits_amount) { + return rows_amount_internal(witness_amount, bits_amount); + } + + /* + It's CRITICAL that these three variables remain on top + Otherwise initialization goes in wrong order, leading to arbitrary values. + */ + const std::size_t bits_amount; + constexpr static const std::size_t chunk_size = 2; + constexpr static const std::size_t reserved_columns = 1; + /* Do NOT move the above variables! */ + + const std::size_t chunks_per_row = chunks_per_row_internal(this->witness_amount()); + const std::size_t bits_per_row = bits_per_row_internal(this->witness_amount()); + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), bits_amount); + + const std::size_t padded_chunks = padded_chunks_internal(this->witness_amount(), bits_amount); + const std::size_t padding_size = padding_size_internal(this->witness_amount(), bits_amount); + const std::size_t padding_bits = padding_bits_internal(this->witness_amount(), bits_amount); + const std::size_t gates_amount = gates_amount_internal(bits_amount); + + struct input_type { + var x; + + std::vector> all_vars() { + return {x}; + } + }; + + struct result_type { + result_type(const range_check &component, std::size_t start_row_index) {} + + std::vector> all_vars() { + return {}; + } + }; + + template + range_check(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t bits_amount_): + component_type(witness, constant, public_input, get_manifest(bits_amount_)), + bits_amount(bits_amount_) {} + + range_check( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t bits_amount_) : + component_type(witnesses, constants, public_inputs, get_manifest(bits_amount_)), + bits_amount(bits_amount_) {} + }; + + + template + using plonk_range_check = + range_check>; + + template + typename plonk_range_check::result_type + generate_circuit( + const plonk_range_check + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_range_check::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index[0], start_row_index + 1, + start_row_index + component.rows_amount - 1); + if ((component.bits_amount % component.chunk_size) != 0) { + if (selector_index.size() != 2) { + std::cerr << "Internal error: range_check component returned the wrong selector amount." + << std::endl; + std::abort(); + } + assignment.enable_selector(selector_index[1], start_row_index + 1); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constants(component, assignment, instance_input, start_row_index); + + return typename plonk_range_check::result_type( + component, start_row_index); + } + + template + typename plonk_range_check::result_type + generate_assignments( + const plonk_range_check + &component, + assignment> + &assignment, + const typename plonk_range_check::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_range_check; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using chunk_type = std::uint8_t; + BOOST_ASSERT(component.chunk_size <= 8); + + value_type x = var_value(assignment, instance_input.x); + + integral_type x_integral = integral_type(x.data); + + std::vector bits(component.bits_amount + component.padding_bits); + std::fill(bits.begin(), bits.end(), false); + { + nil::marshalling::status_type status; + std::array bytes_all = + nil::marshalling::pack(x_integral, status); + std::copy(bytes_all.end() - component.bits_amount, bytes_all.end(), + bits.begin() + component.padding_bits); + assert(status == nil::marshalling::status_type::success); + } + + BOOST_ASSERT(component.chunk_size <= 8); + + std::vector chunks(component.padded_chunks); + for (std::size_t i = 0; i < component.padded_chunks; i++) { + chunk_type chunk_value = 0; + for (std::size_t j = 0; j < component.chunk_size; j++) { + chunk_value <<= 1; + chunk_value |= bits[i * component.chunk_size + j]; + } + chunks[i] = chunk_value; + } + + assignment.witness(component.W(0), row) = 0; + row++; + + value_type sum = 0; + + for (std::size_t i = 0; i < component.rows_amount - 1; i++) { + for (std::size_t j = 0; j < component.chunks_per_row; j++) { + assignment.witness(component.W(0 + component.reserved_columns + j), row) = + chunks[i * component.chunks_per_row + j]; + sum *= (1 << component.chunk_size); + sum += chunks[i * component.chunks_per_row + j]; + } + assignment.witness(component.W(0), row) = sum; + row++; + } + + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_range_check + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_range_check::input_type + &instance_input) { + + using var = typename plonk_range_check::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + typename BlueprintFieldType::value_type base_two = 2; + + std::vector constraints; + + auto generate_chunk_size_constraint = [](var v, std::size_t size) { + constraint_type constraint = v; + for (std::size_t i = 1; i < (std::size_t(1) << size); i++) { + constraint = constraint * (v - i); + } + return constraint; + }; + + // assert chunk size + for (std::size_t i = 0; i < component.chunks_per_row; i++) { + constraint_type chunk_range_constraint = generate_chunk_size_constraint( + var(component.W(0 + component.reserved_columns + i), 0, true), component.chunk_size); + + constraints.push_back(chunk_range_constraint); + } + // assert sum + constraint_type sum_constraint = var(component.W(0 + component.reserved_columns), 0, true); + for (std::size_t i = 1; i < component.chunks_per_row; i++) { + sum_constraint = + base_two.pow(component.chunk_size) * sum_constraint + + var(component.W(0 + component.reserved_columns + i), 0, true); + } + sum_constraint = sum_constraint + + base_two.pow(component.chunk_size * component.chunks_per_row) * + var(component.W(0), -1, true) - + var(component.W(0), 0, true); + constraints.push_back(sum_constraint); + + std::size_t selector_index_1 = bp.add_gate(constraints); + if (component.bits_amount % component.chunk_size == 0) return {selector_index_1}; + // If bits_amount is not divisible by chunk size, the first chunk should be constrained to be + // less than 2^{bits_amount % chunk_size} + constraint_type first_chunk_range_constraint = generate_chunk_size_constraint( + var(component.W(0 + component.reserved_columns + component.padding_size), 0, true), + component.bits_amount % component.chunk_size); + + std::size_t selector_index_2 = bp.add_gate(first_chunk_range_constraint); + return {selector_index_1, selector_index_2}; + } + + template + void generate_copy_constraints( + const plonk_range_check + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_range_check::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_range_check::var; + + var zero(0, start_row_index, false, var::column_type::constant); + bp.add_copy_constraint({zero, var(component.W(0), start_row_index, false)}); + + for (std::size_t i = 1; i <= component.padding_size; i++) { + bp.add_copy_constraint({zero, var(component.W(i), start_row_index + 1, false)}); + } + + bp.add_copy_constraint({instance_input.x, + var(component.W(0), start_row_index + component.rows_amount - 1, false)}); + } + + template + void generate_assignments_constants( + const plonk_range_check + &component, + assignment> + &assignment, + const typename plonk_range_check::input_type + &instance_input, + const std::uint32_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = 0; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_FIELD_RANGE_CHECK_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/sqrt.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/sqrt.hpp new file mode 100644 index 000000000..e0e2faa7c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/sqrt.hpp @@ -0,0 +1,407 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_SQRT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_SQRT_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // square root + // Input: y + // Output: x such that x * x = y + template + class sqrt; + + template + class sqrt, + BlueprintFieldType> : public plonk_component { + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + + constexpr static std::size_t rows() { + std::size_t row = 0; + const std::size_t exp_rows_amount = exp_component::get_rows_amount(15); + const std::size_t mul_rows_amount = mul_component::get_rows_amount(3); + const std::size_t sub_rows_amount = sub_component::get_rows_amount(3); + const std::size_t add_rows_amount = add_component::get_rows_amount(3); + + row += 3; // leave empty cells for exp_component's constants + + row += exp_rows_amount; + + row += mul_rows_amount; + + // qr_check * (1 + qr_check) * (y - x_squared) = 0 for y \in QR(q) + row += add_rows_amount; + row += sub_rows_amount; + row += mul_rows_amount; + row += mul_rows_amount; + + // qr_check * (1 - qr_check) * (1 + x_squared) = 0 for y \in QNR(q) + row += sub_rows_amount; + row += add_rows_amount; + row += mul_rows_amount; + row += mul_rows_amount; + + // (1 - qr_check) * (1 + qr_check) * x_squared = 0 for y = 0 + row += mul_rows_amount; + row += mul_rows_amount; + + row += add_rows_amount; + row += add_rows_amount; + + return row; + } + + public: + using component_type = plonk_component; + using mul_component = multiplication>; + using add_component = addition>; + using sub_component = subtraction>; + using exp_component = exponentiation; + + using manifest_type = plonk_component_manifest; + using var = typename component_type::var; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return sqrt::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = \ + gate_manifest(gate_manifest_type()) + .merge_with(mul_component::get_gate_manifest(witness_amount)) + .merge_with(add_component::get_gate_manifest(witness_amount)) + .merge_with(sub_component::get_gate_manifest(witness_amount)) + .merge_with(exp_component::get_gate_manifest(witness_amount)); + return manifest; + } + + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rows(); + } + + const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct input_type { + var y; + + std::vector> all_vars() { + return {y}; + } + }; + + struct result_type { + var output; + + result_type(const sqrt &component, std::size_t component_start_row) { + output = var( + component.W(0), + component_start_row + 3 + exp_component::get_rows_amount(15), + false + ); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + sqrt(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + sqrt( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + }; + + template + using plonk_sqrt = + sqrt, + BlueprintFieldType>; + + template + typename plonk_sqrt::result_type + generate_circuit( + const plonk_sqrt &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_sqrt::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_sqrt; + using var = typename component_type::var; + using exp_component = typename component_type::exp_component; + using mul_component = typename component_type::mul_component; + using add_component = typename component_type::add_component; + using sub_component = typename component_type::sub_component; + std::size_t row = start_row_index; + + var exp(0, start_row_index, false, var::column_type::constant); + var zero(0, start_row_index + 1, false, var::column_type::constant); + var one(0, start_row_index + 2, false, var::column_type::constant); + + row += 3; // leave empty cells for exp_component's constants + + // check if y \in QR(q) + auto exp_instance = + // qr_check = 1 if y \in QR(q), -1 if y \in QNR(q), 0 if y = 0 + exp_component({ + component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8), component.W(9), component.W(10), component.W(11), + component.W(12), component.W(13), component.W(14)}, {component.C(0)}, + {} + ); + var qr_check = generate_circuit(exp_instance, bp, assignment, {instance_input.y, exp}, row).output; + row += exp_instance.rows_amount; + // x = sqrt(y) if y \in QR(q) or y = 0, -1 otherwise + auto mul_instance = mul_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var x(component.W(0), row, false); + var x_squared = generate_circuit(mul_instance, bp, assignment, {x, x}, row).output; + row += mul_instance.rows_amount; + + // qr_check * (1 + qr_check) * (y - x_squared) = 0 for y \in QR(q) + auto add_instance = add_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var one_plus_qr_check = generate_circuit( + add_instance, bp, assignment, {qr_check, one}, row).output; + row += add_instance.rows_amount; + + auto sub_instance = sub_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var y_minus_x_squared = generate_circuit( + sub_instance, bp, assignment, {instance_input.y, x_squared}, row).output; + row += sub_instance.rows_amount; + + var in_qr = generate_circuit(mul_instance, bp, assignment, {qr_check, one_plus_qr_check}, row).output; + row += mul_instance.rows_amount; + in_qr = generate_circuit(mul_instance, bp, assignment, {in_qr, y_minus_x_squared}, row).output; + row += mul_instance.rows_amount; + + // qr_check * (1 - qr_check) * (1 + x_squared) = 0 for y \in QNR(q) + var one_minus_qr_check = generate_circuit(sub_instance, bp, assignment, {one, qr_check}, row).output; + row += sub_instance.rows_amount; + var x_plus_one = generate_circuit(add_instance, bp, assignment, {x, one}, row).output; + row += add_instance.rows_amount; + + var in_qnr = generate_circuit(mul_instance, bp, assignment, {qr_check, one_minus_qr_check}, row).output; + row += mul_instance.rows_amount; + in_qnr = generate_circuit(mul_instance, bp, assignment, {in_qnr, x_plus_one}, row).output; + row += mul_instance.rows_amount; + + // (1 - qr_check) * (1 + qr_check) * x_squared = 0 for y = 0 + var y_eq_zero = generate_circuit( + mul_instance, bp, assignment, {one_minus_qr_check, one_plus_qr_check}, row).output; + row += mul_instance.rows_amount; + y_eq_zero = generate_circuit(mul_instance, bp, assignment, {y_eq_zero, x_squared}, row).output; + row += mul_instance.rows_amount; + + var last_check = generate_circuit(add_instance, bp, assignment, {in_qr, in_qnr}, row).output; + row += add_instance.rows_amount; + last_check = generate_circuit(add_instance, bp, assignment, {last_check, y_eq_zero}, row).output; + row += add_instance.rows_amount; + + assert(row == start_row_index + component.rows_amount); + + // copy-constarint for last_check and zero + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constants(component, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_sqrt::result_type + generate_assignments( + const plonk_sqrt &component, + assignment> + &assignment, + const typename plonk_sqrt::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_sqrt; + using var = typename component_type::var; + using exp_component = typename component_type::exp_component; + using mul_component = typename component_type::mul_component; + using add_component = typename component_type::add_component; + using sub_component = typename component_type::sub_component; + std::size_t row = start_row_index; + + var exp(0, start_row_index, false, var::column_type::constant); + var zero(0, start_row_index + 1, false, var::column_type::constant); + var one(0, start_row_index + 2, false, var::column_type::constant); + + row += 3; // leave empty cells for exp_component's constants + + // check if y \in QR(q) + // qr_check = 1 if y \in QR(q), -1 if y \in QNR(q), 0 if y = 0 + auto exp_instance = + exp_component({component.W(0), component.W(1), component.W(2), component.W(3), + component.W(4), component.W(5), component.W(6), component.W(7), + component.W(8), component.W(9), component.W(10), component.W(11), + component.W(12), component.W(13), component.W(14)}, {component.C(0)}, + {}); + var qr_check = generate_assignments(exp_instance, assignment, {instance_input.y, exp}, row).output; + row += exp_instance.rows_amount; + // x = sqrt(y) if y \in QR(q) or y = 0, -1 otherwise + typename BlueprintFieldType::value_type qr_check_value = var_value(assignment, qr_check).data; + if (qr_check_value == BlueprintFieldType::value_type::zero() || + qr_check_value == BlueprintFieldType::value_type::one()){ + typename BlueprintFieldType::value_type x_val = var_value(assignment, instance_input.y).sqrt(); + assignment.witness(component.W(0), row) = x_val; + } else if (qr_check_value == -BlueprintFieldType::value_type::one()) { + assignment.witness(component.W(0), row) = -1; + } else { + assert(false); + } + + auto mul_instance = mul_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var x(0, row, false); + var x_squared = generate_assignments(mul_instance, assignment, {x, x}, row).output; + row += mul_instance.rows_amount; + + // qr_check * (1 + qr_check) * (y - x_squared) = 0 for y \in QR(q) + auto add_instance = add_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var one_plus_qr_check = generate_assignments(add_instance, assignment, {qr_check, one}, row).output; + row += add_instance.rows_amount; + + auto sub_instance = sub_component({component.W(0), component.W(1), component.W(2)}, {}, {}); + var y_minus_x_squared = generate_assignments( + sub_instance, assignment, {instance_input.y, x_squared}, row).output; + row += sub_instance.rows_amount; + + var in_qr = generate_assignments(mul_instance, assignment, {qr_check, one_plus_qr_check}, row).output; + row += mul_instance.rows_amount; + in_qr = generate_assignments(mul_instance, assignment,{in_qr, y_minus_x_squared}, row).output; + row += mul_instance.rows_amount; + + // qr_check * (1 - qr_check) * (1 + x) = 0 for y \in QNR(q) + var one_minus_qr_check = generate_assignments(sub_instance, assignment, {one, qr_check}, row).output; + row += sub_instance.rows_amount; + var x_plus_one = generate_assignments(add_instance, assignment, {x, one}, row).output; + row += add_instance.rows_amount; + + var in_qnr = generate_assignments(mul_instance, assignment, {qr_check, one_minus_qr_check}, row).output; + row += mul_instance.rows_amount; + in_qnr = generate_assignments(mul_instance, assignment, {in_qnr, x_plus_one}, row).output; + row += mul_instance.rows_amount; + + // (1 - qr_check) * (1 + qr_check) * x_squared = 0 for y = 0 + var y_eq_zero = generate_assignments(mul_instance, assignment, {one_minus_qr_check, one_plus_qr_check}, row).output; + row += mul_instance.rows_amount; + y_eq_zero = generate_assignments(mul_instance, assignment, {y_eq_zero, x_squared}, row).output; + row += mul_instance.rows_amount; + + var last_check = generate_assignments(add_instance, assignment, {in_qr, in_qnr}, row).output; + row += add_instance.rows_amount; + last_check = generate_assignments(add_instance, assignment, {last_check, y_eq_zero}, row).output; + row += add_instance.rows_amount; + + assert(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + void generate_copy_constraints( + const plonk_sqrt &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_sqrt::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_sqrt::var; + + var zero(0, start_row_index + 1, false, var::column_type::constant); + var last_check(component.W(2), start_row_index + component.rows_amount - 1, + false, var::column_type::witness); + bp.add_copy_constraint({zero, last_check}); + } + + template + void generate_assignments_constants( + const plonk_sqrt &component,\ + assignment> + &assignment, + const typename plonk_sqrt::input_type + &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + assignment.constant(component.C(0), row) = (BlueprintFieldType::value_type::modulus - 1) / 2; + row++; + assignment.constant(component.C(0), row) = 0; + row++; + assignment.constant(component.C(0), row) = 1; + row++; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ALGEBRA_FIELDS_SQRT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/subtraction.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/subtraction.hpp new file mode 100644 index 000000000..8d597f347 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/plonk/subtraction.hpp @@ -0,0 +1,238 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK field element subtraction component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_SUBTRACTION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_SUBTRACTION_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: x, y \in F_p + // Output: z = x - y, z \in F_p + template + class subtraction; + + template + class subtraction, + BlueprintFieldType, NonNativePolicyType> + : public plonk_component { + + public: + using component_type = plonk_component; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return subtraction::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + const std::string component_name = "native field subtraction"; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(3)), + false + ); + return manifest; + } + + struct input_type { + var x = var(0, 0, false); + var y = var(0, 0, false); + + std::vector> all_vars() { + return {x, y}; + } + }; + + struct result_type { + var output = var(0, 0, false); + result_type(const subtraction &component, std::uint32_t start_row_index) { + output = var(component.W(2), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit subtraction(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + subtraction(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + subtraction(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static typename BlueprintFieldType::value_type calculate(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y) { + return x - y; + } + }; + + template + using plonk_subtraction = + subtraction, + BlueprintFieldType, basic_non_native_policy>; + + template + typename plonk_subtraction::result_type generate_assignments( + const plonk_subtraction &component, + assignment> + &assignment, + const typename plonk_subtraction::input_type + instance_input, + const std::uint32_t start_row_index) { + + const std::size_t j = start_row_index; + + assignment.witness(component.W(0), j) = var_value(assignment, instance_input.x); + assignment.witness(component.W(1), j) = var_value(assignment, instance_input.y); + assignment.witness(component.W(2), j) = + var_value(assignment, instance_input.x) - var_value(assignment, instance_input.y); + return typename plonk_subtraction::result_type( + component, start_row_index); + } + + template + typename plonk_subtraction::result_type + generate_empty_assignments( + const plonk_subtraction &component, + assignment> + &assignment, + const typename plonk_subtraction::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_subtraction; + assignment.witness(component.W(2), start_row_index) = component_type::calculate( + var_value(assignment, instance_input.x), var_value(assignment, instance_input.y)); + return typename plonk_subtraction::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_subtraction::input_type + &instance_input) { + + using var = typename plonk_subtraction::var; + + auto constraint_1 = var(component.W(0), 0) - var(component.W(1), 0) - var(component.W(2), 0); + + return bp.add_gate(constraint_1); + } + + template + void generate_copy_constraints( + const plonk_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_subtraction::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_subtraction::var; + + const std::size_t j = start_row_index; + var component_x = var(component.W(0), static_cast(j), false); + var component_y = var(component.W(1), static_cast(j), false); + bp.add_copy_constraint({instance_input.x, component_x}); + bp.add_copy_constraint({component_y, instance_input.y}); + } + + template + typename plonk_subtraction::result_type generate_circuit( + const plonk_subtraction &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_subtraction::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_subtraction::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FIELD_SUBTRACTION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp.hpp new file mode 100644 index 000000000..b2060c01d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp.hpp @@ -0,0 +1,53 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for Fp2 components. +// +// The components verify field arithmetic in Fp2 = Fp[U]/(U^2-non_residue), +// where non_residue is in Fp. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FP_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FP_COMPONENTS_HPP + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /******************************** element_fp ************************************/ + + /** + * Component that represents an element_fp. + */ + template + using element_fp = detail::blueprint_linear_combination; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FP_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp2.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp2.hpp new file mode 100644 index 000000000..316b03ce9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp2.hpp @@ -0,0 +1,354 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for Fp2 components. +// +// The components verify field arithmetic in Fp2 = Fp[U]/(U^2-non_residue), +// where non_residue is in Fp. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FP2_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FP2_COMPONENTS_HPP + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /******************************** element_fp2 ************************************/ + + /** + * Component that represents an element Fp2 component. + */ + template + struct element_fp2 : public component { + + using field_type = Fp2T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp; + + using base_field_value_type = typename base_field_type::value_type; + + using data_type = + std::array; + + data_type data; + + detail::blueprint_linear_combination_vector all_vars; + + element_fp2(blueprint &bp) : component(bp) { + detail::blueprint_variable c0_var, c1_var; + + c0_var.allocate(bp); + c1_var.allocate(bp); + + // c0 = underlying_element_type(c0_var); + // c1 = underlying_element_type(c1_var); + + data = data_type({underlying_element_type(c0_var), underlying_element_type(c1_var)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + } + + element_fp2(blueprint &bp, const typename field_type::value_type &el) : + component(bp) { + underlying_element_type c0_lc; + underlying_element_type c1_lc; + + c0_lc.assign(bp, el.data[0]); + c1_lc.assign(bp, el.data[1]); + + c0_lc.evaluate(bp); + c1_lc.evaluate(bp); + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + } + + element_fp2(blueprint &bp, + const typename field_type::value_type &el, + const detail::blueprint_linear_combination &coeff) : + component(bp) { + + underlying_element_type c0_lc; + underlying_element_type c1_lc; + + c0_lc.assign(bp, el.data[0] * coeff); + c1_lc.assign(bp, el.data[1] * coeff); + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + } + + element_fp2(blueprint &bp, + const underlying_element_type &c0_lc, + const underlying_element_type &c1_lc) : + component(bp) { + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + } + + void generate_r1cs_equals_const_constraints(const typename Fp2T::value_type &el) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, el.data[0], data[0])); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, el.data[1], data[1])); + } + + void generate_assignments(const typename Fp2T::value_type &el) { + this->bp.lc_val(data[0]) = el.data[0]; + this->bp.lc_val(data[1]) = el.data[1]; + } + + typename Fp2T::value_type get_element() { + typename Fp2T::value_type el; + el.data[0] = this->bp.lc_val(data[0]); + el.data[1] = this->bp.lc_val(data[1]); + return el; + } + + element_fp2 operator*(const base_field_value_type &coeff) const { + underlying_element_type new_c0, new_c1; + new_c0.assign(this->bp, this->data[0] * coeff); + new_c1.assign(this->bp, this->data[1] * coeff); + return element_fp2(this->bp, new_c0, new_c1); + } + + element_fp2 operator+(const element_fp2 &other) const { + underlying_element_type new_c0, new_c1; + new_c0.assign(this->bp, this->data[0] + other.data[0]); + new_c1.assign(this->bp, this->data[1] + other.data[1]); + return element_fp2(this->bp, new_c0, new_c1); + } + + element_fp2 operator+(const typename Fp2T::value_type &other) const { + underlying_element_type new_c0, new_c1; + new_c0.assign(this->bp, this->data[0] + other.data[0]); + new_c1.assign(this->bp, this->data[1] + other.data[1]); + return element_fp2(this->bp, new_c0, new_c1); + } + + element_fp2 mul_by_X() const { + underlying_element_type new_c0, new_c1; + new_c0.assign(this->bp, this->data[1] * Fp2T::value_type::non_residue); + + new_c1.assign(this->bp, this->data[0]); + return element_fp2(this->bp, new_c0, new_c1); + } + + void evaluate() const { + (this->data[0]).evaluate(this->bp); + (this->data[1]).evaluate(this->bp); + } + + bool is_constant() const { + return ((this->data[0]).is_constant() && (this->data[1]).is_constant()); + } + + static std::size_t size_in_bits() { + return 2 * base_field_type::value_bits; + } + + static std::size_t num_variables() { + return 2; + } + }; + + /******************************** element_fp2_mul ************************************/ + + /** + * Component that creates constraints for Fp2 by Fp2 multiplication. + */ + template + struct element_fp2_mul : public component { + using base_field_type = typename Fp2T::underlying_field_type; + using base_field_value_type = typename base_field_type::value_type; + + element_fp2 A; + element_fp2 B; + element_fp2 result; + + private: + detail::blueprint_variable v1; + + public: + element_fp2_mul(blueprint &bp, + const element_fp2 &A, + const element_fp2 &B, + const element_fp2 &result) : + component(bp), + A(A), B(B), result(result) { + v1.allocate(bp); + } + + void generate_gates() { + /* + Karatsuba multiplication for Fp2: + v0 = A.data[0] * B.data[0] + v1 = A.data[1] * B.data[1] + result.data[0] = v0 + non_residue * v1 + result.data[1] = (A.data[0] + A.data[1]) * (B.data[0] + B.data[1]) - v0 - v1 + + Enforced with 3 constraints: + A.data[1] * B.data[1] = v1 + A.data[0] * B.data[0] = result.data[0] - non_residue * v1 + (A.data[0]+A.data[1])*(B.data[0]+B.data[1]) = result.data[1] + result.data[0] + (1 - + non_residue) * v1 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint(A.data[1], B.data[1], v1)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0], B.data[0], result.data[0] + v1 * (-Fp2T::value_type::non_residue))); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0] + A.data[1], + B.data[0] + B.data[1], + result.data[1] + result.data[0] + + v1 * (base_field_value_type::one() - Fp2T::value_type::non_residue))); + } + + void generate_assignments() { + const base_field_value_type aA = this->bp.lc_val(A.data[0]) * this->bp.lc_val(B.data[0]); + this->bp.val(v1) = this->bp.lc_val(A.data[1]) * this->bp.lc_val(B.data[1]); + this->bp.lc_val(result.data[0]) = aA + Fp2T::value_type::non_residue * this->bp.val(v1); + + this->bp.lc_val(result.data[1]) = + (this->bp.lc_val(A.data[0]) + this->bp.lc_val(A.data[1])) * + (this->bp.lc_val(B.data[0]) + this->bp.lc_val(B.data[1])) - + aA - this->bp.lc_val(v1); + } + }; + + /******************************** element_fp2_mul_by_lc ************************************/ + + /** + * Component that creates constraints for Fp2 multiplication by a linear combination. + */ + template + struct element_fp2_mul_by_lc : public component { + using base_field_type = typename Fp2T::underlying_field_type; + + element_fp2 A; + detail::blueprint_linear_combination lc; + element_fp2 result; + + element_fp2_mul_by_lc(blueprint &bp, + const element_fp2 &A, + const detail::blueprint_linear_combination &lc, + const element_fp2 &result) : + component(bp), + A(A), lc(lc), result(result) { + } + + void generate_gates() { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(A.data[0], lc, result.data[0])); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(A.data[1], lc, result.data[1])); + } + + void generate_assignments() { + this->bp.lc_val(result.data[0]) = this->bp.lc_val(A.data[0]) * this->bp.lc_val(lc); + this->bp.lc_val(result.data[1]) = this->bp.lc_val(A.data[1]) * this->bp.lc_val(lc); + } + }; + + /******************************** element_fp2_squared ************************************/ + + /** + * Component that creates constraints for Fp2 squaring. + */ + template + struct element_fp2_squared : public component { + using base_field_type = typename Fp2T::base_field_type; + + element_fp2 A; + element_fp2 result; + + using base_field_value_type = typename base_field_type::value_type; + + element_fp2_squared(blueprint &bp, + const element_fp2 &A, + const element_fp2 &result) : + component(bp), + A(A), result(result) { + } + + void generate_gates() { + /* + Complex multiplication for Fp2: + v0 = A.data[0] * A.data[1] + result.data[0] = (A.data[0] + A.data[1]) * (A.data[0] + non_residue * A.data[1]) - + (1 + non_residue) * v0 result.data[1] = 2 * v0 + + Enforced with 2 constraints: + (2*A.data[0]) * A.data[1] = result.data[1] + (A.data[0] + A.data[1]) * (A.data[0] + non_residue * A.data[1]) = result.data[0] + + result.data[1] * (1 + non_residue)/2 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(2 * A.data[0], A.data[1], result.data[1])); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0] + A.data[1], + A.data[0] + Fp2T::value_type::non_residue * A.data[1], + result.data[0] + result.data[1] * + (base_field_value_type::one() + Fp2T::value_type::non_residue) * + base_field_value_type(0x02).inversed())); + } + + void generate_assignments() { + const base_field_value_type a = this->bp.lc_val(A.data[0]); + const base_field_value_type b = this->bp.lc_val(A.data[1]); + this->bp.lc_val(result.data[1]) = base_field_value_type(0x02) * a * b; + this->bp.lc_val(result.data[0]) = (a + b) * (a + Fp2T::value_type::non_residue * b) - a * b - + Fp2T::value_type::non_residue * a * b; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FP2_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp3.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp3.hpp new file mode 100644 index 000000000..50edd8b6b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp3.hpp @@ -0,0 +1,386 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for Fp3 components. +// +// The components verify field arithmetic in Fp3 = Fp[U]/(U^3-non_residue), +// where non_residue is in Fp. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FP3_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FP3_COMPONENTS_HPP + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /******************************** element_fp3 ************************************/ + + /** + * Component that represents an Fp3 element. + */ + template + struct element_fp3 : public component { + + using field_type = Fp3T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp; + + using base_field_value_type = typename base_field_type::value_type; + + using data_type = + std::array; + + data_type data; + + detail::blueprint_linear_combination_vector all_vars; + + element_fp3(blueprint &bp) : component(bp) { + detail::blueprint_variable c0_var, c1_var, c2_var; + + c0_var.allocate(bp); + c1_var.allocate(bp); + c2_var.allocate(bp); + + data = data_type({underlying_element_type(c0_var), underlying_element_type(c1_var), + underlying_element_type(c2_var)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + all_vars.emplace_back(data[2]); + } + + element_fp3(blueprint &bp, const typename Fp3T::value_type &el) : + component(bp) { + underlying_element_type c0_lc; + underlying_element_type c1_lc; + underlying_element_type c2_lc; + + c0_lc.assign(bp, el.data[0]); + c1_lc.assign(bp, el.data[1]); + c2_lc.assign(bp, el.data[2]); + + c0_lc.evaluate(bp); + c1_lc.evaluate(bp); + c2_lc.evaluate(bp); + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc), + underlying_element_type(c2_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + all_vars.emplace_back(data[2]); + } + + element_fp3(blueprint &bp, + const typename Fp3T::value_type &el, + const detail::blueprint_linear_combination &coeff) : + component(bp) { + + underlying_element_type c0_lc; + underlying_element_type c1_lc; + underlying_element_type c2_lc; + + c0_lc.assign(bp, el.data[0] * coeff); + c1_lc.assign(bp, el.data[1] * coeff); + c2_lc.assign(bp, el.data[2] * coeff); + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc), + underlying_element_type(c2_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + all_vars.emplace_back(data[2]); + } + + element_fp3(blueprint &bp, + const underlying_element_type &c0_lc, + const underlying_element_type &c1_lc, + const underlying_element_type &c2_lc) : + component(bp) { + + data = data_type({underlying_element_type(c0_lc), underlying_element_type(c1_lc), + underlying_element_type(c2_lc)}); + + all_vars.emplace_back(data[0]); + all_vars.emplace_back(data[1]); + all_vars.emplace_back(data[2]); + } + + void generate_r1cs_equals_const_constraints(const typename Fp3T::value_type &el) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, el.data[0], data[0])); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, el.data[1], data[1])); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, el.data[2], data[2])); + } + + void generate_assignments(const typename Fp3T::value_type &el) { + this->bp.lc_val(data[0]) = el.data[0]; + this->bp.lc_val(data[1]) = el.data[1]; + this->bp.lc_val(data[2]) = el.data[2]; + } + + typename Fp3T::value_type get_element() { + typename Fp3T::value_type el; + el.data[0] = this->bp.lc_val(data[0]); + el.data[1] = this->bp.lc_val(data[1]); + el.data[2] = this->bp.lc_val(data[2]); + return el; + } + + element_fp3 operator*(const typename base_field_type::value_type &coeff) const { + underlying_element_type new_c0, new_c1, new_c2; + new_c0.assign(this->bp, this->data[0] * coeff); + new_c1.assign(this->bp, this->data[1] * coeff); + new_c2.assign(this->bp, this->data[2] * coeff); + return element_fp3(this->bp, new_c0, new_c1, new_c2); + } + + element_fp3 operator+(const element_fp3 &other) const { + underlying_element_type new_c0, new_c1, new_c2; + new_c0.assign(this->bp, this->data[0] + other.data[0]); + new_c1.assign(this->bp, this->data[1] + other.data[1]); + new_c2.assign(this->bp, this->data[2] + other.data[2]); + return element_fp3(this->bp, new_c0, new_c1, new_c2); + } + + element_fp3 operator+(const typename Fp3T::value_type &other) const { + underlying_element_type new_c0, new_c1, new_c2; + new_c0.assign(this->bp, this->data[0] + other.data[0]); + new_c1.assign(this->bp, this->data[1] + other.data[1]); + new_c2.assign(this->bp, this->data[2] + other.data[2]); + return element_fp3(this->bp, new_c0, new_c1, new_c2); + } + + element_fp3 mul_by_X() const { + underlying_element_type new_c0, new_c1, new_c2; + new_c0.assign(this->bp, this->data[2] * Fp3T::value_type::non_residue); + + new_c1.assign(this->bp, this->data[0]); + new_c2.assign(this->bp, this->data[1]); + return element_fp3(this->bp, new_c0, new_c1, new_c2); + } + + void evaluate() const { + data[0].evaluate(this->bp); + data[1].evaluate(this->bp); + data[2].evaluate(this->bp); + } + + bool is_constant() const { + return (data[0].is_constant() && data[1].is_constant() && data[2].is_constant()); + } + + static std::size_t size_in_bits() { + return 3 * base_field_type::value_bits; + } + + static std::size_t num_variables() { + return 3; + } + }; + + /******************************** element_fp3_mul ************************************/ + + /** + * Component that creates constraints for Fp3 by Fp3 multiplication. + */ + template + struct element_fp3_mul : public component { + using base_field_type = typename Fp3T::base_field_type; + + element_fp3 A; + element_fp3 B; + element_fp3 result; + + detail::blueprint_variable v0; + detail::blueprint_variable v4; + + element_fp3_mul(blueprint &bp, + const element_fp3 &A, + const element_fp3 &B, + const element_fp3 &result) : + component(bp), + A(A), B(B), result(result) { + v0.allocate(bp); + v4.allocate(bp); + } + + void generate_gates() { + /* + Tom-Cook-3x for Fp3: + v0 = A.data[0] * B.data[0] + v1 = (A.data[0] + A.data[1] + A.data[2]) * (B.data[0] + B.data[1] + B.data[2]) + v2 = (A.data[0] - A.data[1] + A.data[2]) * (B.data[0] - B.data[1] + B.data[2]) + v3 = (A.data[0] + 2*A.data[1] + 4*A.data[2]) * (B.data[0] + 2*B.data[1] + + 4*B.data[2]) v4 = A.data[2] * B.data[2] result.data[0] = v0 + non_residue * (v0/2 - v1/2 + - v2/6 + v3/6 - 2*v4) result.data[1] = -(1/2) v0 + v1 - (1/3) v2 - (1/6) v3 + 2 v4 + + non_residue*v4 result.data[2] = -v0 + (1/2) v1 + (1/2) v2 - v4 + + Enforced with 5 constraints. Doing so requires some care, as we first + compute two of the v_i explicitly, and then "inline" result.data[1]/data[2]/c3 + in computations of teh remaining three v_i. + + Concretely, we first compute v0 and v4 explicitly, via 2 constraints: + A.data[0] * B.data[0] = v0 + A.data[2] * B.data[2] = v4 + Then we use the following 3 additional constraints: + v1 = result.data[1] + result.data[2] + (result.data[0] - v0)/non_residue + v0 + v4 - + non_residue v4 v2 = -result.data[1] + result.data[2] + v0 + (-result.data[0] + + v0)/non_residue + v4 + non_residue v4 v3 = 2 * result.data[1] + 4 result.data[2] + + (8*(result.data[0] - v0))/non_residue + v0 + 16 * v4 - 2 * non_residue * v4 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + + NOTE: the expressions above were cherry-picked from the Mathematica result + of the following command: + + (# -> Solve[{data[0] == v0 + non_residue*(v0/2 - v1/2 - v2/6 + v3/6 - 2 v4), + data[1] == -(1/2) v0 + v1 - (1/3) v2 - (1/6) v3 + 2 v4 + non_residue*v4, + data[2] == -v0 + (1/2) v1 + (1/2) v2 - v4}, #] // FullSimplify) & /@ + Subsets[{v0, v1, v2, v3, v4}, {3}] + */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint(A.data[0], B.data[0], v0)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(A.data[2], B.data[2], v4)); + + const typename base_field_type::value_type beta = Fp3T::value_type::non_residue; + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0] + A.data[1] + A.data[2], + B.data[0] + B.data[1] + B.data[2], + result.data[1] + result.data[2] + result.data[0] * beta.inversed() + + v0 * (typename base_field_type::value_type(1) - beta.inversed()) + + v4 * (typename base_field_type::value_type(1) - beta))); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0] - A.data[1] + A.data[2], + B.data[0] - B.data[1] + B.data[2], + -result.data[1] + result.data[2] + + v0 * (typename base_field_type::value_type(1) + beta.inversed()) - + result.data[0] * beta.inversed() + + v4 * (typename base_field_type::value_type(1) + beta))); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0] + 2 * A.data[1] + 4 * A.data[2], + B.data[0] + 2 * B.data[1] + 4 * B.data[2], + 2 * result.data[1] + 4 * result.data[2] + + result.data[0] * (typename base_field_type::value_type(8) * beta.inversed()) + + v0 * (typename base_field_type::value_type(1) - + typename base_field_type::value_type(8) * beta.inversed()) + + v4 * (typename base_field_type::value_type(16) - + typename base_field_type::value_type(2) * beta))); + } + + void generate_assignments() { + this->bp.val(v0) = this->bp.lc_val(A.data[0]) * this->bp.lc_val(B.data[0]); + this->bp.val(v4) = this->bp.lc_val(A.data[2]) * this->bp.lc_val(B.data[2]); + + const typename Fp3T::value_type Aval = A.get_element(); + const typename Fp3T::value_type Bval = B.get_element(); + const typename Fp3T::value_type Rval = Aval * Bval; + result.generate_assignments(Rval); + } + }; + + /******************************** element_fp3_mul_by_lc ************************************/ + + /** + * Component that creates constraints for Fp3 multiplication by a linear combination. + */ + template + struct element_fp3_mul_by_lc : public component { + using base_field_type = typename Fp3T::underlying_field_type; + + element_fp3 A; + detail::blueprint_linear_combination lc; + element_fp3 result; + + element_fp3_mul_by_lc(blueprint &bp, + const element_fp3 &A, + const detail::blueprint_linear_combination &lc, + const element_fp3 &result) : + component(bp), + A(A), lc(lc), result(result) { + } + + void generate_gates() { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(A.data[0], lc, result.data[0])); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(A.data[1], lc, result.data[1])); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(A.data[2], lc, result.data[2])); + } + + void generate_assignments() { + this->bp.lc_val(result.data[0]) = this->bp.lc_val(A.data[0]) * this->bp.lc_val(lc); + this->bp.lc_val(result.data[1]) = this->bp.lc_val(A.data[1]) * this->bp.lc_val(lc); + this->bp.lc_val(result.data[2]) = this->bp.lc_val(A.data[2]) * this->bp.lc_val(lc); + } + }; + + /******************************** element_fp3_squared ************************************/ + + /** + * Component that creates constraints for Fp3 squaring. + */ + template + struct element_fp3_squared : public component { + using base_field_type = typename Fp3T::underlying_field_type; + + element_fp3 A; + element_fp3 result; + + std::shared_ptr> mul; + + element_fp3_squared(blueprint &bp, + const element_fp3 &A, + const element_fp3 &result) : + component(bp), + A(A), result(result) { + mul.reset(new element_fp3_mul(bp, A, A, result)); + } + + void generate_gates() { + mul->generate_gates(); + } + + void generate_assignments() { + mul->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FP3_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp4.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp4.hpp new file mode 100644 index 000000000..b902a9a44 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp4.hpp @@ -0,0 +1,646 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for Fp4 components. +// +// The components verify field arithmetic in Fp4 = Fp2[V]/(V^2-U) where +// Fp2 = Fp[U]/(U^2-non_residue) and non_residue is in Fp. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FP4_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FP4_COMPONENTS_HPP + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /******************************** element_fp4 ************************************/ + + /** + * Component that represents an Fp4 element. + */ + template + struct element_fp4 : public component { + + using field_type = Fp4T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp2; + + using data_type = + std::array; + + data_type data; + + element_fp4(blueprint &bp) : + component(bp), + data({underlying_element_type(bp), underlying_element_type(bp)}) { + } + + element_fp4(blueprint &bp, const typename field_type::value_type &el) : + component(bp), + data({underlying_element_type(bp, el.data[0]), underlying_element_type(bp, el.data[1])}) { + } + + element_fp4(blueprint &bp, const underlying_element_type &in_data0, + const underlying_element_type &in_data1) : + component(bp), + data({underlying_element_type(in_data0), underlying_element_type(in_data1)}) { + } + + void generate_r1cs_equals_const_constraints(const typename field_type::value_type &el) { + data[0].generate_r1cs_equals_const_constraints(el.data[0]); + data[1].generate_r1cs_equals_const_constraints(el.data[1]); + } + + void generate_assignments(const typename field_type::value_type &el) { + data[0].generate_assignments(el.data[0]); + data[1].generate_assignments(el.data[1]); + } + + typename field_type::value_type get_element() { + typename field_type::value_type el; + el.data[0] = data[0].get_element(); + el.data[1] = data[1].get_element(); + return el; + } + + element_fp4 Frobenius_map(const std::size_t power) const { + detail::blueprint_linear_combination new_c0c0, new_c0c1, new_c1c0, new_c1c1; + new_c0c0.assign(this->bp, data[0].data[0]); + new_c0c1.assign(this->bp, + data[0].data[1] * underlying_field_type::Frobenius_coeffs_c1[power % 2]); + new_c1c0.assign(this->bp, data[1].data[0] * field_type::Frobenius_coeffs_c1[power % 4]); + new_c1c1.assign(this->bp, + data[1].data[1] * field_type::Frobenius_coeffs_c1[power % 4] * + underlying_field_type::Frobenius_coeffs_c1[power % 2]); + + return element_fp4(this->bp, + underlying_element_type(this->bp, new_c0c0, new_c0c1), + underlying_element_type(this->bp, new_c1c0, new_c1c1)); + } + + void evaluate() const { + data[0].evaluate(); + data[1].evaluate(); + } + }; + + /******************************** element_fp4_tower_mul ************************************/ + + /** + * Component that creates constraints for Fp4 multiplication (towering formulas). + */ + template + class element_fp4_tower_mul : public component { + public: + using field_type = Fp4T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp2; + + element_fp4 A; + element_fp4 B; + element_fp4 result; + + detail::blueprint_linear_combination v0_c0; + detail::blueprint_linear_combination v0_c1; + + detail::blueprint_linear_combination Ac0_plus_Ac1_c0; + detail::blueprint_linear_combination Ac0_plus_Ac1_c1; + std::shared_ptr Ac0_plus_Ac1; + + std::shared_ptr v0; + std::shared_ptr v1; + + detail::blueprint_linear_combination Bc0_plus_Bc1_c0; + detail::blueprint_linear_combination Bc0_plus_Bc1_c1; + std::shared_ptr Bc0_plus_Bc1; + + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c0; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c1; + + std::shared_ptr result_c1_plus_v0_plus_v1; + + std::shared_ptr> compute_v0; + std::shared_ptr> compute_v1; + std::shared_ptr> compute_result_c1; + + element_fp4_tower_mul(blueprint &bp, + const element_fp4 &A, + const element_fp4 &B, + const element_fp4 &result) : + component(bp), + A(A), B(B), result(result) { + /* + Karatsuba multiplication for Fp4 as a quadratic extension of Fp2: + v0 = A.data[0] * B.data[0] + v1 = A.data[1] * B.data[1] + result.data[0] = v0 + non_residue * v1 + result.data[1] = (A.data[0] + A.data[1]) * (B.data[0] + B.data[1]) - v0 - v1 + where "non_residue * elem" := (non_residue * elt.data[1], elt.data[0]) + + Enforced with 3 element_fp2_mul's that ensure that: + A.data[1] * B.data[1] = v1 + A.data[0] * B.data[0] = v0 + (A.data[0]+A.data[1])*(B.data[0]+B.data[1]) = result.data[1] + v0 + v1 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + v1.reset(new underlying_element_type(bp)); + + compute_v1.reset(new element_fp2_mul(bp, A.data[1], B.data[1], *v1)); + + v0_c0.assign(bp, result.data[0].data[0] - field_type::value_type::non_residue * v1->data[1]); + + v0_c1.assign(bp, result.data[0].data[1] - v1->data[0]); + v0.reset(new underlying_element_type(bp, v0_c0, v0_c1)); + + compute_v0.reset(new element_fp2_mul(bp, A.data[0], B.data[0], *v0)); + + Ac0_plus_Ac1_c0.assign(bp, A.data[0].data[0] + A.data[1].data[0]); + Ac0_plus_Ac1_c1.assign(bp, A.data[0].data[1] + A.data[1].data[1]); + Ac0_plus_Ac1.reset(new underlying_element_type(bp, Ac0_plus_Ac1_c0, Ac0_plus_Ac1_c1)); + + Bc0_plus_Bc1_c0.assign(bp, B.data[0].data[0] + B.data[1].data[0]); + Bc0_plus_Bc1_c1.assign(bp, B.data[0].data[1] + B.data[1].data[1]); + Bc0_plus_Bc1.reset(new underlying_element_type(bp, Bc0_plus_Bc1_c0, Bc0_plus_Bc1_c1)); + + result_c1_plus_v0_plus_v1_c0.assign(bp, result.data[1].data[0] + v0->data[0] + v1->data[0]); + result_c1_plus_v0_plus_v1_c1.assign(bp, result.data[1].data[1] + v0->data[1] + v1->data[1]); + result_c1_plus_v0_plus_v1.reset(new underlying_element_type(bp, result_c1_plus_v0_plus_v1_c0, + result_c1_plus_v0_plus_v1_c1)); + + compute_result_c1.reset(new element_fp2_mul( + bp, *Ac0_plus_Ac1, *Bc0_plus_Bc1, *result_c1_plus_v0_plus_v1)); + } + + void generate_gates() { + compute_v0->generate_gates(); + compute_v1->generate_gates(); + compute_result_c1->generate_gates(); + } + + void generate_assignments() { + compute_v0->generate_assignments(); + compute_v1->generate_assignments(); + + Ac0_plus_Ac1_c0.evaluate(this->bp); + Ac0_plus_Ac1_c1.evaluate(this->bp); + + Bc0_plus_Bc1_c0.evaluate(this->bp); + Bc0_plus_Bc1_c1.evaluate(this->bp); + + compute_result_c1->generate_assignments(); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Bval = B.get_element(); + const typename field_type::value_type Rval = Aval * Bval; + + result.generate_assignments(Rval); + } + }; + + /******************************** element_fp4_direct_mul ************************************/ + + /** + * Component that creates constraints for Fp4 multiplication (direct formulas). + */ + template + class element_fp4_direct_mul : public component { + public: + using field_type = Fp4T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp2; + + using base_field_value_type = typename base_field_type::value_type; + + element_fp4 A; + element_fp4 B; + element_fp4 result; + + detail::blueprint_variable v1; + detail::blueprint_variable v2; + detail::blueprint_variable v6; + + element_fp4_direct_mul(blueprint &bp, + const element_fp4 &A, + const element_fp4 &B, + const element_fp4 &result) : + component(bp), + A(A), B(B), result(result) { + /* + Tom-Cook-4x for Fp4 (beta is the quartic non-residue): + v0 = a0*b0, + v1 = (a0+a1+a2+a3)*(b0+b1+b2+b3), + v2 = (a0-a1+a2-a3)*(b0-b1+b2-b3), + v3 = (a0+2a1+4a2+8a3)*(b0+2b1+4b2+8b3), + v4 = (a0-2a1+4a2-8a3)*(b0-2b1+4b2-8b3), + v5 = (a0+3a1+9a2+27a3)*(b0+3b1+9b2+27b3), + v6 = a3*b3 + + result.data[0] = v0+beta((1/4)v0-(1/6)(v1+v2)+(1/24)(v3+v4)-5v6), + result.data[1] = + -(1/3)v0+v1-(1/2)v2-(1/4)v3+(1/20)v4+(1/30)v5-12v6+beta(-(1/12)(v0-v1)+(1/24)(v2-v3)-(1/120)(v4-v5)-3v6), + result.c2 = -(5/4)v0+(2/3)(v1+v2)-(1/24)(v3+v4)+4v6+beta v6, + result.c3 = (1/12)(5v0-7v1)-(1/24)(v2-7v3+v4+v5)+15v6 + + Enforced with 7 constraints. Doing so requires some care, as we first + compute three of the v_i explicitly, and then "inline" result.data[0]/c1/c2/c3 + in computations of the remaining four v_i. + + Concretely, we first compute v1, v2 and v6 explicitly, via 3 constraints as above. + v1 = (a0+a1+a2+a3)*(b0+b1+b2+b3), + v2 = (a0-a1+a2-a3)*(b0-b1+b2-b3), + v6 = a3*b3 + + Then we use the following 4 additional constraints: + (1-beta) v0 = c0 + beta c2 - (beta v1)/2 - (beta v2)/ 2 - (-1 + beta) beta v6 + (1-beta) v3 = -15 c0 - 30 c1 - 3 (4 + beta) c2 - 6 (4 + beta) c3 + (24 - (3 beta)/2) + v1 + + + (-8 + beta/2) v2 + 3 (-16 + beta) (-1 + beta) v6 (1-beta) v4 = -15 c0 + 30 c1 - 3 (4 + + beta) c2 + 6 (4 + beta) c3 + (-8 + beta/2) v1 + (24 - (3 beta)/2) v2 + 3 (-16 + beta) (-1 + + beta) v6 (1-beta) v5 = -80 c0 - 240 c1 - 8 (9 + beta) c2 - 24 (9 + beta) c3 - 2 (-81 + + beta) v1 + + (-81 + beta) v2 + 8 (-81 + beta) (-1 + beta) v6 + + The isomorphism between the representation above and towering is: + (a0, a1, a2, a3) <-> (a.data[0].data[0], a.data[1].data[0], a.data[0].data[1], + a.data[1].data[1]) + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + + NOTE: the expressions above were cherry-picked from the Mathematica result + of the following command: + + (# -> Solve[{c0 == v0+beta((1/4)v0-(1/6)(v1+v2)+(1/24)(v3+v4)-5v6), + c1 == + -(1/3)v0+v1-(1/2)v2-(1/4)v3+(1/20)v4+(1/30)v5-12v6+beta(-(1/12)(v0-v1)+(1/24)(v2-v3)-(1/120)(v4-v5)-3v6), + c2 + == -(5/4)v0+(2/3)(v1+v2)-(1/24)(v3+v4)+4v6+beta v6, c3 == + (1/12)(5v0-7v1)-(1/24)(v2-7v3+v4+v5)+15v6}, #] // FullSimplify) & /@ Subsets[{v0, v1, v2, + v3, v4, v5}, {4}] + + and simplified by multiplying the selected result by (1-beta) + */ + v1.allocate(bp); + v2.allocate(bp); + v6.allocate(bp); + } + + void generate_gates() { + const base_field_value_type beta = field_type::value_type::non_residue; + + const base_field_value_type u = (base_field_value_type::one() - beta).inversed(); + + const detail::blueprint_linear_combination &a0 = A.data[0].data[0], + &a1 = A.data[1].data[0], + &a2 = A.data[0].data[1], + &a3 = A.data[1].data[1], + &b0 = B.data[0].data[0], + &b1 = B.data[1].data[0], + &b2 = B.data[0].data[1], + &b3 = B.data[1].data[1], + &c0 = result.data[0].data[0], + &c1 = result.data[1].data[0], + &c2 = result.data[0].data[1], + &c3 = result.data[1].data[1]; + + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(a0 + a1 + a2 + a3, b0 + b1 + b2 + b3, v1)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(a0 - a1 + a2 - a3, b0 - b1 + b2 - b3, v2)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(a3, b3, v6)); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + a0, + b0, + u * c0 + beta * u * c2 - beta * u * base_field_value_type(0x02).inversed() * v1 - + beta * u * base_field_value_type(0x02).inversed() * v2 + beta * v6)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + a0 + base_field_value_type(0x02) * a1 + base_field_value_type(0x04) * a2 + + base_field_value_type(0x08) * a3, + b0 + base_field_value_type(0x02) * b1 + base_field_value_type(0x04) * b2 + + base_field_value_type(0x08) * b3, + -base_field_value_type(15) * u * c0 - base_field_value_type(30) * u * c1 - + base_field_value_type(0x03) * (base_field_value_type(0x04) + beta) * u * c2 - + base_field_value_type(6) * (base_field_value_type(0x04) + beta) * u * c3 + + (base_field_value_type(24) - + base_field_value_type(0x03) * beta * base_field_value_type(0x02).inversed()) * + u * v1 + + (-base_field_value_type(0x08) + beta * base_field_value_type(0x02).inversed()) * u * + v2 - + base_field_value_type(0x03) * (-base_field_value_type(16) + beta) * v6)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + a0 - base_field_value_type(0x02) * a1 + base_field_value_type(0x04) * a2 - + base_field_value_type(0x08) * a3, + b0 - base_field_value_type(0x02) * b1 + base_field_value_type(0x04) * b2 - + base_field_value_type(0x08) * b3, + -base_field_value_type(15) * u * c0 + base_field_value_type(30) * u * c1 - + base_field_value_type(0x03) * (base_field_value_type(0x04) + beta) * u * c2 + + base_field_value_type(6) * (base_field_value_type(0x04) + beta) * u * c3 + + (base_field_value_type(24) - + base_field_value_type(0x03) * beta * base_field_value_type(0x02).inversed()) * + u * v2 + + (-base_field_value_type(0x08) + beta * base_field_value_type(0x02).inversed()) * u * + v1 - + base_field_value_type(0x03) * (-base_field_value_type(16) + beta) * v6)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + a0 + base_field_value_type(0x03) * a1 + base_field_value_type(0x09) * a2 + + base_field_value_type(27) * a3, + b0 + base_field_value_type(0x03) * b1 + base_field_value_type(0x09) * b2 + + base_field_value_type(27) * b3, + -base_field_value_type(80) * u * c0 - base_field_value_type(240) * u * c1 - + base_field_value_type(0x08) * (base_field_value_type(0x09) + beta) * u * c2 - + base_field_value_type(24) * (base_field_value_type(0x09) + beta) * u * c3 - + base_field_value_type(0x02) * (-base_field_value_type(81) + beta) * u * v1 + + (-base_field_value_type(81) + beta) * u * v2 - + base_field_value_type(0x08) * (-base_field_value_type(81) + beta) * v6)); + } + + void generate_r1cs_witness() { + const detail::blueprint_linear_combination &a0 = A.data[0].data[0], + &a1 = A.data[1].data[0], + &a2 = A.data[0].data[1], + &a3 = A.data[1].data[1], + &b0 = B.data[0].data[0], + &b1 = B.data[1].data[0], + &b2 = B.data[0].data[1], + &b3 = B.data[1].data[1]; + + this->bp.val(v1) = + ((this->bp.lc_val(a0) + this->bp.lc_val(a1) + this->bp.lc_val(a2) + this->bp.lc_val(a3)) * + (this->bp.lc_val(b0) + this->bp.lc_val(b1) + this->bp.lc_val(b2) + this->bp.lc_val(b3))); + this->bp.val(v2) = + ((this->bp.lc_val(a0) - this->bp.lc_val(a1) + this->bp.lc_val(a2) - this->bp.lc_val(a3)) * + (this->bp.lc_val(b0) - this->bp.lc_val(b1) + this->bp.lc_val(b2) - this->bp.lc_val(b3))); + this->bp.val(v6) = this->bp.lc_val(a3) * this->bp.lc_val(b3); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Bval = B.get_element(); + const typename field_type::value_type Rval = Aval * Bval; + + result.generate_assignments(Rval); + } + }; + + /** + * Alias default multiplication component + */ + template + using element_fp4_mul = element_fp4_direct_mul; + + /******************************** element_fp4_squared ************************************/ + + /** + * Component that creates constraints for Fp4 squaring. + */ + template + class element_fp4_squared : public component { + public: + using field_type = Fp4T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp2; + + element_fp4 A; + element_fp4 result; + + std::shared_ptr v1; + + detail::blueprint_linear_combination v0_c0; + detail::blueprint_linear_combination v0_c1; + std::shared_ptr v0; + + std::shared_ptr> compute_v0; + std::shared_ptr> compute_v1; + + detail::blueprint_linear_combination Ac0_plus_Ac1_c0; + detail::blueprint_linear_combination Ac0_plus_Ac1_c1; + std::shared_ptr Ac0_plus_Ac1; + + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c0; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c1; + + std::shared_ptr result_c1_plus_v0_plus_v1; + + std::shared_ptr> compute_result_c1; + + element_fp4_squared(blueprint &bp, + const element_fp4 &A, + const element_fp4 &result) : + component(bp), + A(A), result(result) { + /* + Karatsuba squaring for Fp4 as a quadratic extension of Fp2: + v0 = A.data[0]^2 + v1 = A.data[1]^2 + result.data[0] = v0 + non_residue * v1 + result.data[1] = (A.data[0] + A.data[1])^2 - v0 - v1 + where "non_residue * elem" := (non_residue * elt.data[1], elt.data[0]) + + Enforced with 3 element_fp2_squared's that ensure that: + A.data[1]^2 = v1 + A.data[0]^2 = v0 + (A.data[0]+A.data[1])^2 = result.data[1] + v0 + v1 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + + v1.reset(new underlying_element_type(bp)); + compute_v1.reset(new element_fp2_squared(bp, A.data[1], *v1)); + + v0_c0.assign(bp, result.data[0].data[0] - field_type::value_type::non_residue * v1->data[1]); + + v0_c1.assign(bp, result.data[0].data[1] - v1->data[0]); + v0.reset(new underlying_element_type(bp, v0_c0, v0_c1)); + + compute_v0.reset(new element_fp2_squared(bp, A.data[0], *v0)); + + Ac0_plus_Ac1_c0.assign(bp, A.data[0].data[0] + A.data[1].data[0]); + Ac0_plus_Ac1_c1.assign(bp, A.data[0].data[1] + A.data[1].data[1]); + Ac0_plus_Ac1.reset(new underlying_element_type(bp, Ac0_plus_Ac1_c0, Ac0_plus_Ac1_c1)); + + result_c1_plus_v0_plus_v1_c0.assign(bp, result.data[1].data[0] + v0->data[0] + v1->data[0]); + result_c1_plus_v0_plus_v1_c1.assign(bp, result.data[1].data[1] + v0->data[1] + v1->data[1]); + result_c1_plus_v0_plus_v1.reset(new underlying_element_type(bp, result_c1_plus_v0_plus_v1_c0, + result_c1_plus_v0_plus_v1_c1)); + + compute_result_c1.reset(new element_fp2_squared( + bp, *Ac0_plus_Ac1, *result_c1_plus_v0_plus_v1)); + } + + void generate_gates() { + compute_v1->generate_gates(); + compute_v0->generate_gates(); + compute_result_c1->generate_gates(); + } + + void generate_assignments() { + compute_v1->generate_assignments(); + + v0_c0.evaluate(this->bp); + v0_c1.evaluate(this->bp); + compute_v0->generate_assignments(); + + Ac0_plus_Ac1_c0.evaluate(this->bp); + Ac0_plus_Ac1_c1.evaluate(this->bp); + compute_result_c1->generate_assignments(); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Rval = Aval.squared(); + result.generate_assignments(Rval); + } + }; + + /******************************** element_fp4_cyclotomic_squared ************************************/ + + /** + * Component that creates constraints for Fp4 cyclotomic squaring + */ + template + class element_fp4_cyclotomic_squared : public component { + public: + using field_type = Fp4T; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp2; + + using base_field_value_type = typename base_field_type::value_type; + + element_fp4 A; + element_fp4 result; + + detail::blueprint_linear_combination c0_expr_c0; + detail::blueprint_linear_combination c0_expr_c1; + std::shared_ptr c0_expr; + std::shared_ptr> compute_c0_expr; + + detail::blueprint_linear_combination A_c0_plus_A_c1_c0; + detail::blueprint_linear_combination A_c0_plus_A_c1_c1; + std::shared_ptr A_c0_plus_A_c1; + + detail::blueprint_linear_combination c1_expr_c0; + detail::blueprint_linear_combination c1_expr_c1; + std::shared_ptr c1_expr; + std::shared_ptr> compute_c1_expr; + + element_fp4_cyclotomic_squared(blueprint &bp, + const element_fp4 &A, + const element_fp4 &result) : + component(bp), + A(A), result(result) { + /* + A = elt.data[1] ^ 2 + B = elt.data[1] + elt.data[0]; + C = B ^ 2 - A + D = Fp2(A.data[1] * non_residue, A.data[0]) + E = C - D + F = D + D + Fp2::one() + G = E - Fp2::one() + + return Fp4(F, G); + + Enforced with 2 element_fp2_squared's that ensure that: + + elt.data[1] ^ 2 = Fp2(result.data[0].data[1] / 2, (result.data[0].data[0] - 1) / (2 * + non_residue)) = A (elt.data[1] + elt.data[0]) ^ 2 = A + result.data[1] + Fp2(A.data[1] * + non_residue + 1, A.data[0]) + + (elt.data[1] + elt.data[0]) ^ 2 = Fp2(result.data[0].data[1] / 2 + result.data[1].data[0] + + (result.data[0].data[0] - 1) / 2 + 1, (result.data[0].data[0] - 1) / (2 * non_residue) + + result.data[1].data[1] + result.data[0].data[1] / 2) + */ + c0_expr_c0.assign(bp, result.data[0].data[1] * base_field_value_type(0x02).inversed()); + c0_expr_c1.assign( + bp, + (result.data[0].data[0] - base_field_value_type(0x01)) * + (base_field_value_type(0x02) * field_type::value_type::non_residue).inversed()); + + c0_expr.reset(new underlying_element_type(bp, c0_expr_c0, c0_expr_c1)); + compute_c0_expr.reset(new element_fp2_squared(bp, A.data[1], *c0_expr)); + + A_c0_plus_A_c1_c0.assign(bp, A.data[0].data[0] + A.data[1].data[0]); + A_c0_plus_A_c1_c1.assign(bp, A.data[0].data[1] + A.data[1].data[1]); + A_c0_plus_A_c1.reset(new underlying_element_type(bp, A_c0_plus_A_c1_c0, A_c0_plus_A_c1_c1)); + + c1_expr_c0.assign( + bp, + (result.data[0].data[1] + result.data[0].data[0] - base_field_value_type(0x01)) * + base_field_value_type(0x02).inversed() + + result.data[1].data[0] + base_field_value_type(0x01)); + c1_expr_c1.assign( + bp, + (result.data[0].data[0] - base_field_value_type(0x01)) * + (base_field_value_type(0x02) * field_type::value_type::non_residue).inversed() + + result.data[1].data[1] + + result.data[0].data[1] * base_field_value_type(0x02).inversed()); + + c1_expr.reset(new underlying_element_type(bp, c1_expr_c0, c1_expr_c1)); + + compute_c1_expr.reset( + new element_fp2_squared(bp, *A_c0_plus_A_c1, *c1_expr)); + } + + void generate_gates() { + compute_c0_expr->generate_gates(); + compute_c1_expr->generate_gates(); + } + + void generate_assignments() { + compute_c0_expr->generate_assignments(); + + A_c0_plus_A_c1_c0.evaluate(this->bp); + A_c0_plus_A_c1_c1.evaluate(this->bp); + compute_c1_expr->generate_assignments(); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Rval = Aval.squared(); + result.generate_assignments(Rval); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FP4_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp6_2over3.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp6_2over3.hpp new file mode 100644 index 000000000..5d9ccdd8f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/element_fp6_2over3.hpp @@ -0,0 +1,586 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for Fp6 components. +// +// The components verify field arithmetic in Fp6 = Fp3[Y]/(Y^2-X) where +// Fp3 = Fp[X]/(X^3-non_residue) and non_residue is in Fp. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FP6_2OVER3_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FP6_2OVER3_COMPONENTS_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /******************************** element_fp6_2over3 ************************************/ + + /** + * Component that represents an Fp6 element. + */ + template // Fp6 2over3 + class element_fp6_2over3 : public component { + + using field_type = FieldType; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp3; + + using data_type = + std::array; + + public: + data_type data; + + element_fp6_2over3(blueprint &bp) : + component(bp), + data({underlying_element_type(bp), underlying_element_type(bp)}) { + } + + element_fp6_2over3(blueprint &bp, const typename field_type::value_type &el) : + component(bp), + data({underlying_element_type(bp, el.data[0]), underlying_element_type(bp, el.data[1])}) { + } + + element_fp6_2over3(blueprint &bp, const underlying_element_type &in_data0, + const underlying_element_type &in_data1) : + component(bp), + data({underlying_element_type(in_data0), underlying_element_type(in_data1)}) { + } + + void generate_r1cs_equals_const_constraints(const typename field_type::value_type &el) { + data[0].generate_r1cs_equals_const_constraints(el.data[0]); + data[1].generate_r1cs_equals_const_constraints(el.data[1]); + } + + void generate_assignments(const typename field_type::value_type &el) { + data[0].generate_assignments(el.data[0]); + data[1].generate_assignments(el.data[1]); + } + + typename field_type::value_type get_element() { + typename field_type::value_type el; + el.data[0] = data[0].get_element(); + el.data[1] = data[1].get_element(); + return el; + } + + element_fp6_2over3 Frobenius_map(const std::size_t power) const { + detail::blueprint_linear_combination new_c0c0, new_c0c1, new_c0c2, new_c1c0, new_c1c1, + new_c1c2; + new_c0c0.assign(this->bp, data[0].data[0]); + new_c0c1.assign(this->bp, + data[0].data[1] * + underlying_field_type::value_type::Frobenius_coeffs_c1[power % 3]); + new_c0c2.assign(this->bp, + data[0].data[2] * + underlying_field_type::value_type::Frobenius_coeffs_c2[power % 3]); + new_c1c0.assign(this->bp, + data[1].data[0] * field_type::value_type::Frobenius_coeffs_c1[power % 6]); + new_c1c1.assign(this->bp, + data[1].data[1] * + (field_type::value_type::Frobenius_coeffs_c1[power % 6] * + underlying_field_type::value_type::Frobenius_coeffs_c1[power % 3])); + new_c1c2.assign(this->bp, + data[1].data[2] * + (field_type::value_type::Frobenius_coeffs_c1[power % 6] * + underlying_field_type::value_type::Frobenius_coeffs_c2[power % 3])); + + return element_fp6_2over3( + this->bp, + underlying_element_type(this->bp, new_c0c0, new_c0c1, new_c0c2), + underlying_element_type(this->bp, new_c1c0, new_c1c1, new_c1c2)); + } + + void evaluate() const { + data[0].evaluate(); + data[1].evaluate(); + } + }; + + /******************************** element_fp6_2over3_mul ************************************/ + + /** + * Component that creates constraints for Fp6 multiplication. + */ + template + class element_fp6_2over3_mul : public component { + + using field_type = FieldType; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp3; + + public: + element_fp6_2over3 A; + element_fp6_2over3 B; + element_fp6_2over3 result; + + detail::blueprint_linear_combination v0_c0; + detail::blueprint_linear_combination v0_c1; + detail::blueprint_linear_combination v0_c2; + + detail::blueprint_linear_combination Ac0_plus_Ac1_c0; + detail::blueprint_linear_combination Ac0_plus_Ac1_c1; + detail::blueprint_linear_combination Ac0_plus_Ac1_c2; + std::shared_ptr Ac0_plus_Ac1; + + std::shared_ptr v0; + std::shared_ptr v1; + + detail::blueprint_linear_combination Bc0_plus_Bc1_c0; + detail::blueprint_linear_combination Bc0_plus_Bc1_c1; + detail::blueprint_linear_combination Bc0_plus_Bc1_c2; + std::shared_ptr Bc0_plus_Bc1; + + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c0; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c1; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c2; + std::shared_ptr result_c1_plus_v0_plus_v1; + + std::shared_ptr> compute_v0; + std::shared_ptr> compute_v1; + std::shared_ptr> compute_result_c1; + + element_fp6_2over3_mul(blueprint &bp, + const element_fp6_2over3 &A, + const element_fp6_2over3 &B, + const element_fp6_2over3 &result) : + component(bp), + A(A), B(B), result(result) { + /* + Karatsuba multiplication for Fp6 as a quadratic extension of Fp3: + v0 = A.data[0] * B.data[0] + v1 = A.data[1] * B.data[1] + result.data[0] = v0 + non_residue * v1 + result.data[1] = (A.data[0] + A.data[1]) * (B.data[0] + B.data[1]) - v0 - v1 + where "non_residue * elem" := (non_residue * elem.data[2], elem.data[0], elem.data[1]) + + Enforced with 3 element_fp3_mul's that ensure that: + A.data[1] * B.data[1] = v1 + A.data[0] * B.data[0] = v0 + (A.data[0]+A.data[1])*(B.data[0]+B.data[1]) = result.data[1] + v0 + v1 + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + v1.reset(new underlying_element_type(bp)); + + compute_v1.reset(new element_fp3_mul(bp, A.data[1], B.data[1], *v1)); + + v0_c0.assign(bp, result.data[0].data[0] - field_type::value_type::non_residue * v1->data[2]); + + v0_c1.assign(bp, result.data[0].data[1] - v1->data[0]); + v0_c2.assign(bp, result.data[0].data[2] - v1->data[1]); + v0.reset(new underlying_element_type(bp, v0_c0, v0_c1, v0_c2)); + + compute_v0.reset(new element_fp3_mul(bp, A.data[0], B.data[0], *v0)); + + Ac0_plus_Ac1_c0.assign(bp, A.data[0].data[0] + A.data[1].data[0]); + Ac0_plus_Ac1_c1.assign(bp, A.data[0].data[1] + A.data[1].data[1]); + Ac0_plus_Ac1_c2.assign(bp, A.data[0].data[2] + A.data[1].data[2]); + Ac0_plus_Ac1.reset( + new underlying_element_type(bp, Ac0_plus_Ac1_c0, Ac0_plus_Ac1_c1, Ac0_plus_Ac1_c2)); + + Bc0_plus_Bc1_c0.assign(bp, B.data[0].data[0] + B.data[1].data[0]); + Bc0_plus_Bc1_c1.assign(bp, B.data[0].data[1] + B.data[1].data[1]); + Bc0_plus_Bc1_c2.assign(bp, B.data[0].data[2] + B.data[1].data[2]); + Bc0_plus_Bc1.reset( + new underlying_element_type(bp, Bc0_plus_Bc1_c0, Bc0_plus_Bc1_c1, Bc0_plus_Bc1_c2)); + + result_c1_plus_v0_plus_v1_c0.assign(bp, result.data[1].data[0] + v0->data[0] + v1->data[0]); + result_c1_plus_v0_plus_v1_c1.assign(bp, result.data[1].data[1] + v0->data[1] + v1->data[1]); + result_c1_plus_v0_plus_v1_c2.assign(bp, result.data[1].data[2] + v0->data[2] + v1->data[2]); + result_c1_plus_v0_plus_v1.reset(new underlying_element_type(bp, + result_c1_plus_v0_plus_v1_c0, + result_c1_plus_v0_plus_v1_c1, + result_c1_plus_v0_plus_v1_c2)); + + compute_result_c1.reset(new element_fp3_mul( + bp, *Ac0_plus_Ac1, *Bc0_plus_Bc1, *result_c1_plus_v0_plus_v1)); + } + + void generate_gates() { + compute_v0->generate_gates(); + compute_v1->generate_gates(); + compute_result_c1->generate_gates(); + } + + void generate_assignments() { + compute_v0->generate_assignments(); + compute_v1->generate_assignments(); + + Ac0_plus_Ac1_c0.evaluate(this->bp); + Ac0_plus_Ac1_c1.evaluate(this->bp); + Ac0_plus_Ac1_c2.evaluate(this->bp); + + Bc0_plus_Bc1_c0.evaluate(this->bp); + Bc0_plus_Bc1_c1.evaluate(this->bp); + Bc0_plus_Bc1_c2.evaluate(this->bp); + + compute_result_c1->generate_assignments(); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Bval = B.get_element(); + const typename field_type::value_type Rval = Aval * Bval; + + result.generate_assignments(Rval); + + result_c1_plus_v0_plus_v1_c0.evaluate(this->bp); + result_c1_plus_v0_plus_v1_c1.evaluate(this->bp); + result_c1_plus_v0_plus_v1_c2.evaluate(this->bp); + + compute_result_c1->generate_assignments(); + } + }; + + /******************************** element_fp6_2over3_mul_by_2345 + * ************************************/ + + /** + * Component that creates constraints for Fp6 multiplication by a Fp6 element B for which + * B.data[0].data[0] = B.data[0].data[1] = 0. + */ + template + class element_fp6_2over3_mul_by_2345 : public component { + using field_type = FieldType; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp3; + + public: + element_fp6_2over3 A; + element_fp6_2over3 B; + element_fp6_2over3 result; + + detail::blueprint_linear_combination v0_c0; + detail::blueprint_linear_combination v0_c1; + detail::blueprint_linear_combination v0_c2; + + detail::blueprint_linear_combination Ac0_plus_Ac1_c0; + detail::blueprint_linear_combination Ac0_plus_Ac1_c1; + detail::blueprint_linear_combination Ac0_plus_Ac1_c2; + std::shared_ptr Ac0_plus_Ac1; + + std::shared_ptr v0; + std::shared_ptr v1; + + detail::blueprint_linear_combination Bc0_plus_Bc1_c0; + detail::blueprint_linear_combination Bc0_plus_Bc1_c1; + detail::blueprint_linear_combination Bc0_plus_Bc1_c2; + std::shared_ptr Bc0_plus_Bc1; + + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c0; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c1; + detail::blueprint_linear_combination result_c1_plus_v0_plus_v1_c2; + std::shared_ptr result_c1_plus_v0_plus_v1; + + std::shared_ptr> compute_v1; + std::shared_ptr> compute_result_c1; + + element_fp6_2over3_mul_by_2345(blueprint &bp, + const element_fp6_2over3 &A, + const element_fp6_2over3 &B, + const element_fp6_2over3 &result) : + component(bp), + A(A), B(B), result(result) { + /* + Karatsuba multiplication for Fp6 as a quadratic extension of Fp3: + v0 = A.data[0] * B.data[0] + v1 = A.data[1] * B.data[1] + result.data[0] = v0 + non_residue * v1 + result.data[1] = (A.data[0] + A.data[1]) * (B.data[0] + B.data[1]) - v0 - v1 + where "non_residue * elem" := (non_residue * elem.data[2], elem.data[0], elem.data[1]) + + We know that B.data[0].data[0] = B.data[0].data[1] = 0 + + Enforced with 2 element_fp3_mul's that ensure that: + A.data[1] * B.data[1] = v1 + (A.data[0]+A.data[1])*(B.data[0]+B.data[1]) = result.data[1] + v0 + v1 + + And one multiplication (three direct constraints) that enforces A.data[0] * B.data[0] + = v0, where B.data[0].data[0] = B.data[0].data[1] = 0. + + Note that (u + v * X + t * X^2) * (0 + 0 * X + z * X^2) = + (v * z * non_residue + t * z * non_residue * X + u * z * X^2) + + Reference: + "Multiplication and Squaring on Pairing-Friendly Fields" + Devegili, OhEigeartaigh, Scott, Dahab + */ + v1.reset(new underlying_element_type(bp)); + compute_v1.reset(new element_fp3_mul(bp, A.data[1], B.data[1], *v1)); + + /* we inline result.data[0] in v0 as follows: v0 = (result.data[0].data[0] - + * field_type::value_type::non_residue * v1->data[2], + * result.data[0].data[1] - v1->data[0], result.data[0].data[2] - v1->data[1]) */ + v0.reset(new underlying_element_type(bp)); + + Ac0_plus_Ac1_c0.assign(bp, A.data[0].data[0] + A.data[1].data[0]); + Ac0_plus_Ac1_c1.assign(bp, A.data[0].data[1] + A.data[1].data[1]); + Ac0_plus_Ac1_c2.assign(bp, A.data[0].data[2] + A.data[1].data[2]); + Ac0_plus_Ac1.reset( + new underlying_element_type(bp, Ac0_plus_Ac1_c0, Ac0_plus_Ac1_c1, Ac0_plus_Ac1_c2)); + + Bc0_plus_Bc1_c0.assign(bp, B.data[0].data[0] + B.data[1].data[0]); + Bc0_plus_Bc1_c1.assign(bp, B.data[0].data[1] + B.data[1].data[1]); + Bc0_plus_Bc1_c2.assign(bp, B.data[0].data[2] + B.data[1].data[2]); + Bc0_plus_Bc1.reset( + new underlying_element_type(bp, Bc0_plus_Bc1_c0, Bc0_plus_Bc1_c1, Bc0_plus_Bc1_c2)); + + result_c1_plus_v0_plus_v1_c0.assign(bp, result.data[1].data[0] + v0->data[0] + v1->data[0]); + result_c1_plus_v0_plus_v1_c1.assign(bp, result.data[1].data[1] + v0->data[1] + v1->data[1]); + result_c1_plus_v0_plus_v1_c2.assign(bp, result.data[1].data[2] + v0->data[2] + v1->data[2]); + result_c1_plus_v0_plus_v1.reset(new underlying_element_type(bp, + result_c1_plus_v0_plus_v1_c0, + result_c1_plus_v0_plus_v1_c1, + result_c1_plus_v0_plus_v1_c2)); + + compute_result_c1.reset(new element_fp3_mul( + bp, *Ac0_plus_Ac1, *Bc0_plus_Bc1, *result_c1_plus_v0_plus_v1)); + } + + void generate_gates() { + compute_v1->generate_gates(); + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0].data[1], underlying_field_type::value_type::non_residue * B.data[0].data[2], + result.data[0].data[0] - field_type::value_type::non_residue * v1->data[2])); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0].data[2], underlying_field_type::value_type::non_residue * B.data[0].data[2], + result.data[0].data[1] - v1->data[0])); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + A.data[0].data[0], B.data[0].data[2], result.data[0].data[2] - v1->data[1])); + compute_result_c1->generate_gates(); + } + + void generate_assignments() { + compute_v1->generate_assignments(); + + const typename underlying_field_type::value_type A_c0_val = A.data[0].get_element(); + const typename underlying_field_type::value_type B_c0_val = B.data[0].get_element(); + assert(B_c0_val.data[0].is_zero()); + assert(B_c0_val.data[1].is_zero()); + + const typename underlying_field_type::value_type v0_val = A_c0_val * B_c0_val; + v0->generate_assignments(v0_val); + + Ac0_plus_Ac1_c0.evaluate(this->bp); + Ac0_plus_Ac1_c1.evaluate(this->bp); + Ac0_plus_Ac1_c2.evaluate(this->bp); + + Bc0_plus_Bc1_c0.evaluate(this->bp); + Bc0_plus_Bc1_c1.evaluate(this->bp); + Bc0_plus_Bc1_c2.evaluate(this->bp); + + compute_result_c1->generate_assignments(); + + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Bval = B.get_element(); + const typename field_type::value_type Rval = Aval * Bval; + + result.generate_assignments(Rval); + + result_c1_plus_v0_plus_v1_c0.evaluate(this->bp); + result_c1_plus_v0_plus_v1_c1.evaluate(this->bp); + result_c1_plus_v0_plus_v1_c2.evaluate(this->bp); + + compute_result_c1->generate_assignments(); + } + }; + + /******************************** element_fp6_2over3_squared ************************************/ + + /** + * Component that creates constraints for Fp6 squaring. + */ + template + class element_fp6_2over3_squared : public component { + + using field_type = FieldType; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp3; + + public: + element_fp6_2over3 A; + element_fp6_2over3 result; + + std::shared_ptr> mul; + + element_fp6_2over3_squared(blueprint &bp, + const element_fp6_2over3 &A, + const element_fp6_2over3 &result) : + component(bp), + A(A), result(result) { + mul.reset(new element_fp6_2over3_mul(bp, A, A, result)); + } + + void generate_gates() { + mul->generate_gates(); + } + + void generate_assignments() { + mul->generate_assignments(); + } + }; + + /******************************** element_fp6_2over3_cyclotomic_squared + * ************************************/ + + /** + * Component that creates constraints for Fp6 cyclotomic squaring + */ + template + class element_fp6_2over3_cyclotomic_squared : public component { + + using field_type = FieldType; + using base_field_type = typename field_type::base_field_type; + using underlying_field_type = typename field_type::underlying_field_type; + + using underlying_element_type = element_fp3; + + typedef typename field_type::underlying_field_type Fp2T; + + public: + element_fp6_2over3 A; + element_fp6_2over3 result; + + std::shared_ptr> a; + std::shared_ptr> b; + std::shared_ptr> c; + + detail::blueprint_linear_combination asq_c0; + detail::blueprint_linear_combination asq_c1; + + detail::blueprint_linear_combination bsq_c0; + detail::blueprint_linear_combination bsq_c1; + + detail::blueprint_linear_combination csq_c0; + detail::blueprint_linear_combination csq_c1; + + std::shared_ptr> asq; + std::shared_ptr> bsq; + std::shared_ptr> csq; + + std::shared_ptr> compute_asq; + std::shared_ptr> compute_bsq; + std::shared_ptr> compute_csq; + + element_fp6_2over3_cyclotomic_squared(blueprint &bp, + const element_fp6_2over3 &A, + const element_fp6_2over3 &result) : + component(bp), + A(A), result(result) { + /* + underlying_field_type a = underlying_field_type(data[0].data[0], data[1].data[1]); + underlying_field_type b = underlying_field_type(data[1].data[0], data[0].data[2]); + underlying_field_type c = underlying_field_type(data[0].data[1], data[1].data[2]); + + underlying_field_type asq = a.squared(); + underlying_field_type bsq = b.squared(); + underlying_field_type csq = c.squared(); + + result.data[0].data[0] = 3 * asq_a - 2 * a_a; + result.data[1].data[1] = 3 * asq_b + 2 * a_b; + + result.data[0].data[1] = 3 * bsq_a - 2 * c_a; + result.data[1].data[2] = 3 * bsq_b + 2 * c_b; + + result.data[0].data[2] = 3 * csq_a - 2 * b_b; + result.data[1].data[0] = 3 * my_Fp3::non_residue * csq_b + 2 * b_a; + + return Fp6_2over3_model(my_Fp3(A_a, C_a, B_b), + my_Fp3(B_a, A_b, C_b)) + */ + a.reset(new element_fp2(bp, A.data[0].data[0], A.data[1].data[1])); + b.reset(new element_fp2(bp, A.data[1].data[0], A.data[0].data[2])); + c.reset(new element_fp2(bp, A.data[0].data[1], A.data[1].data[2])); + + asq_c0.assign(bp, (result.data[0].data[0] + 2 * a->data[0]) * + typename base_field_type::value_type(3).inversed()); + asq_c1.assign(bp, (result.data[1].data[1] - 2 * a->data[1]) * + typename base_field_type::value_type(3).inversed()); + + bsq_c0.assign(bp, (result.data[0].data[1] + 2 * c->data[0]) * + typename base_field_type::value_type(3).inversed()); + bsq_c1.assign(bp, (result.data[1].data[2] - 2 * c->data[1]) * + typename base_field_type::value_type(3).inversed()); + + csq_c0.assign(bp, (result.data[0].data[2] + 2 * b->data[1]) * + typename base_field_type::value_type(3).inversed()); + csq_c1.assign( + bp, + (result.data[1].data[0] - 2 * b->data[0]) * + (typename base_field_type::value_type(3) * Fp2T::value_type::non_residue).inversed()); + + asq.reset(new element_fp2(bp, asq_c0, asq_c1)); + bsq.reset(new element_fp2(bp, bsq_c0, bsq_c1)); + csq.reset(new element_fp2(bp, csq_c0, csq_c1)); + + compute_asq.reset(new element_fp2_squared(bp, *a, *asq)); + compute_bsq.reset(new element_fp2_squared(bp, *b, *bsq)); + compute_csq.reset(new element_fp2_squared(bp, *c, *csq)); + } + + void generate_gates() { + compute_asq->generate_gates(); + compute_bsq->generate_gates(); + compute_csq->generate_gates(); + } + + void generate_assignments() { + const typename field_type::value_type Aval = A.get_element(); + const typename field_type::value_type Rval = Aval.cyclotomic_squared(); + + result.generate_assignments(Rval); + + asq->evaluate(); + bsq->evaluate(); + csq->evaluate(); + + compute_asq->generate_assignments(); + compute_bsq->generate_assignments(); + compute_csq->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FP6_2OVER3_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/exponentiation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/exponentiation.hpp new file mode 100644 index 000000000..05df33abe --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/exponentiation.hpp @@ -0,0 +1,213 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the exponentiation component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_EXPONENTIATION_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_EXPONENTIATION_COMPONENT_HPP + +#include +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /** + * The exponentiation component verifies field exponentiation in the field F_{p^k}. + * + * Note that the power is a constant (i.e., hardcoded into the component). + */ + template + class Fpk_variableT, + template + class Fpk_mul_componentT, + template + class Fpk_sqr_componentT, + typename NumberType = typename FpkT::integral_type> + class exponentiation_component : component { + public: + typedef typename FpkT::base_field_type FieldType; + typedef NumberType integral_type; + std::vector NAF; + + std::vector>> intermediate; + std::vector>> addition_steps; + std::vector>> subtraction_steps; + std::vector>> doubling_steps; + + Fpk_variableT elt; + integral_type power; + Fpk_variableT result; + + std::size_t intermed_count; + std::size_t add_count; + std::size_t sub_count; + std::size_t dbl_count; + + template + exponentiation_component(blueprint &bp, + const Fpk_variableT &elt, + const multiprecision::number &power, + const Fpk_variableT &result) : + component(bp), + elt(elt), power(power), result(result) { + NAF = multiprecision::find_wnaf(1, power); + + intermed_count = 0; + add_count = 0; + sub_count = 0; + dbl_count = 0; + + bool found_nonzero = false; + for (long i = NAF.size() - 1; i >= 0; --i) { + if (found_nonzero) { + ++dbl_count; + ++intermed_count; + } + + if (NAF[i] != 0) { + found_nonzero = true; + + if (NAF[i] > 0) { + ++add_count; + ++intermed_count; + } else { + ++sub_count; + ++intermed_count; + } + } + } + + intermediate.resize(intermed_count); + intermediate[0].reset(new Fpk_variableT(bp, FpkT::value_type::one())); + for (std::size_t i = 1; i < intermed_count; ++i) { + intermediate[i].reset(new Fpk_variableT(bp)); + } + addition_steps.resize(add_count); + subtraction_steps.resize(sub_count); + doubling_steps.resize(dbl_count); + + found_nonzero = false; + + std::size_t dbl_id = 0, add_id = 0, sub_id = 0, intermed_id = 0; + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (found_nonzero) { + doubling_steps[dbl_id].reset(new Fpk_sqr_componentT( + bp, + *intermediate[intermed_id], + (intermed_id + 1 == intermed_count ? result : *intermediate[intermed_id + 1]))); + ++intermed_id; + ++dbl_id; + } + + if (NAF[i] != 0) { + found_nonzero = true; + + if (NAF[i] > 0) { + /* next = cur * elt */ + addition_steps[add_id].reset(new Fpk_mul_componentT( + bp, + *intermediate[intermed_id], + elt, + (intermed_id + 1 == intermed_count ? result : *intermediate[intermed_id + 1]))); + ++add_id; + ++intermed_id; + } else { + /* next = cur / elt, i.e. next * elt = cur */ + subtraction_steps[sub_id].reset(new Fpk_mul_componentT( + bp, + (intermed_id + 1 == intermed_count ? result : *intermediate[intermed_id + 1]), + elt, + *intermediate[intermed_id])); + ++sub_id; + ++intermed_id; + } + } + } + } + void generate_gates() { + for (std::size_t i = 0; i < add_count; ++i) { + addition_steps[i]->generate_gates(); + } + + for (std::size_t i = 0; i < sub_count; ++i) { + subtraction_steps[i]->generate_gates(); + } + + for (std::size_t i = 0; i < dbl_count; ++i) { + doubling_steps[i]->generate_gates(); + } + } + void generate_assignments() { + intermediate[0]->generate_assignments(FpkT::value_type::one()); + + bool found_nonzero = false; + std::size_t dbl_id = 0, add_id = 0, sub_id = 0, intermed_id = 0; + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (found_nonzero) { + doubling_steps[dbl_id]->generate_assignments(); + ++intermed_id; + ++dbl_id; + } + + if (NAF[i] != 0) { + found_nonzero = true; + + if (NAF[i] > 0) { + addition_steps[add_id]->generate_assignments(); + ++intermed_id; + ++add_id; + } else { + const typename FpkT::value_type cur_val = intermediate[intermed_id]->get_element(); + const typename FpkT::value_type elt_val = elt.get_element(); + const typename FpkT::value_type next_val = cur_val * elt_val.inversed(); + + (intermed_id + 1 == intermed_count ? result : *intermediate[intermed_id + 1]) + .generate_assignments(next_val); + + subtraction_steps[sub_id]->generate_assignments(); + + ++intermed_id; + ++sub_id; + } + } + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_EXPONENTIATION_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/field_to_bits.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/field_to_bits.hpp new file mode 100644 index 000000000..2ed60e6ca --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/fields/r1cs/field_to_bits.hpp @@ -0,0 +1,213 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_FIELD_TO_BITS_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_FIELD_TO_BITS_COMPONENTS_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /** + * Converts a field element to bits, with strict validation that + * ensures it's less than the (hard-coded) field modulus. + * + * This allows the 254th bit to be decoded. + * + * Given an array of variable bits and an equal length array of fixed bits + * verify that the variable bits are lower than the fixed bits. + * + * Starting with the MSB, continuing to the LSB, for each pair of bits: + * + * If fixed bit is 1 and variable bit is 1, state is 'equal' + * If fixed bit is 0 and variable bit is 0, state is 'equal' + * If fixed bit is 1 and variable bit is 0, state is 'less' + * If fixed bit is 0 and variable bit is 1, state is 'greater' + * + * The comparison continues until the state 'less' or 'greater' occurs + * any further comparisons are ignored and don't affect the result. + * The first differing bit determines the result, the default is 'equal'. + * + * The result must be 'less' for success to ensure congruency between + * the bits and the field element. + * + * f = fixed bit + * v = variable bit + * + * F(f,v) = LUT [f v] -> [equal, greater, less, equal] + * + * 0 0 -> equal + * 0 1 -> greater + * 1 0 -> less + * 1 1 -> equal + * + * This gives us the bit-by-bit comparison, but what's necessary is + * to terminate the comparison upon the less or greater states. + * One constraint at the end must enforce the final result being 'less' or 'equal' + * + * When the desired result is less or equal to `q-1`, then 3 states can be merged + * into one, where the 'greater' state zeros any further states. This makes an + * accumulator of sorts, where the result of the next comparison is AND'd by the + * previous result. This means the current result can be multiplied by the previous + * assuming the state `greater` maps to zero, and all others are mapped to `1`. + * + * The final state will be `1` if it's less or equal than `F_q`, otherwise 0. + * The constraints necessary for this are: + * + * current * previous = result + * + * Where if `previous` is 0 then `result` will be 0, and all following results + * will be zero. + */ + template + struct field_to_bits_strict : public component { + using field_type = Field; + using field_value_type = typename field_type::value_type; + using result_type = detail::blueprint_variable_vector; + + // Output bits + result_type result; + + // Intermediate variables & components + packing packer; + detail::blueprint_variable_vector results; + std::vector> comparisons; + + private: + void init() { + // Constant bit is 0 + const std::vector table_cmp_0 = { + field_value_type::zero(), // 0, equal + field_value_type::one() // 1, greater + }; + + // Constant bit is 1 + const std::vector table_cmp_1 = { + field_value_type::one(), // 0, less + field_value_type::one() // 1, equal + }; + + const typename field_type::integral_type largest_value = field_type::modulus - 1; + + for (size_t i = 0; i < field_type::value_bits; ++i) { + if (multiprecision::bit_test(largest_value, i)) { + this->comparisons.emplace_back(this->bp, table_cmp_1, this->result[i]); + } else { + this->comparisons.emplace_back(this->bp, table_cmp_0, this->result[i]); + } + } + } + + public: + /// Auto allocation of the result + field_to_bits_strict(blueprint &bp, + const detail::blueprint_linear_combination &in_field_element) : + component(bp), + result([&]() { + detail::blueprint_variable_vector r; + r.allocate(bp, field_type::value_bits); + return r; + }()), + packer(bp, result, in_field_element), results([&]() { + detail::blueprint_variable_vector r; + r.allocate(bp, field_type::value_bits - 1); + return r; + }()) { + init(); + } + + /// Manual allocation of the result + field_to_bits_strict(blueprint &bp, + const detail::blueprint_linear_combination &in_field_element, + const result_type &in_result) : + component(bp), + result(in_result), packer(bp, result, in_field_element), results([&]() { + detail::blueprint_variable_vector r; + r.allocate(bp, field_type::value_bits - 1); + return r; + }()) { + init(); + } + + void generate_gates() { + this->packer.generate_gates(true); + + for (auto &component_it : this->comparisons) { + component_it.generate_gates(); + } + + // AND all of the comparisons + std::size_t last_bit = field_type::value_bits - 1; + for (std::size_t i = last_bit; i > 0; --i) { + if (i == last_bit) { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(this->comparisons[i - 1].result, + this->comparisons[i].result, + this->results[i - 1])); + } else { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + this->comparisons[i - 1].result, this->results[i], this->results[i - 1])); + } + } + } + + void generate_assignments() { + this->packer.generate_assignments_from_packed(); + + for (auto &component_it : this->comparisons) { + component_it.generate_assignments(); + } + + // Iterate from MSB to LSB + std::size_t last_bit = (field_type::value_bits - 1); + for (std::size_t i = last_bit; i > 0; --i) { + // current * previous = result + if (i == last_bit) { + this->bp.val(this->results[i - 1]) = this->bp.val(this->comparisons[i - 1].result) * + this->bp.val(this->comparisons[i].result); + } else { + this->bp.val(this->results[i - 1]) = + this->bp.val(this->results[i]) * this->bp.val(this->comparisons[i - 1].result); + } + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_FIELD_TO_BITS_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt4.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt4.hpp new file mode 100644 index 000000000..1133d91c7 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt4.hpp @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of specializations of basic_pairing_component_policy to +// - basic_pairing_component_policy. +// +// See pairing_params.hpp . +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_PAIRING_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_PAIRING_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + namespace detail { + + using namespace nil::crypto3::algebra; + + template + class basic_pairing_component_policy; + + /** + * Specialization for MNT4. + */ + template + class basic_pairing_component_policy> { + using curve_type = typename curves::mnt4; + + public: + using Fqe_variable_type = typename basic_curve_component_policy::Fqe_variable_type; + using Fqe_mul_component_type = + typename basic_curve_component_policy::Fqe_mul_component_type; + using Fqe_mul_by_lc_component_type = + typename basic_curve_component_policy::Fqe_mul_by_lc_component_type; + using Fqe_sqr_component_type = + typename basic_curve_component_policy::Fqe_sqr_component_type; + + using Fqk_variable_type = typename basic_curve_component_policy::Fqk_variable_type; + using Fqk_mul_component_type = + typename basic_curve_component_policy::Fqk_mul_component_type; + using Fqk_special_mul_component_type = + typename basic_curve_component_policy::Fqk_special_mul_component_type; + using Fqk_sqr_component_type = + typename basic_curve_component_policy::Fqk_sqr_component_type; + + constexpr static const typename curve_type::integral_type pairing_loop_count = + curve_type::pairing::pairing_loop_count; + + constexpr static const typename curve_type::template g1_type<>::field_type::value_type + g1_coeff_a = curve_type::a; + + constexpr static const typename curve_type::template g1_type<>::field_type::value_type + g1_coeff_b = curve_type::b; + + constexpr static const typename curve_type::template g2_type<>::field_type::value_type + g2_coeff_a = typename curve_type::template g2_type<>::field_type::value_type( + g1_coeff_a * curve_type::template g2_type<>::field_type::value_type::non_residue, + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero()); + + constexpr static const typename curve_type::template g2_type<>::field_type::value_type + g2_coeff_b = typename curve_type::template g2_type<>::field_type::value_type( + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero(), + g1_coeff_b *curve_type::template g2_type<>::field_type::value_type::non_residue); + }; + } // namespace detail + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MNT4_BASIC_PAIRING_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt6.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt6.hpp new file mode 100644 index 000000000..796bd1154 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/detail/r1cs/mnt6.hpp @@ -0,0 +1,101 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of specializations of basic_pairing_component_policy to +// - basic_pairing_component_policy. +// +// See pairing_params.hpp . +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_PAIRING_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_PAIRING_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + namespace detail { + + using namespace nil::crypto3::algebra; + + template + class basic_pairing_component_policy; + + /** + * Specialization for MNT6. + */ + template + class basic_pairing_component_policy> { + using curve_type = typename curves::mnt6; + using curve_component_policy = basic_curve_component_policy; + + public: + using Fqe_variable_type = typename basic_curve_component_policy::Fqe_variable_type; + using Fqe_mul_component_type = + typename basic_curve_component_policy::Fqe_mul_component_type; + using Fqe_mul_by_lc_component_type = + typename basic_curve_component_policy::Fqe_mul_by_lc_component_type; + using Fqe_sqr_component_type = + typename basic_curve_component_policy::Fqe_sqr_component_type; + + using Fqk_variable_type = typename basic_curve_component_policy::Fqk_variable_type; + using Fqk_mul_component_type = + typename basic_curve_component_policy::Fqk_mul_component_type; + using Fqk_special_mul_component_type = + typename basic_curve_component_policy::Fqk_special_mul_component_type; + using Fqk_sqr_component_type = + typename basic_curve_component_policy::Fqk_sqr_component_type; + + constexpr static const typename curve_type::integral_type &pairing_loop_count = + curve_type::pairing::pairing_loop_count; + + constexpr static const typename curve_type::template g1_type<>::field_type::value_type + g1_coeff_a = curve_type::a; + + constexpr static const typename curve_type::template g1_type<>::field_type::value_type + g1_coeff_b = curve_type::b; + + constexpr static const typename curve_type::template g2_type<>::field_type::value_type + g2_coeff_a = typename curve_type::template g2_type<>::field_type::value_type( + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero(), + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero(), + g1_coeff_a); + + constexpr static const typename curve_type::template g2_type<>::field_type::value_type + g2_coeff_b = typename curve_type::template g2_type<>::field_type::value_type( + g1_coeff_b * + typename curve_type::template g2_type<>::field_type::value_type::non_residue, + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero(), + curve_type::template g2_type<>::field_type::underlying_field_type::value_type::zero()); + }; + } // namespace detail + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MNT6_BASIC_PAIRING_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/final_exponentiation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/final_exponentiation.hpp new file mode 100644 index 000000000..3e53bced5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/final_exponentiation.hpp @@ -0,0 +1,344 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for final exponentiation components. +// +// The components verify final exponentiation for Weiersrass curves with embedding +// degrees 4 and 6. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_FINAL_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_FINAL_EXPONENTIATION_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /** + * Component for final exponentiation with embedding degree 4. + */ + template + struct final_exp_component; + + template + class final_exp_component> + : public component::scalar_field_type> { + + using curve_type = curves::mnt4; + + using Fqk_variable_type = + typename detail::basic_pairing_component_policy::Fqk_variable_type; + using Fqk_mul_component_type = + typename detail::basic_pairing_component_policy::Fqk_mul_component_type; + + public: + typedef typename curve_type::scalar_field_type field_type; + + Fqk_variable_type el; + std::shared_ptr one; + std::shared_ptr el_inv; + std::shared_ptr el_q_3; + std::shared_ptr el_q_3_minus_1; + std::shared_ptr alpha; + std::shared_ptr beta; + std::shared_ptr beta_q; + std::shared_ptr el_inv_q_3; + std::shared_ptr el_inv_q_3_minus_1; + std::shared_ptr inv_alpha; + std::shared_ptr inv_beta; + std::shared_ptr w1; + std::shared_ptr w0; + std::shared_ptr result; + + std::shared_ptr compute_el_inv; + std::shared_ptr compute_el_q_3_minus_1; + std::shared_ptr compute_beta; + std::shared_ptr compute_el_inv_q_3_minus_1; + std::shared_ptr compute_inv_beta; + + using exponentiation_component_type = + exponentiation_component; + + std::shared_ptr compute_w1; + std::shared_ptr> + compute_w0; + std::shared_ptr compute_result; + + blueprint_variable result_is_one; + + final_exp_component(blueprint &bp, + const Fqk_variable_type &el, + const blueprint_variable &result_is_one) : + component(bp), + el(el), result_is_one(result_is_one) { + one.reset(new Fqk_variable_type(bp)); + el_inv.reset(new Fqk_variable_type(bp)); + el_q_3.reset(new Fqk_variable_type(el.Frobenius_map(3))); + el_q_3_minus_1.reset(new Fqk_variable_type(bp)); + alpha.reset(new Fqk_variable_type(el_q_3_minus_1->Frobenius_map(1))); + beta.reset(new Fqk_variable_type(bp)); + beta_q.reset(new Fqk_variable_type(beta->Frobenius_map(1))); + + el_inv_q_3.reset(new Fqk_variable_type(el_inv->Frobenius_map(3))); + el_inv_q_3_minus_1.reset(new Fqk_variable_type(bp)); + inv_alpha.reset(new Fqk_variable_type(el_inv_q_3_minus_1->Frobenius_map(1))); + inv_beta.reset(new Fqk_variable_type(bp)); + w1.reset(new Fqk_variable_type(bp)); + w0.reset(new Fqk_variable_type(bp)); + result.reset(new Fqk_variable_type(bp)); + + compute_el_inv.reset(new Fqk_mul_component_type(bp, el, *el_inv, *one)); + compute_el_q_3_minus_1.reset(new Fqk_mul_component_type(bp, *el_q_3, *el_inv, *el_q_3_minus_1)); + compute_beta.reset(new Fqk_mul_component_type(bp, *alpha, *el_q_3_minus_1, *beta)); + + compute_el_inv_q_3_minus_1.reset( + new Fqk_mul_component_type(bp, *el_inv_q_3, el, *el_inv_q_3_minus_1)); + compute_inv_beta.reset( + new Fqk_mul_component_type(bp, *inv_alpha, *el_inv_q_3_minus_1, *inv_beta)); + + compute_w1.reset(new exponentiation_component( + bp, *beta_q, curve_type::pairing::final_exponent_last_chunk_w1, *w1)); + + compute_w0.reset(new exponentiation_component( + bp, + (curve_type::pairing::final_exponent_last_chunk_is_w0_neg ? *inv_beta : *beta), + curve_type::pairing::final_exponent_last_chunk_abs_of_w0, + *w0)); + + compute_result.reset(new Fqk_mul_component_type(bp, *w1, *w0, *result)); + } + + void generate_gates() { + one->generate_r1cs_equals_const_constraints( + curve_type::pairing::pair_curve_type::pairing::fqk_type::value_type::one()); + + compute_el_inv->generate_gates(); + compute_el_q_3_minus_1->generate_gates(); + compute_beta->generate_gates(); + + compute_el_inv_q_3_minus_1->generate_gates(); + compute_inv_beta->generate_gates(); + + compute_w0->generate_gates(); + compute_w1->generate_gates(); + compute_result->generate_gates(); + + generate_boolean_r1cs_constraint(this->bp, result_is_one); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, 1 - result->c0.c0, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c0.c1, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c0.c2, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c1.c0, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c1.c1, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c1.c2, 0)); + } + + void generate_assignments() { + one->generate_assignments( + curve_type::pairing::pair_curve_type::pairing::fqk_type::value_type::one()); + el_inv->generate_assignments(el.get_element().inversed()); + + compute_el_inv->generate_assignments(); + el_q_3->evaluate(); + compute_el_q_3_minus_1->generate_assignments(); + alpha->evaluate(); + compute_beta->generate_assignments(); + beta_q->evaluate(); + + el_inv_q_3->evaluate(); + compute_el_inv_q_3_minus_1->generate_assignments(); + inv_alpha->evaluate(); + compute_inv_beta->generate_assignments(); + + compute_w0->generate_assignments(); + compute_w1->generate_assignments(); + compute_result->generate_assignments(); + + this->bp.val(result_is_one) = + (result->get_element() == one->get_element() ? field_type::value_type::one() : + field_type::value_type::zero()); + } + }; + + /** + * Component for final exponentiation with embedding degree 6. + */ + template + class final_exp_component> + : public component::scalar_field_type> { + + using curve_type = curves::mnt6; + + using Fqk_variable_type = + typename detail::basic_pairing_component_policy::Fqk_variable_type; + using Fqk_mul_component_type = + typename detail::basic_pairing_component_policy::Fqk_mul_component_type; + + public: + typedef typename curve_type::scalar_field_type field_type; + + Fqk_variable_type el; + std::shared_ptr one; + std::shared_ptr el_inv; + std::shared_ptr el_q_2; + std::shared_ptr el_q_2_minus_1; + std::shared_ptr el_q_3_minus_q; + std::shared_ptr el_inv_q_2; + std::shared_ptr el_inv_q_2_minus_1; + std::shared_ptr w1; + std::shared_ptr w0; + std::shared_ptr result; + + std::shared_ptr compute_el_inv; + std::shared_ptr compute_el_q_2_minus_1; + std::shared_ptr compute_el_inv_q_2_minus_1; + + std::shared_ptr> + compute_w1; + std::shared_ptr> + compute_w0; + std::shared_ptr compute_result; + + blueprint_variable result_is_one; + + final_exp_component(blueprint &bp, + const Fqk_variable_type &el, + const blueprint_variable &result_is_one) : + component(bp), + el(el), result_is_one(result_is_one) { + one.reset(new Fqk_variable_type(bp)); + el_inv.reset(new Fqk_variable_type(bp)); + el_q_2.reset(new Fqk_variable_type(el.Frobenius_map(2))); + el_q_2_minus_1.reset(new Fqk_variable_type(bp)); + el_q_3_minus_q.reset(new Fqk_variable_type(el_q_2_minus_1->Frobenius_map(1))); + el_inv_q_2.reset(new Fqk_variable_type(el_inv->Frobenius_map(2))); + el_inv_q_2_minus_1.reset(new Fqk_variable_type(bp)); + w1.reset(new Fqk_variable_type(bp)); + w0.reset(new Fqk_variable_type(bp)); + result.reset(new Fqk_variable_type(bp)); + + compute_el_inv.reset(new Fqk_mul_component_type(bp, el, *el_inv, *one)); + compute_el_q_2_minus_1.reset(new Fqk_mul_component_type(bp, *el_q_2, *el_inv, *el_q_2_minus_1)); + compute_el_inv_q_2_minus_1.reset( + new Fqk_mul_component_type(bp, *el_inv_q_2, el, *el_inv_q_2_minus_1)); + + compute_w1.reset(new exponentiation_component > + (bp, *el_q_3_minus_q, curve_type::pairing::final_exponent_last_chunk_w1, *w1)); + compute_w0.reset( + new exponentiation_component > + (bp, + (curve_type::pairing::final_exponent_last_chunk_is_w0_neg ? *el_inv_q_2_minus_1 : + *el_q_2_minus_1), + curve_type::pairing::final_exponent_last_chunk_abs_of_w0, + *w0)); + compute_result.reset(new Fqk_mul_component_type(bp, *w1, *w0, *result)); + } + + void generate_gates() { + one->generate_r1cs_equals_const_constraints( + curve_type::pairing::pair_curve_type::pairing::fqk_type::value_type::one()); + + compute_el_inv->generate_gates(); + compute_el_q_2_minus_1->generate_gates(); + compute_el_inv_q_2_minus_1->generate_gates(); + compute_w1->generate_gates(); + compute_w0->generate_gates(); + compute_result->generate_gates(); + + generate_boolean_r1cs_constraint(this->bp, result_is_one); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, 1 - result->c0.c0, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c0.c1, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c1.c0, 0)); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(result_is_one, result->c1.c1, 0)); + } + + void generate_assignments() { + one->generate_assignments( + curve_type::pairing::pair_curve_type::pairing::fqk_type::value_type::one()); + el_inv->generate_assignments(el.get_element().inversed()); + + compute_el_inv->generate_assignments(); + el_q_2->evaluate(); + compute_el_q_2_minus_1->generate_assignments(); + el_q_3_minus_q->evaluate(); + el_inv_q_2->evaluate(); + compute_el_inv_q_2_minus_1->generate_assignments(); + compute_w1->generate_assignments(); + compute_w0->generate_assignments(); + compute_result->generate_assignments(); + + this->bp.val(result_is_one) = + (result->get_element() == one->get_element() ? field_type::value_type::one() : + field_type::value_type::zero()); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_FINAL_EXPONENTIATION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/miller_loop.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/miller_loop.hpp new file mode 100644 index 000000000..86ebc1b29 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/miller_loop.hpp @@ -0,0 +1,829 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for components for Miller loops. +// +// The components verify computations of (single or multiple simultaneous) Miller loops. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_HPP + +#include + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + using namespace nil::crypto3::algebra::pairing; + + /** + * Component for doubling step in the Miller loop. + * + * Technical note: + * + * mnt_Fqk g_RR_at_P = mnt_Fqk(prec_P.PY_twist_squared, + * -prec_P.PX * c.gamma_twist + c.gamma_X - c.old_RY); + * + *(later in Miller loop: f = f.squared() * g_RR_at_P) + * + * Note the slight interface change: this component allocates g_RR_at_P inside itself (!) + */ + template + class mnt_miller_loop_dbl_line_eval : public component { + + typedef typename CurveType::pairing::fp_type field_type; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + + using component_policy = detail::basic_pairing_component_policy; + + public: + g1_precomputation prec_P; + precompute_G2_component_coeffs c; + std::shared_ptr + &g_RR_at_P; // reference from outside + + std::shared_ptr gamma_twist; + std::shared_ptr g_RR_at_P_c1; + std::shared_ptr compute_g_RR_at_P_c1; + + mnt_miller_loop_dbl_line_eval( + blueprint &bp, + const g1_precomputation &prec_P, + const precompute_G2_component_coeffs &c, + std::shared_ptr &g_RR_at_P) : + component(bp), + prec_P(prec_P), c(c), g_RR_at_P(g_RR_at_P) { + + gamma_twist.reset(new typename component_policy::Fqe_variable_type(c.gamma->mul_by_X())); + // prec_P.PX * c.gamma_twist = c.gamma_X - c.old_RY - g_RR_at_P_c1 + if (gamma_twist->is_constant()) { + gamma_twist->evaluate(); + const typename fqe_type::value_type gamma_twist_const = gamma_twist->get_element(); + g_RR_at_P_c1.reset(new typename component_policy::Fqe_variable_type( + typename component_policy::Fqe_variable_type(this->bp, -gamma_twist_const, + prec_P.P->X) + + *(c.gamma_X) + *(c.RY) * (-field_type::value_type::one()))); + } else if (prec_P.P->X.is_constant()) { + prec_P.P->X.evaluate(bp); + const typename field_type::value_type P_X_const = prec_P.P->X.constant_term(); + g_RR_at_P_c1.reset(new typename component_policy::Fqe_variable_type( + *gamma_twist * (-P_X_const) + *(c.gamma_X) + + *(c.RY) * (-field_type::value_type::one()))); + } else { + g_RR_at_P_c1.reset(new typename component_policy::Fqe_variable_type(bp)); + compute_g_RR_at_P_c1.reset(new typename component_policy::Fqe_mul_by_lc_component_type( + bp, *gamma_twist, prec_P.P->X, + *(c.gamma_X) + *(c.RY) * (-field_type::value_type::one()) + + (*g_RR_at_P_c1) * (-field_type::value_type::one()))); + } + g_RR_at_P.reset(new typename component_policy::Fqk_variable_type(bp, *(prec_P.PY_twist_squared), + *g_RR_at_P_c1)); + } + + void generate_gates() { + if (!gamma_twist->is_constant() && !prec_P.P->X.is_constant()) { + compute_g_RR_at_P_c1->generate_gates(); + } + } + + void generate_assignments() { + gamma_twist->evaluate(); + const typename fqe_type::value_type gamma_twist_val = gamma_twist->get_element(); + const typename field_type::value_type PX_val = this->bp.lc_val(prec_P.P->X); + const typename fqe_type::value_type gamma_X_val = c.gamma_X->get_element(); + const typename fqe_type::value_type RY_val = c.RY->get_element(); + const typename fqe_type::value_type g_RR_at_P_c1_val = + -PX_val * gamma_twist_val + gamma_X_val - RY_val; + g_RR_at_P_c1->generate_assignments(g_RR_at_P_c1_val); + + if (!gamma_twist->is_constant() && !prec_P.P->X.is_constant()) { + compute_g_RR_at_P_c1->generate_assignments(); + } + g_RR_at_P->evaluate(); + } + }; + + /** + * Component for addition step in the Miller loop. + * + * Technical note: + * + * mnt_Fqk g_RQ_at_P = mnt_Fqk(prec_P.PY_twist_squared, + * -prec_P.PX * c.gamma_twist + c.gamma_X - prec_Q.QY); + * + * (later in Miller loop: f = f * g_RQ_at_P) + * + * Note the slight interface change: this component will allocate g_RQ_at_P inside itself (!) + */ + template + class mnt_miller_loop_add_line_eval : public component { + + typedef typename CurveType::pairing::fp_type field_type; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + + using component_policy = detail::basic_pairing_component_policy; + + public: + bool invert_Q; + g1_precomputation prec_P; + precompute_G2_component_coeffs c; + element_g2 Q; + std::shared_ptr + &g_RQ_at_P; // reference from outside + + std::shared_ptr gamma_twist; + std::shared_ptr g_RQ_at_P_c1; + std::shared_ptr compute_g_RQ_at_P_c1; + + mnt_miller_loop_add_line_eval( + blueprint &bp, + const bool invert_Q, + const g1_precomputation &prec_P, + const precompute_G2_component_coeffs &c, + const element_g2 &Q, + std::shared_ptr &g_RQ_at_P) : + component(bp), + invert_Q(invert_Q), prec_P(prec_P), c(c), Q(Q), g_RQ_at_P(g_RQ_at_P) { + gamma_twist.reset(new typename component_policy::Fqe_variable_type(c.gamma->mul_by_X())); + // prec_P.PX * c.gamma_twist = c.gamma_X - prec_Q.QY - g_RQ_at_P_c1 + if (gamma_twist->is_constant()) { + gamma_twist->evaluate(); + const typename fqe_type::value_type gamma_twist_const = gamma_twist->get_element(); + g_RQ_at_P_c1.reset(new typename component_policy::Fqe_variable_type( + typename component_policy::Fqe_variable_type(this->bp, -gamma_twist_const, + prec_P.P->X) + + *(c.gamma_X) + + *(Q.Y) * (!invert_Q ? -field_type::value_type::one() : field_type::value_type::one()))); + } else if (prec_P.P->X.is_constant()) { + prec_P.P->X.evaluate(bp); + const typename field_type::value_type P_X_const = prec_P.P->X.constant_term(); + g_RQ_at_P_c1.reset(new typename component_policy::Fqe_variable_type( + *gamma_twist * (-P_X_const) + *(c.gamma_X) + + *(Q.Y) * (!invert_Q ? -field_type::value_type::one() : field_type::value_type::one()))); + } else { + g_RQ_at_P_c1.reset(new typename component_policy::Fqe_variable_type(bp)); + compute_g_RQ_at_P_c1.reset(new typename component_policy::Fqe_mul_by_lc_component_type( + bp, *gamma_twist, prec_P.P->X, + *(c.gamma_X) + + *(Q.Y) * + (!invert_Q ? -field_type::value_type::one() : field_type::value_type::one()) + + (*g_RQ_at_P_c1) * (-field_type::value_type::one()))); + } + g_RQ_at_P.reset(new typename component_policy::Fqk_variable_type(bp, *(prec_P.PY_twist_squared), + *g_RQ_at_P_c1)); + } + void generate_gates() { + if (!gamma_twist->is_constant() && !prec_P.P->X.is_constant()) { + compute_g_RQ_at_P_c1->generate_gates(); + } + } + void generate_assignments() { + gamma_twist->evaluate(); + const typename fqe_type::value_type gamma_twist_val = gamma_twist->get_element(); + const typename field_type::value_type PX_val = this->bp.lc_val(prec_P.P->X); + const typename fqe_type::value_type gamma_X_val = c.gamma_X->get_element(); + const typename fqe_type::value_type QY_val = Q.Y->get_element(); + const typename fqe_type::value_type g_RQ_at_P_c1_val = + -PX_val * gamma_twist_val + gamma_X_val + (!invert_Q ? -QY_val : QY_val); + g_RQ_at_P_c1->generate_assignments(g_RQ_at_P_c1_val); + + if (!gamma_twist->is_constant() && !prec_P.P->X.is_constant()) { + compute_g_RQ_at_P_c1->generate_assignments(); + } + g_RQ_at_P->evaluate(); + } + }; + + /** + * Component for verifying a single Miller loop. + */ + template + class mnt_miller_loop_component : public component { + + typedef typename CurveType::pairing::fp_type field_type; + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + using component_policy = detail::basic_pairing_component_policy; + + public: + std::vector> g_RR_at_Ps; + std::vector> g_RQ_at_Ps; + std::vector> fs; + + std::vector>> addition_steps; + std::vector>> doubling_steps; + + std::vector> dbl_muls; + std::vector> dbl_sqrs; + std::vector> add_muls; + + std::size_t f_count; + std::size_t add_count; + std::size_t dbl_count; + + g1_precomputation prec_P; + g2_precomputation prec_Q; + typename component_policy::Fqk_variable_type result; + + mnt_miller_loop_component(blueprint &bp, + const g1_precomputation &prec_P, + const g2_precomputation &prec_Q, + const typename component_policy::Fqk_variable_type &result) : + component(bp), + prec_P(prec_P), prec_Q(prec_Q), result(result) { + + f_count = add_count = dbl_count = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + ++dbl_count; + f_count += 2; + + if (NAF[i] != 0) { + ++add_count; + f_count += 1; + } + } + + fs.resize(f_count); + doubling_steps.resize(dbl_count); + addition_steps.resize(add_count); + g_RR_at_Ps.resize(dbl_count); + g_RQ_at_Ps.resize(add_count); + + for (std::size_t i = 0; i < f_count; ++i) { + fs[i].reset(new typename component_policy::Fqk_variable_type(bp)); + } + + dbl_sqrs.resize(dbl_count); + dbl_muls.resize(dbl_count); + add_muls.resize(add_count); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t f_id = 0; + std::size_t prec_id = 0; + + found_nonzero = false; + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P, *prec_Q.coeffs[prec_id], g_RR_at_Ps[dbl_id])); + ++prec_id; + dbl_sqrs[dbl_id].reset( + new typename component_policy::Fqk_sqr_component_type(bp, *fs[f_id], *fs[f_id + 1])); + ++f_id; + dbl_muls[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RR_at_Ps[dbl_id], (f_id + 1 == f_count ? result : *fs[f_id + 1]))); + ++f_id; + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P, *prec_Q.coeffs[prec_id], *prec_Q.Q, g_RQ_at_Ps[add_id])); + ++prec_id; + add_muls[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RQ_at_Ps[add_id], + (f_id + 1 == f_count ? result : *fs[f_id + 1]))); + ++f_id; + ++add_id; + } + } + } + void generate_gates() { + fs[0]->generate_r1cs_equals_const_constraints(fqk_type::value_type::one()); + + for (std::size_t i = 0; i < dbl_count; ++i) { + doubling_steps[i]->generate_gates(); + dbl_sqrs[i]->generate_gates(); + dbl_muls[i]->generate_gates(); + } + + for (std::size_t i = 0; i < add_count; ++i) { + addition_steps[i]->generate_gates(); + add_muls[i]->generate_gates(); + } + } + void generate_assignments() { + fs[0]->generate_assignments(fqk_type::value_type::one()); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps[dbl_id]->generate_assignments(); + dbl_sqrs[dbl_id]->generate_assignments(); + dbl_muls[dbl_id]->generate_assignments(); + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps[add_id]->generate_assignments(); + add_muls[add_id]->generate_assignments(); + ++add_id; + } + } + } + }; + + /** + * Component for verifying a double Miller loop (where the second is inverted). + */ + template + class mnt_e_over_e_miller_loop_component : public component { + + typedef typename CurveType::pairing::fp_type field_type; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + using component_policy = detail::basic_pairing_component_policy; + + public: + std::vector> g_RR_at_P1s; + std::vector> g_RQ_at_P1s; + std::vector> g_RR_at_P2s; + std::vector> g_RQ_at_P2s; + std::vector> fs; + + std::vector>> addition_steps1; + std::vector>> doubling_steps1; + std::vector>> addition_steps2; + std::vector>> doubling_steps2; + + std::vector> dbl_sqrs; + std::vector> dbl_muls1; + std::vector> add_muls1; + std::vector> dbl_muls2; + std::vector> add_muls2; + + std::size_t f_count; + std::size_t add_count; + std::size_t dbl_count; + + g1_precomputation prec_P1; + g2_precomputation prec_Q1; + g1_precomputation prec_P2; + g2_precomputation prec_Q2; + typename component_policy::Fqk_variable_type result; + + mnt_e_over_e_miller_loop_component(blueprint &bp, + const g1_precomputation &prec_P1, + const g2_precomputation &prec_Q1, + const g1_precomputation &prec_P2, + const g2_precomputation &prec_Q2, + const typename component_policy::Fqk_variable_type &result) : + component(bp), + prec_P1(prec_P1), prec_Q1(prec_Q1), prec_P2(prec_P2), prec_Q2(prec_Q2), result(result) { + + f_count = add_count = dbl_count = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + ++dbl_count; + f_count += 3; + + if (NAF[i] != 0) { + ++add_count; + f_count += 2; + } + } + + fs.resize(f_count); + doubling_steps1.resize(dbl_count); + addition_steps1.resize(add_count); + doubling_steps2.resize(dbl_count); + addition_steps2.resize(add_count); + g_RR_at_P1s.resize(dbl_count); + g_RQ_at_P1s.resize(add_count); + g_RR_at_P2s.resize(dbl_count); + g_RQ_at_P2s.resize(add_count); + + for (std::size_t i = 0; i < f_count; ++i) { + fs[i].reset(new typename component_policy::Fqk_variable_type(bp)); + } + + dbl_sqrs.resize(dbl_count); + dbl_muls1.resize(dbl_count); + add_muls1.resize(add_count); + dbl_muls2.resize(dbl_count); + add_muls2.resize(add_count); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t f_id = 0; + std::size_t prec_id = 0; + + found_nonzero = false; + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps1[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P1, *prec_Q1.coeffs[prec_id], g_RR_at_P1s[dbl_id])); + doubling_steps2[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P2, *prec_Q2.coeffs[prec_id], g_RR_at_P2s[dbl_id])); + ++prec_id; + + dbl_sqrs[dbl_id].reset( + new typename component_policy::Fqk_sqr_component_type(bp, *fs[f_id], *fs[f_id + 1])); + ++f_id; + dbl_muls1[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RR_at_P1s[dbl_id], *fs[f_id + 1])); + ++f_id; + dbl_muls2[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, (f_id + 1 == f_count ? result : *fs[f_id + 1]), *g_RR_at_P2s[dbl_id], *fs[f_id])); + ++f_id; + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps1[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P1, *prec_Q1.coeffs[prec_id], *prec_Q1.Q, + g_RQ_at_P1s[add_id])); + addition_steps2[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P2, *prec_Q2.coeffs[prec_id], *prec_Q2.Q, + g_RQ_at_P2s[add_id])); + ++prec_id; + add_muls1[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RQ_at_P1s[add_id], *fs[f_id + 1])); + ++f_id; + add_muls2[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, (f_id + 1 == f_count ? result : *fs[f_id + 1]), *g_RQ_at_P2s[add_id], + *fs[f_id])); + ++f_id; + ++add_id; + } + } + } + void generate_gates() { + fs[0]->generate_r1cs_equals_const_constraints(fqk_type::value_type::one()); + + for (std::size_t i = 0; i < dbl_count; ++i) { + doubling_steps1[i]->generate_gates(); + doubling_steps2[i]->generate_gates(); + dbl_sqrs[i]->generate_gates(); + dbl_muls1[i]->generate_gates(); + dbl_muls2[i]->generate_gates(); + } + + for (std::size_t i = 0; i < add_count; ++i) { + addition_steps1[i]->generate_gates(); + addition_steps2[i]->generate_gates(); + add_muls1[i]->generate_gates(); + add_muls2[i]->generate_gates(); + } + } + void generate_assignments() { + fs[0]->generate_assignments(fqk_type::value_type::one()); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t f_id = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps1[dbl_id]->generate_assignments(); + doubling_steps2[dbl_id]->generate_assignments(); + dbl_sqrs[dbl_id]->generate_assignments(); + ++f_id; + dbl_muls1[dbl_id]->generate_assignments(); + ++f_id; + (f_id + 1 == f_count ? result : *fs[f_id + 1]) + .generate_assignments(fs[f_id]->get_element() * + g_RR_at_P2s[dbl_id]->get_element().inversed()); + dbl_muls2[dbl_id]->generate_assignments(); + ++f_id; + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps1[add_id]->generate_assignments(); + addition_steps2[add_id]->generate_assignments(); + add_muls1[add_id]->generate_assignments(); + ++f_id; + (f_id + 1 == f_count ? result : *fs[f_id + 1]) + .generate_assignments(fs[f_id]->get_element() * + g_RQ_at_P2s[add_id]->get_element().inversed()); + add_muls2[add_id]->generate_assignments(); + ++f_id; + ++add_id; + } + } + } + }; + + /** + * Component for verifying a triple Miller loop (where the third is inverted). + */ + template + class mnt_e_times_e_over_e_miller_loop_component + : public component { + + typedef typename CurveType::pairing::fp_type field_type; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + using component_policy = detail::basic_pairing_component_policy; + + public: + std::vector> g_RR_at_P1s; + std::vector> g_RQ_at_P1s; + std::vector> g_RR_at_P2s; + std::vector> g_RQ_at_P2s; + std::vector> g_RR_at_P3s; + std::vector> g_RQ_at_P3s; + std::vector> fs; + + std::vector>> addition_steps1; + std::vector>> doubling_steps1; + std::vector>> addition_steps2; + std::vector>> doubling_steps2; + std::vector>> addition_steps3; + std::vector>> doubling_steps3; + + std::vector> dbl_sqrs; + std::vector> dbl_muls1; + std::vector> add_muls1; + std::vector> dbl_muls2; + std::vector> add_muls2; + std::vector> dbl_muls3; + std::vector> add_muls3; + + std::size_t f_count; + std::size_t add_count; + std::size_t dbl_count; + + g1_precomputation prec_P1; + g2_precomputation prec_Q1; + g1_precomputation prec_P2; + g2_precomputation prec_Q2; + g1_precomputation prec_P3; + g2_precomputation prec_Q3; + typename component_policy::Fqk_variable_type result; + + mnt_e_times_e_over_e_miller_loop_component( + blueprint &bp, + const g1_precomputation &prec_P1, + const g2_precomputation &prec_Q1, + const g1_precomputation &prec_P2, + const g2_precomputation &prec_Q2, + const g1_precomputation &prec_P3, + const g2_precomputation &prec_Q3, + const typename component_policy::Fqk_variable_type &result) : + component(bp), + prec_P1(prec_P1), prec_Q1(prec_Q1), prec_P2(prec_P2), prec_Q2(prec_Q2), prec_P3(prec_P3), + prec_Q3(prec_Q3), result(result) { + + f_count = add_count = dbl_count = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + ++dbl_count; + f_count += 4; + + if (NAF[i] != 0) { + ++add_count; + f_count += 3; + } + } + + fs.resize(f_count); + doubling_steps1.resize(dbl_count); + addition_steps1.resize(add_count); + doubling_steps2.resize(dbl_count); + addition_steps2.resize(add_count); + doubling_steps3.resize(dbl_count); + addition_steps3.resize(add_count); + g_RR_at_P1s.resize(dbl_count); + g_RQ_at_P1s.resize(add_count); + g_RR_at_P2s.resize(dbl_count); + g_RQ_at_P2s.resize(add_count); + g_RR_at_P3s.resize(dbl_count); + g_RQ_at_P3s.resize(add_count); + + for (std::size_t i = 0; i < f_count; ++i) { + fs[i].reset(new typename component_policy::Fqk_variable_type(bp)); + } + + dbl_sqrs.resize(dbl_count); + dbl_muls1.resize(dbl_count); + add_muls1.resize(add_count); + dbl_muls2.resize(dbl_count); + add_muls2.resize(add_count); + dbl_muls3.resize(dbl_count); + add_muls3.resize(add_count); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t f_id = 0; + std::size_t prec_id = 0; + + found_nonzero = false; + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps1[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P1, *prec_Q1.coeffs[prec_id], g_RR_at_P1s[dbl_id])); + doubling_steps2[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P2, *prec_Q2.coeffs[prec_id], g_RR_at_P2s[dbl_id])); + doubling_steps3[dbl_id].reset(new mnt_miller_loop_dbl_line_eval( + bp, prec_P3, *prec_Q3.coeffs[prec_id], g_RR_at_P3s[dbl_id])); + ++prec_id; + + dbl_sqrs[dbl_id].reset( + new typename component_policy::Fqk_sqr_component_type(bp, *fs[f_id], *fs[f_id + 1])); + ++f_id; + dbl_muls1[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RR_at_P1s[dbl_id], *fs[f_id + 1])); + ++f_id; + dbl_muls2[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RR_at_P2s[dbl_id], *fs[f_id + 1])); + ++f_id; + dbl_muls3[dbl_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, (f_id + 1 == f_count ? result : *fs[f_id + 1]), *g_RR_at_P3s[dbl_id], *fs[f_id])); + ++f_id; + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps1[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P1, *prec_Q1.coeffs[prec_id], *prec_Q1.Q, + g_RQ_at_P1s[add_id])); + addition_steps2[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P2, *prec_Q2.coeffs[prec_id], *prec_Q2.Q, + g_RQ_at_P2s[add_id])); + addition_steps3[add_id].reset(new mnt_miller_loop_add_line_eval( + bp, NAF[i] < 0, prec_P3, *prec_Q3.coeffs[prec_id], *prec_Q3.Q, + g_RQ_at_P3s[add_id])); + ++prec_id; + add_muls1[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RQ_at_P1s[add_id], *fs[f_id + 1])); + ++f_id; + add_muls2[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, *fs[f_id], *g_RQ_at_P2s[add_id], *fs[f_id + 1])); + ++f_id; + add_muls3[add_id].reset(new typename component_policy::Fqk_special_mul_component_type( + bp, (f_id + 1 == f_count ? result : *fs[f_id + 1]), *g_RQ_at_P3s[add_id], + *fs[f_id])); + ++f_id; + ++add_id; + } + } + } + void generate_gates() { + fs[0]->generate_r1cs_equals_const_constraints(fqk_type::value_type::one()); + + for (std::size_t i = 0; i < dbl_count; ++i) { + doubling_steps1[i]->generate_gates(); + doubling_steps2[i]->generate_gates(); + doubling_steps3[i]->generate_gates(); + dbl_sqrs[i]->generate_gates(); + dbl_muls1[i]->generate_gates(); + dbl_muls2[i]->generate_gates(); + dbl_muls3[i]->generate_gates(); + } + + for (std::size_t i = 0; i < add_count; ++i) { + addition_steps1[i]->generate_gates(); + addition_steps2[i]->generate_gates(); + addition_steps3[i]->generate_gates(); + add_muls1[i]->generate_gates(); + add_muls2[i]->generate_gates(); + add_muls3[i]->generate_gates(); + } + } + void generate_assignments() { + fs[0]->generate_assignments(fqk_type::value_type::one()); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t f_id = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps1[dbl_id]->generate_assignments(); + doubling_steps2[dbl_id]->generate_assignments(); + doubling_steps3[dbl_id]->generate_assignments(); + dbl_sqrs[dbl_id]->generate_assignments(); + ++f_id; + dbl_muls1[dbl_id]->generate_assignments(); + ++f_id; + dbl_muls2[dbl_id]->generate_assignments(); + ++f_id; + (f_id + 1 == f_count ? result : *fs[f_id + 1]) + .generate_assignments(fs[f_id]->get_element() * + g_RR_at_P3s[dbl_id]->get_element().inversed()); + dbl_muls3[dbl_id]->generate_assignments(); + ++f_id; + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps1[add_id]->generate_assignments(); + addition_steps2[add_id]->generate_assignments(); + addition_steps3[add_id]->generate_assignments(); + add_muls1[add_id]->generate_assignments(); + ++f_id; + add_muls2[add_id]->generate_assignments(); + ++f_id; + (f_id + 1 == f_count ? result : *fs[f_id + 1]) + .generate_assignments(fs[f_id]->get_element() * + g_RQ_at_P3s[add_id]->get_element().inversed()); + add_muls3[add_id]->generate_assignments(); + ++f_id; + ++add_id; + } + } + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/pairing_checks.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/pairing_checks.hpp new file mode 100644 index 000000000..89b4f6bfa --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/pairing_checks.hpp @@ -0,0 +1,149 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for pairing-check components. +// +// Given that e(.,.) denotes a pairing, +// - the component "check_e_equals_e_component" checks the equation "e(P1,Q1)=e(P2,Q2)"; and +// - the component "check_e_equals_ee_component" checks the equation "e(P1,Q1)=e(P2,Q2)*e(P3,Q3)". +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PAIRING_CHECKS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PAIRING_CHECKS_HPP + +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class check_e_equals_e_component : public component { + + using component_policy = detail::basic_pairing_component_policy; + + using Fqk_variable_type = typename component_policy::Fqk_variable_type; + + public: + typedef typename CurveType::scalar_field_type field_type; + + std::shared_ptr ratio; + std::shared_ptr> compute_ratio; + std::shared_ptr> check_finexp; + + g1_precomputation lhs_G1; + g2_precomputation lhs_G2; + g1_precomputation rhs_G1; + g2_precomputation rhs_G2; + + blueprint_variable result; + + check_e_equals_e_component(blueprint &bp, + const g1_precomputation &lhs_G1, + const g2_precomputation &lhs_G2, + const g1_precomputation &rhs_G1, + const g2_precomputation &rhs_G2, + const blueprint_variable &result) : + component(bp), + lhs_G1(lhs_G1), lhs_G2(lhs_G2), rhs_G1(rhs_G1), rhs_G2(rhs_G2), result(result) { + ratio.reset(new Fqk_variable_type(bp)); + compute_ratio.reset(new mnt_e_over_e_miller_loop_component( + bp, lhs_G1, lhs_G2, rhs_G1, rhs_G2, *ratio)); + check_finexp.reset(new final_exp_component(bp, *ratio, result)); + } + + void generate_gates() { + compute_ratio->generate_gates(); + check_finexp->generate_gates(); + } + + void generate_assignments() { + compute_ratio->generate_assignments(); + check_finexp->generate_assignments(); + } + }; + + template + class check_e_equals_ee_component : public component { + + using component_policy = detail::basic_pairing_component_policy; + + using Fqk_variable_type = typename component_policy::Fqk_variable_type; + + public: + typedef typename CurveType::scalar_field_type field_type; + + std::shared_ptr ratio; + std::shared_ptr> compute_ratio; + std::shared_ptr> check_finexp; + + g1_precomputation lhs_G1; + g2_precomputation lhs_G2; + g1_precomputation rhs1_G1; + g2_precomputation rhs1_G2; + g1_precomputation rhs2_G1; + g2_precomputation rhs2_G2; + + blueprint_variable result; + + check_e_equals_ee_component(blueprint &bp, + const g1_precomputation &lhs_G1, + const g2_precomputation &lhs_G2, + const g1_precomputation &rhs1_G1, + const g2_precomputation &rhs1_G2, + const g1_precomputation &rhs2_G1, + const g2_precomputation &rhs2_G2, + const blueprint_variable &result) : + component(bp), + lhs_G1(lhs_G1), lhs_G2(lhs_G2), rhs1_G1(rhs1_G1), rhs1_G2(rhs1_G2), rhs2_G1(rhs2_G1), + rhs2_G2(rhs2_G2), result(result) { + ratio.reset(new Fqk_variable_type(bp)); + compute_ratio.reset(new mnt_e_times_e_over_e_miller_loop_component( + bp, rhs1_G1, rhs1_G2, rhs2_G1, rhs2_G2, lhs_G1, lhs_G2, *ratio)); + check_finexp.reset(new final_exp_component(bp, *ratio, result)); + } + + void generate_gates() { + compute_ratio->generate_gates(); + check_finexp->generate_gates(); + } + + void generate_assignments() { + compute_ratio->generate_assignments(); + check_finexp->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PAIRING_CHECKS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/precomputation.hpp b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/precomputation.hpp new file mode 100644 index 000000000..02f30b065 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/algebra/pairing/weierstrass/r1cs/precomputation.hpp @@ -0,0 +1,577 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for pairing precomputation components. +// +// The components verify correct precomputation of values for the G1 and G2 elements. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_HPP + +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + using namespace nil::crypto3::algebra::pairing; + + /**************************** G1 Precomputation ******************************/ + + /** + * Not a component. It only holds values. + */ + template + class g1_precomputation { + typedef typename CurveType::pairing::fp_type FieldType; + using component_policy = detail::basic_pairing_component_policy; + + public: + std::shared_ptr> P; + std::shared_ptr PY_twist_squared; + + g1_precomputation() { + // will be filled in precompute_G1_component, so do nothing here + } + + g1_precomputation( + blueprint &bp, + const typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type &P_val) { + typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type P_val_copy = + P_val.to_affine(); + P.reset(new element_g1(bp, P_val_copy)); + PY_twist_squared.reset(new typename component_policy::Fqe_variable_type( + bp, + P_val_copy.Y() * + CurveType::pairing::pair_curve_type::template g2_type<>::value_type::twist.squared())); + } + }; + + /** + * Component that verifies correct precomputation of the G1 element. + */ + template + class precompute_G1_component : public component { + using curve_type = CurveType; + using component_policy = detail::basic_pairing_component_policy; + + public: + using fqk_type = typename CurveType::pairing::pair_curve_type::pairing::fqk_type; + + g1_precomputation &precomp; // must be a reference. + + /* two possible pre-computations one for mnt4 and one for mnt6 */ + template + precompute_G1_component( + blueprint &bp, + const element_g1 &P, + g1_precomputation &precomp, // will allocate this inside + const typename std::enable_if::type & = + typename FieldType::value_type()) : + component(bp), + precomp(precomp) { + + using twist_curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + blueprint_linear_combination c0, c1; + c0.assign(bp, P.Y * ((twist_curve_type::pairing::twist).squared().data[0])); + c1.assign(bp, P.Y * ((twist_curve_type::pairing::twist).squared().data[1])); + + precomp.P.reset(new element_g1(P)); + precomp.PY_twist_squared.reset(new typename component_policy::Fqe_variable_type(bp, c0, c1)); + } + + template + precompute_G1_component( + blueprint &bp, + const element_g1 &P, + g1_precomputation &precomp, // will allocate this inside + const typename std::enable_if::type & = + typename FieldType::value_type()) : + component(bp), + precomp(precomp) { + + using twist_curve_type = nil::crypto3::algebra::curves::mnt6<298>; + + blueprint_linear_combination c0, c1, c2; + c0.assign(bp, P.Y * ((twist_curve_type::pairing::twist).squared().data[0])); + c1.assign(bp, P.Y * ((twist_curve_type::pairing::twist).squared().data[1])); + c2.assign(bp, P.Y * ((twist_curve_type::pairing::twist).squared().data[2])); + + precomp.P.reset(new element_g1(P)); + precomp.PY_twist_squared.reset(new + typename component_policy::Fqe_variable_type(bp, c0, c1, c2)); + } + + void generate_gates() { + /* the same for neither CurveType = mnt4 nor CurveType = mnt6 */ + } + + void generate_assignments() { + precomp.PY_twist_squared + ->evaluate(); /* the same for both CurveType = mnt4 and CurveType = mnt6 */ + } + }; + + /**************************** G2 Precomputation ******************************/ + + /** + * Not a component. It only holds values. + */ + template + class precompute_G2_component_coeffs { + using component_policy = detail::basic_pairing_component_policy; + + public: + typedef typename CurveType::pairing::fp_type FieldType; + + std::shared_ptr RX; + std::shared_ptr RY; + std::shared_ptr gamma; + std::shared_ptr gamma_X; + + precompute_G2_component_coeffs() { + // we will be filled in precomputed case of precompute_G2_component, so do nothing here + } + + precompute_G2_component_coeffs(blueprint &bp) { + RX.reset(new typename component_policy::Fqe_variable_type(bp)); + RY.reset(new typename component_policy::Fqe_variable_type(bp)); + gamma.reset(new typename component_policy::Fqe_variable_type(bp)); + gamma_X.reset(new typename component_policy::Fqe_variable_type(bp)); + } + + precompute_G2_component_coeffs(blueprint &bp, const element_g2 &Q) { + RX.reset(new typename component_policy::Fqe_variable_type(*(Q.X))); + RY.reset(new typename component_policy::Fqe_variable_type(*(Q.Y))); + gamma.reset(new typename component_policy::Fqe_variable_type(bp)); + gamma_X.reset(new typename component_policy::Fqe_variable_type(bp)); + } + }; + + /** + * Not a component. It only holds values. + */ + template + class g2_precomputation { + using component_policy = detail::basic_pairing_component_policy; + + public: + typedef typename CurveType::pairing::fp_type FieldType; + + std::shared_ptr> Q; + + std::vector>> coeffs; + + g2_precomputation() { + } + g2_precomputation( + blueprint &bp, + const typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type &Q_val) { + Q.reset(new element_g2(bp, Q_val)); + const typename CurveType::pairing::pair_curve_type::pairing::affine_ate_g2_precomp + native_precomp = + affine_ate_precompute_g2(Q_val); + + coeffs.resize(native_precomp.coeffs.size() + + 1); // the last precomp remains for convenient programming + for (std::size_t i = 0; i < native_precomp.coeffs.size(); ++i) { + coeffs[i].reset(new precompute_G2_component_coeffs()); + coeffs[i]->RX.reset( + new typename component_policy::Fqe_variable_type(bp, native_precomp.coeffs[i].old_RX)); + coeffs[i]->RY.reset( + new typename component_policy::Fqe_variable_type(bp, native_precomp.coeffs[i].old_RY)); + coeffs[i]->gamma.reset( + new typename component_policy::Fqe_variable_type(bp, native_precomp.coeffs[i].gamma)); + coeffs[i]->gamma_X.reset( + new typename component_policy::Fqe_variable_type(bp, native_precomp.coeffs[i].gamma_X)); + } + } + }; + + /** + * Technical note: + * + * QX and QY -- X and Y coordinates of Q + * + * initialization: + * coeffs[0].RX = QX + * coeffs[0].RY = QY + * + * g2_precompute_doubling_step relates coeffs[i] and coeffs[i+1] as follows + * + * coeffs[i] + * gamma = (3 * RX^2 + twist_coeff_a) * (2*RY).inversed() + * gamma_X = gamma * RX + * + * coeffs[i+1] + * RX = prev_gamma^2 - (2*prev_RX) + * RY = prev_gamma * (prev_RX - RX) - prev_RY + */ + template + class precompute_G2_component_doubling_step : public component { + using component_policy = detail::basic_pairing_component_policy; + + public: + typedef typename CurveType::pairing::fp_type FieldType; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + + precompute_G2_component_coeffs cur; + precompute_G2_component_coeffs next; + + std::shared_ptr RXsquared; + std::shared_ptr compute_RXsquared; + std::shared_ptr three_RXsquared_plus_a; + std::shared_ptr two_RY; + std::shared_ptr compute_gamma; + std::shared_ptr compute_gamma_X; + + std::shared_ptr next_RX_plus_two_RX; + std::shared_ptr compute_next_RX; + + std::shared_ptr RX_minus_next_RX; + std::shared_ptr RY_plus_next_RY; + std::shared_ptr compute_next_RY; + + precompute_G2_component_doubling_step(blueprint &bp, + const precompute_G2_component_coeffs &cur, + const precompute_G2_component_coeffs &next) : + component(bp), + cur(cur), next(next) { + RXsquared.reset(new typename component_policy::Fqe_variable_type(bp)); + compute_RXsquared.reset( + new typename component_policy::Fqe_sqr_component_type(bp, *(cur.RX), *RXsquared)); + three_RXsquared_plus_a.reset(new typename component_policy::Fqe_variable_type( + (*RXsquared) * typename FieldType::value_type(0x03) + + detail::basic_pairing_component_policy< + typename CurveType::pairing::pair_curve_type>::g2_coeff_a)); + + two_RY.reset(new typename component_policy::Fqe_variable_type( + *(cur.RY) * typename FieldType::value_type(0x02))); + + compute_gamma.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *two_RY, *three_RXsquared_plus_a)); + compute_gamma_X.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *(cur.RX), *(cur.gamma_X))); + + next_RX_plus_two_RX.reset(new typename component_policy::Fqe_variable_type( + *(next.RX) + *(cur.RX) * typename FieldType::value_type(0x02))); + compute_next_RX.reset(new typename component_policy::Fqe_sqr_component_type( + bp, *(cur.gamma), *next_RX_plus_two_RX)); + + RX_minus_next_RX.reset(new typename component_policy::Fqe_variable_type( + *(cur.RX) + *(next.RX) * (-FieldType::value_type::one()))); + RY_plus_next_RY.reset(new typename component_policy::Fqe_variable_type(*(cur.RY) + *(next.RY))); + compute_next_RY.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *RX_minus_next_RX, *RY_plus_next_RY)); + } + + void generate_gates() { + compute_RXsquared->generate_gates(); + compute_gamma->generate_gates(); + compute_gamma_X->generate_gates(); + compute_next_RX->generate_gates(); + compute_next_RY->generate_gates(); + } + + void generate_assignments() { + compute_RXsquared->generate_assignments(); + two_RY->evaluate(); + three_RXsquared_plus_a->evaluate(); + + const typename fqe_type::value_type three_RXsquared_plus_a_val = + three_RXsquared_plus_a->get_element(); + const typename fqe_type::value_type two_RY_val = two_RY->get_element(); + const typename fqe_type::value_type gamma_val = + three_RXsquared_plus_a_val * two_RY_val.inversed(); + cur.gamma->generate_assignments(gamma_val); + + compute_gamma->generate_assignments(); + compute_gamma_X->generate_assignments(); + + const typename fqe_type::value_type RX_val = cur.RX->get_element(); + const typename fqe_type::value_type RY_val = cur.RY->get_element(); + const typename fqe_type::value_type next_RX_val = gamma_val.squared() - RX_val - RX_val; + const typename fqe_type::value_type next_RY_val = gamma_val * (RX_val - next_RX_val) - RY_val; + + next.RX->generate_assignments(next_RX_val); + next.RY->generate_assignments(next_RY_val); + + RX_minus_next_RX->evaluate(); + RY_plus_next_RY->evaluate(); + + compute_next_RX->generate_assignments(); + compute_next_RY->generate_assignments(); + } + }; + + /** + * Technical note: + * + * g2_precompute_addition_step relates coeffs[i] and coeffs[i+1] as follows + * + * coeffs[i] + * gamma = (RY - QY) * (RX - QX).inversed() + * gamma_X = gamma * QX + * + * coeffs[i+1] + * RX = prev_gamma^2 + (prev_RX + QX) + * RY = prev_gamma * (prev_RX - RX) - prev_RY + * + * (where prev_ in [i+1] refer to things from [i]) + * + * If invert_Q is set to true: use -QY in place of QY everywhere above. + */ + template + class precompute_G2_component_addition_step : public component { + using component_policy = detail::basic_pairing_component_policy; + + public: + typedef typename CurveType::pairing::fp_type FieldType; + using fqe_type = typename CurveType::pairing::pair_curve_type::pairing::fqe_type; + + bool invert_Q; + precompute_G2_component_coeffs cur; + precompute_G2_component_coeffs next; + element_g2 Q; + + std::shared_ptr RY_minus_QY; + std::shared_ptr RX_minus_QX; + std::shared_ptr compute_gamma; + std::shared_ptr compute_gamma_X; + + std::shared_ptr next_RX_plus_RX_plus_QX; + std::shared_ptr compute_next_RX; + + std::shared_ptr RX_minus_next_RX; + std::shared_ptr RY_plus_next_RY; + std::shared_ptr compute_next_RY; + + precompute_G2_component_addition_step(blueprint &bp, + const bool invert_Q, + const precompute_G2_component_coeffs &cur, + const precompute_G2_component_coeffs &next, + const element_g2 &Q) : + component(bp), + invert_Q(invert_Q), cur(cur), next(next), Q(Q) { + RY_minus_QY.reset(new typename component_policy::Fqe_variable_type( + *(cur.RY) + + *(Q.Y) * (!invert_Q ? -FieldType::value_type::one() : FieldType::value_type::one()))); + + RX_minus_QX.reset(new typename component_policy::Fqe_variable_type( + *(cur.RX) + *(Q.X) * (-FieldType::value_type::one()))); + compute_gamma.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *RX_minus_QX, *RY_minus_QY)); + compute_gamma_X.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *(Q.X), *(cur.gamma_X))); + + next_RX_plus_RX_plus_QX.reset( + new typename component_policy::Fqe_variable_type(*(next.RX) + *(cur.RX) + *(Q.X))); + compute_next_RX.reset(new typename component_policy::Fqe_sqr_component_type( + bp, *(cur.gamma), *next_RX_plus_RX_plus_QX)); + + RX_minus_next_RX.reset(new typename component_policy::Fqe_variable_type( + *(cur.RX) + *(next.RX) * (-FieldType::value_type::one()))); + RY_plus_next_RY.reset(new typename component_policy::Fqe_variable_type(*(cur.RY) + *(next.RY))); + compute_next_RY.reset(new typename component_policy::Fqe_mul_component_type( + bp, *(cur.gamma), *RX_minus_next_RX, *RY_plus_next_RY)); + } + + void generate_gates() { + compute_gamma->generate_gates(); + compute_gamma_X->generate_gates(); + compute_next_RX->generate_gates(); + compute_next_RY->generate_gates(); + } + + void generate_assignments() { + RY_minus_QY->evaluate(); + RX_minus_QX->evaluate(); + + const typename fqe_type::value_type RY_minus_QY_val = RY_minus_QY->get_element(); + const typename fqe_type::value_type RX_minus_QX_val = RX_minus_QX->get_element(); + const typename fqe_type::value_type gamma_val = RY_minus_QY_val * RX_minus_QX_val.inversed(); + cur.gamma->generate_assignments(gamma_val); + + compute_gamma->generate_assignments(); + compute_gamma_X->generate_assignments(); + + const typename fqe_type::value_type RX_val = cur.RX->get_element(); + const typename fqe_type::value_type RY_val = cur.RY->get_element(); + const typename fqe_type::value_type QX_val = Q.X->get_element(); + const typename fqe_type::value_type next_RX_val = gamma_val.squared() - RX_val - QX_val; + const typename fqe_type::value_type next_RY_val = gamma_val * (RX_val - next_RX_val) - RY_val; + + next.RX->generate_assignments(next_RX_val); + next.RY->generate_assignments(next_RY_val); + + next_RX_plus_RX_plus_QX->evaluate(); + RX_minus_next_RX->evaluate(); + RY_plus_next_RY->evaluate(); + + compute_next_RX->generate_assignments(); + compute_next_RY->generate_assignments(); + } + }; + + /** + * Component that verifies correct precomputation of the G2 element. + */ + template + class precompute_G2_component : public component { + using component_policy = detail::basic_pairing_component_policy; + + public: + typedef typename CurveType::pairing::fp_type FieldType; + + std::vector>> addition_steps; + std::vector>> doubling_steps; + + std::size_t add_count; + std::size_t dbl_count; + + g2_precomputation &precomp; // important to have a reference here + + precompute_G2_component(blueprint &bp, + const element_g2 &Q, + g2_precomputation &precomp) : + component(bp), + precomp(precomp) { + precomp.Q.reset(new element_g2(Q)); + + std::size_t coeff_count = 1; // the last RX/RY are unused in Miller loop, but will need + // to get allocated somehow + this->add_count = 0; + this->dbl_count = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + ++dbl_count; + ++coeff_count; + + if (NAF[i] != 0) { + ++add_count; + ++coeff_count; + } + } + + precomp.coeffs.resize(coeff_count); + addition_steps.resize(add_count); + doubling_steps.resize(dbl_count); + + precomp.coeffs[0].reset(new precompute_G2_component_coeffs(bp, Q)); + for (std::size_t i = 1; i < coeff_count; ++i) { + precomp.coeffs[i].reset(new precompute_G2_component_coeffs(bp)); + } + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + std::size_t coeff_id = 0; + + found_nonzero = false; + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps[dbl_id].reset(new precompute_G2_component_doubling_step( + bp, *(precomp.coeffs[coeff_id]), *(precomp.coeffs[coeff_id + 1]))); + ++dbl_id; + ++coeff_id; + + if (NAF[i] != 0) { + addition_steps[add_id].reset(new precompute_G2_component_addition_step( + bp, NAF[i] < 0, *(precomp.coeffs[coeff_id]), *(precomp.coeffs[coeff_id + 1]), Q)); + ++add_id; + ++coeff_id; + } + } + } + + void generate_gates() { + for (std::size_t i = 0; i < dbl_count; ++i) { + doubling_steps[i]->generate_gates(); + } + + for (std::size_t i = 0; i < add_count; ++i) { + addition_steps[i]->generate_gates(); + } + } + + void generate_assignments() { + precomp.coeffs[0]->RX->generate_assignments(precomp.Q->X->get_element()); + precomp.coeffs[0]->RY->generate_assignments(precomp.Q->Y->get_element()); + + std::size_t add_id = 0; + std::size_t dbl_id = 0; + + bool found_nonzero = false; + std::vector NAF = find_wnaf(1, CurveType::pairing::pairing_loop_count); + + for (long i = NAF.size() - 1; i >= 0; --i) { + if (!found_nonzero) { + /* this skips the MSB itself */ + found_nonzero |= (NAF[i] != 0); + continue; + } + + doubling_steps[dbl_id]->generate_assignments(); + ++dbl_id; + + if (NAF[i] != 0) { + addition_steps[add_id]->generate_assignments(); + ++add_id; + } + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/comparison.hpp b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/comparison.hpp new file mode 100644 index 000000000..1b4edbbd2 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/comparison.hpp @@ -0,0 +1,137 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_COMPARISON_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_COMPARISON_COMPONENT_HPP + +#include +#include + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /* + the components below are Fp specific: + I * X = R + (1-R) * X = 0 + + if X = 0 then R = 0 + if X != 0 then R = 1 and I = X^{-1} + */ + template + class comparison : public nil::blueprint::components::component { + private: + detail::blueprint_variable_vector alpha; + detail::blueprint_variable alpha_packed; + std::shared_ptr> pack_alpha; + + std::shared_ptr> all_zeros_test; + detail::blueprint_variable not_all_zeros; + + public: + const std::size_t n; + const detail::blueprint_linear_combination A; + const detail::blueprint_linear_combination B; + const detail::blueprint_variable less; + const detail::blueprint_variable less_or_eq; + + comparison(blueprint &bp, + std::size_t n, + const detail::blueprint_linear_combination &A, + const detail::blueprint_linear_combination &B, + const detail::blueprint_variable &less, + const detail::blueprint_variable &less_or_eq) : + nil::blueprint::components::component(bp), + n(n), A(A), B(B), less(less), less_or_eq(less_or_eq) { + alpha.allocate(bp, n); + alpha.emplace_back(less_or_eq); // alpha[n] is less_or_eq + + alpha_packed.allocate(bp); + not_all_zeros.allocate(bp); + + pack_alpha.reset(new packing(bp, alpha, alpha_packed)); + + all_zeros_test.reset(new disjunction( + bp, detail::blueprint_variable_vector(alpha.begin(), alpha.begin() + n), not_all_zeros)); + }; + + void generate_gates() { + /* + packed(alpha) = 2^n + B - A + + not_all_zeros = \bigvee_{i=0}^{n-1} alpha_i + + if B - A > 0, then 2^n + B - A > 2^n, + so alpha_n = 1 and not_all_zeros = 1 + if B - A = 0, then 2^n + B - A = 2^n, + so alpha_n = 1 and not_all_zeros = 0 + if B - A < 0, then 2^n + B - A \in {0, 1, \ldots, 2^n-1}, + so alpha_n = 0 + + therefore alpha_n = less_or_eq and alpha_n * not_all_zeros = less + */ + + /* not_all_zeros to be Boolean, alpha_i are Boolean by packing component */ + generate_boolean_r1cs_constraint(this->bp, not_all_zeros); + + /* constraints for packed(alpha) = 2^n + B - A */ + pack_alpha->generate_gates(true); + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint( + 1, (typename FieldType::value_type(0x02).pow(n)) + B - A, alpha_packed)); + + /* compute result */ + all_zeros_test->generate_gates(); + this->bp.add_r1cs_constraint( + zk::snark::r1cs_constraint(less_or_eq, not_all_zeros, less)); + } + + void generate_assignments() { + A.evaluate(this->bp); + B.evaluate(this->bp); + + /* unpack 2^n + B - A into alpha_packed */ + this->bp.val(alpha_packed) = + (typename FieldType::value_type(0x02).pow(n)) + this->bp.lc_val(B) - this->bp.lc_val(A); + pack_alpha->generate_assignments_from_packed(); + + /* compute result */ + all_zeros_test->generate_assignments(); + this->bp.val(less) = this->bp.val(less_or_eq) * this->bp.val(not_all_zeros); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_COMPARISON_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/conjunction.hpp b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/conjunction.hpp new file mode 100644 index 000000000..b13c160fd --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/conjunction.hpp @@ -0,0 +1,112 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_CONJUNCTION_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_CONJUNCTION_COMPONENT_HPP + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /* + the components below are Fp specific: + I * X = R + (1-R) * X = 0 + + if X = 0 then R = 0 + if X != 0 then R = 1 and I = X^{-1} + */ + template + class conjunction : public nil::blueprint::components::component { + private: + detail::blueprint_variable inv; + + public: + const detail::blueprint_variable_vector inputs; + const detail::blueprint_variable output; + + conjunction(blueprint &bp, + const detail::blueprint_variable_vector &inputs, + const detail::blueprint_variable &output) : + nil::blueprint::components::component(bp), + inputs(inputs), output(output) { + assert(inputs.size() >= 1); + inv.allocate(bp); + } + + void generate_gates() { + /* inv * (n-sum) = 1-output */ + math::non_linear_combination a1, b1, c1; + a1.add_term(inv); + b1.add_term(detail::blueprint_variable(0), inputs.size()); + for (std::size_t i = 0; i < inputs.size(); ++i) { + b1.add_term(inputs[i], -1); + } + c1.add_term(detail::blueprint_variable(0)); + c1.add_term(output, -1); + + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a1, b1, c1)); + + /* output * (n-sum) = 0 */ + math::non_linear_combination a2, b2, c2; + a2.add_term(output); + b2.add_term(detail::blueprint_variable(0), inputs.size()); + for (std::size_t i = 0; i < inputs.size(); ++i) { + b2.add_term(inputs[i], -1); + } + c2.add_term(detail::blueprint_variable(0), 0); + + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a2, b2, c2)); + } + void generate_assignments() { + typename FieldType::value_type sum = typename FieldType::value_type(inputs.size()); + + for (std::size_t i = 0; i < inputs.size(); ++i) { + sum -= this->bp.val(inputs[i]); + } + + if (sum.is_zero()) { + this->bp.val(inv) = FieldType::value_type::zero(); + this->bp.val(output) = FieldType::value_type::one(); + } else { + this->bp.val(inv) = sum.inversed(); + this->bp.val(output) = FieldType::value_type::zero(); + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_CONJUNCTION_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/disjunction.hpp b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/disjunction.hpp new file mode 100644 index 000000000..557c38160 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/disjunction.hpp @@ -0,0 +1,113 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_DISJUNCTION_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_DISJUNCTION_COMPONENT_HPP + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /* + the components below are Fp specific: + I * X = R + (1-R) * X = 0 + + if X = 0 then R = 0 + if X != 0 then R = 1 and I = X^{-1} + */ + + template + class disjunction : public nil::blueprint::components::component { + private: + detail::blueprint_variable inv; + + public: + const detail::blueprint_variable_vector inputs; + const detail::blueprint_variable output; + + disjunction(blueprint &bp, + const detail::blueprint_variable_vector &inputs, + const detail::blueprint_variable &output) : + nil::blueprint::components::component(bp), + inputs(inputs), output(output) { + assert(inputs.size() >= 1); + inv.allocate(bp); + } + + void generate_gates() { + /* inv * sum = output */ + math::non_linear_combination a1, b1, c1; + a1.add_term(inv); + for (std::size_t i = 0; i < inputs.size(); ++i) { + b1.add_term(inputs[i]); + } + c1.add_term(output); + + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a1, b1, c1)); + + /* (1-output) * sum = 0 */ + math::non_linear_combination a2, b2, c2; + a2.add_term(detail::blueprint_variable(0)); + a2.add_term(output, -1); + for (std::size_t i = 0; i < inputs.size(); ++i) { + b2.add_term(inputs[i]); + } + c2.add_term(detail::blueprint_variable(0), 0); + + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a2, b2, c2)); + } + + void generate_assignments() { + typename FieldType::value_type sum = FieldType::value_type::zero(); + + for (std::size_t i = 0; i < inputs.size(); ++i) { + sum += this->bp.val(inputs[i]); + } + + if (sum.is_zero()) { + this->bp.val(inv) = FieldType::value_type::zero(); + this->bp.val(output) = FieldType::value_type::zero(); + } else { + this->bp.val(inv) = sum.inversed(); + this->bp.val(output) = FieldType::value_type::one(); + } + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_DISJUNCTION_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/inner_product.hpp b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/inner_product.hpp new file mode 100644 index 000000000..ed140d4aa --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/boolean/r1cs/inner_product.hpp @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_INNER_PRODUCT_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_INNER_PRODUCT_COMPONENT_HPP + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /* + the components below are Fp specific: + I * X = R + (1-R) * X = 0 + + if X = 0 then R = 0 + if X != 0 then R = 1 and I = X^{-1} + */ + template + class inner_product : public nil::blueprint::components::component { + private: + /* S_i = \sum_{k=0}^{i+1} A[i] * B[i] */ + detail::blueprint_variable_vector S; + + public: + const detail::blueprint_linear_combination_vector A; + const detail::blueprint_linear_combination_vector B; + const detail::blueprint_variable result; + + inner_product(blueprint &bp, + const detail::blueprint_linear_combination_vector &A, + const detail::blueprint_linear_combination_vector &B, + const detail::blueprint_variable &result) : + nil::blueprint::components::component(bp), + A(A), B(B), result(result) { + assert(A.size() >= 1); + assert(A.size() == B.size()); + + S.allocate(bp, A.size() - 1); + } + + void generate_gates() { + /* + S_i = \sum_{k=0}^{i+1} A[i] * B[i] + S[0] = A[0] * B[0] + S[i+1] - S[i] = A[i] * B[i] + */ + for (std::size_t i = 0; i < A.size(); ++i) { + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint( + A[i], B[i], + (i == A.size() - 1 ? result : S[i]) + + (i == 0 ? 0 * detail::blueprint_variable(0) : -S[i - 1]))); + } + } + + void generate_assignments() { + typename FieldType::value_type total = FieldType::value_type::zero(); + for (std::size_t i = 0; i < A.size(); ++i) { + A[i].evaluate(this->bp); + B[i].evaluate(this->bp); + + total += this->bp.lc_val(A[i]) * this->bp.lc_val(B[i]); + this->bp.val(i == A.size() - 1 ? result : S[i]) = total; + } + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_INNER_PRODUCT_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/component_from_r1cs.hpp b/libs/blueprint/include/nil/blueprint/components/component_from_r1cs.hpp new file mode 100644 index 000000000..2e4a355a0 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/component_from_r1cs.hpp @@ -0,0 +1,109 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for a component that can be created from an R1CS constraint system. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_COMPONENT_FROM_R1CS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_COMPONENT_FROM_R1CS_HPP + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class component_from_r1cs : public component { + + const std::vector> vars; + const snark::r1cs_constraint_system cs; + std::map cs_to_vars; + + public: + component_from_r1cs(blueprint &bp, + const std::vector> &vars, + const snark::r1cs_constraint_system &cs) : + component(bp), + vars(vars), cs(cs) { + cs_to_vars[0] = 0; /* constant term maps to constant term */ + + std::size_t cs_var_idx = 1; + for (auto va : vars) { + for (auto v : va) { + cs_to_vars[cs_var_idx] = v.index; + ++cs_var_idx; + } + } + + assert(cs_var_idx - 1 == cs.num_variables()); + } + + void generate_gates() { + for (std::size_t i = 0; i < cs.num_constraints(); ++i) { + const snark::r1cs_constraint &constr = cs.constraints[i]; + snark::r1cs_constraint translated_constr; + + for (const linear_term &t : constr.a.terms) { + translated_constr.a.terms.emplace_back( + linear_term(variable(cs_to_vars[t.index]), t.coeff)); + } + + for (const linear_term &t : constr.b.terms) { + translated_constr.b.terms.emplace_back( + linear_term(variable(cs_to_vars[t.index]), t.coeff)); + } + + for (const linear_term &t : constr.c.terms) { + translated_constr.c.terms.emplace_back( + linear_term(variable(cs_to_vars[t.index]), t.coeff)); + } + + this->bp.add_r1cs_constraint(translated_constr); + } + } + void generate_assignments(const snark::r1cs_primary_input &primary_input, + const snark::r1cs_auxiliary_input &auxiliary_input) { + assert(cs.num_inputs() == primary_input.size()); + assert(cs.num_variables() == primary_input.size() + auxiliary_input.size()); + + for (std::size_t i = 0; i < primary_input.size(); ++i) { + this->bp.val(variable(cs_to_vars[i + 1])) = primary_input[i]; + } + + for (std::size_t i = 0; i < auxiliary_input.size(); ++i) { + this->bp.val(variable(cs_to_vars[primary_input.size() + i + 1])) = + auxiliary_input[i]; + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_COMPONENT_FROM_R1CS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_1bit.hpp b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_1bit.hpp new file mode 100644 index 000000000..87ff602c9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_1bit.hpp @@ -0,0 +1,91 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_DETAIL_LOOKUP_1BIT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_DETAIL_LOOKUP_1BIT_HPP + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /** + * One-bit window lookup table using one constraint + */ + template + struct lookup_1bit : public component { + using field_type = Field; + using field_value_type = typename Field::value_type; + using result_type = detail::blueprint_variable; + + const std::vector constants; + const detail::blueprint_variable bit; + result_type result; + + /// Auto allocation of the result + template + lookup_1bit(blueprint &bp, + const Constants &in_constants, + const detail::blueprint_variable &in_bit) : + component(bp), + constants(std::cbegin(in_constants), std::cend(in_constants)), bit(in_bit) { + assert(this->constants.size() == 2); + this->result.allocate(this->bp); + } + + /// Manual allocation of the result + template + lookup_1bit(blueprint &bp, + const Constants &in_constants, + const detail::blueprint_variable &in_bit, + const result_type &in_result) : + component(bp), + constants(std::cbegin(in_constants), std::cend(in_constants)), bit(in_bit), result(in_result) { + assert(this->constants.size() == 2); + } + + void generate_gates() { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + {constants[0] + bit * constants[1] - (bit * constants[0])}, + {field_value_type::one()}, + result)); + } + + void generate_assignments() { + std::size_t i = static_cast( + static_cast((this->bp.val(bit)).data)); + this->bp.val(result) = constants[i]; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_DETAIL_LOOKUP_1BIT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_signed_3bit.hpp b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_signed_3bit.hpp new file mode 100644 index 000000000..0fa9746fb --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/lookup_signed_3bit.hpp @@ -0,0 +1,131 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Three-bit window lookup (2bits + signature bit) in 2bit table using two constraints. Maps the bits `b` to a +// list of constants `c` +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_LOOKUP_SIGNED_3BIT_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_LOOKUP_SIGNED_3BIT_COMPONENT_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct lookup_signed_3bit : public component { + using field_type = Field; + using field_value_type = typename field_type::value_type; + + static constexpr std::size_t chunk_bits = 3; + static constexpr std::size_t lookup_bits = 2; + + // Input variables + std::vector c; + const detail::blueprint_variable_vector b; + // Intermediate variable + detail::blueprint_variable b0b1; + // Output variable + detail::blueprint_variable result; + + /// Auto allocation of the result + template::value_type>::value, + bool>::type = true> + lookup_signed_3bit(blueprint &bp, + const Constants &in_constants, + const detail::blueprint_variable_vector &in_bits) : + component(bp), + b(in_bits) { + this->b0b1.allocate(this->bp); + this->result.allocate(this->bp); + std::copy(std::cbegin(in_constants), std::cend(in_constants), std::back_inserter(this->c)); + } + + /// Manual allocation of the result + template::value_type>::value, + bool>::type = true> + lookup_signed_3bit(blueprint &bp, + const Constants &in_constants, + const detail::blueprint_variable_vector &in_bits, + const detail::blueprint_variable &in_result) : + component(bp), + b(in_bits), result(in_result) { + this->b0b1.allocate(this->bp); + std::copy(std::cbegin(in_constants), std::cend(in_constants), std::back_inserter(this->c)); + } + + void generate_gates() { + /// b0b1 = b[0] * b[1] + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(this->b[0], this->b[1], this->b0b1)); + + /// y_lc = c[0] + b[0] * (c[1]-c0) + b[1] * (c[2]-c[0]) + b[0]&b[1] * (c[3] - c[2] - c[1] + + /// c[0]) + detail::blueprint_linear_combination y_lc; + y_lc.assign( + this->bp, + math::linear_term(detail::blueprint_variable(0), this->c[0]) + + math::linear_term(this->b[0], this->c[1] - this->c[0]) + + math::linear_term(this->b[1], this->c[2] - this->c[0]) + + math::linear_term(this->b0b1, + this->c[3] - this->c[2] - this->c[1] + this->c[0])); + + /// (y_lc + y_lc) * b[2] == y_lc - result + this->bp.add_r1cs_constraint( + snark::r1cs_constraint({y_lc + y_lc}, this->b[2], {y_lc - this->result})); + } + + void generate_assignments() { + auto i = static_cast(static_cast( + this->b.get_field_element_from_bits(this->bp).data)); + field_value_type result = this->c[i & 3]; + if (i > 3) { + result = result * (-field_value_type::one()); + } + this->bp.val(this->b0b1) = this->bp.val(this->b[0]) * this->bp.val(this->b[1]); + this->bp.val(this->result) = result; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_LOOKUP_SIGNED_3BIT_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/detail/r1cs/loose_multiplexing.hpp b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/loose_multiplexing.hpp new file mode 100644 index 000000000..2d811f3cb --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/loose_multiplexing.hpp @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_LOOSE_MULTIPLEXING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_LOOSE_MULTIPLEXING_COMPONENT_HPP + +#include +#include + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /* + loose_multiplexing implements loose multiplexer: + index not in bounds -> success_flag = 0 + index in bounds && success_flag = 1 -> result is correct + however if index is in bounds we can also set success_flag to 0 (and then result will be forced to + be 0) + */ + template + class loose_multiplexing : public nil::blueprint::components::component { + public: + detail::blueprint_variable_vector alpha; + + private: + std::shared_ptr> compute_result; + + public: + const detail::blueprint_linear_combination_vector arr; + const detail::blueprint_variable index; + const detail::blueprint_variable result; + const detail::blueprint_variable success_flag; + + loose_multiplexing(blueprint &bp, + const detail::blueprint_linear_combination_vector &arr, + const detail::blueprint_variable &index, + const detail::blueprint_variable &result, + const detail::blueprint_variable &success_flag) : + nil::blueprint::components::component(bp), + arr(arr), index(index), result(result), success_flag(success_flag) { + alpha.allocate(bp, arr.size()); + compute_result.reset(new inner_product(bp, alpha, arr, result)); + }; + + void generate_gates() { + /* \alpha_i (index - i) = 0 */ + for (std::size_t i = 0; i < arr.size(); ++i) { + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(alpha[i], index - i, 0)); + } + + /* 1 * (\sum \alpha_i) = success_flag */ + detail::blueprint_linear_combination a, b, c; + a.add_term(detail::blueprint_variable(0)); + for (std::size_t i = 0; i < arr.size(); ++i) { + b.add_term(alpha[i]); + } + c.add_term(success_flag); + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a, b, c)); + + /* now success_flag is constrained to either 0 (if index is out of + range) or \alpha_i. constrain it and \alpha_i to zero */ + generate_boolean_r1cs_constraint(this->bp, success_flag); + + /* compute result */ + compute_result->generate_gates(); + } + + void generate_assignments() { + + /* assumes that idx can be fit in ulong; true for our purposes for now */ + const typename FieldType::value_type valint = this->bp.val(index); + + unsigned long idx = static_cast(typename FieldType::integral_type(valint.data)); + + if (idx >= arr.size() || typename FieldType::integral_type(valint.data) >= arr.size()) { + for (std::size_t i = 0; i < arr.size(); ++i) { + this->bp.val(alpha[i]) = FieldType::value_type::zero(); + } + + this->bp.val(success_flag) = FieldType::value_type::zero(); + } else { + for (std::size_t i = 0; i < arr.size(); ++i) { + this->bp.val(alpha[i]) = + (i == idx ? FieldType::value_type::one() : FieldType::value_type::zero()); + } + + this->bp.val(success_flag) = FieldType::value_type::one(); + } + + compute_result->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_LOOSE_MULTIPLEXING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/detail/r1cs/packing.hpp b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/packing.hpp new file mode 100644 index 000000000..33320c720 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/detail/r1cs/packing.hpp @@ -0,0 +1,342 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BASIC_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BASIC_COMPONENTS_HPP + +#include +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */ + template + void generate_boolean_r1cs_constraint(blueprint &bp, + const math::non_linear_combination &lc) { + bp.add_r1cs_constraint(zk::snark::r1cs_constraint(lc, Field::value_type::one() - lc, + Field::value_type::zero())); + } + + template + void generate_r1cs_equals_const_constraint(blueprint &bp, + const math::non_linear_combination &lc, + const typename Field::value_type &c) { + bp.add_r1cs_constraint(zk::snark::r1cs_constraint(Field::value_type::one(), lc, c)); + } + + template + struct packing : public nil::blueprint::components::component { + using field_type = Field; + using field_value_type = typename field_type::value_type; + + const detail::blueprint_linear_combination_vector bits; + const detail::blueprint_linear_combination packed; + + packing(blueprint &bp, + const detail::blueprint_linear_combination_vector &bits, + const detail::blueprint_linear_combination &packed) : + nil::blueprint::components::component(bp), + bits(bits), packed(packed) { + } + explicit packing(const detail::blueprint_linear_combination_vector &bits) : bits(bits) { + } + + /* adds constraint result = \sum bits[i] * 2^i */ + void generate_gates(bool enforce_bitness) { + this->bp.add_r1cs_constraint(zk::snark::r1cs_constraint( + field_type::value_type::one(), detail::blueprint_packing_sum(bits), packed)); + + if (enforce_bitness) { + for (std::size_t i = 0; i < bits.size(); ++i) { + generate_boolean_r1cs_constraint(this->bp, bits[i]); + } + } + } + + void generate_assignments_from_packed() { + packed.evaluate(this->bp); + + // `bits` is large enough to represent this packed value + assert(multiprecision::msb( + static_cast(this->bp.lc_val(packed).data)) + + 1 <= + bits.size()); + bits.fill_with_bits_of_field_element(this->bp, this->bp.lc_val(packed)); + } + + void generate_assignments_from_bits() { + bits.evaluate(this->bp); + this->bp.lc_val(packed) = bits.get_field_element_from_bits(this->bp); + } + }; + + template + class multipacking_component : public nil::blueprint::components::component { + private: + std::vector> packers; + + public: + const detail::blueprint_linear_combination_vector bits; + const detail::blueprint_linear_combination_vector packed_vars; + + const std::size_t chunk_size; + const std::size_t num_chunks; + // const std::size_t last_chunk_size; + + // last_chunk_size(bits.size() - (num_chunks-1) * chunk_size) + multipacking_component(blueprint &bp, + const detail::blueprint_linear_combination_vector &bits, + const detail::blueprint_linear_combination_vector &packed_vars, + std::size_t chunk_size) : + nil::blueprint::components::component(bp), + bits(bits), packed_vars(packed_vars), chunk_size(chunk_size), + num_chunks((bits.size() + (chunk_size - 1)) / chunk_size) { + + assert(packed_vars.size() == num_chunks); + for (std::size_t i = 0; i < num_chunks; ++i) { + packers.emplace_back( + packing(this->bp, + detail::blueprint_linear_combination_vector( + bits.begin() + i * chunk_size, + bits.begin() + std::min((i + 1) * chunk_size, bits.size())), + packed_vars[i])); + } + } + + void generate_gates(const bool enforce_bitness) { + for (std::size_t i = 0; i < num_chunks; ++i) { + packers[i].generate_gates(enforce_bitness); + } + } + + void generate_assignments_from_packed() { + for (std::size_t i = 0; i < num_chunks; ++i) { + packers[i].generate_assignments_from_packed(); + } + } + + void generate_assignments_from_bits() { + for (std::size_t i = 0; i < num_chunks; ++i) { + packers[i].generate_assignments_from_bits(); + } + } + }; + + template + class field_vector_copy_component : public nil::blueprint::components::component { + public: + const detail::blueprint_variable_vector source; + const detail::blueprint_variable_vector target; + const detail::blueprint_linear_combination do_copy; + + field_vector_copy_component(blueprint &bp, + const detail::blueprint_variable_vector &source, + const detail::blueprint_variable_vector &target, + const detail::blueprint_linear_combination &do_copy) : + nil::blueprint::components::component(bp), + source(source), target(target), do_copy(do_copy) { + + assert(source.size() == target.size()); + } + void generate_gates() { + for (std::size_t i = 0; i < source.size(); ++i) { + this->bp.add_r1cs_constraint( + zk::snark::r1cs_constraint(do_copy, source[i] - target[i], 0)); + } + } + + void generate_assignments() { + do_copy.evaluate(this->bp); + assert(this->bp.lc_val(do_copy) == Field::value_type::one() || + this->bp.lc_val(do_copy) == Field::value_type::zero()); + if (this->bp.lc_val(do_copy) != Field::value_type::zero()) { + for (std::size_t i = 0; i < source.size(); ++i) { + this->bp.val(target[i]) = this->bp.val(source[i]); + } + } + } + }; + + template + class bit_vector_copy_component : public nil::blueprint::components::component { + public: + const detail::blueprint_variable_vector source_bits; + const detail::blueprint_variable_vector target_bits; + const detail::blueprint_linear_combination do_copy; + + detail::blueprint_variable_vector packed_source; + detail::blueprint_variable_vector packed_target; + + std::shared_ptr> pack_source; + std::shared_ptr> pack_target; + std::shared_ptr> copier; + + const std::size_t chunk_size; + const std::size_t num_chunks; + + bit_vector_copy_component(blueprint &bp, + const detail::blueprint_variable_vector &source_bits, + const detail::blueprint_variable_vector &target_bits, + const detail::blueprint_linear_combination &do_copy, + std::size_t chunk_size) : + nil::blueprint::components::component(bp), + source_bits(source_bits), target_bits(target_bits), do_copy(do_copy), chunk_size(chunk_size), + num_chunks((source_bits.size() + (chunk_size - 1)) / chunk_size) { + + assert(source_bits.size() == target_bits.size()); + + packed_source.allocate(bp, num_chunks); + pack_source.reset( + new multipacking_component(bp, source_bits, packed_source, chunk_size)); + + packed_target.allocate(bp, num_chunks); + pack_target.reset( + new multipacking_component(bp, target_bits, packed_target, chunk_size)); + + copier.reset(new field_vector_copy_component(bp, packed_source, packed_target, do_copy)); + } + + void generate_gates(bool enforce_source_bitness, bool enforce_target_bitness) { + pack_source->generate_gates(enforce_source_bitness); + pack_target->generate_gates(enforce_target_bitness); + + copier->generate_gates(); + } + + void generate_assignments() { + do_copy.evaluate(this->bp); + assert(this->bp.lc_val(do_copy) == Field::value_type::zero() || + this->bp.lc_val(do_copy) == Field::value_type::one()); + if (this->bp.lc_val(do_copy) == Field::value_type::one()) { + for (std::size_t i = 0; i < source_bits.size(); ++i) { + this->bp.val(target_bits[i]) = this->bp.val(source_bits[i]); + } + } + + pack_source->generate_assignments_from_bits(); + pack_target->generate_assignments_from_bits(); + } + }; + + template + class dual_variable_component : public nil::blueprint::components::component { + private: + std::shared_ptr> consistency_check; + + public: + detail::blueprint_variable packed; + detail::blueprint_variable_vector bits; + + dual_variable_component(blueprint &bp, std::size_t width) : + nil::blueprint::components::component(bp) { + packed.allocate(bp); + bits.allocate(bp, width); + consistency_check.reset(new packing(bp, bits, packed)); + } + + dual_variable_component(blueprint &bp, + const detail::blueprint_variable_vector &bits) : + nil::blueprint::components::component(bp), + bits(bits) { + packed.allocate(bp); + consistency_check.reset(new packing(bp, bits, packed)); + } + + dual_variable_component(blueprint &bp, const detail::blueprint_variable &packed, + std::size_t width) : + nil::blueprint::components::component(bp), + packed(packed) { + bits.allocate(bp, width); + consistency_check.reset(new packing(bp, bits, packed)); + } + + void generate_gates(bool enforce_bitness) { + consistency_check->generate_gates(enforce_bitness); + } + + void generate_assignments_from_packed() { + consistency_check->generate_assignments_from_packed(); + } + void generate_assignments_from_bits() { + consistency_check->generate_assignments_from_bits(); + } + }; + + template + void create_linear_combination_constraints( + blueprint &bp, + const std::vector &base, + const std::vector> &v, + const VarT &target) { + + for (std::size_t i = 0; i < base.size(); ++i) { + detail::blueprint_linear_combination a, b, c; + + a.add_term(detail::blueprint_variable(0)); + b.add_term(detail::blueprint_variable(0), base[i]); + + for (auto &p : v) { + b.add_term(p.first.all_vars[i], p.second); + } + + c.add_term(target.all_vars[i]); + + bp.add_r1cs_constraint(zk::snark::r1cs_constraint(a, b, c)); + } + } + + template + void + create_linear_combination_witness(blueprint &bp, + const std::vector &base, + const std::vector> &v, + const VarT &target) { + for (std::size_t i = 0; i < base.size(); ++i) { + bp.val(target.all_vars[i]) = base[i]; + + for (auto &p : v) { + bp.val(target.all_vars[i]) += p.second * bp.val(p.first.all_vars[i]); + } + } + } + + template + std::size_t multipacking_num_chunks(const std::size_t num_bits) { + return (num_bits + (Field::capacity()) - 1) / Field::capacity(); + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BASIC_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/digest_selector_component.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/digest_selector_component.hpp new file mode 100644 index 000000000..52a3c2a52 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/digest_selector_component.hpp @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_DIGEST_SELECTOR_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_DIGEST_SELECTOR_COMPONENT_HPP + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class digest_selector_component : public component { + public: + std::size_t digest_size; + digest_variable input; + detail::blueprint_linear_combination is_right; + digest_variable left; + digest_variable right; + + digest_selector_component(blueprint &bp, + const std::size_t digest_size, + const digest_variable &input, + const detail::blueprint_linear_combination &is_right, + const digest_variable &left, + const digest_variable &right) : + component(bp), + digest_size(digest_size), input(input), is_right(is_right), left(left), right(right) { + } + + void generate_gates() { + for (std::size_t i = 0; i < digest_size; ++i) { + /* + input = is_right * right + (1-is_right) * left + input - left = is_right(right - left) + */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + is_right, right.bits[i] - left.bits[i], input.bits[i] - left.bits[i])); + } + } + void generate_assignments() { + is_right.evaluate(this->bp); + + assert(this->bp.lc_val(is_right) == FieldType::value_type::one() || + this->bp.lc_val(is_right) == FieldType::value_type::zero()); + if (this->bp.lc_val(is_right) == FieldType::value_type::one()) { + for (std::size_t i = 0; i < digest_size; ++i) { + this->bp.val(right.bits[i]) = this->bp.val(input.bits[i]); + } + } else { + for (std::size_t i = 0; i < digest_size; ++i) { + this->bp.val(left.bits[i]) = this->bp.val(input.bits[i]); + } + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_DIGEST_SELECTOR_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/hash_io.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/hash_io.hpp new file mode 100644 index 000000000..98b3df0d3 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/hash_io.hpp @@ -0,0 +1,179 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_HASH_IO_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_HASH_IO_HPP + +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class digest_variable : public component { + public: + std::size_t digest_size; + detail::blueprint_variable_vector bits; + + digest_variable(blueprint &bp, std::size_t digest_size) : + component(bp), digest_size(digest_size) { + + bits.allocate(bp, digest_size); + } + + digest_variable(blueprint &bp, + std::size_t digest_size, + const detail::blueprint_variable_vector &partial_bits, + const detail::blueprint_variable &padding) : + component(bp), + digest_size(digest_size) { + + assert(bits.size() <= digest_size); + bits = partial_bits; + while (bits.size() != digest_size) { + bits.emplace_back(padding); + } + } + + void generate_gates() { + for (std::size_t i = 0; i < digest_size; ++i) { + generate_boolean_r1cs_constraint(this->bp, bits[i]); + } + } + + void generate_assignments(const std::vector &contents) { + bits.fill_with_bits(this->bp, contents); + } + + std::vector get_digest() const { + return bits.bits(this->bp); + } + }; + + template + class block_variable : public component { + public: + std::size_t block_size; + detail::blueprint_variable_vector bits; + + block_variable(blueprint &bp, std::size_t block_size) : + component(bp), block_size(block_size) { + bits.allocate(bp, block_size); + } + + block_variable(blueprint &bp, + const std::vector> &parts) : + component(bp) { + + for (auto &part : parts) { + bits.insert(bits.end(), part.begin(), part.end()); + } + block_size = bits.size(); + } + + block_variable(blueprint &bp, + const digest_variable &left, + const digest_variable &right) : + component(bp) { + + assert(left.bits.size() == right.bits.size()); + block_size = 2 * left.bits.size(); + bits.insert(bits.end(), left.bits.begin(), left.bits.end()); + bits.insert(bits.end(), right.bits.begin(), right.bits.end()); + } + + void generate_gates() { + for (std::size_t i = 0; i < block_size; ++i) { + generate_boolean_r1cs_constraint(this->bp, bits[i]); + } + } + + template + void generate_assignments(const InputRange &contents) { + bits.fill_with_bits(this->bp, contents); + } + + std::vector get_block() const { + return bits.bits(this->bp); + } + }; + + template + class merkle_damagard_padding : public component { + public: + detail::blueprint_variable_vector bits; + detail::blueprint_variable one; + detail::blueprint_variable zero; + + merkle_damagard_padding(blueprint &bp, + size_t message_length, + size_t message_length_bits_size, + size_t block_bits) : + component(bp) { + assert(message_length_bits_size <= block_bits); + one.allocate(bp); + zero.allocate(bp); + std::size_t message_remainder = message_length % block_bits; + size_t padding_length = 2 * block_bits - message_remainder - message_length_bits_size; + padding_length = padding_length % block_bits; + + bits.resize(padding_length + message_length_bits_size); + if (padding_length > 0) { + bits[0] = one; + for (size_t i = 1; i < padding_length; ++i) { + bits[i] = zero; + } + } + + unsigned long long message_length_iter = message_length; + for (int i = message_length_bits_size - 1; i >= 0; --i) { + bits[padding_length + i] = (message_length_iter & 1 ? one : zero); + message_length_iter = message_length_iter >> 1; + } + assert(message_length_iter == 0); + } + + void generate_gates() { + generate_r1cs_equals_const_constraint(this->bp, one, FieldType::value_type::one()); + generate_r1cs_equals_const_constraint(this->bp, zero, FieldType::value_type::zero()); + } + + void generate_assignments() { + this->bp.val(one) = 1; + this->bp.val(zero) = 0; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_HASH_IO_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/hmac_component.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/hmac_component.hpp new file mode 100644 index 000000000..4fb0610af --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/hmac_component.hpp @@ -0,0 +1,175 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_HMAC_COMPONENT_HPP +#define CRYPTO3_ZK_BLUEPRINT_HMAC_COMPONENT_HPP + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + class hmac_component : component { + static_assert(std::is_same>::value); + + public: + blueprint_variable_vector padded_key; + blueprint_variable_vector key_xor_ipad; + blueprint_variable_vector key_xor_opad; + std::shared_ptr hash1; + std::shared_ptr> hash1_result; + std::shared_ptr hash2; + blueprint_variable zero; + + public: + hmac_component(blueprint &bp, + const block_variable &key, + const block_variable &message, + const typename Hash2::hash_variable_type &output) : + component(bp) { + assert(Hash1::get_block_len() == Hash2::get_block_len()); + assert(Hash1::get_block_len() == 0 || key.block_size <= Hash1::get_block_len()); + + std::size_t padded_key_size = + (Hash1::get_block_len() != 0 ? Hash1::get_block_len() : key.block_size); + zero.allocate(bp); + blueprint_variable_vector padding(padded_key_size - key.block_size, zero); + padded_key.reserve(padded_key_size); + padded_key.insert(padded_key.end(), key.bits.begin(), key.bits.end()); + padded_key.insert(padded_key.end(), padding.begin(), padding.end()); + + key_xor_ipad.allocate(bp, padded_key_size); + key_xor_opad.allocate(bp, padded_key_size); + + block_variable iblock(bp, {key_xor_ipad, message.bits}); + hash1_result.reset(new digest_variable(bp, Hash1::get_digest_len())); + hash1.reset(new Hash1(bp, iblock.block_size, iblock, *hash1_result)); + + block_variable oblock(bp, {key_xor_opad, hash1_result->bits}); + hash2.reset(new Hash2(bp, oblock.block_size, oblock, output)); + } + + void generate_gates() { + generate_r1cs_equals_const_constraint(this->bp, zero, FieldType::value_type::zero()); + generate_xor_constraints(0x36, padded_key, key_xor_ipad); + generate_xor_constraints(0x5c, padded_key, key_xor_opad); + hash1->generate_gates(); + hash2->generate_gates(); + } + + void generate_assignments() { + this->bp.val(zero) = FieldType::value_type::zero(); + generate_xor_witness(0x36, padded_key, key_xor_ipad); + generate_xor_witness(0x5c, padded_key, key_xor_opad); + hash1->generate_assignments(); + hash2->generate_assignments(); + } + + static typename Hash2::hash_value_type get_hmac(const std::vector &key, + const std::vector &message) { + assert(Hash1::get_block_len() == Hash2::get_block_len()); + assert(Hash1::get_block_len() == 0 || key.size() <= Hash1::get_block_len()); + + std::size_t padded_key_size = + (Hash1::get_block_len() != 0 ? Hash1::get_block_len() : key.size()); + + std::vector padded_key; + padded_key.reserve(padded_key_size); + padded_key.insert(padded_key.end(), key.begin(), key.end()); + padded_key.insert(padded_key.end(), padded_key_size - key.size(), false); + + std::vector ipad_bits = unpack_byte(0x36); + std::vector opad_bits = unpack_byte(0x5c); + std::vector key_xor_ipad(padded_key_size); + std::vector key_xor_opad(padded_key_size); + + for (std::size_t i = 0; i < padded_key_size; ++i) { + key_xor_ipad[i] = padded_key[i] != ipad_bits[i % 8]; + key_xor_opad[i] = padded_key[i] != opad_bits[i % 8]; + } + + std::vector hash1_input; + hash1_input.reserve(padded_key_size + message.size()); + hash1_input.insert(hash1_input.end(), key_xor_ipad.begin(), key_xor_ipad.end()); + hash1_input.insert(hash1_input.end(), message.begin(), message.end()); + std::vector hash1_output = Hash1::get_hash(hash1_input); + + std::vector hash2_input; + hash2_input.reserve(padded_key_size + hash1_output.size()); + hash2_input.insert(hash2_input.end(), key_xor_opad.begin(), key_xor_opad.end()); + hash2_input.insert(hash2_input.end(), hash1_output.begin(), hash1_output.end()); + return Hash2::get_hash(hash2_input); + } + + private: + void generate_xor_constraints(std::uint8_t xor_pad, + const blueprint_variable_vector &input, + const blueprint_variable_vector &output) { + assert(input.size() == output.size()); + std::vector xor_pad_bits = unpack_byte(xor_pad); + for (std::size_t i = 0; i < input.size(); ++i) { + // x xor 0 = x + // x xor 1 = !x + if (!xor_pad_bits[i % 8]) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint(1, input[i], output[i])); + } else { + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(1, 1 - input[i], output[i])); + } + } + } + + void generate_xor_witness(std::uint8_t xor_pad, + const blueprint_variable_vector &input, + const blueprint_variable_vector &output) { + assert(input.size() == output.size()); + std::vector xor_pad_bits = unpack_byte(xor_pad); + for (std::size_t i = 0; i < input.size(); ++i) { + // x xor 0 = x + // x xor 1 = !x + if (!xor_pad_bits[i % 8]) { + this->bp.val(output[i]) = this->bp.val(input[i]); + } else { + this->bp.val(output[i]) = (this->bp.val(input[i]) == 0 ? 1 : 0); + } + } + } + + static std::vector unpack_byte(std::uint8_t byte) { + std::vector bits(8); + for (std::size_t i = 0; i < 8; ++i) { + bits[7 - i] = byte & (1 << i); + } + return bits; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_HMAC_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/knapsack/r1cs/knapsack.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/knapsack/r1cs/knapsack.hpp new file mode 100644 index 000000000..37f3f9766 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/knapsack/r1cs/knapsack.hpp @@ -0,0 +1,310 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the knapsack component. +// +// The component checks the correct execution of a knapsack (modular subset-sum) over +// the field specified in the template parameter. With suitable choices of parameters +// such knapsacks are collision-resistant hashes (CRHs). See \[Ajt96] and \[GGH96]. +// +// Given two positive integers m (the input length) and d (the dimension), +// and a matrix M over the field F and of dimension dxm, the hash H_M maps {0,1}^m +// to F^d by sending x to M*x. Security of the function (very roughly) depends on +// d*log(|F|). +// +// Below, we give two different components: +// - knapsack_crh_with_field_out_component, which verifies H_M +// - knapsack_crh_with_bit_out_component, which verifies H_M when its output is "expanded" to bits. +// In both cases, a method ("sample_randomness") allows to sample M. +// +// The parameter d (the dimension) is fixed at compile time in the struct +// knapsack_dimension below. The parameter m (the input length) can be chosen +// at run time (in either component). +// +// +// References: +// +// \[Ajt96]: +// "Generating hard instances of lattice problems", +// Miklos Ajtai, +// STOC 1996 +// +// \[GGH96]: +// "Collision-free hashing from lattice problems", +// Oded Goldreich, Shafi Goldwasser, Shai Halevi, +// ECCC TR95-042 +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_HPP + +#include + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /************************** Choice of dimension ******************************/ + + template + struct knapsack_dimension { + // the size of typename FieldType::value_type should be (approximately) at least 200 bits + static const std::size_t dimension = 1; + }; + + /*********************** Knapsack with field output **************************/ + + template + class knapsack_crh_with_field_out_component : public component { + private: + static std::vector knapsack_coefficients; + static std::size_t num_cached_coefficients; + + public: + typedef std::vector hash_value_type; + typedef detail::blueprint_linear_combination_vector hash_variable_type; + std::size_t input_len; + std::size_t dimension; + + block_variable input_block; + detail::blueprint_linear_combination_vector output; + + knapsack_crh_with_field_out_component( + blueprint &bp, + std::size_t input_len, + const block_variable &input_block, + const detail::blueprint_linear_combination_vector &output) : + component(bp), + input_len(input_len), dimension(knapsack_dimension::dimension), + input_block(input_block), output(output) { + BOOST_ASSERT(input_block.bits.size() == input_len); + if (num_cached_coefficients < dimension * input_len) { + sample_randomness(input_len); + } + BOOST_ASSERT(output.size() == this->get_digest_len()); + } + void generate_gates() { + for (std::size_t i = 0; i < dimension; ++i) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, + zk::detail::blueprint_coeff_sum( + input_block.bits, + std::vector( + knapsack_coefficients.begin() + input_len * i, + knapsack_coefficients.begin() + input_len * (i + 1))), + output[i])); + } + } + void generate_assignments() { + const std::vector input = input_block.get_block(); + + for (std::size_t i = 0; i < dimension; ++i) { + typename FieldType::value_type sum = FieldType::value_type::zero(); + for (std::size_t k = 0; k < input_len; ++k) { + if (input[k]) { + sum += knapsack_coefficients[input_len * i + k]; + } + } + + this->bp.lc_val(output[i]) = sum; + } + } + + static std::size_t get_digest_len() { + return knapsack_dimension::dimension; + } + + /* return 0 as block length, as the hash function is variable-input */ + static std::size_t get_block_len() { + return 0; + } + + static std::vector get_hash(const std::vector &input) { + const std::size_t dimension = knapsack_dimension::dimension; + if (num_cached_coefficients < dimension * input.size()) { + sample_randomness(input.size()); + } + + std::vector result(dimension, FieldType::value_type::zero()); + + for (std::size_t i = 0; i < dimension; ++i) { + for (std::size_t k = 0; k < input.size(); ++k) { + if (input[k]) { + result[i] += knapsack_coefficients[input.size() * i + k]; + } + } + } + + return result; + } + + static void sample_randomness(std::size_t input_len) { + const std::size_t num_coefficients = knapsack_dimension::dimension * input_len; + random::hash, typename FieldType::value_type> rng; + + if (num_coefficients > num_cached_coefficients) { + knapsack_coefficients.resize(num_coefficients); + for (std::size_t i = num_cached_coefficients; i < num_coefficients; ++i) { + rng.seed(i); + knapsack_coefficients[i] = rng(); + } + num_cached_coefficients = num_coefficients; + } + } + + /* for debugging */ + static std::size_t expected_constraints() { + return knapsack_dimension::dimension; + } + }; + + /********************** Knapsack with binary output **************************/ + + template + class knapsack_crh_with_bit_out_component : public component { + public: + typedef std::vector hash_value_type; + typedef digest_variable hash_variable_type; + typedef snark::merkle_authentication_path merkle_authentication_path_type; + + std::size_t input_len; + std::size_t dimension; + + detail::blueprint_linear_combination_vector output; + + std::shared_ptr> hasher; + + block_variable input_block; + digest_variable output_digest; + + knapsack_crh_with_bit_out_component(blueprint &bp, + std::size_t input_len, + const block_variable &input_block, + const digest_variable &output_digest) : + component(bp), + input_len(input_len), dimension(knapsack_dimension::dimension), + input_block(input_block), output_digest(output_digest) { + BOOST_ASSERT(output_digest.bits.size() == this->get_digest_len()); + + output.resize(dimension); + + for (std::size_t i = 0; i < dimension; ++i) { + output[i].assign(bp, + zk::detail::blueprint_packing_sum( + zk::detail::blueprint_variable_vector( + output_digest.bits.begin() + i * FieldType::value_bits, + output_digest.bits.begin() + (i + 1) * FieldType::value_bits))); + } + + hasher.reset( + new knapsack_crh_with_field_out_component(bp, input_len, input_block, output)); + } + + void generate_gates(bool enforce_bitness = true) { + hasher->generate_gates(); + + if (enforce_bitness) { + for (std::size_t k = 0; k < output_digest.bits.size(); ++k) { + generate_boolean_r1cs_constraint(this->bp, output_digest.bits[k]); + } + } + } + + void generate_assignments() { + hasher->generate_assignments(); + + /* do unpacking in place */ + const std::vector input = input_block.bits.bits(this->bp); + for (std::size_t i = 0; i < dimension; ++i) { + zk::detail::blueprint_variable_vector va( + output_digest.bits.begin() + i * FieldType::value_bits, + output_digest.bits.begin() + (i + 1) * FieldType::value_bits); + va.fill_with_bits_of_field_element(this->bp, this->bp.lc_val(output[i])); + } + } + + static std::size_t get_digest_len() { + return knapsack_dimension::dimension * FieldType::value_bits; + } + + /* return 0 as block length, as the hash function is variable-input */ + static std::size_t get_block_len() { + return 0; + } + static hash_value_type get_hash(const std::vector &input) { + const std::vector hash_elems = + knapsack_crh_with_field_out_component::get_hash(input); + hash_value_type result; + + typedef boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_backend<>> + integral_type; + + for (const typename FieldType::value_type &elt : hash_elems) { + // std::vector elt_bytes; + std::vector elt_bits(FieldType::modulus_bits); + + std::vector::iterator write_iter = elt_bits.begin(); + // little-endian, to preserve compatibility with blueprint_packing_sum. + auto end = ::boost::multiprecision::export_bits( + integral_type(elt.data), write_iter, 1, false); + + result.insert(result.end(), elt_bits.begin(), elt_bits.end()); + } + + return result; + } + + static void sample_randomness(std::size_t input_len) { + knapsack_crh_with_field_out_component::sample_randomness(input_len); + } + + /* for debugging */ + static std::size_t expected_constraints(bool enforce_bitness = true) { + const std::size_t hasher_constraints = + knapsack_crh_with_field_out_component::expected_constraints(); + const std::size_t bitness_constraints = (enforce_bitness ? get_digest_len() : 0); + return hasher_constraints + bitness_constraints; + } + }; + + template + std::vector + knapsack_crh_with_field_out_component::knapsack_coefficients; + template + std::size_t knapsack_crh_with_field_out_component::num_cached_coefficients; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/pedersen.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/pedersen.hpp new file mode 100644 index 000000000..06c89d001 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/pedersen.hpp @@ -0,0 +1,500 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_HASHES_PEDERSEN_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_HASHES_PEDERSEN_HPP + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + /** + * Windowed hash function using elliptic curves point multiplication + * + * For a given input of scalars, create an equivalent set of base points within a namespace. + */ + template, + typename HashParams = hashes::find_group_hash_default_params> + struct pedersen_to_point : public component { + using curve_type = Curve; + using commitment_component = fixed_base_mul_zcash; + using field_type = typename commitment_component::field_type; + using element_component = typename commitment_component::twisted_edwards_element_component; + + using result_type = element_component; + + // hash_type is corresponding to the component hash policy + // void means there is no implementation of the corresponding hash algorithm + using hash_type = void; + + commitment_component m_commitment; + result_type result; + + static std::vector get_base_points(std::size_t n) { + using group_hash_type = hashes::find_group_hash; + assert(n > 0); + std::vector basepoints; + for (std::uint32_t i = 0; i < n; ++i) { + basepoints.emplace_back(hash({ + i, + })); + } + return basepoints; + } + + /// Auto allocation of the result. + /// Take in_bits as blueprint_variable_vector. + pedersen_to_point(blueprint &bp, + const detail::blueprint_variable_vector &in_bits) : + component(bp), + m_commitment(bp, get_base_points(commitment_component::basepoints_required(in_bits.size())), + in_bits), + result(m_commitment.result) { + } + + /// Auto allocation of the result. + /// Take in_bits as block_variable. + pedersen_to_point(blueprint &bp, const block_variable &in_block) : + pedersen_to_point(bp, in_block.bits) { + } + + /// Auto allocation of the result. + /// Take in_bits as digest_variable. + pedersen_to_point(blueprint &bp, const digest_variable &in_digest) : + pedersen_to_point(bp, in_digest.bits) { + } + + /// Auto allocation of the result. + /// Take in_bits as container of block_variable. + template< + typename Blocks, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen_to_point(blueprint &bp, const Blocks &in_blocks) : + pedersen_to_point(bp, [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_block : in_blocks) { + in_bits.insert(std::end(in_bits), std::cbegin(in_block.bits), std::cend(in_block.bits)); + } + return in_bits; + }()) { + } + + /// Auto allocation of the result. + /// Take in_bits as container of digest_variable. + template< + typename Digests, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen_to_point(blueprint &bp, const Digests &in_digests) : + pedersen_to_point(bp, [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_digest : in_digests) { + in_bits.insert(std::end(in_bits), std::cbegin(in_digest.bits), + std::cend(in_digest.bits)); + } + return in_bits; + }()) { + } + + /// Manual allocation of the result + /// Take in_bits as blueprint_variable_vector. + pedersen_to_point(blueprint &bp, + const detail::blueprint_variable_vector &in_bits, + const result_type &in_result) : + component(bp), + m_commitment(bp, get_base_points(commitment_component::basepoints_required(in_bits.size())), + in_bits, in_result), + result(m_commitment.result) { + } + + /// Manual allocation of the result + /// Take in_bits as block_variable. + pedersen_to_point(blueprint &bp, const block_variable &in_block, + const result_type &in_result) : + pedersen_to_point(bp, in_block.bits, in_result) { + } + + /// Manual allocation of the result + /// Take in_bits as digest_variable. + pedersen_to_point(blueprint &bp, const digest_variable &in_digest, + const result_type &in_result) : + pedersen_to_point(bp, in_digest.bits, in_result) { + } + + /// Manual allocation of the result + /// Take in_bits as container of block_variable. + template< + typename Blocks, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen_to_point(blueprint &bp, const Blocks &in_blocks, + const result_type &in_result) : + pedersen_to_point( + bp, + [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_block : in_blocks) { + in_bits.insert(std::end(in_bits), std::cbegin(in_block.bits), + std::cend(in_block.bits)); + } + return in_bits; + }(), + in_result) { + } + + /// Manual allocation of the result + /// Take in_bits as container of digest_variable. + template< + typename Digests, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen_to_point(blueprint &bp, const Digests &in_digests, + const result_type &in_result) : + pedersen_to_point( + bp, + [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_digest : in_digests) { + in_bits.insert(std::end(in_bits), std::cbegin(in_digest.bits), + std::cend(in_digest.bits)); + } + return in_bits; + }(), + in_result) { + } + + // TODO: ignored for now, enforce bitness checking constrains + void generate_gates(bool ensure_output_bitness = false) { + this->m_commitment.generate_gates(); + } + + void generate_assignments() { + this->m_commitment.generate_assignments(); + } + }; + + template, + typename HashParams = hashes::find_group_hash_default_params> + struct pedersen : public component { + using curve_type = Curve; + using hash_component = pedersen_to_point; + using field_type = typename hash_component::field_type; + using element_component = typename hash_component::element_component; + using to_bits_component = typename element_component::to_bits_component; + + using result_type = digest_variable; + + // hash_type is corresponding to the component hash policy + using hash_type = nil::crypto3::hashes::pedersen; + // TODO: retrieve digest_bits from hash_type + static constexpr std::size_t digest_bits = field_type::value_bits; + + hash_component hasher; + to_bits_component to_bits_converter; + result_type result; + + /// Auto allocation of the result. + /// Take in_bits as blueprint_variable_vector. + pedersen(blueprint &bp, const detail::blueprint_variable_vector &in_bits) : + component(bp), hasher(bp, in_bits), to_bits_converter(bp, hasher.result), + result(bp, digest_bits, to_bits_converter.result, 0) { + assert(this->result.digest_size == digest_bits); + } + + /// Auto allocation of the result. + /// Take in_bits as block_variable. + pedersen(blueprint &bp, const block_variable &in_block) : + pedersen(bp, in_block.bits) { + } + + /// Auto allocation of the result. + /// Take in_bits as digest_variable. + pedersen(blueprint &bp, const digest_variable &in_digest) : + pedersen(bp, in_digest.bits) { + } + + /// Auto allocation of the result. + /// Take in_bits as container of block_variable. + template< + typename Blocks, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen(blueprint &bp, const Blocks &in_blocks) : + pedersen(bp, [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_block : in_blocks) { + in_bits.insert(std::end(in_bits), std::cbegin(in_block.bits), std::cend(in_block.bits)); + } + return in_bits; + }()) { + } + + /// Auto allocation of the result. + /// Take in_bits as container of digest_variable. + template< + typename Digests, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen(blueprint &bp, const Digests &in_digests) : + pedersen(bp, [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_digest : in_digests) { + in_bits.insert(std::end(in_bits), std::cbegin(in_digest.bits), + std::cend(in_digest.bits)); + } + return in_bits; + }()) { + } + + /// Manual allocation of the result. + /// Take in_bits as blueprint_variable_vector. + pedersen(blueprint &bp, const detail::blueprint_variable_vector &in_bits, + const result_type &in_result) : + component(bp), + hasher(bp, in_bits), to_bits_converter(bp, hasher.result, in_result.bits), result(in_result) { + assert(this->result.digest_size == digest_bits); + } + + /// Manual allocation of the result. + /// Take in_bits as block_variable. + pedersen(blueprint &bp, const block_variable &in_block, + const result_type &in_result) : + pedersen(bp, in_block.bits, in_result) { + } + + /// Manual allocation of the result. + /// Take in_bits as digest_variable. + pedersen(blueprint &bp, const digest_variable &in_digest, + const result_type &in_result) : + pedersen(bp, in_digest.bits, in_result) { + } + + /// Manual allocation of the result. + /// Take in_bits as container of block_variable. + template< + typename Blocks, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen(blueprint &bp, const Blocks &in_blocks, const result_type &in_result) : + pedersen( + bp, + [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_block : in_blocks) { + in_bits.insert(std::end(in_bits), std::cbegin(in_block.bits), + std::cend(in_block.bits)); + } + return in_bits; + }(), + in_result) { + } + + /// Manual allocation of the result. + /// Take in_bits as container of digest_variable. + template< + typename Digests, + typename std::enable_if< + std::is_same, + typename std::iterator_traits::value_type>::value, + bool>::type = true> + pedersen(blueprint &bp, const Digests &in_digests, const result_type &in_result) : + pedersen( + bp, + [&]() { + detail::blueprint_variable_vector in_bits; + for (const auto &in_digest : in_digests) { + in_bits.insert(std::end(in_bits), std::cbegin(in_digest.bits), + std::cend(in_digest.bits)); + } + return in_bits; + }(), + in_result) { + } + + // TODO: ignored for now, enforce bitness checking constrains + void generate_gates(bool ensure_output_bitness = false) { + this->hasher.generate_gates(ensure_output_bitness); + this->to_bits_converter.generate_gates(); + this->result.generate_gates(); + } + + void generate_assignments() { + this->hasher.generate_assignments(); + // to_bits_converter generate witness also for result + this->to_bits_converter.generate_assignments(); + } + + static std::size_t get_digest_len() { + return digest_bits; + } + }; + + /// @brief See https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit + template, + typename HashParams = hashes::find_group_hash_default_params> + struct pedersen_commitment_to_point : public component { + using hash_component = pedersen_to_point; + using element_component = typename hash_component::element_component; + using addition_component = typename element_component::addition_component; + + using field_type = typename hash_component::field_type; + using result_type = typename hash_component::result_type; + + public: + result_type result; + + private: + hash_component hasher; + element_component random_point; + addition_component adder; + + /// Auto allocation of the result + /// Take in_bits as blueprint_variable_vector. + pedersen_commitment_to_point(blueprint &bp, + const detail::blueprint_variable_vector &in_bits) : + component(bp), + // public field + result(bp), + // private fields + hasher(bp, in_bits), random_point(bp), adder(bp, hasher.result, random_point, result) { + } + + /// Manual allocation of the result + /// Take in_bits as blueprint_variable_vector. + pedersen_commitment_to_point(blueprint &bp, + const detail::blueprint_variable_vector &in_bits, + const result_type &result) : + component(bp), + // public field + result(result), + // private fields + hasher(bp, in_bits), random_point(bp), adder(bp, hasher.result, random_point, result) { + } + + void generate_gates(bool ensure_output_bitness = false) { + hasher.generate_gates(ensure_output_bitness); + adder.generate_gates(); + } + + void generate_assignments(const typename field_type::value_type &r) { + using group_hash_type = hashes::find_group_hash; + + hasher.generate_assignments(); + random_point.generate_assignments(r * hash(std::vector { + 'r', + })); + adder.generate_assignments(); + } + }; + + template, + typename HashParams = hashes::find_group_hash_default_params> + struct pedersen_commitment : public component { + using commitment_component = + pedersen_commitment_to_point; + using element_component = typename commitment_component::element_component; + using to_bits_component = typename element_component::to_bits_component; + + using field_type = typename commitment_component::field_type; + using result_type = digest_variable; + + private: + commitment_component commiter; + to_bits_component to_bits_converter; + + public: + result_type result; + + /// Auto allocation of the result + /// Take in_bits as blueprint_variable_vector. + pedersen_commitment(blueprint &bp, + const detail::blueprint_variable_vector &in_bits) : + component(bp), + // private fields + commiter(bp, in_bits), to_bits_converter(bp, commiter.result), + // public field + result(bp, field_type::value_bits, to_bits_converter.result, 0) { + } + + /// Manual allocation of the result + /// Take in_bits as blueprint_variable_vector. + pedersen_commitment(blueprint &bp, + const detail::blueprint_variable_vector &in_bits, + const result_type &result) : + component(bp), + // private fields + commiter(bp, in_bits), to_bits_converter(bp, commiter.result, result.bits), + // public field + result(result) { + } + + void generate_gates(bool ensure_output_bitness = false) { + commiter.generate_gates(ensure_output_bitness); + to_bits_converter.generate_gates(); + } + + void generate_assignments(const typename field_type::value_type &r) { + commiter.generate_assignments(r); + // to_bits_converter generate witness also for result + to_bits_converter.generate_assignments(); + result.generate_gates(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_HASHES_PEDERSEN_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon.hpp new file mode 100644 index 000000000..0cc70c12e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon.hpp @@ -0,0 +1,412 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_PLONK_POSEIDON_HPP +#define CRYPTO3_BLUEPRINT_PLONK_POSEIDON_HPP + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: [x_0, x_1, x_2] \in Fp + // Output: [y_0, y_1, y_2] - Poseidon permutation of [x_0, x_1, x_2] + template + class poseidon; + + template + class poseidon, + FieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + constexpr static const std::uint32_t state_size = 3; + constexpr static const std::uint32_t rounds_amount = 55; + + constexpr static const std::size_t rounds_per_row = 5; + + constexpr static const std::size_t sbox_alpha = 7; + + constexpr static const std::array, state_size> + mds = detail::poseidon_constants::mds; + constexpr static const std::array, rounds_amount> + round_constant = detail::poseidon_constants::round_constant; + + constexpr static const std::size_t rate = 2; + constexpr static const std::size_t gates_amount = 11; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "poseidon hash"; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return poseidon::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + + static manifest_type get_manifest() { + using manifest_param = nil::blueprint::manifest_param; + using manifest_single_value_param = nil::blueprint::manifest_single_value_param; + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return rounds_amount / rounds_per_row + 1; + } + + struct input_type { + std::array input_state; + + std::vector> all_vars() { + return {input_state[0], input_state[1], input_state[2]}; + } + }; + + struct result_type { + std::array output_state = {var(0, 0, false), var(0, 0, false), var(0, 0, false)}; + + result_type(const poseidon, + FieldType> &component, + std::uint32_t start_row_index) { + + output_state = {var(component.W(0), start_row_index + component.rows_amount - 1, false), + var(component.W(1), start_row_index + component.rows_amount - 1, false), + var(component.W(2), start_row_index + component.rows_amount - 1, false)}; + } + + std::vector> all_vars() { + return {output_state[0], output_state[1], output_state[2]}; + } + }; + + constexpr static std::array, state_size> + mds_constants() { + return mds; + } + + template + explicit poseidon(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + poseidon(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + poseidon(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_poseidon = + poseidon, + FieldType>; + + template + typename plonk_poseidon::result_type + generate_assignments( + const plonk_poseidon &component, + assignment> + &assignment, + const typename plonk_poseidon::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_poseidon; + + constexpr static const std::uint32_t state_size = component_type::state_size; + + std::array state = { + var_value(assignment, instance_input.input_state[0]), + var_value(assignment, instance_input.input_state[1]), + var_value(assignment, instance_input.input_state[2])}; + std::array next_state; + + std::size_t row = start_row_index; + assignment.witness(component.W(0), row) = state[0]; + assignment.witness(component.W(1), row) = state[1]; + assignment.witness(component.W(2), row) = state[2]; + + static_assert(state_size == 3); + + for (std::size_t i = row; i < row + component.rows_amount - 1; i++) { + for (std::size_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[(i - row) * 5][j]; + } + + assignment.witness(component.W(3), i) = next_state[0]; + assignment.witness(component.W(4), i) = next_state[1]; + assignment.witness(component.W(5), i) = next_state[2]; + state = next_state; + for (std::uint32_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[(i - row) * 5 + 1][j]; + } + assignment.witness(component.W(6), i) = next_state[0]; + assignment.witness(component.W(7), i) = next_state[1]; + assignment.witness(component.W(8), i) = next_state[2]; + state = next_state; + for (std::uint32_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[(i - row) * 5 + 2][j]; + } + assignment.witness(component.W(9), i) = next_state[0]; + assignment.witness(component.W(10), i) = next_state[1]; + assignment.witness(component.W(11), i) = next_state[2]; + state = next_state; + for (std::uint32_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[(i - row) * 5 + 3][j]; + } + assignment.witness(component.W(12), i) = next_state[0]; + assignment.witness(component.W(13), i) = next_state[1]; + assignment.witness(component.W(14), i) = next_state[2]; + state = next_state; + for (std::uint32_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[(i - row) * 5 + 4][j]; + } + assignment.witness(component.W(0), i + 1) = next_state[0]; + assignment.witness(component.W(1), i + 1) = next_state[1]; + assignment.witness(component.W(2), i + 1) = next_state[2]; + state = next_state; + } + + return typename plonk_poseidon::result_type( + component, start_row_index); + } + + template + std::array::rounds_amount / + plonk_poseidon::rounds_per_row> + generate_gates( + const plonk_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_poseidon::input_type + &instance_input) { + + using component_type = plonk_poseidon; + using var = typename component_type::var; + + std::array selectors; + + std::size_t j = 0; + for (std::size_t z = 0; z < component_type::rounds_amount; z += component_type::rounds_per_row) { + auto constraint_1 = + var(component.W(3), 0) - + (var(component.W(0), 0).pow(component_type::sbox_alpha) * component_type::mds[0][0] + + var(component.W(1), 0).pow(component_type::sbox_alpha) * component_type::mds[0][1] + + var(component.W(2), 0).pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[z][0]); + auto constraint_2 = + var(component.W(4), 0) - + (var(component.W(0), 0).pow(component_type::sbox_alpha) * component_type::mds[1][0] + + var(component.W(1), 0).pow(component_type::sbox_alpha) * component_type::mds[1][1] + + var(component.W(2), 0).pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[z][1]); + auto constraint_3 = + var(component.W(5), 0) - + (var(component.W(0), 0).pow(component_type::sbox_alpha) * component_type::mds[2][0] + + var(component.W(1), 0).pow(component_type::sbox_alpha) * component_type::mds[2][1] + + var(component.W(2), 0).pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[z][2]); + + auto constraint_4 = + var(component.W(6), 0) - + (var(component.W(3), 0).pow(component_type::sbox_alpha) * component_type::mds[0][0] + + var(component.W(4), 0).pow(component_type::sbox_alpha) * component_type::mds[0][1] + + var(component.W(5), 0).pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[z + 1][0]); + auto constraint_5 = + var(component.W(7), 0) - + (var(component.W(3), 0).pow(component_type::sbox_alpha) * component_type::mds[1][0] + + var(component.W(4), 0).pow(component_type::sbox_alpha) * component_type::mds[1][1] + + var(component.W(5), 0).pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[z + 1][1]); + auto constraint_6 = + var(component.W(8), 0) - + (var(component.W(3), 0).pow(component_type::sbox_alpha) * component_type::mds[2][0] + + var(component.W(4), 0).pow(component_type::sbox_alpha) * component_type::mds[2][1] + + var(component.W(5), 0).pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[z + 1][2]); + + auto constraint_7 = + var(component.W(9), 0) - + (var(component.W(6), 0).pow(component_type::sbox_alpha) * component_type::mds[0][0] + + var(component.W(7), 0).pow(component_type::sbox_alpha) * component_type::mds[0][1] + + var(component.W(8), 0).pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[z + 2][0]); + + auto constraint_8 = + var(component.W(10), 0) - + (var(component.W(6), 0).pow(component_type::sbox_alpha) * component_type::mds[1][0] + + var(component.W(7), 0).pow(component_type::sbox_alpha) * component_type::mds[1][1] + + var(component.W(8), 0).pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[z + 2][1]); + auto constraint_9 = + var(component.W(11), 0) - + (var(component.W(6), 0).pow(component_type::sbox_alpha) * component_type::mds[2][0] + + var(component.W(7), 0).pow(component_type::sbox_alpha) * component_type::mds[2][1] + + var(component.W(8), 0).pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[z + 2][2]); + + auto constraint_10 = + var(component.W(12), 0) - + (var(component.W(9), 0).pow(component_type::sbox_alpha) * component_type::mds[0][0] + + var(component.W(10), 0).pow(component_type::sbox_alpha) * component_type::mds[0][1] + + var(component.W(11), 0).pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[z + 3][0]); + auto constraint_11 = + var(component.W(13), 0) - + (var(component.W(9), 0).pow(component_type::sbox_alpha) * component_type::mds[1][0] + + var(component.W(10), 0).pow(component_type::sbox_alpha) * component_type::mds[1][1] + + var(component.W(11), 0).pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[z + 3][1]); + auto constraint_12 = + var(component.W(14), 0) - + (var(component.W(9), 0).pow(component_type::sbox_alpha) * component_type::mds[2][0] + + var(component.W(10), 0).pow(component_type::sbox_alpha) * component_type::mds[2][1] + + var(component.W(11), 0).pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[z + 3][2]); + + auto constraint_13 = + var(component.W(0), +1) - + (var(component.W(12), 0).pow(component_type::sbox_alpha) * component_type::mds[0][0] + + var(component.W(13), 0).pow(component_type::sbox_alpha) * component_type::mds[0][1] + + var(component.W(14), 0).pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[z + 4][0]); + auto constraint_14 = + var(component.W(1), +1) - + (var(component.W(12), 0).pow(component_type::sbox_alpha) * component_type::mds[1][0] + + var(component.W(13), 0).pow(component_type::sbox_alpha) * component_type::mds[1][1] + + var(component.W(14), 0).pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[z + 4][1]); + auto constraint_15 = + var(component.W(2), +1) - + (var(component.W(12), 0).pow(component_type::sbox_alpha) * component_type::mds[2][0] + + var(component.W(13), 0).pow(component_type::sbox_alpha) * component_type::mds[2][1] + + var(component.W(14), 0).pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[z + 4][2]); + selectors[j] = bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7, constraint_8, constraint_9, constraint_10, constraint_11, constraint_12, + constraint_13, constraint_14, constraint_15}); + j++; + } + return selectors; + } + + template + void generate_copy_constraints( + const plonk_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_poseidon::input_type + &instance_input, + const std::size_t start_row_index) { + + // CRITICAL: these copy constraints might not be sufficient, but are definitely required. + // I've added copy constraints for the inputs, but internal ones might be missing + // Proceed with care + using var = typename plonk_poseidon::var; + for (std::size_t i = 0; i < 3; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.input_state[i]}); + } + } + + template + typename plonk_poseidon::result_type + generate_circuit( + const plonk_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_poseidon::input_type + &instance_input, + const std::size_t start_row_index) { + + auto selector_indices = generate_gates(component, bp, assignment, instance_input); + for (std::size_t z = 0, i = 0; + z < plonk_poseidon::rounds_amount; + z += plonk_poseidon::rounds_per_row, + i++) { + assignment.enable_selector(selector_indices[i], start_row_index + i); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + return typename plonk_poseidon::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_PLONK_POSEIDON_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon_constants.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon_constants.hpp new file mode 100644 index 000000000..b3efcc39a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/poseidon/plonk/poseidon_constants.hpp @@ -0,0 +1,546 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_PLONK_DETAIL_POSEIDON_CONSTANTS_HPP +#define CRYPTO3_BLUEPRINT_PLONK_DETAIL_POSEIDON_CONSTANTS_HPP + +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + struct poseidon_constants; + + template + struct poseidon_constants { + using FieldType = nil::crypto3::algebra::fields::pallas_base_field; + + constexpr static const std::array, + state_size> + mds = {{ + {{0x1a9bd250757e29ef4959b9bef59b4e60e20a56307d6491e7b7ea1fac679c7903_cppui_modular253, + 0x384aa09faf3a48737e2d64f6a030aa242e6d5d455ae4a13696b48a7320c506cd_cppui_modular253, + 0x3d2b7b0209bc3080064d5ce4a7a03653f8346506bfa6d076061217be9e6cfed5_cppui_modular253}}, + {{0x9ee57c70bc351220b107983afcfabbea79868a4a8a5913e24b7aaf3b4bf3a42_cppui_modular253, + 0x20989996bc29a96d17684d3ad4c859813115267f35225d7e1e9a5b5436a2458f_cppui_modular253, + 0x14e39adb2e171ae232116419ee7f26d9191edde8a5632298347cdb74c3b2e69d_cppui_modular253}}, + {{0x174544357b687f65a9590c1df621818b5452d5d441597a94357f112316ef67cb_cppui_modular253, + 0x3ca9263dc1a19d17cfbf15b0166bb25f95dffc53212db207fcee35f02c2c4137_cppui_modular253, + 0x3cf1fbef75d4ab63b7a812f80b7b0373b2dc21d269ba7c4c4d6581d50aae114c_cppui_modular253}}, + }}; + + constexpr static const std::array, + rounds_amount> + round_constant = {{ + {{0x2ec559cd1a1f2f6889fc8ae5f07757f202b364429677c8ff6603fd6d93659b47_cppui_modular253, + 0x2553b08c788551bfe064d91c17eb1edb8662283229757711b2b30895f0aa3bad_cppui_modular253, + 0x25a706fb0f35b260b6f28d61e082d36a8f161be1f4d9416371a7b65f2bfafe4e_cppui_modular253}}, + {{0x37c0281fda664cc2448d0e7dd77aaa04752250817a945abeea8cfaaf3ee39ba0_cppui_modular253, + 0x140488321291998b8582eaceeb3fa9ca3980eb64a453573c5aaa2910405936b6_cppui_modular253, + 0x3a73fe35b1bdd66b809aad5eab47b5c83b0146fd7fc632dfb49cd91ae1169378_cppui_modular253}}, + {{0x21b7c2b35fd7710b06245711f26c0635d3e21de4db10dd3a7369f59f468d7be6_cppui_modular253, + 0x1803a068d25fef2ef652c8a4847aa18a29d1885e7bf77fd6a34d66536d09cad7_cppui_modular253, + 0x291de61c5e6268213772cf7e03c80c2e833eb77c58c46548d158a70fbbd9724b_cppui_modular253}}, + {{0x230043a0dc2dfab63607cbe1b9c482fdd937fdefecc6905aa5012e89babead13_cppui_modular253, + 0x218af77a05c502d3fa3144efcf47a0f2a0292498c10c6e2368565674e78764f4_cppui_modular253, + 0x223e2d94c177d27e071d55729d13a9b216955c7102cc9a95ea40058efb506117_cppui_modular253}}, + {{0x2a18257c15ad9b6fe8b7c5ad2129394e902c3c3802e738f24ce2f585ae5f6a38_cppui_modular253, + 0xa6f7ba75f216403d2e4940469d199474a65aa5ef814e36400bddef06158dcf8_cppui_modular253, + 0x169be41c6227956efef5b4cdde65d00d5e04fe766178bdc731615c6e5b93e31e_cppui_modular253}}, + {{0x2e28f50a9a55d2e91774083072734544417e290a1cfebc01801b94d0728fe663_cppui_modular253, + 0xfdedf8da8654a22831040cfc74432464b173ee68628fd90498480b9902f2819_cppui_modular253, + 0x46a3ed9863d2d739dd8bc9e90a746fda1197162d0a0bec3db1f2f6042cf04e2_cppui_modular253}}, + {{0x219e08b460c305b428670bacab86ac1e9458075778d35c3619ae7ba1f9b2ed76_cppui_modular253, + 0x38bb36a12ebcec4d4e8728eb43e3f12a6e33b1ffa1463379018d4e12424e62ca_cppui_modular253, + 0x1e9aa3fe25d116ccfbd6a8fccdae0aa9bc164a03ab7e951704ee9a715fbedee6_cppui_modular253}}, + {{0x30f33ed70da4c2bfb844ff1a7558b817d1ec300da86a1694f2db45047d5f18b_cppui_modular253, + 0x282b04137350495ab417cf2c47389bf681c39f6c22d9e370b7af75cbcbe4bb1_cppui_modular253, + 0x9b1528dea2eb5bd96905b88ff05fdf3e0f220fe1d93d1b54953ac98fec825f0_cppui_modular253}}, + {{0x30083dbbb5eab39311c7a8bfd5e55567fa864b3468b5f9200e529cda03d9ef71_cppui_modular253, + 0x17eace73cf67c6112239cbf51dec0e714ee4e5a91dbc9209dc17bbea5bcd094_cppui_modular253, + 0x37af1de8f5475ba165b90f8d568683d54e215df97e9287943370cf4118428097_cppui_modular253}}, + {{0x16ff7592836a45340ec6f2b0f122736d03f0bcb84012f922a4baa73ea0e66f51_cppui_modular253, + 0x1a5985d4b359d03de60b2edabb1853f476915febc0e40f83a2d1d0084efc3fd9_cppui_modular253, + 0x255a9d4beb9b5ea18ab9782b1abb267fc5b773b98ab655fd4d469698e1e1f975_cppui_modular253}}, + {{0x34a8d9f45200a9ac28021712be81e905967bac580a0b9ee57bc4231f5ecb936a_cppui_modular253, + 0x979556cb3edcbe4f33edd2094f1443b4b4ec6c457b0425b8463e788b9a2dcda_cppui_modular253, + 0x2a4d028c09ad39c30666b78b45cfadd5279f6239379c689a727f626679272654_cppui_modular253}}, + {{0xc31b68f6850b3bd71fe4e89984e2c87415523fb54f24ec8ae71430370154b33_cppui_modular253, + 0x1a27ca0b953d3dba6b8e01cf07d76c611a211d139f2dff5ac023ed2454f2ed90_cppui_modular253, + 0x109ae97c25d60242b86d7169196d2212f268b952dfd95a3937916b9905303180_cppui_modular253}}, + {{0x3698c932f2a16f7bb9abac089ec2de79c9965881708878683caf53caa83ad9c4_cppui_modular253, + 0x3c7e25e0ac8fba3dc1360f8a9a9fa0be0e031c8c76a93497b7cac7ed32ade6c0_cppui_modular253, + 0x2fc5023c5e4aed5aa7dfca0f5492f1b6efab3099360ec960237512f48c858a79_cppui_modular253}}, + {{0x2c124735f3f924546fb4fdfa2a018e03f53063d3a2e87fd285ba8d647eda6765_cppui_modular253, + 0x12c875c9b79591acf9033f8b6c1e357126c44b23f3486fbee0d98340a3382251_cppui_modular253, + 0x3cda935e895857d39a7db8476aeda5a5131cb165a353073fd3e473fd8855528d_cppui_modular253}}, + {{0x218eb756fa5f1df9f1eb922ef80b0852588779a7368e3d010def1512815d8759_cppui_modular253, + 0x23bcf1032957015ef171fbb4329bca0c57d59885522f25f4b082a3cf301cfbc6_cppui_modular253, + 0x17474c3b6a9bc1057df64b9e4d62badbc7f3867b3dd757c71c1f656205d7bceb_cppui_modular253}}, + {{0x19826c0ee22972deb41745d3bd412c2ae3d4c18535f4b60c9e870edffa3d550_cppui_modular253, + 0x30bcb17dfd622c46f3275f698319b68d8816bed0368ded435ed61992bc43efa9_cppui_modular253, + 0x3bd816c214c66410229cfbd1f4a3a42e6a0f82f3c0d49b09bc7b4c042ff2c94b_cppui_modular253}}, + {{0x8943ec01d9fb9f43c840757738979b146c3b6d1982280e92a52e8d045633ea1_cppui_modular253, + 0x2670bf8c01822e31c70976269d89ed58bc79ad2f9d1e3145df890bf898b57e47_cppui_modular253, + 0xdd53b41599ae78dbd3e689b65ebcca493effa94ed765eeec75a0d3bb20407f9_cppui_modular253}}, + {{0x68177d293585e0b8c8e76a8a565c8689a1d88e6a9afa79220bb0a2253f203c3_cppui_modular253, + 0x35216f471043866edc324ad8d8cf0cc792fe7a10bf874b1eeac67b451d6b2cf5_cppui_modular253, + 0x1fd6efb2536bfe11ec3736e7f7448c01eb2a5a9041bbf84631cc83ee0464f6af_cppui_modular253}}, + {{0x2c982c7352102289fc1b48dafcd9e3cc364d5a4324575e4721daf0af10033c67_cppui_modular253, + 0x352f7e8c7662d86db9c722d4d07778858771b832af5bb5dc3b13cf94851c1b45_cppui_modular253, + 0x18e3c0c1caa5e3ed66ee1ab6f55a5c8063d8c9b034ae47db43435147149e37d5_cppui_modular253}}, + {{0x3124b12deb37dcbb3d96c1a08d507523e30e03e0919559bf2daaab238422eade_cppui_modular253, + 0x143bf0def31437eb21095200d2d406e6e5727833683d9740b9bfc1713215dc9a_cppui_modular253, + 0x1ebee92143f32b4f9d9a90ad62b8483c977480767b53c71f6bde934a8ef38f17_cppui_modular253}}, + {{0xff6c794ad1afaa494088d5f8ee6c47bf9e83013478628cf9f41f2e81383ebeb_cppui_modular253, + 0x3d0a10ac3ee707c62e8bdf2cdb49ac2cf4096cf41a7f214fdd1f8f9a24804f17_cppui_modular253, + 0x1d61014cd3ef0d87d037c56bdfa370a73352b95d472ead1937bed06a31801c91_cppui_modular253}}, + {{0x123e185b2ec7f072507ac1e4e743589bb25c8fdb468e329e7de169875f90c525_cppui_modular253, + 0x30b780c0c1cb0609623732824c75017da9799bdc7e08b527bae7f409ebdbecf2_cppui_modular253, + 0x1dfb3801b7ae4e209f68195612965c6e37a2ed5cf1eeee3d46edf655d6f5afef_cppui_modular253}}, + {{0x2fdee42805b2774064e963c741552556019a9611928dda728b78311e1f049528_cppui_modular253, + 0x31b2b65c431212ed36fdda5358d90cd9cb51c9f493bff71cdc75654547e4a22b_cppui_modular253, + 0x1e3ca033d8413b688db7a543e62ac2e69644c0614801379cfe62fa220319e0ef_cppui_modular253}}, + {{0xc8ef1168425028c52a32d93f9313153e52e9cf15e5ec2b4ca09d01730dad432_cppui_modular253, + 0x378c73373a36a5ed94a34f75e5de7a7a6187ea301380ecfb6f1a22cf8552638e_cppui_modular253, + 0x3218aeec20048a564015e8f221657fbe489ba404d7f5f15b829c7a75a85c2f44_cppui_modular253}}, + {{0x3312ef7cbbad31430f20f30931b070379c77119c1825c6560cd2c82cf767794e_cppui_modular253, + 0x356449a71383674c607fa31ded8c0c0d2d20fb45c36698d258cecd982dba478c_cppui_modular253, + 0xcc88d1c91481d5321174e55b49b2485682c87fac2adb332167a20bcb57db359_cppui_modular253}}, + {{0x1defccbd33740803ad284bc48ab959f349b94e18d773c6c0c58a4b9390cc300f_cppui_modular253, + 0x2d263cc2e9af126d768d9e1d2bf2cbf32063be831cb1548ffd716bc3ee7034fe_cppui_modular253, + 0x111e314db6fb1a28e241028ce3d347c52558a33b6b11285a97fffa1b479e969d_cppui_modular253}}, + {{0x27409401e92001d434cba2868e9e371703199c2372d23ef329e537b513f453e_cppui_modular253, + 0x24a852bdf9cb2a8fedd5e85a59867d4916b8a57bdd5f84e1047d410770ffffa0_cppui_modular253, + 0x205d1b0ee359f621845ac64ff7e383a3eb81e03d2a2966557746d21b47329d6e_cppui_modular253}}, + {{0x25c327e2cc93ec6f0f23b5e41c931bfbbe4c12da7d55a2b1c91c79db982df903_cppui_modular253, + 0x39df3e22d22b09b4265da50ef175909ce79e8f0b9599dff01cf80e70884982b9_cppui_modular253, + 0x9b08d58853d8ac908c5b14e5eb8611b45f40faaa59cb8dff98fb30efcdfaa01_cppui_modular253}}, + {{0x1ece62374d79e717db4a68f9cddaaf52f8884f397375c0f3c5c1dbaa9c57a0a6_cppui_modular253, + 0x3bd089b727a0ee08e263fa5e35b618db87d7bcce03441475e3fd49639b9fa1c1_cppui_modular253, + 0x3fedea75f37ad9cfc94c95141bfb4719ee9b32b874b93dcfc0cc12f51a7b2aff_cppui_modular253}}, + {{0x36dfa18a9ba1b194228494a8acaf0668cb43aca9d4e0a251b20ec3424d0e65cd_cppui_modular253, + 0x119e98db3f49cd7fcb3b0632567d9ccaa5498b0d411a1437f57c658f41931d0c_cppui_modular253, + 0x1100b21c306475d816b3efcd75c3ae135c54ad3cc56ca22abd9b7f45e6d02c19_cppui_modular253}}, + {{0x15791f9bbea213937208c82794eb667f157f003c65b64aa9800f4bbee4ea5119_cppui_modular253, + 0x1adbeb5e9c4d515ecfd250ebee56a2a816eb3e3dc8d5d440c1ab4285b350be64_cppui_modular253, + 0x1fbf4738844a9a249aec253e8e4260e4ab09e26bea29ab0020bf0e813ceecbc3_cppui_modular253}}, + {{0x3418a929556ec51a086459bb9e63a821d407388cce83949b9af3e3b0434eaf0e_cppui_modular253, + 0x9406b5c3af0290f997405d0c51be69544afb240d48eeab1736cda0432e8ff9e_cppui_modular253, + 0x23ece5d70b38ccc9d43cd923e5e3e2f62d1d873c9141ef01f89b6de1336f5bc7_cppui_modular253}}, + {{0x1852d574e46d370a0b1e64f6c41eeb8d40cf96c524a62965661f2ef87e67234d_cppui_modular253, + 0xa657027cce8d4f238ea896dde273b7537b508674a366c66b3789d9828b0ce90_cppui_modular253, + 0x3482f98a46ec358108fbbb68fd94f8f2baa73c723baf21922a850e45511f5a2d_cppui_modular253}}, + {{0x3f62f164f8c905b335a6cbf76131d2430237e17ad6abc76d2a6329c1ec5463ee_cppui_modular253, + 0x7e397f503f9c1cea028465b2950ea444b15c5eab567d5a69ea2925685694df0_cppui_modular253, + 0x405f1fc711872373d6eb50a09fbfb05b2703ae0a0b4edb86aedb216db17a876_cppui_modular253}}, + {{0xbe0848eb3e09c7027110ad842c502441c97afa14a844406fcfec754a25658c1_cppui_modular253, + 0x26b78788fd98ac020bac92d0e7792bb5ffed06b697d847f61d984f905d9ba870_cppui_modular253, + 0x38fd5318d39055c82fef9bdd33315a541c0ec4363e6cc0687005871355dfa573_cppui_modular253}}, + {{0x380bd03b840c48c8ba3830e7cace72f91a5002218c617294e8c8bc687d5216de_cppui_modular253, + 0x2c6e57ddc1d7c81a0299ed49c3d74759416bc8426f30e2af5622895c531b4e1c_cppui_modular253, + 0x11d3a81b262fc76ef506ee6d88e5991d0de8cb9dd162d97c58b175e3bc4584f3_cppui_modular253}}, + {{0x9b6b283ebaf45fbb1e448969ace9be62adf67ddf58614925741deb6a1ba7def_cppui_modular253, + 0x15d5095164c885763fa83cdf776d436382821a17bc5563a5b6f6dfcdac504ade_cppui_modular253, + 0x3427fdbfca3cea23063eb138c5055c6cad9c4252b23d12c12293308eff7d9124_cppui_modular253}}, + {{0x272f12e731077b74317ef2543c33b86194db1da5f6a7e1eee0656672c81685fe_cppui_modular253, + 0x5323f85deb8c07c193c37a73d76f6114967913a2bdce11995f183e769f42967_cppui_modular253, + 0x3d5ce415ecae4ba42b417ea3a501b44694f46efddff2fcca952b097f3852d3d8_cppui_modular253}}, + {{0xe8ec18c7b52c514d42047f1f0b2a90cb8c0c7391cf9479cd7fd5bfe1d3db8f2_cppui_modular253, + 0x1591c865ea7065d54304519f8bb268bddbeaf3afae54edcd01a833ed0a9ef1a_cppui_modular253, + 0x3eddbeeee5eca5deee4bf1789c435e1241e0d71186d8f0f62d74729dfc3119fb_cppui_modular253}}, + {{0x23691c7009b9283b268766e8d491716d3c1993e6ecf458def8f762af3e355707_cppui_modular253, + 0x26cdab2c837ebeac5bea4be1d6f0488034907374d81a61a34f1c4db397d4c09b_cppui_modular253, + 0x2d2206730664d58be0676dad1fee0e990c264a7410a2cdb6b55653c1df72ef56_cppui_modular253}}, + {{0x2bb74bb185372334a4ef5f6d18e2ece54086e62b04985dd794b7117b0be9217f_cppui_modular253, + 0x366250fe928c45d8d5aa35f0a142754907ff3c598410199b589b28cd851b2204_cppui_modular253, + 0x1868f8118482c6b4a5a61a81c8aaca128953179c20f73a44022d9976bdc34af1_cppui_modular253}}, + {{0xb7901c670e1d75d726eb88d000950b3c963f0f7a6ca24994bdc07ae2f78b4d3_cppui_modular253, + 0x32c4bd8ab70e1f25af77af57dd340c8e6c8a101dfc5e8dd03314566db90b870_cppui_modular253, + 0x1ce36db31fe6ea3cd9308db9aa43a8af5c41a8f0a6509bfe00f0e7b486c0ab8a_cppui_modular253}}, + {{0x26596ea9e1915e53da3479e9d13c3c920505e2449e325810ff6ca855fe4b7c6e_cppui_modular253, + 0x30f296a269868a7fca8f5b1e269c0116304df31729559a270e713509d3a6d5dc_cppui_modular253, + 0x2588961eff7897d87eb6ac72350ef9f52640647cbd23136919a994dfd1979d5_cppui_modular253}}, + {{0x16a49e69721e80690d41e06229e9bc2dbaf9a2abf4b89388db2485595409d62b_cppui_modular253, + 0x3d7aca02c051fcad8073cfd67210cd423a31888afc4a444d9d3adf3d6c5da7bf_cppui_modular253, + 0x299bd48a740b7790075268312ab8072c72421de5a6437fa5e25431ef951847b4_cppui_modular253}}, + {{0x11a69b867d9ea22ec1b2f28e96617129e36eefaea9e8126bdc6a42b99072902b_cppui_modular253, + 0x25bc1af391f3c1f2284a95da92b5883d1b3a40794b2358b2e7a70fca22da64ce_cppui_modular253, + 0x361ab3843f4d8ddadede39d82bb1a8109f89b6d9aa117b8f365de43895de0baa_cppui_modular253}}, + {{0x38ef3ab5b61c117a3465a017a9c8ba4c227659b41fdf145206d5c960f49dd45b_cppui_modular253, + 0x3992f83f26143dbdbd335604a1a14daf238ae43c249783f694feaf560aaae20f_cppui_modular253, + 0x350287977eb71c81b10ecd039aad99cfa9ed84a04301cb30869e1dc7fa1dc638_cppui_modular253}}, + {{0x3afb5bc126020586dcccba32dd054cd9a3f3b834ca9678d6802c48b1da97d6ed_cppui_modular253, + 0x172b7c2d8e7e4b06d183a2575b790749d0970c54966407fa8f59072c729de671_cppui_modular253, + 0x2eb53fe3a278688a70494569e54a0f0d269935aec6c897bef4d368c1f67d57e4_cppui_modular253}}, + {{0x375ae56b8d9310d553ed77d406dedc3f0393e5a321b71caee6a5bb7078b5035_cppui_modular253, + 0x1d49a0d53bc2993cbf1fb5d1da9bb76fe46a7031d5e5d43fadbf54bc17c1ef38_cppui_modular253, + 0x132d17b87cab6d707ddfa1f01df1724ad37957e989c44f1ff71426367f953160_cppui_modular253}}, + {{0x62da5280948d8c6c4acc7e6a1aa421f0f9ec179a44146750060be4be6755f85_cppui_modular253, + 0xa4b4d5cde54a974ea4e57ee4132d2ab2510c300f21930d6bbbf211d1add80f9_cppui_modular253, + 0x3356f1fbeac493ccab752b70bbed821ce49965c19284d7aacd78fbf3ff864e91_cppui_modular253}}, + {{0x42721e8a9cc32557851feb0e0190c5dfbf4cb1b8f47d37e7e653ec6ff8a4059_cppui_modular253, + 0x53d9b2633fff31ca4fc5724ce6b4422318128cdf01897d321e86f47cdf748b1_cppui_modular253, + 0x267d96caeafde5dbd3db1f0668b09ccd532a22f0205494716a786219fb4c801c_cppui_modular253}}, + {{0x39316997737610193c3f9ffcfd4e23d38aac12cd7b95b8d256d774101650a6ca_cppui_modular253, + 0x191e377462986563fdabf9b23529f7c84c6b200b9101b3a5096bca5f377981fb_cppui_modular253, + 0x20f89af9722f79c860d2059a0ec209cf3a7925ad0798cab655eca62fe73ff3d9_cppui_modular253}}, + {{0x1ca568aeddb2ef391a7c78ecf104d32d785b9ca145d97e35879df3534a7d1e0b_cppui_modular253, + 0x25de9ba0a37472c3b4c0b9c3bc25cbbf78d91881b6f94ee70e4abf090211251c_cppui_modular253, + 0x3393debd38d311881c7583bee07e605ef0e55c62f0508ccc2d26518cd568e1ef_cppui_modular253}}, + {{0x38df2fd18a8d7563806aa9d994a611f642d5c397388d1dd3e78bc7a4515c5b1_cppui_modular253, + 0x5c6503ff1ee548f2435ad9148d7fb94c9222b0908f445537a6667047f6d501c_cppui_modular253, + 0x104c88d6d0682d82d3d664826dc9565db101a220aa8f90572eb798468a82a2ab_cppui_modular253}}, + {{0x2caad6108c09ee6aee7851b4a2d2d3b7c3ca3c56a80003c8471f90bfa4ac628b_cppui_modular253, + 0xa57dbd4c327826c8a97bc7285f94bcddb966177346f1792c4bd7088aa0353f3_cppui_modular253, + 0x3c15552f9124318b8433d01bb53ba04ba1cc9eb91d83b918e32fea39fbe908fa_cppui_modular253}}, + {{0xe10c10cbbe1717a9441c6299c4fc087c222208bd4fa8f3be66d2075f623b513_cppui_modular253, + 0x1e8b254cbff2c92a83dff1728c81dd22a9570f590e497cb2d640042cb879a930_cppui_modular253, + 0x1812dbcd70c440610057bbfdd0cc4d31d1faf5786419b53841c4adc43f2b2352_cppui_modular253}}, + }}; + }; + + template + struct poseidon_constants { + using FieldType = nil::crypto3::algebra::fields::vesta_base_field; + + constexpr static const std::array, + state_size> + mds = {{ + {{ + 0x3e28f7dd17f47a7e304a54d377dd7aeead6b92027d60baf300246cf023dd594e_cppui_modular255, + 0x30db06abb696fccb92b28ac214f4893d3fd84b3d4a9018754975e24477c32600_cppui_modular255, + 0x174110bc1b058c6016ff5e8152ab3ffb6e2e6c4d01e66aba302659c51b7f563a_cppui_modular255, + }}, + {{ + 0x12d36fa83503146980c05a1d48bcd50d2e9d4390e353a158a0fe387e2b4aeb0c_cppui_modular255, + 0x2ab17c8eb369bea76e9f0c385e8bafc71536bedc8e06d06fd65c1670e94d9c55_cppui_modular255, + 0xcc915328165c13986af127e108b9e5d9a60c5dc92e3e7636b8c3da5b4a8537_cppui_modular255, + }}, + {{ + 0x4d9a6d270696688eb4346153b380c613a3dcaf0fb5a1e8380409ae0a143d31b_cppui_modular255, + 0x2a805eee3317c8bae1f7d15abe4d27fee5fabcf9a3334d18b1932a33774c324_cppui_modular255, + 0x19b092e9c6dffd1eb1b6df2dbc00bb2283b9a787273dcbad9b8d89cd502b7bbd_cppui_modular255, + }}, + }}; + + constexpr static const std::array, + rounds_amount> + round_constant = {{ + {{ + 0x590ef2a14ba3cef7e8f93a6dde4d481057d5d0547f6f09341b6b8be19c00ee6_cppui_modular255, + 0x77faa77ed78ff8b695859df34db5157f6b491567f5f382a8fce538f0e5ffe6f_cppui_modular255, + 0x3e54b7c94955c8994ed16ec9950d59aca4c9b6e419ef4935682528c2eba2de50_cppui_modular255, + }}, + {{ + 0x37d991dc8d4de3912355745c7d78f8b04516b14d30e29324bb5dd075ca0f0c1d_cppui_modular255, + 0xc0614dd1cff6c6817aff09d82ef828e80caed4da023823088fd021020f81f0e_cppui_modular255, + 0x3335e335a3fed44842359528b3e88e1824a173da819d7ee6905e82eed054243_cppui_modular255, + }}, + {{ + 0xb2202aa54d42f4f07693766723b9624c9fca4d33a2b9ee40f1c809a15a48a1d_cppui_modular255, + 0x290253e0e1d2c72b32a5b272137a0892b5934b0b8f26b4fc25ea00d63a70e9df_cppui_modular255, + 0x3e99873e73025d7c8b71fd209d13dba7a1021013f0815ea33a42ae94b63d00f3_cppui_modular255, + }}, + {{ + 0x164682f55ec314f639f5f8062a4ddf11ed80d5822591a22ff54f340d90165d85_cppui_modular255, + 0x309ba21093c9d04c81bd5273ad1064e1bd9067312d3269dddadf74c2eb1d3e01_cppui_modular255, + 0x159e72bb030cb8994b2eac1d4ee7d0f06b0b092e7611d460605b3d8c60a274d9_cppui_modular255, + }}, + {{ + 0xd743dbfc6f3c833ce2ef4956bead3c118fd3198652038781903ac929218fdd6_cppui_modular255, + 0x18cb5a9230eb74045ede834ac6dd129bd2a0462dca1d96d167b9be0e1e96a688_cppui_modular255, + 0x2d82f85fc222b215902d61c85c968b39759d6c2e9aa0e11fd08881bfae311e66_cppui_modular255, + }}, + {{ + 0x2920828be5972cb8ff8023386a90a837bbfcca99be240137f7d211ecb72521e6_cppui_modular255, + 0x3101774e1c3d72d010efb29c16c476e988bdb47321af3f82e05cc9c6b0360853_cppui_modular255, + 0x327b4e6353c099e41a8ffab9103996b9d29d07da0f1a191aa6fb55c0720c6f54_cppui_modular255, + }}, + {{ + 0x71c29018dd48d5c557379ea9d4afd80b92788ed509ced6bac47a65ba8b475c_cppui_modular255, + 0x25efdeef6c5ad56834b24cfe03d57360b4335ec902c78ee9348ebaceab726038_cppui_modular255, + 0x109ffe5cd918fcd7da7fdb40d32ac406f453874fda431c35c9e35601bcf708e9_cppui_modular255, + }}, + {{ + 0x1f4de5d78b4378e0eca49ed94999d8bc91489fadfd896c8affbaa6e2654d18bf_cppui_modular255, + 0x173185e1eaad0664ba1c01b8e417a4422c22a43d622c5df98c11481e205e499e_cppui_modular255, + 0x161a0e8b31a6fd42727dc0a37ae4f715683af35873bd37e78e10abcb0e21fabd_cppui_modular255, + }}, + {{ + 0x3decab3f42934acc644cc227315ecd6bcee79e5d92dc686823f60e6a3c40a7cd_cppui_modular255, + 0x29d7541d2a4fcdf9c7f144ce1e957a5e5c6d5d064618416817d0ad39708b2807_cppui_modular255, + 0x1d0525558685977d321fe86c05f462ae2e569e6d202bd5c62b0815320454114a_cppui_modular255, + }}, + {{ + 0x27d1aec0ccc80f71d09d2a9c0b76ee5fe9a87516f0e691a9f5fba360cb79f32_cppui_modular255, + 0x1c28ed68159e54df8296e654b0c1b5872de41557b7b02adc256dcc1600229ba8_cppui_modular255, + 0x15c9cbe29bf4e7d8bae22dd2213c86724e9944ea4b9e34b6681beb1b0972215e_cppui_modular255, + }}, + {{ + 0xd479e19db4686f5cb1ef9a8331a1ab680c5d3770e9a9a8a7a6ac58f8006c38a_cppui_modular255, + 0x3494f6ecf12d5c3d758c5380652154e26f7f3c888d362ea512da8dc265fc32b0_cppui_modular255, + 0x37ed9343bcc46adb4300f3d8cb88c311383061710836351ded0a146de837966_cppui_modular255, + }}, + {{ + 0x35548be14e1cbcbd7d2c0e8c4a95e5fc2893daba34197ef41c350ae7072cde4e_cppui_modular255, + 0x34e58327efe8d41b81b66b6c3fad424b2ff9008392909bb90eb10f08462b998b_cppui_modular255, + 0xf55c1223abf50500c4ac4103f679dcfea4eebb368cf64ef3a63ee27146846f_cppui_modular255, + }}, + {{ + 0x11dd4ab1734f7069498cc390a41b7de375d8968cec91b5c74cef9812e8ee7ce7_cppui_modular255, + 0x1e344f255d7c5e537439e75f9c4ea64dd1fda1b0988e5c83626055859369b43c_cppui_modular255, + 0x147db9afad2d2f7c4249357587faba99a6a38da16fe9ba74ef2f3fc5a0878f44_cppui_modular255, + }}, + {{ + 0x31774ce29d00f566bd499f181517df231be7205c05e7527d71a1c89cb0e841a7_cppui_modular255, + 0x32bdf60a6685665871f654169996f508be8710c99f3fa6f44a7bc4d2c25fbfd8_cppui_modular255, + 0x2f567f84ec13720611900c4b9e8303f04c8cc5c57daa4d95d9ee009514205e65_cppui_modular255, + }}, + {{ + 0x2dbd279621e591da57f54459f4160dde2f5c78e478d20f2f4763832e013bc07f_cppui_modular255, + 0x1275fb5ba53b7d2b5322e63f09a48026d684369c8e12241a808085a78ab3a369_cppui_modular255, + 0x1dd0beba925fe1df13f732b03287cad943569d62ec9059afc2c8120655e97d78_cppui_modular255, + }}, + {{ + 0xa37d78e392a5c8441f98e9dbd51a9151e78fb877885ecb885b0834c50cfea4d_cppui_modular255, + 0x1ebb7e2592122cd16d27e13410b2b48d520d8e99d38c1d86af0ac13565dfeb88_cppui_modular255, + 0x24a6454b0a69c59916d64f532b56226f8d49969432b7d0efc675f599c3bdb64f_cppui_modular255, + }}, + {{ + 0x269668b3e7835df2f85b82e9ef8647c43205e799135ce669256bf55f07448209_cppui_modular255, + 0x15c87375d4514bbdddbfd84e51f246446f1b16bb58bd4bd9fa2ff57e6aa66057_cppui_modular255, + 0x11ce62bbe1242334c260a67817be908a9422d9b9c6ee96c00772fcc8fc501db6_cppui_modular255, + }}, + {{ + 0x20348b7d6b381bfd3ac923d60b965086d281d8a654ad5f3210d277789641fe98_cppui_modular255, + 0x1398d090fd1144d1e84798e3a0efa942abfe650947e4a3cfa409ff14b541fae9_cppui_modular255, + 0x2461a1a2d6e3a0b2e5185ae6c844fe2a3b2d85dfb1cf891efc79ae80dd776bed_cppui_modular255, + }}, + {{ + 0x3e1f1de94c4af008188ba5eaef1da9ab9792ce54eda56bb5a519a65cd808885b_cppui_modular255, + 0x1dee6ead07fbc0fe883f4d397994d75ba3c4f90720e74ae2da13066bc3a7dc3b_cppui_modular255, + 0x287d06396bcb63555cb2ff408ea075cf402b10a3c608043d0cf2e3685ec6e2ad_cppui_modular255, + }}, + {{ + 0x36d84c953d584607478da6183dc4da71bdbf737d45fb57d5a53badc123ae071c_cppui_modular255, + 0x24c8fd13d2687a9f90c61da26823d4934b350cfa488d528482399e106a70ac74_cppui_modular255, + 0x52e052a6a493457c9476ccc4fd9924e5c7247b98e58a3cfa688c0f8314bea68_cppui_modular255, + }}, + {{ + 0x2fd32bae8a40ab498f6ba290733bb82504de1be782c1cdf039e2fbc843a01e52_cppui_modular255, + 0x4e8e7d3413c8c8ccfe154dc51f31c7682627c71fa4b50daab27f2a4d2623ea6_cppui_modular255, + 0x20c16d0097cebeb385508b606487baaf3bad515ba8a0b977f15cb50239418e38_cppui_modular255, + }}, + {{ + 0x34f1df6035aac75204368125b0c4cec107e2f9eb0005517d26d6113e1f366271_cppui_modular255, + 0x375973b59ed7b4bdb33642d20e6364f37a942f9018f6bca5abc10705481425e0_cppui_modular255, + 0x269e8c978803e51d43439b7c18c4260e819e09e7d8c8d38706463bbb811c698c_cppui_modular255, + }}, + {{ + 0x21be1913f874f3edb88a1f60cd157fcb76ff20b4eb139aae205b5a2764098782_cppui_modular255, + 0x37a0a8ba83db884f721c25027d188c7ab7c7840b7860675b33e1c93e4023927f_cppui_modular255, + 0x56d0e67fde779b7be5f308a3ce119e23e0503e6dabdbbd5189bb44dc6a6f0a4_cppui_modular255, + }}, + {{ + 0x144723436a329da5644cce96fee4952b066092c36bd12838b4ffd4283cfe82c4_cppui_modular255, + 0xec0b5f14ba50aa2b022d06fbb920a2aafb465b8c7f81fc119371a4cbb6acff7_cppui_modular255, + 0x685de18d9a346a35c44a2a4ac7283d6fe2e4a9dc058bd537700bc2495271721_cppui_modular255, + }}, + {{ + 0x178dcb74b546adea41afd5d93ef564cb3adb0ef5200201daea0faa5026bb8cbc_cppui_modular255, + 0x1c1dcb1ef6cf5f036ae0030bf78f1643c439843959dd74fa28ea3663735cc923_cppui_modular255, + 0xcfae6c99994c5f702cba3b32a4e38f3764207bfe7cd9bf577633b41843ea138_cppui_modular255, + }}, + {{ + 0x2838a02558716d2b49c06fb34c49cd820ec71e861caa935f4a303e42030ae3df_cppui_modular255, + 0x2c1944f3ec2852ed6b50fbc4abbc8f284797b36a23b321d2763ef48b1a5a0212_cppui_modular255, + 0x30a218acd109f04657954e82f9faccc477731f4a954cf8ac12d15ebd450e9dcb_cppui_modular255, + }}, + {{ + 0x2488defa4553fa5bd5afbb5fd28a1e99c585c5f541c6242e702215b2212c1d23_cppui_modular255, + 0x3d0c9d7282245c776daa1655697fa879e470a26fcbb3bea62fa8ff32a4f04e50_cppui_modular255, + 0x33aac46524f32f3556ed16a0912ef27482c2afcacbfb99ced98394b6c0e3765f_cppui_modular255, + }}, + {{ + 0x1858a5f543ab0a70cb3957e0884b146b42cc3863fba4e034145ab09cc77d428d_cppui_modular255, + 0x2d9d6fae68eff2e79396617207e28dba3d793b1e3739d30e9e9b10644e9f99cd_cppui_modular255, + 0x1747fab074b37cc1ca7dbf7d6dc136740f5d26e30319b3577fc8987f1247caae_cppui_modular255, + }}, + {{ + 0x38f905db5128f24e498e36a84df5a58ed3c0b0ed8f39336eb792cb634f86b87_cppui_modular255, + 0xfffe42ce4a87a0b3a9ebe7eedf16c0cdb29c959b6e594faa69c0727c6e825f_cppui_modular255, + 0x314c3090cd0a465da95afd515c0771703e4ee2a8eabe8fa405daf8bd49bce458_cppui_modular255, + }}, + {{ + 0x3e5fb71d9071c658c39fe64392e90bac65bdaf8f723b6790cce7dd7440ce06aa_cppui_modular255, + 0x3e9fe7b8fd0aaa379fa7be0dbd64309607cc5b00474ef6670370e631902e98cd_cppui_modular255, + 0x33ee4f76ff95bd735ec602ee6f4d1664caec27a7c435ead3b4c8df6cb51f010e_cppui_modular255, + }}, + {{ + 0x1670c2080f2965bed3f49db0b63aef5f562b347235645b921c0132b01cc82130_cppui_modular255, + 0x210565224e2ee64dd479be3a969dc30c65933352ba9b2271a0942bf1bf485743_cppui_modular255, + 0x9a7c6dd48dfbf50b13055b30fe85f934be9518b8af074b88f9de4b1df689616_cppui_modular255, + }}, + {{ + 0x1f9116811eaadf677e6cb50fb59ce0fab11fa9f0ddf1432403610e1932a7aa1c_cppui_modular255, + 0x19b51a48c225daf9b34611ccc5ba077ebbc0a19cfc9bbbd78ade11cfa655075f_cppui_modular255, + 0x3286d29eb60c3d6204eb534d13f40d1af6364f0fe1622a12ba5fa069886f31fe_cppui_modular255, + }}, + {{ + 0x9bd403d05db137ea793f10b6dd087a74a78c9b01bcd6f9daf39af2ef57d346e_cppui_modular255, + 0x3a71654023e43363e60889eac50eb1f17c044606886771eaaf851bb2d00b3aeb_cppui_modular255, + 0x3415b94f62c59466f102442b4bae7d6bb348987154cce16bd187525a6fb5b443_cppui_modular255, + }}, + {{ + 0x3ca35f0fc660092b81f15dd6f0b3d17a16a053480ef2f935fce806dd0d9a3466_cppui_modular255, + 0x26e1360af7fdc62e9be08651c2c5900ed5aefcb0d84b3aa88e354c6658a07863_cppui_modular255, + 0x30d05884174d7a1de9d34c89224d17f3b9dbdfb0793b54c0d2aaaeedcc357bd6_cppui_modular255, + }}, + {{ + 0x2c7f66f8b0580236f025dd626520049a09e1bfff0e5fd9f69cbc70daf0ac56c4_cppui_modular255, + 0xc5cb9a350d2dc463dd05dbd696e122c6917b76654180c323937dee44c6beb93_cppui_modular255, + 0x14d4d799d43d91b4d09d9c2bfdc13a64b48d18750503324361f9bf7267ec9b92_cppui_modular255, + }}, + {{ + 0x60c56a884cd6a1d3514f2895816b84e7160df5106e8d031710769be1ac5c04c_cppui_modular255, + 0x23e15f37c21266c86ead998a46e42f6e97fbd5d1c384f51d8b54d051a80d753d_cppui_modular255, + 0x25eb2911034ab6bef4a969653f5cc33e6914b8b6411f064ec01bcf157fea4e55_cppui_modular255, + }}, + {{ + 0x1e95c04c5057abd1b43a2fbc942b2391d0e0daef873838b3494e6d5fb067a117_cppui_modular255, + 0x1547602fc83558aa1327221fd220fa22bcb1f6ec42edb7cc05eff508c65883cb_cppui_modular255, + 0x16b669eac31e72a9e739fb03fd7ea3882fc5791b157143929ae12fc2fefe8b3d_cppui_modular255, + }}, + {{ + 0x7034f4e251a65c4423479dc9d5287a341c108e0b56e29a391f9a07a0ca822f1_cppui_modular255, + 0x3fdf9d5731ba040dc568e61b8571ea95ead2e89f0a9856b2d12a7e87e43f5683_cppui_modular255, + 0x33f2cdf6960139a0fb4a3a8127992e2abbd42847728425228a35ee72bd5b01c7_cppui_modular255, + }}, + {{ + 0x35616d55033d8fc092398f6c58bfc6eaaf2ec9dd500122516f489dbc631457b_cppui_modular255, + 0x1eca80189643df1473e98da93fe58a9576def0d192d4153faebcd1b210c1603f_cppui_modular255, + 0x26223ca4af2d8d878ca5530c3e67ff1c95b50b9c5b8295e19150bc31ef90ba98_cppui_modular255, + }}, + {{ + 0x19180fa5facb64ee9b4827ccd766622adf12fe80ab17c7395075368e10a2a361_cppui_modular255, + 0x169f165855e097501f25d6b3aae815ce6e8a1c289850936d956657f0ed99446_cppui_modular255, + 0x363a8f891de5974f06bae043bc6a26b4518d217af6590e9318e325fb215cda00_cppui_modular255, + }}, + {{ + 0x122aaa7c330ddcb57180749e659600a4dfac5dda7b9b68ab0f8b2ee6de350ced_cppui_modular255, + 0xed203defca13ebdf6af805a9f5dbdfef90007df2ad32fb1c83165e837ab5e3f_cppui_modular255, + 0x11cce94bbc7a96e9708e99d3666c0a275329ac4bff42634a5f989ddcfc28fd68_cppui_modular255, + }}, + {{ + 0x1705663587a03cb11485ac9d01fd10cb1138be1820d26a14e4ab7b1c0fdec8d2_cppui_modular255, + 0x12ad28a60485a2d911639051971f43dd15a0dfd2f8a0de756f0c847fed63ed7d_cppui_modular255, + 0xa9e61cc35eba9374eea117753aaaa93d6b29f550c2c54bce0a6078e05db9475_cppui_modular255, + }}, + {{ + 0x72c3d62cf006a95dc8b2a53f878bb26fcaf3c28d709a91634f3a09f525054ad_cppui_modular255, + 0x1ce8f168b446f7e797b91677fc46a975d2caa63dc359132c7c9729f5be24a7c_cppui_modular255, + 0xe846a7211efda3d8115b5bf76aab7eac2b6099026fc7504fb81ac4a77c5560d_cppui_modular255, + }}, + {{ + 0xabb8fd9d6fa3772022fa88800c12bdcbb1234473022cd141213d452255a0f55_cppui_modular255, + 0x1c5d9938bc35a4832e8375dc307dba7a116d2a566e406ab31e8b03a36ec807cf_cppui_modular255, + 0x35bea7ac6f40e0f50f08d325be9f051fd75ada8c03461f4d15b2c5e1a3d72431_cppui_modular255, + }}, + {{ + 0x419357c205a7e1e028c0f49cbdeab85b82f4db78f1afb1b5568ec1bd2e48cb0_cppui_modular255, + 0x1933e424c788e7466a159f1fe015ac7210f47044d9df6872cdfa227ae4d2190a_cppui_modular255, + 0xde27ccdda95abb3d98db76d6f7f152a08d37ba81758beaf2eddbc58d13e560f_cppui_modular255, + }}, + {{ + 0x35a312d5d6cbf00d55f097febaf9bd5eac5f2881ebf0afa377e2ba7cdcf2f51_cppui_modular255, + 0xce6f415449ca515e4da9177527c9242adcc988de5e1846d07cdd5284f39f9d0_cppui_modular255, + 0x38fd71543da5c4c0447dc22aa2c1e3744cb84eb1ff17040640b50f5ddf8c8e61_cppui_modular255, + }}, + {{ + 0x158de859aad53c6a17de455ab067a09ad6cba22f4101d19e77d8a2975c0dc965_cppui_modular255, + 0x2c300588eeae8cbc3814bd1d7646f472ef6b44a60c710bf6100937504e532c8b_cppui_modular255, + 0xb198cf742a029409ac02397b91e2704fa94ecf147909fa8d71ece5087e2cfc3_cppui_modular255, + }}, + {{ + 0x100b375c21d357d5679d8e6d9eb7bff8edd4575535bf651ba0b1bd83cfb54598_cppui_modular255, + 0x15a474d44590e2b23b8bb1e79f5613f1659e7ae2bce10def0ce1a101eb3e3ce5_cppui_modular255, + 0x2aa20e6642a989e1e6f9814c24f022991c23a7e40af505d4b931079025b7ed4d_cppui_modular255, + }}, + {{ + 0x196597f2d65c5692706795bf46eb7be96b31647c23441213642ccceedc01ebc4_cppui_modular255, + 0x248291aa516daa0a6cd191c1c651a82f7d1b5f087dcb7cee91a27c488483e2bd_cppui_modular255, + 0x36c02b98ad2722b774aeb131b31bfd087c6a7f2d0a3faa40bd9899e5f270877f_cppui_modular255, + }}, + {{ + 0x1240e06949a1ad92bd8ae90772b5d8505174182c87a23227aa74b7630dba4195_cppui_modular255, + 0x3b83f7e36f30939a78ec63cb2554aa0669a1bfc1b8b8714c6b8a3958beb6a163_cppui_modular255, + 0x1668b0582ce04f7f5b1e35e1b7cc3e05be23cc2c9e0be9436559193f2a8d102e_cppui_modular255, + }}, + {{ + 0x26d6a708e9464c85e9c7605e87fb96036fd1fe87379ac43ad560885582e4026d_cppui_modular255, + 0x594fccf1863993b43ad0a13c5fc7a53f59f7d622e7b206d425907243a69e62d_cppui_modular255, + 0x78e4c588b6ddd0fe7ed53a9f25b6ac3c2eac1c63faecc7e916f4d4599051940_cppui_modular255, + }}, + {{ + 0xf44ea3e14c3e4849ee7a525fe77170b8658a6753680e269c9fd1d12932af69d_cppui_modular255, + 0x2e8567bc9e8e369bdf7748d6c7f677837c601455d4651a2f102b94ff1f951379_cppui_modular255, + 0x37c35b056171982cc7d74e6081fcac2f764f1fe30ee985db306a22b097d51bae_cppui_modular255, + }}, + {{ + 0x29dbcffd5b55d671c85ca42037ac5e64d2ef42d2704af47a20877e3a5e5f1d9d_cppui_modular255, + 0x201098422e054c1ddcc465411d002d2bc5a824e1c7f4f2ded9443c37bd04a520_cppui_modular255, + 0x7de32ed4c5143430ef43aef100f948ef859ab3793aa52640156f5e7d92cdc84_cppui_modular255, + }}, + {{ + 0x34e95adcc0c5c34fd38ab9246a04cc1029f678ba53c0f6fd27f8805094e36199_cppui_modular255, + 0x1d5faf157126c599232982356ca0ea7b81d875c01d842b5cd1998a5c470fa623_cppui_modular255, + 0x160a80176bd281e3fa9b82e44063cc7bf86eb81397e51e41fe4745e27c57e1d2_cppui_modular255, + }}, + {{ + 0x17ecc7f5deb148c542a22d02b098439724910a3bbd4903428c8fc680f31b2406_cppui_modular255, + 0x20a6aae17f822bc7035da3b8931896c82152346f2a43ab4e0029dbf0101b3d_cppui_modular255, + 0x9ea0ec10c0e77b9385a58ccd5ecc3c88b5bed58af72a6d87bb446e14fa7c8d6_cppui_modular255, + }}, + }}; + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_PLONK_DETAIL_POSEIDON_CONSTANTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp new file mode 100644 index 000000000..d2e0636db --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp @@ -0,0 +1,329 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the DECOMPOSITION component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_DECOMPOSITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_DECOMPOSITION_HPP + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + template + class decomposition; + + template + class decomposition, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return decomposition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 3; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + std::array data; + + std::vector> all_vars() { + return {data[0], data[1]}; + } + }; + + struct result_type { + std::array output; + + result_type(const decomposition &component, std::uint32_t start_row_index) { + output = {var(component.W(0), start_row_index + 1, false), + var(component.W(1), start_row_index + 1, false), + var(component.W(2), start_row_index + 1, false), + var(component.W(3), start_row_index + 1, false), + var(component.W(4), start_row_index + 1, false), + var(component.W(5), start_row_index + 1, false), + var(component.W(6), start_row_index + 1, false), + var(component.W(7), start_row_index + 1, false)}; + } + + result_type(const decomposition &component, std::uint32_t start_row_index, bool skip) { + output = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false), + var(component.W(4), start_row_index, false), + var(component.W(5), start_row_index, false), + var(component.W(6), start_row_index, false), + var(component.W(7), start_row_index, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3], + output[4], output[5], output[6], output[7]}; + } + }; + + template + explicit decomposition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + decomposition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + decomposition(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + static std::array + calculate(std::array data) { + std::array range_chunks; + std::size_t shift = 0; + + for (std::size_t i = 0; i < 8; i++) { + range_chunks[i] = (typename BlueprintFieldType::integral_type(data[0].data) >> shift) & ((65536) - 1); + range_chunks[i + 8] = (typename BlueprintFieldType::integral_type(data[1].data) >> shift) & ((65536) - 1); + shift += 16; + } + + std::array integral_output = + {range_chunks[7] * (65536) + range_chunks[6], + range_chunks[5] * (65536) + range_chunks[4], + range_chunks[3] * (65536) + range_chunks[2], + range_chunks[1] * (65536) + range_chunks[0], + range_chunks[15] * (65536) + range_chunks[14], + range_chunks[13] * (65536) + range_chunks[12], + range_chunks[11] * (65536) + range_chunks[10], + range_chunks[9] * (65536) + range_chunks[8]}; + std::array output; + for (std::size_t i = 0; i < output.size(); i++){ + output[i] = typename BlueprintFieldType::value_type(integral_output[i]); + } + return output; + } + }; + + template + using plonk_native_decomposition = + decomposition, + BlueprintFieldType>; + + template + typename plonk_native_decomposition::result_type + generate_assignments( + const plonk_native_decomposition &component, + assignment> + &assignment, + const typename plonk_native_decomposition::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::array data = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.data[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.data[1]).data)}; + std::array range_chunks; + std::size_t shift = 0; + + for (std::size_t i = 0; i < 8; i++) { + range_chunks[i] = (data[0] >> shift) & ((65536) - 1); + assignment.witness(component.W(i), row) = range_chunks[i]; + range_chunks[i + 8] = (data[1] >> shift) & ((65536) - 1); + assignment.witness(component.W(i), row + 2) = range_chunks[i + 8]; + shift += 16; + } + + assignment.witness(component.W(8), row) = data[0]; + assignment.witness(component.W(8), row + 2) = data[1]; + + assignment.witness(component.W(3), row + 1) = range_chunks[1] * (65536) + range_chunks[0]; + assignment.witness(component.W(2), row + 1) = range_chunks[3] * (65536) + range_chunks[2]; + assignment.witness(component.W(1), row + 1) = range_chunks[5] * (65536) + range_chunks[4]; + assignment.witness(component.W(0), row + 1) = range_chunks[7] * (65536) + range_chunks[6]; + + assignment.witness(component.W(7), row + 1) = range_chunks[9] * (65536) + range_chunks[8]; + assignment.witness(component.W(6), row + 1) = range_chunks[11] * (65536) + range_chunks[10]; + assignment.witness(component.W(5), row + 1) = range_chunks[13] * (65536) + range_chunks[12]; + assignment.witness(component.W(4), row + 1) = range_chunks[15] * (65536) + range_chunks[14]; + + return typename plonk_native_decomposition::result_type( + component, start_row_index); + } + template + typename plonk_native_decomposition::result_type + generate_empty_assignments( + const plonk_native_decomposition &component, + assignment> + &assignment, + const typename plonk_native_decomposition::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_native_decomposition; + + std::size_t row = start_row_index; + std::array data = {var_value(assignment, instance_input.data[0]).data, + var_value(assignment, instance_input.data[1]).data}; + + std::array output = component_type::calculate(data); + for (std::size_t i = 0; i < 8; i++) { + assignment.witness(component.W(i), row) = output[i]; + } + + return typename plonk_native_decomposition::result_type( + component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_native_decomposition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_native_decomposition::input_type + &instance_input) { + + using var = typename plonk_native_decomposition::var; + + auto constraint_1 = + var(component.W(8), -1) - (var(component.W(3), 0) + var(component.W(2), 0) * 0x100000000_cppui_modular255 + + var(component.W(1), 0) * 0x10000000000000000_cppui_modular255 + + var(component.W(0), 0) * 0x1000000000000000000000000_cppui_modular255); + auto constraint_2 = + var(component.W(8), 1) - (var(component.W(7), 0) + var(component.W(6), 0) * 0x100000000_cppui_modular255 + + var(component.W(5), 0) * 0x10000000000000000_cppui_modular255 + + var(component.W(4), 0) * 0x1000000000000000000000000_cppui_modular255); + auto constraint_3 = var(component.W(3), 0) - + (var(component.W(0), -1) + var(component.W(1), -1) * (65536)); + auto constraint_4 = var(component.W(2), 0) - + (var(component.W(2), -1) + var(component.W(3), -1) * (65536)); + auto constraint_5 = var(component.W(1), 0) - + (var(component.W(4), -1) + var(component.W(5), -1) * (65536)); + auto constraint_6 = var(component.W(0), 0) - + (var(component.W(6), -1) + var(component.W(7), -1) * (65536)); + auto constraint_7 = var(component.W(7), 0) - + (var(component.W(0), +1) + var(component.W(1), +1) * (65536)); + auto constraint_8 = var(component.W(6), 0) - + (var(component.W(2), +1) + var(component.W(3), +1) * (65536)); + auto constraint_9 = var(component.W(5), 0) - + (var(component.W(4), +1) + var(component.W(5), +1) * (65536)); + auto constraint_10 = var(component.W(4), 0) - + (var(component.W(6), +1) + var(component.W(7), +1) * (65536)); + return bp.add_gate( + {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7, constraint_8, constraint_9, constraint_10}); + } + + template + void generate_copy_constraints( + const plonk_native_decomposition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_native_decomposition::input_type + &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_native_decomposition::var; + // CRITICAL: these copy constraints might not be sufficient, but are definitely required. + // I've added copy constraints for the inputs, but internal ones might be missing + // Proceed with care + bp.add_copy_constraint({instance_input.data[0], var(component.W(8), start_row_index, false)}); + bp.add_copy_constraint({instance_input.data[1], var(component.W(8), start_row_index + 2, false)}); + } + + template + typename plonk_native_decomposition::result_type + generate_circuit( + const plonk_native_decomposition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_native_decomposition::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t j = start_row_index + 1; + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, j); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_native_decomposition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_DECOMPOSITION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_4.txt b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_4.txt new file mode 100644 index 000000000..45c0e028b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_4.txt @@ -0,0 +1,65537 @@ +65536 +fb ffcf +f7 ff3f +f3 ff8f +f3 ff2f +fb ff4f +f7 ff1f +f3 ff0f +ef fcff +fb f7cf +df f3df +d7 fb3f +df f37f +db fb4f +d7 fb1f +f3 f78f +d7 f39f +f3 f72f +df f35f +cf f8ff +ef f4ff +df f1ff +cf f0ff +b7 ef3f +bf cf7f +b7 cf3f +b7 ef1f +b7 cf1f +eb fecf +eb fcef +fb fdcf +ef fcdf +eb fccf +cf f8df +eb f6cf +eb f4ef +db f1ef +cb f0ef +fb f5cf +ef f4df +eb f4cf +df f1df +db f1cf +cf f0df +cb f0cf +af ecff +ef dcff +af ccff +ab eccf +eb dccf +ab cccf +bf c7ff +df d3df +9f c3df +97 eb3f +9f e37f +d7 db3f +9f cb7f +97 cb3f +b7 c7bf +df d37f +bf c77f +9f c37f +97 eb1f +b3 e72f +9f e35f +d7 db1f +97 cb1f +d7 d39f +97 c39f +f3 d72f +db d36f +b3 c72f +9b c36f +df d35f +9f c35f +fd fff3 +f9 ffcb +f9 ffe3 +fd ffd3 +fb ffc7 +f9 ffc3 +f5 ff3b +8f e8ff +af e4ff +9f e1ff +8f e0ff +f7 ff37 +f5 ff33 +cf d8ff +8f c8ff +ef d4ff +df d1ff +cf d0ff +bf c5ff +af c4ff +9f c1ff +8f c0ff +f1 ff2b +f1 ff8b +f9 ff4b +f1 ff0b +f3 ff27 +f1 ff23 +f5 ff93 +f3 ff87 +f1 ff83 +f7 ff17 +fd ff53 +f5 ff13 +fb ff47 +f3 ff07 +f9 ff43 +f1 ff03 +ed fcfb +ed fef3 +ef fcf7 +fd fdf3 +ed fcf3 +e9 fceb +e9 fecb +f9 fdcb +e9 fccb +e9 fee3 +eb fce7 +f9 fde3 +e9 fce3 +ed fed3 +e9 fec3 +fd fdd3 +ed fcd3 +fb fdc7 +f9 fdc3 +e9 fcc3 +f9 f7cb +fb f7c7 +f9 f7c3 +d9 fb4b +f1 f78b +d7 fb17 +db fb47 +d3 fb07 +d9 fb43 +f3 f787 +f1 f783 +cd f8fb +ed f4fb +cd f0fb +cf f8f7 +cd f8f3 +ef f4f7 +cf f0f7 +ed f4f3 +cd f0f3 +e9 f4eb +c9 f0eb +e9 f6cb +f9 f5cb +e9 f4cb +d9 f1cb +c9 f0cb +cb f8e7 +c9 f8e3 +eb f4e7 +cb f0e7 +e9 f4e3 +c9 f0e3 +cd f8d3 +db f9c7 +d9 f9c3 +c9 f8c3 +e9 f6c3 +ed f4d3 +cd f0d3 +fb f5c7 +db f1c7 +f9 f5c3 +e9 f4c3 +d9 f1c3 +c9 f0c3 +bf cff7 +fd dff3 +f9 dfe3 +fd dfd3 +bd cf7b +8b e8cf +ab e4ef +9b e1ef +8b e0ef +ab e4cf +9f e1df +9b e1cf +8b e0cf +b7 ef37 +b7 cfb7 +bf cf77 +b7 cf37 +bd cf73 +8b c8cf +eb d4ef +db d1ef +cb d0ef +ab c4ef +9b c1ef +8b c0ef +ef d4df +eb d4cf +df d1df +db d1cf +cf d0df +cb d0cf +af c4df +ab c4cf +9f c1df +9b c1cf +8b c0cf +b3 ef27 +b3 cf27 +f1 df23 +b7 ef17 +bd ef53 +b3 ef07 +b9 ef43 +b7 cf17 +fd df53 +bd cf53 +b3 cf07 +b9 cf43 +af ecf7 +bd edf3 +ad ecf3 +ef dcf7 +af ccf7 +fd ddf3 +ed dcf3 +bd cdf3 +ad ccf3 +b9 ede3 +a9 ece3 +e9 dee3 +f9 dde3 +e9 dce3 +b9 cde3 +a9 cce3 +bd edd3 +b9 edc3 +a9 ecc3 +ed dcd3 +ad ccd3 +f9 ddc3 +e9 dcc3 +b9 cdc3 +a9 ccc3 +bd c7fb +9f cbf7 +bf c7f7 +9f c3f7 +bd c7f3 +9d cb7b +b5 c7bb +bd c77b +b5 c73b +9d c37b +97 cbb7 +9f cb77 +97 cb37 +9d cb73 +b7 c7b7 +97 c3b7 +b5 c7b3 +d7 d337 +bf c777 +b7 c737 +9f c377 +97 c337 +bd c773 +b5 c733 +9d c373 +b1 c72b +93 cb27 +d3 d327 +b3 c727 +93 c327 +b1 c723 +97 eb17 +93 eb07 +d7 db17 +97 cb17 +d3 db07 +93 cb07 +f7 d717 +d7 d317 +b7 c717 +97 c317 +f3 d707 +d3 d307 +b3 c707 +93 c307 +ad e4fb +8d e0fb +ad c6fb +ed d4fb +cd d0fb +bd c5fb +ad c4fb +9d c1fb +8d c0fb +8f e8f7 +8d e8f3 +af e4f7 +ad e4f3 +8d e0f3 +cf d8f7 +8f c8f7 +cd d8f3 +9d c9f3 +8d c8f3 +af c6f7 +8f c2f7 +ad c6f3 +ef d4f7 +cf d0f7 +bf c5f7 +af c4f7 +9f c1f7 +ed d4f3 +cd d0f3 +bd c5f3 +ad c4f3 +9d c1f3 +8d c0f3 +a9 e4eb +89 e0eb +e9 d4eb +c9 d0eb +a9 c4eb +89 c0eb +a9 e4cb +89 e0cb +89 e8e3 +ab e4e7 +a9 e4e3 +89 e0e3 +c9 d8e3 +89 c8e3 +eb d4e7 +cb d0e7 +ab c4e7 +e9 d4e3 +c9 d0e3 +a9 c4e3 +89 c0e3 +89 e8c3 +a9 e4c3 +89 e0c3 +cd d8d3 +8d c8d3 +c9 d8c3 +89 c8c3 +ed d4d3 +cd d0d3 +ad c4d3 +8d c0d3 +e9 d4c3 +c9 d0c3 +a9 c4c3 +89 c0c3 +fa ffce +fd 7ff3 +7f 3ff7 +f6 ff3e +fb 7fcf +f6 ffbc +f7 ff3d +f6 ff3c +f2 ff8e +fa ff4e +f6 ff1e +f2 ff0e +f9 7fe3 +7b 3fe7 +f9 7fcb +fd 7fd3 +fb 7fc7 +f9 7fc3 +f6 ff9c +f7 ff1d +f6 ff1c +ea fcce +f5 7fb3 +f5 7f33 +7d 3f73 +7b bf4f +f3 7f8f +fb 7f4f +f3 7f0f +7b 3f4f +79 bf4b +7d bf53 +7b bf47 +79 bf43 +73 bf07 +f1 7fa3 +f1 7f8b +f5 7f93 +f3 7f87 +f1 7f83 +f1 7f23 +79 3f63 +f9 7f4b +f1 7f0b +79 3f4b +fd 7f53 +fb 7f47 +f9 7f43 +f5 7f13 +f3 7f07 +f1 7f03 +7d 3f53 +7b 3f47 +79 3f43 +73 3f07 +e7 fe3f +e7 fcbf +f7 fd3f +ef fc7f +e7 fc3f +c7 fa3f +e7 f4bf +d7 f1bf +c7 f0bf +d7 f93f +cf f87f +c7 f83f +f7 f53f +ef f47f +e7 f43f +cf f07f +c7 f03f +a7 ee3f +af ce7f +a7 ce3f +a7 ecbf +e7 dcbf +a7 ccbf +af ec7f +a7 ec3f +bf cd7f +f7 dd3f +b7 cd3f +ef dc7f +af cc7f +e7 dc3f +a7 cc3f +87 ea3f +c7 da3f +87 ca3f +87 e8bf +87 c8bf +a7 e4bf +97 e1bf +87 e0bf +e7 d4bf +a7 c4bf +d7 d1bf +97 c1bf +c7 d0bf +87 c0bf +97 e93f +8f e87f +87 e83f +d7 d93f +97 c93f +cf d87f +8f c87f +c7 d83f +87 c83f +af e47f +a7 e43f +8f e07f +87 e03f +f7 d53f +b7 c53f +ef d47f +af c47f +e7 d43f +a7 c43f +df d17f +9f c17f +d7 d13f +97 c13f +cf d07f +8f c07f +c7 d03f +87 c03f +ef 7cff +fa f7ce +de f3de +ed 7ef3 +f5 df1b +6f 3ef7 +d6 fb3e +f6 f73e +de f37e +d6 f33e +7b bdcf +6b bccf +eb 7ecf +fb 7dcf +ef 7cdf +eb 7ccf +7b 3dcf +6b 3ccf +d7 fb3d +d6 fb3c +d7 f3bd +f6 f7bc +d6 f3bc +f6 f73c +d6 f33c +da fb4e +d6 fb1e +f2 f78e +d6 f39e +fa f74e +f6 f71e +f2 f70e +de f35e +da f34e +d6 f31e +79 bdcb +6d bcdb +69 bccb +7d bdd3 +7b bdc7 +79 bdc3 +6d bcd3 +69 bcc3 +e9 7ee3 +f1 df0b +6b 3ee7 +e9 7ecb +ed 7ed3 +e9 7ec3 +f9 7dcb +e9 7ccb +7d 3ddb +79 3dcb +6d 3cdb +69 3ccb +fd 7dd3 +f9 7dc3 +7f 3dd7 +7d 3dd3 +7b 3dc7 +79 3dc3 +6d 3cd3 +69 3cc3 +d6 fb9c +d7 fb1d +d6 fb1c +f2 f7ac +f3 f78d +d7 f39d +f6 f79c +f2 f78c +d6 f39c +f3 f72d +f2 f72c +de f1fe +ce f0fe +6f bc7f +67 bc3f +e7 7cbf +ef 7c7f +e7 7c3f +6f 3c7f +67 3c3f +ca f8ce +ea f6ce +ce f2de +da f1ee +ca f0ee +fa f5ce +ea f4ce +de f1de +da f1ce +ce f0de +ca f0ce +eb f6cd +ea f6cc +ce f2dc +d1 f38b +eb f4cd +e6 fe3e +e7 fe3d +e6 fe3c +e6 fc3e +c6 fa3e +c7 fa3d +c6 fa3c +c6 f83e +d7 f93d +d6 f93c +c7 f83d +c6 f83c +e6 f63e +ce f27e +c6 f23e +e6 f63c +ce f27c +c6 f23c +d6 f1be +c6 f0be +d7 f1bd +d6 f1bc +c7 f0bd +c6 f0bc +f6 f53e +e6 f43e +ce f07e +c6 f03e +b5 ef3b +cf f07d +7f 37ff +7d 37fb +7f 37f7 +7d 37f3 +b6 ef3e +be cf7e +b6 cf3e +7b b7cf +7b 37ef +fb 77cf +df 73df +7b 37cf +b6 efbc +b7 ef3d +b6 ef3c +b7 cfbd +b6 cfbc +b7 cf3d +b6 cf3c +b6 ef1e +b6 cf1e +79 b7cb +7b b7c7 +79 b7c3 +79 37eb +7b 37e7 +79 37e3 +f9 77cb +7d 37db +79 37cb +fb 77c7 +f9 77c3 +7f 37d7 +7d 37d3 +7b 37c7 +79 37c3 +b6 ef9c +b6 ef1c +b2 cfac +b6 cf9c +b2 cf8c +b3 cf2d +b2 cf2c +b7 cf1d +b3 cf0d +b6 cf1c +b2 cf0c +57 b33f +5f 337f +57 333f +af cefd +ae cefc +95 cbbb +af ccfd +a6 ee3e +a6 ec3e +ae ce7e +a6 ce3e +a6 ccbe +e6 dc3e +be cd7e +ae cc7e +a6 cc3e +5b bb4f +73 b78f +57 b39f +73 b70f +5f b35f +5b b34f +57 b31f +db 7b4f +5b 3b4f +a7 ee3d +a6 ee3c +f3 778f +d7 739f +73 378f +5b 336f +a7 cebd +a6 cebc +af ce7d +a7 ce3d +ae ce7c +a6 ce3c +df 735f +73 370f +5f 335f +5b 334f +57 331f +b7 cdbd +a7 ccbd +a6 ccbc +9d cbfb +b7 cd3d +95 cb3b +af cc7d +59 bb4b +5b bb47 +59 bb43 +53 bb07 +71 b78b +73 b787 +71 b783 +71 b70b +59 b34b +d9 7b4b +59 3b4b +db 7b47 +d9 7b43 +d3 7b07 +5b 3b47 +59 3b43 +53 3b07 +f1 778b +71 378b +f3 7787 +f1 7783 +73 3787 +71 3783 +71 370b +59 334b +fb 7747 +db 7347 +7b 3747 +79 3743 +5b 3347 +59 3343 +e7 fe37 +f5 fdb3 +e7 fcb7 +f5 fd3b +ed fc7b +f7 fd37 +f5 fd33 +ef fc77 +e7 fc37 +c7 f8b7 +d5 f93b +cd f87b +d7 f937 +d5 f933 +cf f877 +cd f873 +c7 f837 +bf cf7d +a5 ce3b +a7 ee37 +a7 ce37 +df f37d +c5 f23b +c7 f237 +bf cdfd +a5 ccbb +a7 ecb7 +e7 dcb7 +a7 ccb7 +df f1fd +c5 f0bb +e7 f4b7 +c7 f0b7 +b5 ed3b +ad ec7b +ed dc7b +ad cc7b +bf cd7d +a5 cc3b +b5 ed33 +af ec77 +ad ec73 +a7 ec37 +f7 dd37 +b7 cd37 +f5 dd33 +b5 cd33 +ef dc77 +af cc77 +ed dc73 +ad cc73 +e7 dc37 +a7 cc37 +f5 f53b +ed f47b +d5 f13b +cd f07b +df f17d +c5 f03b +f7 f537 +f5 f533 +d5 f133 +cf f077 +cd f073 +c7 f037 +87 e8b7 +c7 d8b7 +87 c8b7 +8d e87b +cd d87b +8d c87b +8f e877 +8d e873 +87 e837 +cf d877 +8f c877 +cd d873 +8d c873 +c7 d837 +87 c837 +87 c2b7 +ed 7e73 +6d 3e73 +8d c27b +87 e237 +8f c277 +8d c273 +c7 d237 +87 c237 +95 c1bb +a7 e4b7 +b5 c5b3 +e7 d4b7 +a7 c4b7 +97 c1b7 +95 c1b3 +c7 d0b7 +6d bc7b +6d 3c7b +7d bd73 +75 bd33 +6d bc73 +7d 3d73 +75 3d33 +ef 7c77 +6f 3c77 +6d 3c73 +ad e47b +95 e13b +8d e07b +ed d47b +ad c47b +9d c17b +d5 d13b +95 c13b +cd d07b +8d c07b +95 e133 +8d e073 +af c477 +ed d473 +ad c473 +e7 d437 +a7 c437 +9f c177 +9d c173 +d7 d137 +97 c137 +d5 d133 +95 c133 +cf d077 +cd d073 +8d c073 +c7 d037 +be c7fe +6f b4ff +5f b1ff +4f b0ff +cf 78ff +85 c2bb +9f c3fd +6f 36ff +ef 74ff +df 71ff +7f 35ff +6f 34ff +5f 31ff +4f 30ff +9e e3de +de d3de +9e c3de +6d b4fb +4d b0fb +6d b4f3 +4d b0f3 +6d 36fb +ed 74fb +cd 70fb +7d 35fb +6d 34fb +5d 31fb +4d 30fb +ef 74f7 +ed 74f3 +cd 70f3 +7f 35f7 +7d 35f3 +6d 34f3 +5f 31f7 +5d 31f3 +4d 30f3 +96 eb3e +b6 e73e +9e e37e +96 e33e +d6 db3e +9e cb7e +96 cb3e +b6 c7be +f6 d73e +de d37e +d6 d33e +be c77e +b6 c73e +9e c37e +96 c33e +5b b9cf +4f b8df +4b b8cf +6b b6cf +4f b2df +6b b4ef +5b b1ef +4b b0ef +7b b5cf +6b b4cf +5b b1cf +4b b0cf +96 ebbc +97 eb3d +96 eb3c +97 e3bd +b6 e7bc +96 e3bc +85 e23b +9f e37d +b6 e73c +96 e33c +97 cbbd +96 cbbc +d7 db3d +97 cb3d +d6 db3c +96 cb3c +d7 d3bd +b7 c7bd +97 c3bd +f6 d7bc +d6 d3bc +b6 c7bc +96 c3bc +c5 d23b +df d37d +85 c23b +9f c37d +f6 d73c +d6 d33c +b6 c73c +96 c33c +db 79cf +cf 78df +cb 78cf +5b 39cf +4b 38cf +6b 36ef +eb 76cf +6b 36cf +eb 74ef +db 71ef +cb 70ef +7b 35ef +6b 34ef +5b 31ef +4b 30ef +fb 75cf +ef 74df +eb 74cf +df 71df +db 71cf +cb 70cf +7b 35cf +6b 34cf +5b 31cf +4b 30cf +96 eb1e +b2 e72e +b6 e71e +b2 e70e +9e e35e +9a e34e +96 e31e +d6 db1e +96 cb1e +d6 d39e +96 c39e +f2 d72e +da d36e +b2 c72e +9a c36e +f6 d71e +f2 d70e +de d35e +da d34e +d6 d31e +59 b9cb +4d b8db +49 b8cb +5b b9c7 +59 b9c3 +4d b8d3 +49 b8c3 +69 b6cb +69 b6c3 +69 b4eb +49 b0eb +69 b4e3 +49 b0e3 +79 b5cb +6d b4db +69 b4cb +59 b1cb +4d b0db +49 b0cb +7b b5c7 +79 b5c3 +6d b4d3 +69 b4c3 +5b b1c7 +59 b1c3 +4d b0d3 +49 b0c3 +92 ebac +92 eb8c +93 eb2d +92 eb2c +93 eb0d +96 eb1c +92 eb0c +93 e3ad +b2 e7ac +92 e3ac +93 e38d +b6 e79c +b2 e78c +92 e38c +b3 e72d +93 e32d +b2 e72c +92 e32c +92 cbac +d6 db9c +96 cb9c +92 cb8c +d3 db2d +93 cb2d +d2 db2c +92 cb2c +d7 db1d +d3 db0d +97 cb1d +93 cb0d +d6 db1c +d2 db0c +96 cb1c +92 cb0c +d3 d3ad +93 c3ad +d9 79cb +c9 78cb +5d 39db +59 39cb +4d 38db +49 38cb +f2 d7ac +d2 d3ac +b2 c7ac +92 c3ac +d7 d39d +d3 d38d +97 c39d +93 c38d +f6 d79c +f2 d78c +d6 d39c +d2 d38c +b6 c79c +b2 c78c +96 c39c +92 c38c +b3 c72d +93 c32d +f2 d72c +d2 d32c +b2 c72c +92 c32c +5f 39d7 +5d 39d3 +5b 39c7 +59 39c3 +4d 38d3 +49 38c3 +69 36eb +e9 76cb +6d 36db +69 36cb +e9 74eb +c9 70eb +79 35eb +69 34eb +59 31eb +49 30eb +eb 74e7 +e9 74e3 +cb 70e7 +c9 70e3 +7b 35e7 +79 35e3 +69 34e3 +5b 31e7 +59 31e3 +49 30e3 +f9 75cb +e9 74cb +d9 71cb +c9 70cb +7d 35db +79 35cb +6d 34db +69 34cb +5d 31db +59 31cb +4d 30db +49 30cb +fb 75c7 +f9 75c3 +ed 74d3 +e9 74c3 +db 71c7 +d9 71c3 +cd 70d3 +c9 70c3 +7f 35d7 +7d 35d3 +7b 35c7 +79 35c3 +6d 34d3 +69 34c3 +5f 31d7 +5d 31d3 +5b 31c7 +59 31c3 +4d 30d3 +49 30c3 +9e e1fe +8e e0fe +ae c6fe +de d1fe +ce d0fe +be c5fe +ae c4fe +9e c1fe +8e c0fe +4f b87f +47 b83f +47 b23f +67 b4bf +57 b1bf +47 b0bf +6f b47f +67 b43f +5f b17f +57 b13f +4f b07f +47 b03f +8e e2fc +85 e0bb +9f e1fd +8f e0fd +8e e0fc +8f cafd +8e cafc +c7 78bf +85 c8bb +9f c9fd +8f c8fd +ce d2fc +ae c6fc +8e c2fc +c5 d0bb +df d1fd +b5 cfbb +cf d0fd +a5 c4bb +bf c5fd +95 c3bb +af c4fd +85 c0bb +9f c1fd +cf 787f +c7 783f +4f 387f +47 383f +4f 327f +47 323f +e7 74bf +d7 71bf +c7 70bf +67 34bf +57 31bf +47 30bf +ef 747f +e7 743f +df 717f +6f 347f +67 343f +5f 317f +57 313f +4f 307f +47 303f +8e e2de +9a e1ee +8a e0ee +9e e1de +9a e1ce +8e e0de +8a e0ce +ce d2de +8e c2de +da d1ee +ca d0ee +9a c1ee +8a c0ee +de d1de +da d1ce +ce d0de +ca d0ce +9e c1de +9a c1ce +8e c0de +8a c0ce +4d b87b +4f b877 +4d b873 +47 b837 +6d b47b +4d b07b +4d b073 +8b e2ed +8a e2ec +8b e2cd +8a e2cc +8b e0ed +8a e0ec +8b e0cd +8a e0cc +8b c2ed +ca d2ec +8a c2ec +8b c2cd +ce d2dc +ca d2cc +8e c2dc +8a c2cc +71 bf8b +8b c0cd +4d 387b +cf 7877 +4f 3877 +4d 3873 +e7 74b7 +c7 70b7 +ed d4db +67 34b7 +cd d0db +47 30b7 +ed 747b +6d 347b +4d 307b +ef 7477 +e7 7437 +cd 7073 +6f 3477 +6d 3473 +4f 3077 +4d 3073 +86 ea3e +86 e83e +a6 e63e +8e e27e +86 e23e +96 e1be +86 e0be +a6 e43e +8e e07e +86 e03e +c6 da3e +86 ca3e +86 c8be +c6 d83e +8e c87e +86 c83e +e6 d63e +ce d27e +c6 d23e +a6 c63e +8e c27e +86 c23e +d6 d1be +c6 d0be +a6 c4be +96 c1be +86 c0be +f6 d53e +e6 d43e +de d17e +d6 d13e +ce d07e +c6 d03e +b6 c53e +ae c47e +a6 c43e +9e c17e +96 c13e +8e c07e +86 c03e +87 ea3d +86 ea3c +97 e93d +87 e83d +96 e93c +86 e83c +87 e2bd +86 e2bc +a6 e63c +8e e27c +86 e23c +97 e1bd +87 e0bd +96 e1bc +86 e0bc +85 e03b +9f e17d +97 e13d +8f e07d +87 e03d +96 e13c +8e e07c +86 e03c +87 cabd +86 cabc +c7 da3d +8f ca7d +87 ca3d +c6 da3c +8e ca7c +86 ca3c +97 c9bd +87 c8bd +86 c8bc +d7 d93d +c7 d83d +85 c83b +9f c97d +97 c93d +8f c87d +87 c83d +c6 d83c +8e c87c +86 c83c +c7 d2bd +a7 c6bd +87 c2bd +c6 d2bc +a6 c6bc +86 c2bc +e6 d63c +ce d27c +c6 d23c +ae c67c +a6 c63c +8e c27c +86 c23c +d7 d1bd +c7 d0bd +b7 c5bd +a7 c4bd +97 c1bd +87 c0bd +d6 d1bc +c6 d0bc +b6 c5bc +a6 c4bc +96 c1bc +86 c0bc +c5 d03b +df d17d +bd cffb +d7 d13d +b5 cf3b +cf d07d +a5 c43b +bf c57d +9d c3fb +b7 c53d +95 c33b +af c47d +8d c2fb +a7 c43d +85 c03b +9f c17d +7f 1fff +fa 7fce +7e 3fde +3f 8ff7 +7d 1ffb +fd 5ff3 +bf 4ff7 +7d 1ff3 +76 bfbc +76 bf3c +f6 7fbc +76 3fbc +f6 7f3c +76 3f3c +7b 1fef +7f 1fdf +7b 1fcf +3f fdf +7a bf4e +f2 7f8e +fa 7f4e +f2 7f0e +7a 3f4e +7d 9fd3 +73 bf8d +72 bf8c +72 bf2c +73 bf0d +76 bf1c +72 bf0c +72 3fac +73 3f8d +76 3f9c +72 3f8c +72 3f2c +73 3f0d +f6 7f1c +76 3f1c +72 3f0c +79 1feb +f9 5fe3 +79 1fe3 +3b fe7 +7d 1fdb +79 1fcb +fd 5fd3 +7f 1fd7 +7d 1fd3 +79 1fc3 +3f 8f7f +37 8f3f +6f 3efd +6e 3efc +bf 4f7f +b7 4f3f +3f f7f +37 f3f +6a bcce +ea 7ece +6e 3ede +f9 774b +7a 3dee +ea 7cce +6a 3cce +3d 8f7b +75 9f33 +3f 8f77 +3d 8f73 +37 8f37 +3d 2f73 +51 bb8b +6b bccd +6b 3eed +6a 3eec +6b 3ecd +6e 3edc +6a 3ecc +d1 7b8b +eb 7ccd +51 3b8b +6b 3ccd +b7 4fb7 +75 1fb3 +37 fb7 +bd 4f7b +3d f7b +fd 5f73 +bf 4f77 +bd 4f73 +b7 4f37 +7d 1f73 +75 1f33 +3f f77 +3d f73 +37 f37 +3d af53 +39 af43 +75 9f93 +cf 7077 +71 9f23 +39 8f63 +7d 9f53 +79 9f43 +75 9f13 +3d 8f53 +39 8f43 +b9 6f63 +39 2f63 +3d 2f53 +39 2f43 +f1 5fa3 +71 1fa3 +75 1f93 +71 1f83 +f9 5f63 +f1 5f23 +b9 4f63 +39 f63 +fd 5f53 +bd 4f53 +b9 4f43 +7d 1f53 +79 1f43 +75 1f13 +71 1f03 +3d f53 +39 f43 +eb fe4f +e7 fe1f +f3 fd8f +e7 fc9f +f3 fd2f +fb fd4f +f3 fd0f +eb fc4f +cb fa4f +c7 fa1f +c7 f89f +d3 f92f +cb f86f +db f94f +d7 f91f +d3 f90f +cf f85f +cb f84f +c7 f81f +c7 f29f +d3 f1af +a7 ee1f +a7 ce1f +f3 f58f +e7 f49f +d7 f19f +d3 f18f +c7 f09f +f3 f52f +d3 f12f +cb f06f +fb f54f +f3 f50f +eb f44f +d3 f10f +cf f05f +cb f04f +c7 f01f +b3 ed2f +ab ec6f +f3 dd2f +b3 cd2f +eb dc6f +ab cc6f +b3 ed0f +af ec5f +ab ec4f +a7 ec1f +f7 dd1f +b7 cd1f +f3 dd0f +b3 cd0f +ef dc5f +af cc5f +eb dc4f +ab cc4f +e7 dc1f +a7 cc1f +87 ea1f +c7 da1f +87 ca1f +93 e92f +d3 d92f +93 c92f +97 e91f +93 e90f +8b e84f +87 e81f +d7 d91f +97 c91f +d3 d90f +93 c90f +cb d84f +8b c84f +c7 d81f +87 c81f +c7 d29f +87 c29f +cb d26f +8b c26f +93 e1af +d3 d1af +93 c1af +6b be4f +eb 7e4f +6b 3e4f +93 e18f +d7 d19f +d3 d18f +93 c18f +c7 d09f +b3 e52f +9b e16f +93 e12f +8b e06f +f3 d52f +b3 c52f +db d16f +9b c16f +d3 d12f +93 c12f +cb d06f +8b c06f +73 bd8f +f3 7d8f +73 3d8f +e7 7c9f +b3 e50f +ab e44f +a7 e41f +93 e10f +8f e05f +8b e04f +f7 d51f +b7 c51f +f3 d50f +b3 c50f +eb d44f +e7 d41f +a7 c41f +df d15f +9f c15f +db d14f +9b c14f +d7 d11f +d3 d10f +93 c10f +cf d05f +8f c05f +cb d04f +8b c04f +c7 d01f +6b bc6f +6b 3c6f +7b bd4f +73 bd0f +6f bc5f +6b bc4f +fb 7d4f +7b 3d4f +f3 7d0f +73 3d0f +6f 3c5f +eb 7c4f +6b 3c4f +67 3c1f +4b ba4f +cb 7a4f +4b 3a4f +53 b98f +47 b89f +d3 798f +53 398f +c7 789f +4b b86f +4b 386f +5b b94f +53 b90f +4f b85f +4b b84f +47 b81f +db 794f +5b 394f +d3 790f +53 390f +cf 785f +4f 385f +cb 784f +4b 384f +c7 781f +47 381f +4b b24f +47 b21f +4f 325f +4b 324f +47 321f +53 31af +73 b58f +53 b18f +f3 758f +73 358f +e7 749f +d3 718f +53 318f +c7 709f +6b b46f +53 b12f +4b b06f +6b 346f +5b 316f +d3 712f +53 312f +cb 706f +4b 306f +73 b50f +5b b14f +53 b10f +4f b05f +4b b04f +fb 754f +f3 750f +73 350f +eb 744f +67 341f +5f 315f +5b 314f +57 311f +53 310f +4f 305f +4b 304f +47 301f +6f 1eff +ef 5cff +bf 4dff +af 4cff +7a b7ce +5e 3bde +fa 77ce +de 73de +7e 37de +7a 37ce +5e 33de +6d 1efb +af 4ef7 +6d 1ef3 +2f ef7 +bd 4dfb +bf 4df7 +bd 4df3 +af 4cf7 +ad 4cf3 +56 b33e +d6 733e +56 333e +2b accf +6b 9ccf +ab 6cef +3b 2dcf +2b 2ccf +56 bbbc +56 bb3c +6b 1eef +57 b3bd +76 b7bc +56 b3bc +57 b33d +76 b73c +56 b33c +6f 1edf +6b 1ecf +2f edf +d6 7bbc +56 3bbc +d6 7b3c +56 3b3c +ab 4cef +f6 77bc +d6 73bc +76 37bc +56 33bc +57 333d +f6 773c +d6 733c +76 373c +56 333c +ef 5cdf +eb 5ccf +af 4cdf +ab 4ccf +7f 1ddf +7b 1dcf +6f 1cdf +6b 1ccf +5a bb4e +72 b78e +da 7b4e +5a 3b4e +f2 778e +72 378e +fa 774e +f2 770e +da 734e +d6 731e +3d add3 +39 adc3 +2d acd3 +29 acc3 +c7 7037 +69 9ee3 +6d 9ed3 +79 9dc3 +6d 9cd3 +69 9cc3 +39 8dc3 +29 8cc3 +a9 6ceb +b9 6de3 +ab 6ce7 +a9 6ce3 +3d 2ddb +39 2dcb +2d 2cdb +29 2ccb +3f 2dd7 +3d 2dd3 +3b 2dc7 +39 2dc3 +2d 2cd3 +29 2cc3 +69 1eeb +e9 5ee3 +69 1ee3 +2b ee7 +6d 1edb +69 1ecb +6d 1ed3 +69 1ec3 +53 bb8d +52 bb8c +52 bb2c +53 bb0d +56 bb1c +52 bb0c +73 b78d +53 b38d +72 b78c +56 b39c +52 b38c +53 b32d +72 b72c +52 b32c +73 b70d +57 b31d +53 b30d +76 b71c +72 b70c +56 b31c +52 b30c +b9 4de3 +ab 4ce7 +a9 4ce3 +7d 1ddb +79 1dcb +39 dcb +fd 5dd3 +f9 5dc3 +bd 4dd3 +b9 4dc3 +ad 4cd3 +a9 4cc3 +7f 1dd7 +7d 1dd3 +79 1dc3 +6d 1cd3 +69 1cc3 +3b dc7 +39 dc3 +29 cc3 +3d ddb +d3 7b8d +53 3b8d +52 3b8c +52 3b2c +53 3b0d +d6 7b1c +56 3b1c +52 3b0c +f3 778d +73 378d +53 338d +f2 778c +72 378c +52 338c +53 332d +f2 772c +72 372c +52 332c +73 370d +57 331d +53 330d +76 371c +72 370c +56 331c +52 330c +5e b1fe +4e b0fe +de 71fe +7e 35fe +6e 34fe +5e 31fe +4e 30fe +2f ac7f +27 ac3f +2f 8e7f +27 8e3f +6f 9c7f +67 9c3f +27 8c3f +2f 2c7f +27 2c3f +af 4e7f +a7 4e3f +2f e7f +27 e3f +4f b2fd +4e b2fc +45 b0bb +5f b1fd +e7 5cbf +b7 4dbf +a7 4cbf +4e 3afc +ef 5c7f +e7 5c3f +bf 4d7f +b7 4d3f +af 4c7f +a7 4c3f +6f 1c7f +67 1c3f +27 c3f +4f 38fd +4f 32fd +6e 36fc +4e 32fc +c5 70bb +df 71fd +65 34bb +7f 35fd +45 30bb +5f 31fd +4a b8ce +5a b1ee +4a b0ee +6a b4ce +5a b1ce +4a b0ce +ca 78ce +4a 38ce +ce 72de +6a 36ce +4e 32de +da 71ee +ca 70ee +6a 34ee +5a 31ee +4a 30ee +ea 74ce +de 71de +da 71ce +ca 70ce +7a 35ce +6e 34de +6a 34ce +5e 31de +5a 31ce +4e 30de +4a 30ce +2d ac7b +35 ad33 +2d ac73 +27 8e37 +6d 9c7b +75 9d33 +6f 9c77 +6d 9c73 +67 9c37 +37 8d37 +2f 8c77 +27 8c37 +2d 2e73 +2d 2c7b +3d 2d73 +35 2d33 +2f 2c77 +2d 2c73 +a7 4eb7 +27 eb7 +4b b8cd +4a b8cc +ad 4e7b +2d e7b +4f b2dd +4b b2cd +6a b6cc +4e b2dc +4a b2cc +ed 5e73 +af 4e77 +ad 4e73 +a7 4e37 +6d 1e73 +2f e77 +2d e73 +27 e37 +51 b38b +6b b4cd +b5 4dbb +b7 4db7 +b5 4db3 +a7 4cb7 +4b 38ed +cb 78cd +4b 38cd +bd 4d7b +b5 4d3b +ad 4c7b +6d 1c7b +ca 78cc +4e 38dc +4a 38cc +4b 32ed +4a 32ec +ef 5c77 +bf 4d77 +bd 4d73 +b7 4d37 +b5 4d33 +af 4c77 +4b 32cd +ad 4c73 +a7 4c37 +7d 1d73 +75 1d33 +6f 1c77 +6d 1c73 +3f d77 +37 d37 +2f c77 +27 c37 +ce 72dc +4e 32dc +4a 32cc +d1 738b +eb 74cd +51 338b +6b 34cd +46 b23e +56 b1be +46 b0be +5e b17e +56 b13e +4e b07e +46 b03e +c6 723e +46 323e +d6 71be +c6 70be +56 31be +46 30be +de 717e +5e 317e +56 313e +4e 307e +46 303e +2b ac6f +2f ac5f +2b ac4f +6b 9c6f +6f 9c5f +6b 9c4f +67 9c1f +27 8c1f +ab 6c6f +2b 2c6f +2f 2c5f +2b 2c4f +27 2c1f +47 b2bd +46 b2bc +4f b27d +47 b23d +4e b27c +46 b23c +57 b1bd +47 b0bd +56 b1bc +46 b0bc +45 b03b +5f b17d +e7 5c9f +a7 4c9f +67 1c9f +ab 4c6f +6f 1c5f +6b 1c4f +67 1c1f +27 c1f +46 32bc +47 323d +ce 727c +c6 723c +4e 327c +46 323c +d7 71bd +c7 70bd +57 31bd +47 30bd +d6 71bc +c6 70bc +56 31bc +46 30bc +c5 703b +df 717d +45 303b +5f 317d +3d 2ffb +57 313d +2d 2efb +47 303d +ea fe4e +e6 fe1e +eb fe4d +ea fe4c +e7 fe1d +e6 fe1c +f2 fd8c +e2 fc8c +ea fc4e +d1 fb0b +eb fc4d +c6 f29e +2d 8c7b +c3 fa2d +e2 f68c +c7 f29d +c6 f29c +c6 fa1e +ca fa4c +c7 fa1d +c6 fa1c +e3 f62d +e2 f62c +cb f26d +ca f26c +e6 f61e +ce f25e +ca f24e +c6 f21e +6b bcef +ea f64c +67 bcbf +e6 f61c +4f b8ff +ce f25c +4b b8ef +ca f24c +47 b8bf +c6 f21c +d2 f1ae +d3 f1ad +d2 f1ac +c3 f0ad +c2 f0ac +d6 f19e +d2 f18e +c6 f09e +f2 f58c +d2 f92c +e3 f48d +e2 f48c +c2 f82c +d7 f19d +d6 f19c +d3 f18d +d2 f18c +c6 f09c +c3 f08d +c2 f08c +ca f84e +c6 f81e +d7 f91d +d6 f91c +d2 f90c +cb f84d +ca f84c +c7 f81d +c6 f81c +c2 f80c +b1 ef2b +cb f06d +f2 f50e +ea f44e +d2 f10e +ce f05e +ca f04e +c6 f01e +d9 f3cb +f3 f50d +c9 f2cb +e3 f40d +b5 ef1b +cf f05d +b1 ef0b +cb f04d +a3 ce2d +a2 ce2c +a6 ee1e +a6 ce1e +a7 ee1d +a6 ee1c +a7 ce1d +a6 ce1c +a3 ce0d +a2 ce0c +b2 ed2e +a6 ec1e +e6 dc1e +a6 cc1e +83 e2ad +82 e2ac +c3 d2ad +83 c2ad +c2 d2ac +82 c2ac +c6 d29e +86 c29e +83 e28d +82 e28c +c7 d29d +87 c29d +c6 d29c +86 c29c +c3 d28d +83 c28d +c2 d28c +82 c28c +83 ea2d +82 ea2c +c3 da2d +83 ca2d +c2 da2c +82 ca2c +ca d26e +8a c26e +86 ea1e +c6 da1e +86 ca1e +a3 e62d +a2 e62c +8b e26d +8a e26c +83 e22d +82 e22c +a3 c62d +e2 d62c +a2 c62c +8b c26d +ca d26c +8a c26c +83 c22d +c2 d22c +82 c22c +87 ea1d +86 ea1c +83 ea0d +82 ea0c +c7 da1d +87 ca1d +c6 da1c +86 ca1c +c3 da0d +83 ca0d +c2 da0c +82 ca0c +a6 e61e +8e e25e +8a e24e +86 e21e +e6 d61e +ce d25e +8e c25e +ca d24e +8a c24e +c6 d21e +86 c21e +27 acbf +a6 e61c +67 9cbf +e6 d61c +27 8cbf +a6 c61c +92 e1ae +d2 d1ae +92 c1ae +93 e1ad +92 e1ac +83 e0ad +82 e0ac +d3 d1ad +93 c1ad +d2 d1ac +92 c1ac +c3 d0ad +83 c0ad +c2 d0ac +82 c0ac +96 e19e +92 e18e +86 e09e +d6 d19e +96 c19e +d2 d18e +92 c18e +c6 d09e +86 c09e +92 e92e +96 e19c +93 e18d +92 e18c +83 e08d +82 e08c +d7 d19d +97 c19d +d6 d19c +96 c19c +d3 d18d +93 c18d +d2 d18c +92 c18c +c7 d09d +87 c09d +c6 d09c +86 c09c +c3 d08d +83 c08d +c2 d08c +82 c08c +93 e92d +83 e82d +82 e82c +93 c92d +83 c82d +c2 d82c +82 c82c +b2 e52e +9a e16e +92 e12e +8a e06e +f2 d52e +b2 c52e +da d16e +9a c16e +d2 d12e +92 c12e +ca d06e +8a c06e +86 e81e +c6 d81e +86 c81e +93 e12d +92 e12c +8b e06d +8a e06c +83 e02d +82 e02c +b1 cf2b +cb d06d +96 e91c +93 e90d +87 e81d +86 e81c +83 e80d +82 e80c +d7 d91d +97 c91d +93 c90d +c7 d81d +87 c81d +c6 d81c +86 c81c +83 c80d +c2 d80c +82 c80c +b2 e50e +a6 e41e +92 e10e +8e e05e +8a e04e +86 e01e +f6 d51e +b6 c51e +f2 d50e +b2 c50e +e6 d41e +de d15e +9e c15e +da d14e +9a c14e +d6 d11e +96 c11e +d2 d10e +92 c10e +ce d05e +8e c05e +ca d04e +8a c04e +c6 d01e +86 c01e +96 e11c +93 e10d +92 e10c +8f e05d +8e e05c +8b e04d +8a e04c +86 e01c +83 e00d +82 e00c +79 bfcb +93 c10d +b5 cf1b +2f 2ef7 +cf d05d +b1 cf0b +2b 2ee7 +cb d04d +71 bf0b +8b c04d +69 becb +83 c00d +63 3e8d +62 3e8c +6a be4e +ea 7e4e +6a 3e4e +6b be4d +6a be4c +63 be0d +62 be0c +eb 7e4d +6b 3e4d +ea 7e4c +6a 3e4c +63 3e0d +62 3e0c +63 bc8d +62 bc8c +73 3d8d +72 3d8c +e3 7c8d +63 3c8d +e2 7c8c +62 3c8c +6a bc4e +ea 7c4e +6a 3c4e +59 bbcb +73 bd0d +51 bb0b +6b bc4d +49 bacb +63 bc0d +d9 7bcb +f3 7d0d +59 3bcb +73 3d0d +d1 7b0b +eb 7c4d +51 3b0b +6b 3c4d +c9 7acb +e3 7c0d +49 3acb +63 3c0d +46 b29c +42 b28c +4a b26c +43 b22d +42 b22c +ca 7a4e +4a 3a4e +43 ba0d +42 ba0c +4b 3a4d +4a 3a4c +43 3a0d +42 3a0c +46 b21e +4a 324e +c6 721e +46 321e +63 b60d +62 b60c +4e b25c +4a b24c +47 b21d +46 b21c +43 b20d +42 b20c +63 360d +62 360c +47 321d +47 38bf +c6 721c +46 321c +43 320d +42 320c +53 b1ad +52 b1ac +43 b0ad +42 b0ac +d2 71ac +52 31ac +c3 70ad +43 30ad +c2 70ac +42 30ac +43 b88d +42 b88c +43 388d +c2 788c +42 388c +72 358e +52 318e +c6 709e +63 b48d +62 b48c +56 b19c +52 b18c +47 b09d +46 b09c +43 b08d +42 b08c +e3 748d +63 348d +e2 748c +62 348c +c7 709d +c6 709c +c3 708d +43 308d +c2 708c +42 308c +52 b12e +4a b06e +52 312e +4a 306e +39 2feb +53 312d +29 2eeb +43 302d +4a b84e +ca 784e +4a 384e +53 b90d +52 b90c +4b b84d +4a b84c +43 b80d +42 b80c +d3 790d +53 390d +d2 790c +52 390c +cb 784d +4b 384d +ca 784c +4a 384c +c3 780d +43 380d +c2 780c +42 380c +72 b50e +56 b11e +52 b10e +4e b05e +4a b04e +46 b01e +f2 750e +72 350e +ea 744e +5a 314e +56 311e +52 310e +4e 305e +4a 304e +46 301e +59 b3cb +73 b50d +51 b30b +6b b44d +49 b2cb +63 b40d +d9 73cb +f3 750d +59 33cb +73 350d +51 330b +6b 344d +c9 72cb +e3 740d +49 32cb +63 340d +3d 2fdb +57 311d +39 2fcb +53 310d +2d 2edb +47 301d +29 2ecb +43 300d +72 bfac +76 bf9c +f5 ffb3 +f1 ffa3 +ed fe73 +7e 1ffe +ea fece +6a becc +62 be8c +72 bd8c +4a bacc +3f 87ff +3f 27ff +bf 47ff +7f 17ff +3f 7ff +a5 4ebb +bf 4ffd +25 ebb +3f ffd +56 bb9c +72 b7ac +76 b79c +f5 f73b +f1 f72b +d7 fb37 +d3 fb27 +c7 fa37 +ca fa4e +cb fa4d +e3 f68d +ea f64e +f2 f58e +f3 f58d +fa f54e +42 ba8c +4a ba4c +52 b98c +62 b68c +72 b58c +b5 edb3 +bd ed73 +3e 2fde +7a 1fee +7e 1fde +7a 1fce +3e fde +ab ecef +8b e8ef +ab ece7 +8b e8e7 +1f 8bf7 +3f 87f7 +3d 87f3 +1f 83f7 +3d 27fb +3f 27f7 +3d 27f3 +9f 4bf7 +1f bf7 +bd 47fb +7d 17fb +3d 7fb +bf 47f7 +bd 47f3 +9f 43f7 +7f 17f7 +7d 17f3 +3f 7f7 +3d 7f3 +1f 3f7 +95 e93b +b5 e53b +97 e937 +95 e933 +b5 e533 +8b e86f +8f e85f +ab e46f +af e45f +3e 8f7e +36 8f3e +be 4f7e +b6 4f3e +3e f7e +36 f3e +6a b6ce +6b b6cd +7a b5ce +ea 76cc +5f 93df +1f 83df +3b 27ef +3b 27cf +5f 1bdf +1f bdf +36 afbc +7b 17ef +3b 7ef +36 af3c +df 53df +9f 43df +7f 17df +7b 17cf +5f 13df +3f 7df +3b 7cf +1f 3df +25 8e3b +3f 8f7d +37 8f3d +36 8f3c +b6 6fbc +36 2fbc +b6 6f3c +36 2f3c +b7 4fbd +37 fbd +b6 4fbc +76 1fbc +36 fbc +a5 4e3b +bf 4f7d +b7 4f3d +25 e3b +3f f7d +37 f3d +b6 4f3c +76 1f3c +36 f3c +5f b37f +5e b37e +4f b27f +4e b27e +d7 73bd +57 b39d +4b b26d +d6 739c +47 b29f +4f b25f +53 b1af +52 b1ae +5b b16f +5a b16e +5f b15f +d3 71ad +47 b29d +43 b28d +4e b25e +4f b25d +4a b24e +4b b24d +57 b19d +52 b18e +53 b18d +5e b15e +5a b14e +c6 729c +4f 38ff +ce 725c +4b 38ef +ca 724c +d6 719c +d2 718c +39 27eb +3b 27e7 +39 27e3 +3d 27db +39 27cb +3f 27d7 +3d 27d3 +3b 27c7 +39 27c3 +32 af8c +32 af2c +36 af1c +32 af0c +79 17eb +39 7eb +36 8f9c +32 8f8c +32 8f2c +76 9f1c +36 8f1c +32 8f0c +79 17e3 +3b 7e7 +39 7e3 +7d 17db +79 17cb +3d 7db +39 7cb +7f 17d7 +7d 17d3 +79 17c3 +3f 7d7 +3d 7d3 +3b 7c7 +39 7c3 +32 2fac +32 2f8c +32 2f2c +b6 6f1c +36 2f1c +32 2f0c +b2 4fac +72 1fac +32 fac +b6 4f9c +b2 4f8c +76 1f9c +72 1f8c +36 f9c +32 f8c +b2 4f2c +32 f2c +b6 4f1c +b2 4f0c +76 1f1c +72 1f0c +36 f1c +32 f0c +36 af9c +76 9f9c +bd ef73 +b9 ef63 +ad ee73 +f1 dfa3 +fd df73 +6e 1efe +17 a33f +1f 8b7f +17 8b3f +37 87bf +57 933f +3f 877f +37 873f +1f 837f +17 833f +1f 237f +17 233f +9f 4b7f +97 4b3f +1f b7f +17 b3f +b7 47bf +37 7bf +2f 2efd +2e 2efc +bf 477f +9f 437f +57 133f +1f 37f +17 33f +6f 1efd +2f efd +ae 4efc +6e 1efc +2e efc +b5 e73b +b1 e72b +f5 d73b +f1 d72b +97 eb37 +93 eb27 +87 ea37 +d7 db37 +d3 db27 +f7 d737 +f5 d733 +f3 d727 +f1 d723 +bf cdff +b7 cdbf +9f c9ff +c7 d03d +ad cefb +af cef7 +bf cdf7 +a7 cc3d +8d cafb +8f caf7 +9f c9f7 +bf cffd +a5 cebb +ad ce7b +bd cd7b +ed de73 +f5 ddb3 +fd dd73 +a7 ceb7 +af ce77 +ad ce73 +b7 cdb7 +b5 cdb3 +bf cd77 +bd cd73 +2e 2ede +3e 2dde +2e 2cde +6a 1eee +6e 1ede +6a 1ece +2e ede +7e 1dde +7a 1dce +6e 1cde +6a 1cce +eb dcef +ef dcdf +cf d8df +ab ccef +af ccdf +8b c8ef +8f c8df +eb dce7 +cb d8e7 +ab cce7 +8b c8e7 +2f 8cfd +15 8bbb +17 8bb7 +1d 8b7b +1f 8b77 +1d 8b73 +17 8b37 +35 87bb +37 87b7 +35 87b3 +17 83b7 +3f 8777 +3d 8773 +37 8737 +35 8733 +1f 8377 +1d 8373 +17 8337 +97 4bb7 +17 bb7 +9d 4b7b +1d b7b +9f 4b77 +9d 4b73 +97 4b37 +1f b77 +1d b73 +17 b37 +b5 47bb +35 7bb +b7 47b7 +b5 47b3 +97 43b7 +37 7b7 +35 7b3 +17 3b7 +2b 2eed +2a 2eec +2b 2ecd +2e 2edc +2a 2ecc +bd 477b +b5 473b +9d 437b +35 73b +bf 4777 +bd 4773 +b7 4737 +b5 4733 +9f 4377 +9d 4373 +97 4337 +d2 dbac +35 733 +1f 377 +1d 373 +17 337 +6b 1eed +2b eed +6a 1eec +2a eec +6f 1edd +6b 1ecd +6e 1edc +6a 1ecc +2e edc +2a ecc +8f ca7f +8e ca7e +97 c9bf +9f c97f +9e c97e +a7 c6bf +a6 c6be +ae c67e +b7 c5bf +b6 c5be +bf c57f +be c57e +9f cbfd +85 cabb +87 cab7 +97 c9b7 +bf c7fd +a5 c6bb +a7 c6b7 +b5 c5bb +b7 c5b7 +f5 d53b +8d ca7b +9f cb7d +85 ca3b +9d c97b +ad c67b +bf c77d +a5 c63b +bd c57b +b5 c53b +c7 da37 +d7 d937 +d5 d933 +e7 d637 +f7 d537 +f5 d533 +8f ca77 +8d ca73 +87 ca37 +9f c977 +9d c973 +97 c937 +95 c933 +af c677 +ad c673 +a7 c637 +bf c577 +bd c573 +b7 c537 +b5 c533 +cb d86f +cf d85f +e7 d49f +eb d46f +ef d45f +8b c86f +8f c85f +a7 c49f +ab c46f +2e 8e7e +26 8e3e +26 8c3e +ae 4e7e +a6 4e3e +2e e7e +26 e3e +a6 4cbe +be 4d7e +ae 4c7e +a6 4c3e +26 c3e +1f a35f +1b a34f +57 939f +1b 836f +5f 935f +5b 934f +57 931f +1f 835f +1b 834f +1b 236f +1f 235f +1b 234f +17 231f +2f 8e7d +27 8e3d +2e 8e7c +26 8e3c +37 8dbd +27 8cbd +25 8c3b +3f 8d7d +1d 8bfb +37 8d3d +15 8b3b +2f 8c7d +d7 539f +97 439f +57 139f +9b 436f +1b 36f +a6 4ebc +af 4e7d +a7 4e3d +2f e7d +27 e3d +ae 4e7c +a6 4e3c +2e e7c +26 e3c +a7 4cbd +27 cbd +b6 4dbc +b7 e737 +a6 4cbc +a5 4c3b +bf 4d7d +95 4b3b +af 4c7d +25 c3b +3f d7d +15 b3b +2f c7d +df 535f +9f 435f +5f 135f +5b 134f +57 131f +1f 35f +1b 34f +17 31f +1f a37f +5f 937f +5b 936f +fb ffcd +e1 fe8b +e9 fe63 +e3 fe27 +e9 fe4b +fb ff4d +e1 fe0b +ed fe53 +eb fe47 +e9 fe43 +e7 fe17 +e3 fe07 +f1 fda3 +e3 fca7 +f1 fd8b +fb fdcd +e1 fc8b +f5 fd93 +f3 fd87 +f1 fd83 +f1 fd2b +e9 fc6b +f3 fd27 +f1 fd23 +e3 fc27 +f9 fd4b +f1 fd0b +e9 fc4b +fb fd4d +e1 fc0b +fd fd53 +fb fd47 +f9 fd43 +f7 fd17 +f5 fd13 +f3 fd07 +f1 fd03 +ef fc57 +ed fc53 +eb fc47 +e9 fc43 +e7 fc17 +e3 fc07 +c3 fa27 +c9 fa4b +db fb4d +c1 fa0b +fb f7cd +e1 f68b +db f3cd +c1 f28b +cb fa47 +c9 fa43 +c7 fa17 +c3 fa07 +db f36d +c1 f22b +c3 f227 +d3 f987 +d1 f983 +cb f247 +c7 f217 +c3 f207 +d1 f92b +c9 f86b +db f1ed +c1 f0ab +d3 f927 +d1 f923 +cb f867 +c9 f863 +c3 f827 +e3 f4a7 +c3 f0a7 +d9 f94b +d1 f90b +c9 f84b +db f94d +c1 f80b +f1 f58b +fb f5cd +e1 f48b +d1 f18b +df f1dd +c5 f09b +db f1cd +c1 f08b +db f947 +d9 f943 +d7 f917 +d5 f913 +d3 f907 +d1 f903 +cf f857 +cd f853 +cb f847 +c9 f843 +c7 f817 +c3 f807 +f3 f587 +f1 f583 +d3 f187 +d1 f183 +f1 f52b +e9 f46b +d1 f12b +c9 f06b +db f16d +c1 f02b +f3 f527 +f1 f523 +d3 f127 +d1 f123 +cb f067 +c9 f063 +c3 f027 +f9 f54b +f1 f50b +e9 f44b +fb f54d +e1 f40b +d9 f14b +d1 f10b +c9 f04b +df f15d +c5 f01b +db f14d +c1 f00b +f9 f543 +f7 f517 +f5 f513 +f3 f507 +f1 f503 +d5 f113 +d3 f107 +d1 f103 +cf f057 +cd f053 +cb f047 +c9 f043 +c7 f017 +c3 f007 +a9 ee63 +a3 ee27 +e9 de63 +a9 ce63 +a3 ce27 +ad ee53 +a9 ee43 +a7 ee17 +a3 ee07 +ed de53 +ad ce53 +a9 ce43 +a7 ce17 +a3 ce07 +b1 eda3 +a3 eca7 +f1 dda3 +b1 cda3 +e3 dca7 +a3 cca7 +f1 dd83 +b1 cd83 +a9 ec6b +e9 dc6b +a9 cc6b +b9 ed63 +b3 ed27 +b1 ed23 +ab ec67 +a9 ec63 +a3 ec27 +f3 dd27 +b3 cd27 +f1 dd23 +b1 cd23 +eb dc67 +ab cc67 +e3 dc27 +a3 cc27 +b5 ed1b +ad ec5b +a9 ec4b +67 3c37 +ed dc5b +27 2c37 +ad cc5b +b5 ed13 +b3 ed07 +b1 ed03 +af ec57 +ad ec53 +ab ec47 +a9 ec43 +a7 ec17 +a3 ec07 +fd dd53 +bd cd53 +f9 dd43 +b9 cd43 +f7 dd17 +b7 cd17 +f5 dd13 +b5 cd13 +f3 dd07 +b3 cd07 +f1 dd03 +b1 cd03 +ef dc57 +af cc57 +ed dc53 +ad cc53 +eb dc47 +ab cc47 +e9 dc43 +a9 cc43 +e7 dc17 +a7 cc17 +e3 dc07 +a3 cc07 +83 ea27 +c3 da27 +83 ca27 +87 ea17 +83 ea07 +c7 da17 +87 ca17 +c3 da07 +83 ca07 +c3 d8a7 +9b e36d +81 e22b +db d36d +c1 d22b +9b c36d +81 c22b +83 e227 +e3 d627 +a3 c627 +c3 d227 +83 c227 +89 e86b +c9 d86b +89 c86b +87 e217 +83 e207 +e7 d617 +a7 c617 +e3 d607 +a3 c607 +c7 d217 +87 c217 +c3 d207 +83 c207 +93 e927 +91 e923 +8b e867 +89 e863 +83 e827 +d3 d927 +93 c927 +d1 d923 +91 c923 +cb d867 +8b c867 +c9 d863 +89 c863 +c3 d827 +83 c827 +9b e1ed +81 e0ab +db d1ed +c1 d0ab +9b c1ed +81 c0ab +95 e91b +8d e85b +89 e84b +47 3837 +cd d85b +a3 e4a7 +e3 d4a7 +a3 c4a7 +c3 d0a7 +97 e917 +95 e913 +93 e907 +91 e903 +8f e857 +8d e853 +8b e847 +89 e843 +87 e817 +83 e807 +d7 d917 +97 c917 +d5 d913 +95 c913 +d3 d907 +93 c907 +d1 d903 +91 c903 +cf d857 +8f c857 +cd d853 +8d c853 +cb d847 +8b c847 +c9 d843 +89 c843 +c7 d817 +87 c817 +c3 d807 +83 c807 +9b e1cd +81 e08b +df d1dd +c5 d09b +9f c1dd +85 c09b +db d1cd +c1 d08b +9b c1cd +81 c08b +b1 e52b +a9 e46b +91 e12b +89 e06b +9b e16d +81 e02b +f1 d52b +b1 c52b +e9 d46b +a9 c46b +d1 d12b +91 c12b +c9 d06b +89 c06b +db d16d +c1 d02b +9b c16d +81 c02b +7b bfcd +61 be8b +fb 7fcd +e1 7e8b +7b 3fcd +61 3e8b +b3 e527 +b1 e523 +93 e127 +91 e123 +89 e063 +f3 d527 +b3 c527 +f1 d523 +b1 c523 +eb d467 +ab c467 +56 339e +e9 d463 +e3 d427 +a3 c427 +d3 d127 +93 c127 +d1 d123 +91 c123 +cb d067 +89 c063 +c3 d027 +b5 e51b +b1 e50b +ad e45b +a9 e44b +95 e11b +91 e10b +8d e05b +89 e04b +9f e15d +85 e01b +9b e14d +81 e00b +6f 34f7 +f5 d51b +6b 34e7 +f1 d50b +67 3437 +ed d45b +4f 30f7 +d5 d11b +4b 30e7 +d1 d10b +47 3037 +cd d05b +3f 2ff7 +df d15d +c5 d01b +9f c15d +85 c01b +3b 2fe7 +db d14d +c1 d00b +9b c14d +81 c00b +b5 e513 +b3 e507 +b1 e503 +95 e113 +93 e107 +91 e103 +8f e057 +8d e053 +8b e047 +89 e043 +87 e017 +83 e007 +f7 d517 +b7 c517 +f5 d513 +b5 c513 +f3 d507 +b3 c507 +f1 d503 +b1 c503 +af c457 +ed d453 +ad c453 +eb d447 +ab c447 +e9 d443 +a9 c443 +e7 d417 +a7 c417 +e3 d407 +a3 c407 +d7 d117 +97 c117 +d5 d113 +95 c113 +d3 d107 +93 c107 +d1 d103 +91 c103 +cf d057 +8f c057 +cd d053 +8d c053 +cb d047 +8b c047 +c9 d043 +89 c043 +c7 d017 +87 c017 +c3 d007 +83 c007 +e9 7e63 +69 3e63 +69 be4b +7b bf4d +61 be0b +e9 7e4b +69 3e4b +fb 7f4d +e1 7e0b +7b 3f4d +61 3e0b +6b be47 +69 be43 +63 be07 +ed 7e53 +6d 3e53 +eb 7e47 +6b 3e47 +e9 7e43 +69 3e43 +e3 7e07 +63 3e07 +71 bd8b +f1 7d8b +71 3d8b +7b 3dcd +61 3c8b +73 bd87 +71 bd83 +75 3d93 +73 3d87 +71 3d83 +69 bc6b +69 3c6b +71 bd23 +69 bc63 +79 3d63 +71 3d23 +6b 3c67 +69 3c63 +e9 dc4b +63 3c27 +79 bd4b +71 bd0b +6d bc5b +69 bc4b +7b bd4d +61 bc0b +f9 7d4b +79 3d4b +f1 7d0b +71 3d0b +6d 3c5b +e9 7c4b +69 3c4b +fb 7d4d +e1 7c0b +7b 3d4d +61 3c0b +7b bd47 +79 bd43 +75 bd13 +73 bd07 +71 bd03 +6f bc57 +6d bc53 +6b bc47 +69 bc43 +67 bc17 +63 bc07 +fd 7d53 +7d 3d53 +7b 3d47 +f9 7d43 +79 3d43 +75 3d13 +73 3d07 +71 3d03 +ef 7c57 +6f 3c57 +ed 7c53 +6d 3c53 +eb 7c47 +6b 3c47 +e9 7c43 +69 3c43 +67 3c17 +63 3c07 +5b bbcd +41 ba8b +db 7bcd +c1 7a8b +5b 3bcd +41 3a8b +49 ba4b +5b bb4d +41 ba0b +c9 7a4b +49 3a4b +db 7b4d +c1 7a0b +5b 3b4d +41 3a0b +4b ba47 +49 ba43 +43 ba07 +cb 7a47 +4b 3a47 +c9 7a43 +49 3a43 +c3 7a07 +43 3a07 +7b b7cd +61 b68b +5b b3cd +41 b28b +fb 77cd +e1 768b +7b 37cd +61 368b +db 73cd +c1 728b +5b 33cd +41 328b +51 b98b +d1 798b +51 398b +53 b987 +51 b983 +53 3987 +51 3983 +7b b74d +61 b60b +49 b24b +5b b34d +41 b20b +6a 3cee +e9 764b +7b 374d +61 360b +4a 38ee +c9 724b +49 324b +5b 334d +41 320b +49 b86b +49 386b +4b b247 +49 b243 +43 b207 +f2 dd0e +eb 7647 +d2 d90e +cb 7247 +4b b867 +49 b863 +43 b827 +cb 7867 +4b 3867 +49 3863 +c9 d84b +43 3827 +5b b1ed +41 b0ab +db 71ed +c1 70ab +5b 31ed +41 30ab +59 b94b +51 b90b +4d b85b +49 b84b +d9 794b +59 394b +d1 790b +51 390b +4d 385b +c9 784b +49 384b +5b 394d +41 380b +e3 74a7 +e9 d4cb +63 34a7 +c3 70a7 +c9 d0cb +43 30a7 +5b b947 +59 b943 +53 b907 +51 b903 +4f b857 +4d b853 +4b b847 +49 b843 +47 b817 +43 b807 +5b 3947 +d9 7943 +59 3943 +53 3907 +51 3903 +cf 7857 +4f 3857 +cd 7853 +4d 3853 +cb 7847 +4b 3847 +c9 7843 +49 3843 +47 3817 +43 3807 +71 b58b +7b b5cd +61 b48b +51 b18b +5f b1dd +45 b09b +5b b1cd +41 b08b +f1 758b +71 358b +d1 718b +51 318b +df 71dd +c5 709b +db 71cd +c1 708b +5b 31cd +41 308b +73 b587 +71 b583 +53 b187 +51 b183 +f3 7587 +73 3587 +f1 7583 +71 3583 +d3 7187 +53 3187 +d1 7183 +51 3183 +69 b46b +49 b06b +5b b16d +41 b02b +e9 746b +69 346b +c9 706b +49 306b +db 716d +c1 702b +49 b063 +6b 3467 +69 3463 +e3 7427 +e9 d44b +63 3427 +cb 7067 +4b 3067 +c9 7063 +49 3063 +c3 7027 +c9 d04b +43 3027 +71 b50b +59 b14b +51 b10b +4d b05b +49 b04b +5f b15d +45 b01b +5b b14d +41 b00b +f9 754b +f1 750b +71 350b +e9 744b +d9 714b +59 314b +d1 710b +51 310b +4d 305b +c9 704b +49 304b +79 b543 +73 b507 +71 b503 +5b b147 +59 b143 +53 b107 +51 b103 +4f b057 +4d b053 +4b b047 +49 b043 +47 b017 +43 b007 +fb 7547 +7b 3547 +f9 7543 +79 3543 +f3 7507 +73 3507 +f1 7503 +71 3503 +ef 7457 +6f 3457 +ed 7453 +6d 3453 +eb 7447 +6b 3447 +e9 7443 +69 3443 +e7 7417 +67 3417 +e3 7407 +63 3407 +7d 9ff3 +db 7147 +5b 3147 +59 3143 +53 3107 +51 3103 +71 9f03 +cf 7057 +4f 3057 +4d 3053 +6d 9ef3 +cb 7047 +4b 3047 +49 3043 +69 9ec3 +c7 7017 +47 3017 +43 3007 +29 2e63 +a9 4e63 +69 1e63 +29 e63 +29 ae43 +2d 8e53 +69 9e43 +29 8e43 +2d 2e53 +29 2e43 +ed 5e53 +ad 4e53 +6d 1e53 +2d e53 +a9 4e43 +69 1e43 +29 e43 +b1 6da3 +a3 6ca7 +b1 4da3 +a3 4ca7 +3b 2dcd +21 2c8b +71 9d83 +31 8d83 +35 2d93 +b1 6d83 +31 2d83 +b5 4d93 +75 1d93 +35 d93 +b1 4d83 +71 1d83 +31 d83 +a9 6c6b +29 2c6b +a9 4c6b +29 c6b +31 ad23 +71 9d23 +31 8d23 +63 9c27 +23 8c27 +b9 6d63 +39 2d63 +b1 6d23 +31 2d23 +ab 6c67 +2b 2c67 +a9 6c63 +29 2c63 +a3 6c27 +a9 cc4b +23 2c27 +39 d63 +b1 4d23 +31 d23 +eb 5c67 +ab 4c67 +2b c67 +a9 4c63 +29 c63 +a3 4c27 +23 c27 +29 ac4b +69 9c4b +29 8c4b +2d 2c5b +29 2c4b +ed 5c5b +ad 4c5b +6d 1c5b +c3 7a0d +2d c5b +e9 5c4b +a9 4c4b +69 1c4b +29 c4b +39 ad43 +35 ad13 +31 ad03 +2b ac47 +29 ac43 +27 ac17 +23 ac07 +79 9d43 +75 9d13 +71 9d03 +6b 9c47 +2b 8c47 +69 9c43 +29 8c43 +67 9c17 +27 8c17 +63 9c07 +23 8c07 +3d 2d53 +39 2d43 +35 2d13 +31 2d03 +2f 2c57 +2d 2c53 +2b 2c47 +29 2c43 +27 2c17 +23 2c07 +7d 1d53 +f9 5d43 +b9 4d43 +79 1d43 +75 1d13 +b1 4d03 +71 1d03 +6f 1c57 +2f c57 +6d 1c53 +2b c47 +e9 5c43 +a9 4c43 +69 1c43 +29 c43 +67 1c17 +27 c17 +23 c07 +a9 6e63 +e9 5e63 +b9 4d63 +ff ffff +fc fff8 +fe fffe +fc fff2 +f6 7f9c +fd fffb +ff fff7 +fc fffa +fd fff9 +fe fff6 +e5 feb3 +ff fff5 +fb ffef +ff ffdf +f8 ffca +7d bf73 +79 bf63 +6d be73 +f8 ffe8 +fa ffee +fe ffde +ee fedc +fc ffd8 +f9 ffc9 +f8 ffc8 +f8 ffe2 +3e 87fe +f9 ffeb +fb ffe7 +ff ffd7 +d3 f10d +b9 efcb +fc ffd2 +fa ffc6 +f8 ffc2 +be 47fe +f9 ffe1 +f8 ffe0 +f8 ffea +fa ffe6 +e1 fea3 +fb ffe5 +fd ffd9 +fe ffd6 +e5 fe93 +ff ffd5 +e3 fe8f +fd ffd1 +e2 fe8e +fc ffd0 +e1 fe83 +fb ffc5 +f9 ffc1 +f8 ffc0 +f4 ff3a +8e e25c +f a8ff +f7 ffbf +ff ff7f +77 bf3f +f4 ffb8 +f5 ff39 +2f a4ff +f9 dfc3 +1f a1ff +e9 dec3 +f a0ff +f6 ffbe +f7 ffbd +fe ff7e +e5 fe3b +ff ff7d +ee fe7c +76 bf3e +77 bf3d +66 be3c +f4 ffb2 +f6 ff36 +6b becd +63 be8d +61 bc8b +7b bdcd +73 bd8d +4b bacd +41 b88b +5b b9cd +ce d25c +4f 98ff +8e c25c +f 88ff +e1 7c8b +fb 7dcd +f3 7d8d +f2 7d8c +f5 ffbb +f7 ffb7 +fd ff7b +ff ff77 +8f c07d +75 bf3b +f6 ffb4 +f7 ff35 +f6 ff34 +e3 fe2f +fd ff71 +2f 86ff +6f 94ff +f5 ffb1 +5f 91ff +f4 ffba +f5 ffb9 +f6 ffb6 +f7 ffb5 +fc ff7a +fd ff79 +e5 fe33 +ff ff75 +f0 ff2a +f3 ffaf +f7 ff9f +ff ff5f +bb ef4f +77 bf1f +f0 ffa8 +eb fec7 +f8 ff68 +e3 fe87 +f0 ff28 +f0 ff8a +f8 ff4a +f0 ff0a +2f 26ff +69 be63 +6d be53 +75 bd93 +79 bd63 +7d bd53 +3f 25ff +2f 24ff +f9 5fc3 +1f 21ff +e9 5ec3 +f 20ff +f0 ffa2 +f2 ffae +f6 ff9e +f7 ff9d +e1 fe2b +fb ff6d +fe ff5e +e5 fe1b +ff ff5d +e6 fe9c +ea fe6c +ee fe5c +ba ef4e +a1 ee0b +bb ef4d +aa ee4c +73 bf2d +76 bf1e +77 bf1d +62 be2c +66 be1c +33 af0d +22 ae0c +f4 ff98 +f1 ff89 +f0 ff88 +f5 ff19 +fc ff58 +f4 ff18 +f9 ff49 +f1 ff09 +f8 ff48 +f0 ff08 +cf 58ff +8f 48ff +f9 ff61 +f1 ffab +f3 ffa7 +f5 ff9b +f7 ff97 +f9 ff6b +ff ff57 +dd fb53 +cb f0cd +b1 ef8b +b9 ef4b +bb ef47 +99 eb43 +8b c06d +71 bf2b +8f c05d +75 bf1b +77 bf17 +4b b04d +31 af0b +33 af07 +f4 ff92 +f2 ff86 +f0 ff82 +f6 ff16 +fc ff52 +f4 ff12 +fa ff46 +f2 ff06 +f8 ff42 +f0 ff02 +6f 16ff +2f 6ff +ef 54ff +df 51ff +cf 50ff +af 44ff +9f 41ff +8f 40ff +7f 15ff +6f 14ff +f0 ffaa +f1 ffa9 +f2 ffa6 +f4 ff9a +f5 ff99 +f6 ff96 +f8 ff6a +e1 fe23 +fb ff65 +fd ff59 +fe ff56 +f7 ff95 +f6 ff94 +e5 fe13 +ff ff55 +f7 ff15 +f6 ff14 +e3 fe0f +fd ff51 +e2 fe0e +fc ff50 +e1 fe03 +fb ff45 +f9 ff41 +f8 ff40 +ec fef8 +ef feff +ff fdff +ec fef2 +fc fdf2 +ec fcf2 +ee fefe +e5 fcbb +ff fdfd +d6 7b9c +f2 77ac +f6 779c +e3 fcaf +fd fdf1 +ed fefb +ef fef7 +fd fdfb +ff fdf7 +ee fcf6 +dd f9f3 +ec fefa +ed fef9 +ee fef6 +fc fdf8 +fd fdf9 +e5 fcb3 +ff fdf5 +ec fcf8 +eb feef +ef fedf +fb fdef +ff fddf +ea fcee +bb edcf +aa ecce +e8 feca +e8 fcca +e8 fee2 +f8 fde2 +e8 fce2 +ea feee +ee fede +ef fedd +fa fdee +e1 fcab +fb fded +e5 fc9b +ff fddd +a1 ec8b +bb edcd +ec fed8 +e9 fec9 +e8 fec8 +f9 fdc9 +f8 fdc8 +e8 fcc8 +f9 fde1 +f8 fde0 +e8 fce0 +e9 feeb +eb fee7 +fc ff78 +ef fed7 +c3 f00d +a9 eecb +f9 fdeb +fb fde7 +ff fdd7 +e8 fcea +ea fce6 +d9 f9e3 +dd f9d3 +bb edc7 +99 e9c3 +ec fed2 +ea fec6 +e8 fec2 +ee fcd6 +fc fdd2 +ec fcd2 +ea fcc6 +f8 fdc2 +e8 fcc2 +fe ff7c +e4 fe3a +e8 feea +ea fee6 +ed fed9 +ee fed6 +f8 fde8 +fc fdd8 +f8 fdea +f9 fde9 +e1 fca3 +fb fde5 +fd fdd9 +e8 fce8 +ec fcd8 +ef fed5 +ee fed4 +e5 fc93 +ff fdd5 +d5 fb93 +ef fcd5 +e3 fc8f +fd fdd1 +e2 fc8e +fc fdd0 +d2 fb8e +ec fcd0 +e1 fc83 +fb fdc5 +d1 fb83 +eb fcc5 +f9 fdc1 +f8 fdc0 +e8 fcc0 +e4 feb8 +e5 fe39 +ec fe78 +e4 fe38 +e7 fc97 +f4 fd38 +e7 febf +ef fe7f +67 be3f +f7 fdbf +ff fd7f +e6 fcbe +ee fc7e +77 bd3f +66 bc3e +fe fff4 +e4 feb2 +e6 fe36 +f4 fdb2 +fe fdf4 +e4 fcb2 +e6 fc36 +e6 febe +ee fe7e +ef fe7d +f6 fdbc +e4 fc3a +fe fd7c +66 be3e +67 be3d +f7 fdbd +fe fd7e +e5 fc3b +ff fd7d +e6 fcbc +d5 fb3b +ef fc7d +43 ba8d +4a ba4e +4b ba4d +53 b98d +41 b80b +5b b94d +63 b68d +6b b64d +72 b58e +73 b58d +61 b40b +7b b54d +cb 7a4d +ca 7a4c +d2 798c +c1 780b +db 794d +4f 90ff +e5 feb1 +e7 fe35 +ee fe74 +e6 fe34 +ed fe71 +ec fe70 +f4 fdb0 +e4 fcb0 +dd fbf3 +f7 fd35 +cd faf3 +e7 fc35 +e3 fc2f +fd fd71 +d3 fb2f +ed fc71 +e2 768c +6b 3cef +ea 764c +f2 758c +ff fffd +e5 febb +e7 feb7 +ed fe7b +ef fe77 +cd fa73 +67 be37 +f5 fdbb +f7 fdb7 +fd fd7b +ff fd77 +fe fdfc +e4 fcba +e6 fcb6 +ec fc7a +75 bd3b +55 b933 +fe fffc +e4 feba +e5 feb9 +e6 feb6 +ec fe7a +ed fe79 +ee fe76 +ef fe75 +f4 fdb8 +f6 fdb4 +ef fcd7 +fc fd78 +f7 fdb5 +fc fd7a +e5 fc33 +ff fd75 +e4 fcb8 +b6 c71e +e7 fcb5 +e6 fcb4 +d5 fb33 +ef fc75 +fd fff1 +e3 feaf +e7 fe9f +eb fe6f +ef fe5f +ab ee4f +67 be1f +f3 fdaf +f7 fd9f +fc fdf0 +e2 fcae +e6 fc9e +aa ec4e +73 bd2f +66 bc1e +33 ad0f +fc fff0 +e2 feae +e6 fe9e +e7 fe9d +eb fe6d +ee fe5e +ef fe5d +f2 fdac +f6 fd9c +aa ee4e +ab ee4d +63 be2d +66 be1e +67 be1d +23 ae0d +f2 fdae +f7 fd9d +e1 fc2b +fb fd6d +e2 fcac +e7 fc9d +e6 fc9c +d1 fb2b +eb fc6d +a1 ec0b +bb ed4d +91 eb0b +ab ec4d +72 bd2e +32 ad0e +fb ffed +e1 feab +e3 fea7 +ff ffdd +e5 fe9b +f4 ff38 +e7 fe97 +e9 fe6b +eb fe67 +ef fe57 +c9 fa63 +cd fa53 +bb efcd +a1 ee8b +a9 ee4b +ab ee47 +89 ea43 +63 be27 +67 be17 +23 ae07 +f1 fdab +f3 fda7 +f5 fd9b +f7 fd97 +f9 fd6b +ff fd57 +d5 f993 +dd f953 +b9 ed4b +99 e943 +71 bd2b +75 bd1b +77 bd17 +51 b923 +55 b913 +33 ad07 +fc f7f8 +dc f3f8 +ff f7ff +f7 f53d +dd f3fb +f6 fd34 +dc fbf2 +fe f7fe +ee f6fc +f6 f53c +dc f3fa +dd f3f9 +c2 f2ae +dc f3f0 +f7 fd3d +dd fbfb +df fbf7 +fd f7f3 +fd f7fb +ff f7f7 +df f3f7 +f6 fd3c +dc fbfa +de fbf6 +fc f7f2 +fc f7fa +fd f7f9 +fe f7f6 +de f3f6 +c3 f2af +dd f3f1 +f8 f7e8 +d8 f3e8 +df fbdf +fb f7ef +ff f7df +f3 f52d +d9 f3eb +bb e7cf +b3 e50d +99 e3cb +f2 fd0c +d8 fbca +f8 f7ca +f2 f50c +d8 f3ca +3d ad73 +f4 fd30 +da fbee +de fbde +ce fadc +8a eacc +fa f7ee +fe f7de +ea f6ec +ee f6dc +f2 f52c +d8 f3ea +ba e7ce +aa e6cc +dd f3d9 +fc f7d8 +dc f3d8 +f9 f7c9 +d9 f3c9 +f8 f7c8 +d8 f3c8 +5e 93de +1e 83de +1e 2bde +5e 1bde +1e bde +de 53de +9e 43de +7e 17de +7a 17ce +5e 13de +f8 f7e0 +d8 f3e0 +db fbe7 +ec fc78 +df fbd7 +f9 f7e3 +fd f7d3 +b3 ed0d +99 ebcb +b9 e7c3 +f9 f7eb +fb f7e7 +ff f7d7 +db f3e7 +b9 e7cb +bb e7c7 +da fbc6 +fa f7c6 +da f3c6 +f8 f7c2 +ee fc7c +d4 fb3a +f4 f73a +f2 fd2c +d8 fbea +da fbe6 +de fbd6 +f8 f7e2 +f9 f7e1 +fc f7d2 +f8 f7ea +f9 f7e9 +fa f7e6 +fd f7d9 +fe f7d6 +da f3e6 +d9 f3e1 +de f3d6 +c5 fa93 +df fbd5 +c1 fa83 +db fbc5 +e3 f68f +fd f7d1 +c3 f28f +dd f3d1 +e2 f68e +fc f7d0 +c2 f28e +dc f3d0 +e1 f683 +fb f7c5 +c1 f283 +db f3c5 +f9 f7c1 +d9 f3c1 +f8 f7c0 +d8 f3c0 +d5 fb39 +f4 f7b8 +d4 f3b8 +f5 f739 +d5 f339 +df fb7f +57 bb3f +f7 f7bf +ff f77f +dd f37b +77 b73f +2d acdb +2d a4fb +2d a4f3 +ee fcf4 +d4 fbb2 +d6 fb36 +de fb7e +c5 fa3b +df fb7d +ce fa7c +56 bb3e +57 bb3d +46 ba3c +f6 f7be +f7 f7bd +fe f77e +e5 f63b +ff f77d +e6 f6bc +ee f67c +d5 f3b9 +dc f37a +dd f379 +76 b73e +77 b73d +66 b63c +d7 fb35 +d6 fb34 +c3 fa2f +dd fb71 +3f 8d7f +d5 fb31 +3e 8d7e +d4 fb30 +f6 f7b4 +d6 f3b4 +f4 f7b0 +f7 f735 +d7 f335 +f5 f731 +f4 f730 +d7 fbb7 +dd fb7b +df fb77 +f5 f7b3 +f5 f7bb +f7 f7b7 +fd f77b +d7 f3b7 +75 b73b +2f 86f7 +2d 86f3 +f 82f7 +6d 94fb +e3 fead +4d 90fb +d3 f3ad +3d 85fb +c3 f2ad +2d 84fb +6f 94f7 +6d 94f3 +4f 90f7 +3f 85f7 +2f 84f7 +1f 81f7 +ea fc6c +d0 fb2a +f0 f72a +ee fcfc +d4 fbba +d6 fbb6 +a6 c61e +d7 fbb5 +dc fb7a +dd fb79 +c5 fa33 +df fb75 +f4 f7b2 +f5 f7b1 +e3 f62f +fd f771 +f4 f7ba +f5 f7b9 +f6 f7b6 +f7 f7b5 +fc f77a +fd f779 +d6 f3b6 +d7 f3b5 +3f 85ff +d5 f3b1 +c5 f233 +df f375 +c3 f22f +dd f371 +29 ac6b +2d ac5b +cb fac7 +d8 fb68 +c3 fa87 +d0 fb28 +f0 f7a8 +d0 f3a8 +f1 f729 +eb f6c7 +f8 f768 +e3 f687 +f0 f728 +cb f2c7 +d8 f368 +c3 f287 +d0 f328 +d7 fb9f +df fb5f +9b eb4f +57 bb1f +f3 f7af +f7 f79f +ff f75f +b3 e78f +73 b72f +77 b71f +ea fccc +d0 fb8a +d8 fb4a +ea fc4c +d0 fb0a +f0 f78a +ea f4cc +d0 f38a +f8 f74a +f0 f70a +d8 f34a +ea f44c +d0 f30a +2d 26fb +29 ac63 +2f ac57 +2d ac53 +3d 25fb +2d 24fb +3f 25f7 +3d 25f3 +b5 c51b +2f 24f7 +2d 24f3 +1f 21f7 +95 c11b +f 20f7 +ea fce4 +d0 fba2 +ec fcf0 +d2 fbae +d6 fb9e +d7 fb9d +c1 fa2b +db fb6d +de fb5e +c5 fa1b +df fb5d +c6 fa9c +ca fa6c +ce fa5c +93 eb8d +9a eb4e +81 ea0b +9b eb4d +82 ea8c +8a ea4c +53 bb2d +56 bb1e +57 bb1d +42 ba2c +46 ba1c +f2 f7ae +f3 f7ad +f6 f79e +f7 f79d +e1 f62b +fb f76d +fe f75e +e2 f6ac +e6 f69c +ea f66c +6f bcff +ee f65c +d1 f3a9 +b2 e78e +b3 e78d +ba e74e +a2 e68c +2b acef +aa e64c +72 b72e +73 b72d +77 b71d +62 b62c +66 b61c +d5 fb19 +dc fb58 +d4 fb18 +d9 fb49 +d1 fb09 +d8 fb48 +d0 fb08 +d5 f399 +f4 f798 +d4 f398 +f1 f789 +d1 f389 +f0 f788 +d0 f388 +27 c3d +d afb +f af7 +9d 49f3 +8f 48f7 +8d 48f3 +d9 fb61 +f2 f7a4 +f0 f7a0 +f3 f725 +f1 f721 +f0 f720 +ef fcdd +d5 fb9b +e4 fc38 +d7 fb97 +d9 fb6b +df fb57 +f1 f7a3 +f5 f793 +ab eccd +91 eb8b +99 eb4b +9b eb47 +b1 e783 +57 bb17 +13 ab07 +f1 f7ab +f3 f7a7 +f5 f79b +f7 f797 +d3 f3a7 +b1 e78b +b3 e787 +71 b72b +75 b71b +ee fcd4 +d4 fb92 +ea fcc4 +d0 fb82 +d6 fb16 +dc fb52 +da fb46 +d2 fb06 +d8 fb42 +f2 f786 +f0 f782 +e9 5ccb +fa f746 +f8 f742 +d8 f342 +6d 16fb +2d 6fb +ad 46f3 +8f 42f7 +2f 6f7 +2d 6f3 +f 2f7 +ed 54fb +cd 50fb +ad 44fb +9d 41fb +8d 40fb +7d 15fb +6d 14fb +ef 54f7 +ed 54f3 +cf 50f7 +cd 50f3 +bd 45f3 +af 44f7 +ad 44f3 +9f 41f7 +9d 41f3 +8f 40f7 +8d 40f3 +7f 15f7 +7d 15f3 +6d 14f3 +5f 11f7 +3f 5f7 +2f 4f7 +1f 1f7 +f f7 +ea fcec +d0 fbaa +d2 fba6 +ee fcdc +d4 fb9a +d5 fb99 +fd fd79 +d6 fb96 +d8 fb6a +c1 fa23 +db fb65 +dd fb59 +de fb56 +f0 f7a2 +f1 f7a1 +f4 f792 +f9 f761 +fc f752 +f0 f7aa +f1 f7a9 +f2 f7a6 +f3 f7a5 +f4 f79a +f5 f799 +f6 f796 +f9 f769 +ed 5cdb +fe f756 +d2 f3a6 +3d 85f3 +d3 f3a5 +c1 f223 +db f365 +d9 f361 +dc f352 +c5 fa13 +df fb55 +d7 fb15 +d6 fb14 +c3 fa0f +dd fb51 +c2 fa0e +dc fb50 +c1 fa03 +db fb45 +d9 fb41 +d8 fb40 +f7 f795 +d7 f395 +f6 f794 +d6 f394 +f5 f791 +f4 f790 +f3 f785 +f2 f784 +f1 f781 +f0 f780 +f7 f715 +d7 f315 +7d bdf3 +e2 f60e +fc f750 +75 bdb3 +f4 f710 +f3 f705 +79 bde3 +f8 f740 +71 bda3 +f0 f700 +ec f6f8 +cc f2f8 +df f9ff +e7 f43d +cd f2fb +ff f5ff +ee f4fe +dd f1fb +cc f0fa +e6 fc34 +cc faf2 +dc f9f2 +cc f8f2 +ce f8fc +ee f6fe +e6 f43c +cc f2fa +cd f2f9 +dc f1f8 +fe f5fe +e5 f4bb +ff f5fd +d5 f3bb +ef f4fd +d4 f3ba +ee f4fc +dc f1fa +dd f1f9 +cd f0f9 +cc f0f8 +37 8d3f +cd faf1 +ee f6f4 +ec f6f0 +e7 fc3d +cd fafb +cf faf7 +ed f6f3 +dd f9fb +df f9f7 +ce f8f6 +fd f5f3 +ec f4f2 +ed f6fb +ef f6f7 +cf f2f7 +fd f5fb +ff f5f7 +ec f4fa +ee f4f6 +df f1f7 +dd f1f3 +ce f0f6 +cc f0f2 +e6 fc3c +cc fafa +cd faf9 +ce faf6 +39 8d43 +cf faf5 +ed f6f1 +9e c35e +cf f8f5 +ce f8f4 +fc f5f2 +e3 f4af +fd f5f1 +d3 f3af +ed f4f1 +d2 f3ae +ec f4f0 +ec f6fa +ed f6f9 +ef f6f5 +fc f5f8 +c2 f0ae +dc f1f0 +fc f5fa +fd f5f9 +fe f5f6 +e5 f4b3 +ff f5f5 +ed f4f9 +ec f4f8 +d5 f3b3 +ef f4f5 +d4 f3b2 +ee f4f4 +de f1f6 +c5 f0b3 +df f1f5 +dc f1f2 +c3 f0af +dd f1f1 +b5 efb3 +cf f0f5 +e8 f6e8 +cf fadf +df f9df +ca f8ee +9b e9cf +8a e8ce +eb f6ef +e3 f42d +c9 f2eb +ab e6cf +a3 e40d +89 e2cb +fb f5ef +ff f5df +ea f4ee +ee f4de +d9 f1eb +c8 f0ea +bb e5cf +aa e4ce +99 e1cb +e2 fc0c +c8 faca +c8 f8ca +e8 f6ca +e2 f40c +c8 f2ca +f8 f5ca +e8 f4ca +d8 f1ca +c8 f0ca +d8 f9e2 +c8 f8e2 +e4 fc30 +ca faee +ce fade +cf fadd +8b eacd +da f9ee +c5 f89b +df f9dd +cf f8dd +ce f8dc +81 e88b +9b e9cd +8b e8cd +ea f6ee +eb f6ed +ee f6de +e2 f42c +c8 f2ea +d8 f1e8 +aa e6ce +ab e6cd +fa f5ee +e1 f4ab +fb f5ed +fe f5de +e5 f49b +ff f5dd +d1 f3ab +eb f4ed +d0 f3aa +ea f4ec +d5 f39b +ef f4dd +d4 f39a +ee f4dc +d8 f1ea +d9 f1e9 +c9 f0e9 +c8 f0e8 +ba e5ce +a1 e48b +bb e5cd +91 e38b +ab e4cd +cd f2d9 +ec f6d8 +cc f2d8 +e9 f6c9 +c9 f2c9 +e8 f6c8 +c8 f2c8 +dd f1d9 +cd f0d9 +dc f1d8 +cc f0d8 +f9 f5c9 +e9 f4c9 +d9 f1c9 +c9 f0c9 +f8 f5c8 +e8 f4c8 +d8 f1c8 +c8 f0c8 +ea f6e4 +e8 f6e0 +cb fae7 +dc fb78 +cf fad7 +e9 f6e3 +ed f6d3 +a3 ec0d +89 eacb +a9 e6c3 +db f9e7 +df f9d7 +c8 f8ea +ca f8e6 +f9 f5e3 +fd f5d3 +e8 f4e2 +9b e9c7 +b9 e5c3 +e9 f6eb +eb f6e7 +fc f778 +ef f6d7 +cb f2e7 +dc f378 +cf f2d7 +a9 e6cb +f9 f5eb +fb f5e7 +ff f5d7 +e8 f4ea +ea f4e6 +db f1e7 +d9 f1e3 +df f1d7 +dd f1d3 +ca f0e6 +c8 f0e2 +b9 e5cb +bb e5c7 +9b e1c7 +99 e1c3 +ca fac6 +ce f8d6 +dc f9d2 +cc f8d2 +ca f8c6 +d8 f9c2 +c8 f8c2 +ee f4d6 +ce f0d6 +ec f4d2 +cc f0d2 +fa f5c6 +ea f4c6 +da f1c6 +ca f0c6 +f8 f5c2 +e8 f4c2 +d8 f1c2 +c8 f0c2 +de fb7c +c4 fa3a +fe f77c +e4 f63a +de f37c +c4 f23a +f4 f53a +d4 f13a +de f17c +c4 f03a +e2 fc2c +c8 faea +ca fae6 +35 8d33 +cb fae5 +cd fad9 +ce fad6 +e9 f6e1 +f8 f5e0 +d8 f9ea +9a c34e +cb f8e5 +ca f8e4 +f8 f5e2 +f9 f5e1 +fc f5d2 +e9 f4e1 +e8 f4e0 +e8 f6ea +e9 f6e9 +eb f6e5 +ed f6d9 +f8 f5e8 +fc f5d8 +d8 f1e0 +f8 f5ea +f9 f5e9 +fa f5e6 +e1 f4a3 +fb f5e5 +fd f5d9 +fe f5d6 +e9 f4e9 +e8 f4e8 +d1 f3a3 +eb f4e5 +d0 f3a2 +ea f4e4 +ed f4d9 +ec f4d8 +da f1e6 +c1 f0a3 +db f1e5 +d8 f1e2 +d9 f1e1 +de f1d6 +dc f1d2 +b1 efa3 +cb f0e5 +c9 f0e1 +c8 f0e0 +cf fad5 +ce fad4 +35 8d13 +cb fac5 +c5 f893 +df f9d5 +cf f8d5 +ce f8d4 +c1 f883 +db f9c5 +cb f8c5 +ca f8c4 +ef f6d5 +cf f2d5 +ee f6d4 +ce f2d4 +ed f6d1 +ec f6d0 +eb f6c5 +ea f6c4 +e9 f6c1 +e8 f6c0 +e5 f493 +ff f5d5 +d5 f393 +ef f4d5 +c5 f093 +df f1d5 +b5 ef93 +cf f0d5 +d4 f392 +ee f4d4 +e3 f48f +fd f5d1 +d3 f38f +ed f4d1 +c3 f08f +dd f1d1 +b3 ef8f +cd f0d1 +e2 f48e +fc f5d0 +d2 f38e +ec f4d0 +c2 f08e +dc f1d0 +b2 ef8e +cc f0d0 +e1 f483 +fb f5c5 +d1 f383 +eb f4c5 +c1 f083 +db f1c5 +b1 ef83 +cb f0c5 +d0 f382 +ea f4c4 +f9 f5c1 +e9 f4c1 +d9 f1c1 +c9 f0c1 +f8 f5c0 +e8 f4c0 +d8 f1c0 +c8 f0c0 +c5 fa39 +cc fa78 +c4 fa38 +c7 f897 +d4 f938 +c4 f838 +e4 f6b8 +c4 f2b8 +e5 f639 +c5 f239 +ec f678 +e4 f638 +cc f278 +c4 f238 +f5 f539 +e5 f439 +d5 f139 +c5 f039 +e7 f497 +f4 f538 +d7 f397 +e4 f438 +c7 f097 +d4 f138 +cf fa7f +47 ba3f +df f97f +c6 f8be +ce f87e +57 b93f +46 b83e +e7 f6bf +df f3fd +c5 f2bb +cd f27b +67 b63f +5f b37d +45 b23b +f7 f5bf +ff f57f +e6 f4be +ee f47e +d5 f1bb +dd f17b +de f1fc +c4 f0ba +77 b53f +66 b43e +55 b13b +de fbf4 +c4 fab2 +c6 fa36 +d4 f9b2 +de f9f4 +c4 f8b2 +c6 f836 +f6 f536 +c6 f036 +ce fa7e +cf fa7d +c4 f83a +de f97c +46 ba3e +47 ba3d +56 b93c +de f97e +c5 f83b +df f97d +cf f87d +ce f87c +57 b93d +47 b83d +46 b83c +e6 f6be +e7 f6bd +ee f67e +f6 f5bc +e4 f43a +fe f57c +de f3fc +c4 f2ba +c5 f2b9 +cc f27a +cd f279 +d4 f1b8 +cf f0d7 +dc f178 +66 b63e +67 b63d +f6 f5be +f7 f5bd +fe f57e +e5 f43b +ff f57d +e7 f4bd +e6 f4bc +d5 f33b +ef f47d +d4 f33a +ee f47c +d4 f1ba +d5 f1b9 +c5 f0b9 +c4 f0b8 +76 b53e +c7 fa35 +ce fa74 +c6 fa34 +cd fa71 +2f 8c7f +c5 fa31 +cc fa70 +2e 8c7e +c4 fa30 +d7 f935 +c7 f835 +d6 f934 +c6 f834 +c3 f82f +dd f971 +cd f871 +d4 f930 +cc f870 +c4 f830 +e6 f6b4 +e4 f6b0 +e7 f635 +c7 f235 +ce f274 +c6 f234 +e5 f631 +ec f670 +e4 f630 +cc f270 +dd f3f3 +f7 f535 +cd f2f3 +e7 f435 +bd eff3 +d7 f135 +ad eef3 +c7 f035 +dc f3f2 +f6 f534 +db f3ef +f5 f531 +cb f2ef +e5 f431 +da f3ee +f4 f530 +ca f2ee +e4 f430 +c7 fab7 +cd fa7b +cf fa77 +ff f7f5 +e5 f6b3 +47 ba37 +d7 f9b7 +dd f97b +df f977 +de f9fc +c4 f8ba +c6 f8b6 +cc f87a +ce f876 +f5 f5b3 +fe f5f4 +e4 f4b2 +55 b93b +75 b533 +ff f7fd +e5 f6bb +e7 f6b7 +ed f67b +c7 f2b7 +df f3f5 +c5 f2b3 +cf f277 +cd f273 +47 b237 +f5 f5bb +f7 f5b7 +fd f57b +fe f5fc +e4 f4ba +e6 f4b6 +ec f47a +d7 f1b7 +d5 f1b3 +c6 f0b6 +de f1f4 +c4 f0b2 +ce f076 +75 b53b +55 b133 +de fbfc +c4 faba +c6 fab6 +31 8d03 +c7 fab5 +cc fa7a +cd fa79 +ce fa76 +cf fa75 +cf f8d7 +dc f978 +fe f7f4 +e4 f6b2 +e5 f6b1 +ed f671 +f4 f5b0 +a6 c41e +d7 f9b5 +dc f97a +dd f979 +c5 f833 +df f975 +96 c31e +c7 f8b5 +af c45f +c6 f8b4 +cd f879 +cc f878 +cf f875 +ce f874 +f4 f5b2 +f5 f5b1 +e3 f42f +fd f571 +e5 f4b1 +e4 f4b0 +d3 f32f +ed f471 +fe f7fc +e4 f6ba +e5 f6b9 +e6 f6b6 +e7 f6b5 +ec f67a +ed f679 +f4 f5b8 +f6 f5b4 +ef f4d7 +fc f578 +c6 f2b6 +de f3f4 +c4 f2b2 +2f 84ff +c5 f2b1 +cf f275 +cd f271 +d6 f1b4 +d4 f1b0 +f4 f5ba +f5 f5b9 +f6 f5b6 +f7 f5b5 +fc f57a +d6 f396 +fd f579 +e5 f4b9 +e4 f4b8 +e7 f4b5 +e6 f4b4 +df f3d7 +ec f478 +d6 f1b6 +d7 f1b5 +d4 f1b2 +d5 f1b1 +c5 f033 +df f175 +c3 f02f +dd f171 +c7 f0b5 +c6 f0b4 +c5 f0b1 +c4 f0b0 +b5 ef33 +cf f075 +b3 ef2f +cd f071 +b2 ef2e +cc f070 +c7 fa9f +cb fa6f +cf fa5f +8b ea4f +47 ba1f +d7 f99f +df f95f +c6 f89e +ce f85e +9b e94f +8a e84e +53 b92f +57 b91f +46 b81e +fd f7f1 +e3 f6af +e7 f69f +db f3ed +c1 f2ab +df f3dd +c5 f29b +9b e3cd +81 e28b +67 b61f +5b b36d +41 b22b +5f b35d +45 b21b +f3 f5af +f7 f59f +fc f5f0 +e2 f4ae +e6 f49e +d1 f1ab +d5 f19b +d9 f16b +b3 e58f +aa e44e +91 e18b +99 e14b +73 b52f +51 b12b +55 b11b +c6 fa9e +c7 fa9d +cb fa6d +ce fa5e +cf fa5d +d6 f99c +83 ea8d +8a ea4e +8b ea4d +43 ba2d +46 ba1e +47 ba1d +52 b92c +56 b91c +d2 f9ae +d7 f99d +c1 f82b +db f96d +de f95e +c5 f81b +df f95d +c7 f89d +c6 f89c +cb f86d +ca f86c +cf f85d +ce f85c +81 e80b +9b e94d +8b e84d +8a e84c +52 b92e +53 b92d +57 b91d +43 b82d +42 b82c +47 b81d +46 b81c +fc f7f0 +e2 f6ae +e3 f6ad +e6 f69e +e7 f69d +eb f66d +ee f65e +f2 f5ac +f6 f59c +a3 e68d +aa e64e +b2 e58c +63 b62d +67 b61d +f2 f5ae +f3 f5ad +f6 f59e +f7 f59d +e1 f42b +fb f56d +e3 f4ad +e2 f4ac +e7 f49d +e6 f49c +d1 f32b +eb f46d +d0 f32a +ea f46c +b2 e58e +b3 e58d +a1 e40b +bb e54d +a3 e48d +a2 e48c +72 b52e +76 b51e +df fbdd +c5 fa9b +d4 fb38 +c7 fa97 +c9 fa6b +cb fa67 +cf fa57 +fb f7e5 +e1 f6a3 +ff f7d5 +e5 f693 +9b ebcd +81 ea8b +89 ea4b +8b ea47 +43 ba27 +47 ba17 +d5 f99b +d7 f997 +d9 f96b +df f957 +f1 f5a3 +f5 f593 +fd f553 +99 e94b +9b e947 +b1 e583 +51 b92b +55 b91b +57 b917 +71 b523 +75 b513 +fb f7ed +e1 f6ab +e3 f6a7 +ff f7dd +e5 f69b +f4 f738 +e7 f697 +c3 f2a7 +db f3e5 +c1 f2a3 +d4 f338 +c7 f297 +df f3d5 +c5 f293 +cb f267 +c9 f263 +cf f257 +bb e7cd +a1 e68b +8b e247 +43 b227 +47 b217 +f1 f5ab +f3 f5a7 +f5 f59b +f7 f597 +f9 f56b +d3 f1a7 +d1 f1a3 +d7 f197 +d5 f193 +b1 e58b +b3 e587 +b9 e54b +93 e187 +91 e183 +71 b52b +75 b51b +77 b517 +51 b123 +57 b117 +55 b113 +d6 d13c +bc cffa +4f 3afd +7e 37fe +7a 37ee +6e 36fe +6f 36fd +bd cff9 +bc cff8 +ff dfff +d6 f134 +bc eff2 +be cff6 +fc dff2 +fe dffe +aa ceec +52 3bac +4a 3aec +6a 36ec +d7 f13d +bd effb +bf eff7 +a5 ceb3 +bf cff5 +ff dff7 +d3 d12d +b9 cfeb +bb cfe7 +d6 f13c +bc effa +7b 1fe7 +bd eff9 +be eff6 +a5 eeb3 +bf eff5 +fc dffa +fe dff6 +d2 d12c +b8 cfea +d5 f131 +bb efef +bf efdf +fb dfcf +b8 efe8 +fb dfef +ff dfdf +b9 cfe9 +b8 cfe8 +bf cfdf +d2 f10c +b8 efca +d4 f130 +ba efee +be efde +fa dfce +fa dfee +fe dfde +be cfde +ae cedc +aa cecc +ba cfe6 +f8 dfe2 +1e a37e +16 a33e +1e 8b7e +16 8b3e +36 87be +5e 937e +56 933e +3e 877e +36 873e +1e 837e +16 833e +cd f8f9 +16 233e +9e 4b7e +96 4b3e +1e b7e +16 b3e +b6 47be +36 7be +d6 533e +be 477e +b6 473e +9e 437e +96 433e +56 133e +d3 f12d +b9 efeb +bb efe7 +d7 f11d +bd efdb +cc f078 +bf efd7 +fb dfc7 +b9 efe1 +b8 efe0 +fb dfe7 +ff dfd7 +a1 cea3 +bb cfe5 +f9 dfe1 +b9 cfe1 +f8 dfe0 +b8 cfe0 +ba efc6 +fc dfd2 +d2 f12c +b8 efea +ba efe6 +a1 eea3 +bb efe5 +d6 f11c +bc efda +7b 1fc7 +bd efd9 +be efd6 +fa dfc6 +ce f07c +b4 ef3a +f8 dfea +fa dfe6 +e1 dea3 +fb dfe5 +fe dfd6 +be cfd6 +ba cfc6 +ce d0fc +b4 cfba +f4 df3a +bc cf7a +ce d07c +b4 cf3a +a3 ee8f +bd efd1 +a2 ee8e +bc efd0 +a1 ee83 +bb efc5 +b9 efc1 +b8 efc0 +a5 ce93 +bf cfd5 +a1 ce83 +bb cfc5 +b9 cfc1 +b8 cfc0 +4b 3aed +4e 3ade +8a e24c +b a8ef +d9 734b +5a 39ee +6a 36ee +6b 36ed +6e 36de +f a8df +b a8cf +7a 35ee +61 34ab +7b 35ed +7e 35de +bf ef7f +37 af3f +ff df7f +bb cf6f +77 9f3f +b5 cfb9 +f4 dfb8 +b4 cfb8 +bd cf79 +b5 cf39 +2b a4ef +f5 dfb3 +1b a1ef +e5 deb3 +ff dff5 +b a0ef +2b a4cf +f5 df93 +1b a1cf +e5 de93 +ff dfd5 +b a0cf +b7 efbd +be ef7e +a5 ee3b +bf ef7d +ae ee7c +36 af3e +37 af3d +26 ae3c +ce f0f4 +b4 efb2 +fe df7e +e5 de3b +ff df7d +b6 ef36 +bc ef72 +ce f074 +b4 ef32 +b3 cfad +ba cf6e +a1 ce2b +bb cf6d +a2 ceac +aa ce6c +76 9f3e +33 8f2d +22 8e2c +b6 cfb6 +be cf76 +b6 cf36 +fc df72 +bc cf72 +c1 788b +db 79cd +d3 798d +ea 76ce +eb 76cd +fa 75ce +e1 748b +fb 75cd +4e 3adc +4b 3acd +4a 3acc +43 3a8d +42 3a8c +41 388b +5b 39cd +53 398d +52 398c +6e 36dc +6b 36cd +6a 36cc +4b 98cf +b 88cf +61 348b +7b 35cd +cf f0fd +b5 efbb +b7 efb7 +bd ef7b +bf ef77 +9d eb73 +4f b07d +35 af3b +f5 dfbb +ff df77 +b6 efb4 +dd db73 +1f 81ff +b5 efb1 +cb d0ed +b1 cfab +b3 cfa7 +b9 cf6b +bb cf67 +b7 ef35 +b6 ef34 +a3 ee2f +bd ef71 +a2 ee2e +bc ef70 +75 9f3b +77 9f37 +33 8f27 +b7 cfb5 +b6 cfb4 +b5 cfb1 +b4 cfb0 +a5 ce33 +bf cf75 +b7 cf35 +b6 cf34 +a3 ce2f +bd cf71 +b5 cf31 +a2 ce2e +bc cf70 +b4 cf30 +4f 92df +6b 94ef +f1 ffa1 +5b 91ef +6f 94df +6b 94cf +f5 ff91 +5f 91df +f1 ff81 +5b 91cf +ce f0fc +b4 efba +b6 efb6 +b7 efb5 +bc ef7a +be ef76 +a5 ee33 +bf ef75 +f4 dfba +f5 dfb9 +fc df7a +fe df76 +ca f06c +b0 ef2a +ca d0ec +b0 cfaa +f0 df2a +b8 cf6a +ca d06c +b0 cf2a +1b 29cf +b 28cf +cd f0f1 +b3 efaf +b7 ef9f +bb ef6f +bf ef5f +fb df4f +37 af1f +73 9f0f +fb df6f +ff df5f +b0 efa8 +b7 cf9f +bf cf5f +bb cf4f +ab eec7 +b8 ef68 +a3 ee87 +b0 ef28 +73 9f2f +77 9f1f +37 8f1f +b1 cfa9 +f0 dfa8 +b0 cfa8 +b9 cf69 +b1 cf29 +ca f0cc +b0 ef8a +ce f05c +b4 ef1a +b8 ef4a +ca f04c +b0 ef0a +2b 26ef +2b 26cf +9b 61ef +8b 60ef +3b 25ef +2b 24ef +f5 5fb3 +1b 21ef +3b 25cf +2b 24cf +f5 5f93 +1b 21cf +cc f0f0 +b2 efae +1d 81fb +b3 efad +b6 ef9e +ba ef6e +a1 ee2b +bb ef6d +be ef5e +a5 ee1b +bf ef5d +aa ee6c +ae ee5c +fa df4e +e1 de0b +fb df4d +ea de4c +33 af2d +36 af1e +37 af1d +22 ae2c +26 ae1c +72 9f0e +fa df6e +e1 de2b +fb df6d +fe df5e +e5 de1b +ff df5d +ee de5c +b6 cf9e +b7 cf9d +b3 cf8d +be cf5e +a5 ce1b +bf cf5d +ba cf4e +a1 ce0b +bb cf4d +a6 ce9c +a2 ce8c +ae ce5c +aa ce4c +72 9f2e +76 9f1e +77 9f1d +66 9e1c +ca f0e4 +b0 efa2 +36 8f1e +37 8f1d +33 8f0d +26 8e1c +22 8e0c +b2 ef26 +b8 ef62 +b2 cfa6 +f0 dfa2 +ba cf66 +b2 cf26 +7f 3fdf +f8 df62 +3f 2fdf +b8 cf62 +b1 ef89 +b0 ef88 +6f 1ef7 +b1 ef09 +f4 df98 +b4 cf98 +f0 df88 +b0 cf88 +16 abbc +16 ab3c +17 a3bd +36 a7bc +16 a3bc +17 a33d +ef 7cf7 +36 a73c +cf 78f7 +16 a33c +4f 1adf +f adf +17 8bbd +16 8bbc +17 8b3d +16 8b3c +37 87bd +17 83bd +36 87bc +16 83bc +17 833d +ef 5cf7 +36 873c +cf 58f7 +16 833c +96 6bbc +16 2bbc +96 6b3c +16 2b3c +97 63bd +b6 67bc +96 63bc +36 27bc +16 23bc +17 233d +b6 673c +96 633c +36 273c +16 233c +cb 58cf +8b 48cf +5f 19df +5b 19cf +4f 18df +4b 18cf +1f 9df +1b 9cf +f 8df +b 8cf +97 4bbd +17 bbd +e7 f637 +d6 5bbc +a7 e637 +96 4bbc +67 b637 +56 1bbc +97 4b3d +17 b3d +d6 5b3c +96 4b3c +56 1b3c +16 b3c +d7 53bd +b7 47bd +97 43bd +37 7bd +17 3bd +f6 57bc +d6 53bc +b6 47bc +96 43bc +76 17bc +56 13bc +36 7bc +16 3bc +cb f0ed +b1 efab +b3 efa7 +cf f0dd +b5 ef9b +c4 f038 +b7 ef97 +b9 ef6b +bb ef67 +bd ef5b +bf ef57 +99 eb63 +9d eb53 +f1 df8b +fb df47 +d9 db43 +4b b06d +31 af2b +4f b05d +35 af1b +37 af17 +cf 705f +71 9f0b +73 9f07 +57 133d +37 73d +17 33d +f6 573c +d6 533c +b6 473c +96 433c +76 173c +56 133c +36 73c +16 33c +f1 dfab +f5 df9b +fb df67 +ff df57 +dd db53 +cf d0dd +b5 cf9b +cb d0cd +b1 cf8b +bf cf57 +bb cf47 +9d cb53 +99 cb43 +1b 81ef +b1 efa1 +cf 707f +71 9f2b +73 9f27 +75 9f1b +77 9f17 +b9 ef61 +b8 ef60 +37 8f17 +33 8f07 +b3 cfa5 +b2 cfa4 +f1 dfa1 +b1 cfa1 +f0 dfa0 +b0 cfa0 +a1 ce23 +bb cf65 +b3 cf25 +b2 cf24 +f1 df21 +b1 cf21 +ce f0d4 +b4 ef92 +ca f0c4 +b0 ef82 +b6 ef16 +bc ef52 +ba ef46 +b2 ef06 +b8 ef42 +b6 cf16 +fc df52 +bc cf52 +b2 cf06 +b8 cf42 +6b 16ef +2b 6ef +6f 16df +6b 16cf +4f 12df +2f 6df +2b 6cf +f 2df +db 51ef +cb 50ef +9b 41ef +8b 40ef +7b 15ef +6b 14ef +eb 54cf +df 51df +db 51cf +cf 50df +cb 50cf +ab 44cf +9f 41df +9b 41cf +8f 40df +8b 40cf +7f 15df +7b 15cf +6f 14df +6b 14cf +ca f0ec +b0 efaa +b1 efa9 +b2 efa6 +1d 81f3 +b3 efa5 +ce f0dc +b4 ef9a +dd f179 +b6 ef96 +b8 ef6a +ba ef66 +a1 ee23 +bb ef65 +bc ef5a +be ef56 +f0 df8a +f1 df89 +fa df46 +f0 dfaa +f1 dfa9 +f4 df9a +f5 df99 +f8 df6a +fa df66 +e1 de23 +fb df65 +fe df56 +ce d0dc +b4 cf9a +b5 cf99 +ca d0cc +b0 cf8a +b1 cf89 +be cf56 +ba cf46 +c6 d03c +ac cefa +b6 ef94 +1f 81df +b5 ef91 +1b 81cf +b1 ef81 +a5 ee13 +bf ef55 +b6 ef14 +a3 ee0f +bd ef51 +a2 ee0e +bc ef50 +a1 ee03 +bb ef45 +b9 ef41 +b8 ef40 +b7 cf95 +b6 cf94 +b5 cf91 +b4 cf90 +b3 cf85 +b2 cf84 +b1 cf81 +b0 cf80 +a5 ce13 +bf cf55 +b7 cf15 +b6 cf14 +a3 ce0f +bd cf51 +b5 cf11 +a2 ce0e +bc cf50 +b4 cf10 +a1 ce03 +bb cf45 +b3 cf05 +b2 cf04 +b9 cf41 +b1 cf01 +b8 cf40 +b0 cf00 +df 737f +de 737e +ce 727e +57 33bd +5e 337e +5a 336e +47 32bd +4e 327e +4f 327d +bf edff +ff ddff +bb cdef +ad cef9 +ac cef8 +bd cdf9 +ad ccf9 +ab ceed +c6 f034 +ac eef2 +bc edf2 +ac ecf2 +e5 dcbb +ff ddfd +a1 ccab +bb cded +ae cef6 +be cdf6 +ae ccf6 +fc ddf2 +ec dcf2 +bc cdf2 +ac ccf2 +3d 5fb +d3 73ad +d6 739e +d7 739d +de 735e +2d 4fb +c3 72ad +72 37ac +76 379c +53 33ad +52 33ac +42 32ac +c7 f03d +ad eefb +af eef7 +bf edf7 +ae ecf6 +9d e9f3 +c3 d02d +a9 ceeb +ab cee7 +a3 ecaf +bd edf1 +ff ddf7 +ee dcf6 +dd d9f3 +bb cde7 +99 c9e3 +af cef5 +ae cef4 +ad cef1 +ac cef0 +a5 ccb3 +bf cdf5 +e3 dcaf +fd ddf1 +a3 ccaf +bd cdf1 +c6 f03c +ac eefa +6b 1ee7 +ad eef9 +ae eef6 +be edf6 +a5 ecb3 +bf edf5 +fd ddf9 +e5 dcb3 +ff ddf5 +36 73e +ed dcf9 +d5 dbb3 +ef dcf5 +c2 d02c +a8 ceea +c5 f031 +ab eeef +bb edef +bf eddf +aa ecee +fb ddcf +e3 7607 +ea dcce +af cedf +fb ddef +ff dddf +bf cddf +bb cdcf +a9 cee9 +a8 cee8 +b9 cde9 +a9 cce9 +c2 f00c +a8 eeca +c4 f030 +aa eeee +ae eede +ba edee +a1 ecab +bb eded +f3 7707 +fa ddce +e1 dc8b +fb ddcd +ae cede +af cedd +ab cecd +e1 dcab +fb dded +e5 dc9b +ff dddd +a5 cc9b +bf cddd +a1 cc8b +bb cdcd +b8 ede2 +a8 ece2 +aa cee6 +e8 dee2 +ba cde6 +aa cce6 +f8 dde2 +e8 dce2 +b8 cde2 +a8 cce2 +c3 f02d +a9 eeeb +ab eee7 +bb ede7 +bd eddb +bf edd7 +aa ece6 +99 e9e3 +9d e9d3 +fb ddc7 +d9 d9c3 +bc cf78 +af ced7 +b8 cf68 +ab cec7 +fb dde7 +ea dce6 +d9 d9e3 +bb cdc7 +99 c9c3 +b9 ede1 +b8 ede0 +a8 ece0 +ab cee5 +aa cee4 +e9 dee1 +a9 cee1 +e8 dee0 +a8 cee0 +aa eec6 +a1 cca3 +bb cde5 +ae ecd6 +f9 dde1 +b9 cde1 +f8 dde0 +e8 dce0 +b8 cde0 +a8 cce0 +bc edd2 +ac ecd2 +ba edc6 +aa ecc6 +b8 edc2 +a8 ecc2 +ee dcd6 +ae ccd6 +ec dcd2 +ac ccd2 +ea dcc6 +aa ccc6 +f8 ddc2 +e8 dcc2 +b8 cdc2 +a8 ccc2 +c2 f02c +a8 eeea +aa eee6 +c6 f01c +ac eeda +ae eed6 +b8 edea +ba ede6 +a1 eca3 +bb ede5 +7b 1dc7 +bd edd9 +be edd6 +7a b74e +91 eba3 +ab ece5 +ae ced6 +aa cec6 +f9 dde9 +e1 dca3 +fb dde5 +d1 dba3 +eb dce5 +ba cdc6 +be ef7c +a4 ee3a +79 1f63 +ae eed4 +be cffc +a4 ceba +ac ce7a +be cf7c +a4 ce3a +92 eb8e +ac ecd0 +91 eb83 +ab ecc5 +a9 ecc1 +a8 ecc0 +bc cd7a +ac cc7a +be cd7c +a4 cc3a +af ced5 +ae ced4 +ad ced1 +ac ced0 +ab cec5 +aa cec4 +a9 cec1 +a8 cec0 +d5 db93 +ef dcd5 +95 cb93 +af ccd5 +e1 dc83 +fb ddc5 +d1 db83 +eb dcc5 +a1 cc83 +bb cdc5 +91 cb83 +ab ccc5 +f9 ddc1 +b9 cdc1 +a9 ccc1 +f8 ddc0 +e8 dcc0 +b8 cdc0 +a8 ccc0 +c7 729f +d3 71af +d2 71ae +d7 719f +df 715f +43 32ad +4e 325e +52 31ae +53 31ad +56 319e +5a 316e +41 302b +5b 316d +5e 315e +af ee7f +27 ae3f +b7 edbf +a6 ecbe +ae ec7e +37 ad3f +26 ac3e +d a0fb +e7 debf +ef de7f +bd cff1 +a3 ceaf +ab ce6f +67 9e3f +f7 ddbf +ff dd7f +e6 dcbe +ee dc7e +b3 cdaf +bb cd6f +bc cdf0 +a2 ccae +aa cc6e +77 9d3f +66 9c3e +c9 fae1 +33 8d2f +63 1c27 +a5 ec39 +a5 ceb9 +a4 ceb8 +e5 de39 +ad ce79 +a5 ce39 +ac ce78 +a4 ce38 +b5 cdb9 +a5 ccb9 +a4 ccb8 +3e 77e +f5 dd39 +b5 cd39 +a5 cc39 +af ccd7 +bc cd78 +ae ee7e +af ee7d +a4 ec3a +be ed7c +26 ae3e +27 ae3d +b7 edbd +a5 ec3b +bf ed7d +a7 ecbd +95 eb3b +af ec7d +36 ad3e +ee de7e +ef de7d +e4 dc3a +fe dd7c +bc cff0 +a2 ceae +a3 cead +aa ce6e +ab ce6d +66 9e3e +23 8e2d +f7 ddbd +fe dd7e +e5 dc3b +ff dd7d +e6 dcbc +d5 db3b +ef dc7d +b3 cdad +ba cd6e +a1 cc2b +bb cd6d +a3 ccad +a2 ccac +91 cb2b +ab cc6d +be eff4 +a4 eeb2 +a6 ee36 +ac ee72 +be ef74 +a4 ee32 +b4 edb2 +be edf4 +a4 ecb2 +a6 ec36 +bc ed72 +b4 ed32 +ac ec72 +a6 ceb6 +be cff4 +a4 ceb2 +ae ce76 +a6 ce36 +ec de72 +ac ce72 +be cf74 +a4 ce32 +b6 cdb6 +a6 ccb6 +f4 ddb2 +fe ddf4 +e4 dcb2 +b4 cdb2 +be cdf4 +a4 ccb2 +e6 dc36 +be cd76 +b6 cd36 +ae cc76 +a6 cc36 +fc dd72 +f4 dd32 +ec dc72 +bc cd72 +b4 cd32 +ac cc72 +e3 768d +ea 764e +f2 758e +f3 758d +fa 754e +e1 740b +fb 754d +c6 729e +c7 729d +ce 725e +ca 724e +d6 719e +d7 719d +d2 718e +d3 718d +de 715e +c5 701b +df 715d +63 368d +62 368c +6b 364d +73 358d +72 358c +61 340b +7b 354d +43 328d +42 328c +4f 325d +4e 325c +4b 324d +4a 324c +56 319c +53 318d +52 318c +45 301b +5f 315d +41 300b +5b 314d +bf effd +a5 eebb +a7 eeb7 +ad ee7b +af ee77 +27 ae37 +b7 edb7 +bd ed7b +a6 ecb6 +ae ec76 +9d e973 +35 ad3b +ff dffd +e5 debb +d a0f3 +e7 deb7 +ef de77 +cd da73 +bb cfed +a1 ceab +a3 cea7 +a9 ce6b +ab ce67 +89 ca63 +67 9e37 +23 8e27 +f7 ddb7 +fd dd7b +ff dd77 +e6 dcb6 +ec dc7a +ee dc76 +d5 d9b3 +dd d973 +b3 cda7 +b9 cd6b +bb cd67 +77 9d37 +33 8d27 +f 80ff +a5 eeb1 +a7 ee35 +ae ee74 +a6 ee34 +ad ee71 +ac ee70 +b5 edb1 +b4 edb0 +a4 ecb0 +9d ebf3 +b7 ed35 +8d eaf3 +a7 ec35 +a3 ec2f +bd ed71 +93 eb2f +ad ec71 +a2 ec2e +bc ed70 +92 eb2e +ac ec70 +a7 ceb5 +a6 ceb4 +a5 ceb1 +a4 ceb0 +af ce75 +a7 ce35 +ee de74 +ae ce74 +a6 ce34 +ed de71 +ad ce71 +a5 ce31 +ec de70 +ac ce70 +a4 ce30 +b7 cdb5 +b6 cdb4 +a6 ccb4 +b5 cdb1 +f4 ddb0 +e4 dcb0 +b4 cdb0 +a4 ccb0 +dd dbf3 +f7 dd35 +cd daf3 +e7 dc35 +a5 cc33 +bf cd75 +9d cbf3 +b7 cd35 +95 cb33 +af cc75 +8d caf3 +a7 cc35 +a4 cc32 +be cd74 +e3 dc2f +fd dd71 +d3 db2f +ed dc71 +a3 cc2f +bd cd71 +93 cb2f +ad cc71 +e2 dc2e +fc dd70 +d2 db2e +ec dc70 +a2 cc2e +bc cd70 +92 cb2e +ac cc70 +be effc +a4 eeba +a6 eeb6 +ac ee7a +ae ee76 +af ee75 +b6 edb4 +a4 ec32 +be ed74 +b6 edb6 +b7 edb5 +a5 ec33 +bf ed75 +76 b71e +a7 ecb5 +a6 ecb4 +95 eb33 +af ec75 +e5 deb9 +ee de76 +ef de75 +f6 ddb4 +ef dcd7 +fc dd78 +e4 dc32 +fe dd74 +3e 7fe +f5 ddb9 +f7 ddb5 +fc dd7a +e5 dc33 +ff dd75 +e4 dcb8 +e7 dcb5 +e6 dcb4 +d5 db33 +ef dc75 +bd eff1 +a3 eeaf +ab ee6f +af ee5f +eb de4f +27 ae1f +b3 edaf +b7 ed9f +bb ed6f +bc edf0 +a2 ecae +a6 ec9e +aa ec6e +ae ec5e +fb dd4f +ea dc4e +33 ad2f +26 ac1e +73 9d0f +eb de6f +ef de5f +a7 ce9f +bd cfd1 +a3 ce8f +af ce5f +ab ce4f +67 9e1f +27 8e1f +f3 ddaf +fb dd6f +ff dd5f +fc ddf0 +e2 dcae +ea dc6e +ee dc5e +bf cd5f +bb cd4f +ae cc5e +aa cc4e +73 9d2f +77 9d1f +66 9c1e +cd fad1 +37 8d1f +c9 fac1 +33 8d0f +26 8c1e +bc eff0 +a2 eeae +d 80fb +a3 eead +a6 ee9e +72 1f2c +a7 ee9d +aa ee6e +ab ee6d +ae ee5e +af ee5d +b6 ed9c +ea de4e +eb de4d +23 ae2d +26 ae1e +27 ae1d +b2 edae +b3 edad +ba ed6e +a1 ec2b +bb ed6d +a5 ec1b +bf ed5d +a3 ecad +a2 ecac +91 eb2b +ab ec6d +95 eb1b +af ec5d +fa dd4e +5b 3be7 +e1 dc0b +fb dd4d +e2 dc8c +d1 db0b +4b 3ae7 +eb dc4d +32 ad2e +36 ad1e +6b 3647 +72 9d0e +ea de6e +eb de6d +ee de5e +ef de5d +a6 ce9e +a7 ce9d +bc cfd0 +a2 ce8e +a3 ce8d +ae ce5e +af ce5d +aa ce4e +ab ce4d +66 9e1e +67 9e1d +26 8e1e +27 8e1d +23 8e0d +f7 dd9d +fa dd6e +e1 dc2b +fb dd6d +fe dd5e +5f 3bf7 +e5 dc1b +ff dd5d +e2 dcac +e7 dc9d +e6 dc9c +d1 db2b +eb dc6d +d5 db1b +4f 3af7 +ef dc5d +b7 cd9d +b3 cd8d +be cd5e +1f 2bf7 +a5 cc1b +bf cd5d +1b 2be7 +a1 cc0b +bb cd4d +a7 cc9d +a6 cc9c +a3 cc8d +a2 cc8c +95 cb1b +f 2af7 +af cc5d +bb efed +a1 eeab +a3 eea7 +a9 ee6b +ab ee67 +ad ee5b +af ee57 +89 ea63 +fb dfcd +e1 de8b +eb de47 +c9 da43 +23 ae27 +27 ae17 +63 9e07 +b3 eda7 +b5 ed9b +b7 ed97 +b9 ed6b +bb ed67 +bd ed5b +7a b54e +91 e9a3 +99 e963 +9d e953 +fb dd47 +d1 d983 +d9 d943 +35 ad1b +37 ad17 +73 9d07 +fb dfed +e1 deab +ff dfdd +e5 de9b +eb de67 +ef de57 +c9 da63 +cd da53 +bf cfdd +a5 ce9b +b4 cf38 +a7 ce97 +bb cfcd +a1 ce8b +b0 cf28 +a3 ce87 +af ce57 +ab ce47 +8d ca53 +89 ca43 +63 9e27 +67 9e17 +27 8e17 +23 8e07 +f3 dda7 +f9 dd6b +fb dd67 +ff dd57 +d1 d9a3 +dd d953 +bf cd57 +bb cd47 +91 c983 +9d c953 +99 c943 +73 9d27 +77 9d17 +33 8d07 +bc c7fa +b6 c53c +9c c3fa +bf e7ff +b7 e53d +9d e3fb +b5 cd31 +9b cbef +ff d7ff +f7 d53d +dd d3fb +bb c7ef +b3 c52d +99 c3eb +9d cbf9 +3f 77f +dc dbf8 +9c cbf8 +bd c7f9 +9d c3f9 +fc d7f8 +dc d3f8 +bc c7f8 +9c c3f8 +8e eafc +be e7fe +ae e6fc +b6 e53c +9c e3fa +5b 13e7 +9d e3f9 +ce dafc +b4 cd30 +9a cbee +8a caec +fe d7fe +ee d6fc +f6 d53c +dc d3fa +dd d3f9 +ba c7ee +aa c6ec +b6 ed34 +9c ebf2 +9e cbf6 +f6 dd34 +dc dbf2 +b6 cd34 +9c cbf2 +be c7f6 +9e c3f6 +bc c7f2 +b7 ed3d +9d ebfb +9f ebf7 +bd e7f3 +bd e7fb +bf e7f7 +9f e3f7 +f7 dd3d +dd dbfb +df dbf7 +fd d7f3 +b3 cd2d +99 cbeb +9b cbe7 +b9 c7e3 +fd d7fb +ff d7f7 +df d3f7 +b9 c7eb +bb c7e7 +9b c3e7 +82 e2ae +9c e3f0 +a5 c6b3 +bf c7f5 +85 c2b3 +9f c3f5 +83 c2af +9d c3f1 +c2 d2ae +dc d3f0 +82 c2ae +9c c3f0 +b6 ed3c +9c ebfa +5b 1be7 +9d ebf9 +9e ebf6 +bc e7f2 +bc e7fa +7b 17e7 +bd e7f9 +be e7f6 +9e e3f6 +83 e2af +9d e3f1 +de dbf6 +c5 dab3 +df dbf5 +fc d7f2 +fc d7fa +fd d7f9 +fe d7f6 +de d3f6 +c3 d2af +dd d3f1 +b8 c7ea +b2 c52c +98 c3ea +b5 ed31 +9b ebef +bb e7ef +bf e7df +b3 e52d +99 e3eb +fb d7cf +df dbdf +9f cbdf +fb d7ef +ff d7df +f3 d52d +d9 d3eb +bf c7df +bb c7cf +b8 e7ca +b2 e50c +98 e3ca +b8 e7e8 +98 e3e8 +99 cbe9 +98 cbe8 +b9 c7e9 +99 c3e9 +f8 d7e8 +d8 d3e8 +b8 c7e8 +98 c3e8 +b4 ed30 +9a ebee +9e ebde +8a eaec +ba e7ee +be e7de +aa e6ec +b2 e52c +98 e3ea +99 e3e9 +b6 e51c +9c e3da +fa d7ce +ea d6cc +f4 dd30 +da dbee +de dbde +ce dadc +9e cbde +8e cadc +8a cacc +fa d7ee +fe d7de +ea d6ec +ee d6dc +f2 d52c +d8 d3ea +d9 d3e9 +be c7de +ba c7ce +ae c6dc +aa c6cc +9a cbe6 +ba c7e6 +9a c3e6 +b8 c7e2 +1e a35e +1a a34e +56 939e +5a 936e +1a 836e +a9 c463 +16 239e +56 933c +9a 634e +cd f8d9 +16 231e +b3 ed2d +99 ebeb +9b ebe7 +b9 e7e3 +bd e7d3 +f9 d7c3 +b9 e7eb +bb e7e7 +bd e7db +bf e7d7 +9b e3e7 +fb d7c7 +db dbe7 +ec dc78 +df dbd7 +f9 d7e3 +fd d7d3 +ac cc78 +9f cbd7 +96 439e +56 139e +16 39e +bd c7d3 +b9 c7c3 +f9 d7eb +fb d7e7 +ff d7d7 +db d3e7 +bf c7d7 +bb c7c7 +9a 436e +9a ebc6 +da 534e +d6 531e +ba e7c6 +9a e3c6 +b8 e7c2 +98 ebe0 +b8 e7e0 +98 e3e0 +98 cbe0 +b9 c7e1 +99 c3e1 +f8 d7e0 +d8 d3e0 +b8 c7e0 +98 c3e0 +99 ebe9 +9a ebe6 +6a b64e +81 eaa3 +9b ebe5 +b6 ed1c +9c ebda +9e ebd6 +b8 e7e2 +b9 e7e1 +bc e7d2 +da dbc6 +f8 d7c2 +b8 e7ea +b9 e7e9 +ba e7e6 +bc e7da +7b 17c7 +bd e7d9 +be e7d6 +9a e3e6 +99 e3e1 +9e e3d6 +fa d7c6 +da d3c6 +da dbe6 +c1 daa3 +db dbe5 +de dbd6 +f8 d7e2 +f9 d7e1 +fc d7d2 +9e cbd6 +9a cbc6 +bc c7d2 +b8 c7c2 +f8 d7ea +f9 d7e9 +fa d7e6 +fe d7d6 +da d3e6 +d9 d3e1 +de d3d6 +be c7d6 +ba c7c6 +9e c3d6 +9a c3c6 +82 ea8e +9c ebd0 +81 ea83 +9b ebc5 +99 ebc1 +98 ebc0 +a3 e68f +bd e7d1 +a2 e68e +bc e7d0 +82 e28e +9c e3d0 +a1 e683 +bb e7c5 +81 e283 +9b e3c5 +b9 e7c1 +99 e3c1 +b8 e7c0 +98 e3c0 +c5 da93 +df dbd5 +85 ca93 +9f cbd5 +c1 da83 +db dbc5 +81 ca83 +9b cbc5 +ae ec7c +94 eb3a +99 cbc1 +98 cbc0 +c3 d28f +dd d3d1 +83 c28f +9d c3d1 +c2 d28e +dc d3d0 +82 c28e +9c c3d0 +f9 d7c1 +d9 d3c1 +b9 c7c1 +b4 e73a +99 c3c1 +f8 d7c0 +d8 d3c0 +b8 c7c0 +98 c3c0 +ee dc7c +d4 db3a +9c cb7a +ae cc7c +94 cb3a +b4 c7ba +ae c4fc +94 c3ba +f4 d73a +bc c77a +b4 c73a +9c c37a +ae c47c +94 c33a +8b 68ef +ab 64ef +d a8db +d a8d3 +9 a8c3 +9f eb7f +17 ab3f +b7 e7bf +bf e77f +9d e37b +37 a73f +df db7f +9b cb6f +57 9b3f +f7 d7bf +ff d77f +dd d37b +b3 c7af +bb c76f +99 c36b +77 973f +33 872f +95 cbb9 +37 73f +d4 dbb8 +94 cbb8 +d5 db39 +9d cb79 +95 cb39 +b5 c7b9 +95 c3b9 +f4 d7b8 +d4 d3b8 +b4 c7b8 +94 c3b8 +bd c779 +b5 c739 +9d c379 +95 c339 +29 a4eb +fd dff1 +e3 deaf +9 a0eb +29 a4e3 +e3 dea7 +9 a0e3 +2d a4db +29 a4cb +e7 de9f +d a0db +fd dfd1 +e3 de8f +9 a0cb +2d a4d3 +29 a4c3 +f4 df38 +e7 de97 +d a0d3 +f0 df28 +e3 de87 +9 a0c3 +97 ebbd +9e eb7e +85 ea3b +9f eb7d +86 eabc +8e ea7c +16 ab3e +17 ab3d +b6 e7be +b7 e7bd +be e77e +a5 e63b +bf e77d +a6 e6bc +ae e67c +9c e37a +36 a73e +de db7e +c5 da3b +df db7d +ce da7c +ac ccf0 +92 cbae +9a cb6e +81 ca2b +9b cb6d +82 caac +8a ca6c +56 9b3e +f6 d7be +f7 d7bd +fe d77e +e5 d63b +ff d77d +e6 d6bc +ee d67c +d5 d3b9 +dc d37a +dd d379 +b2 c7ae +b3 c7ad +ba c76e +a1 c62b +bb c76d +a2 c6ac +aa c66c +76 973e +32 872e +ae ecf4 +94 ebb2 +96 eb36 +9c eb72 +ae ec74 +94 eb32 +96 cbb6 +ee dcf4 +d4 dbb2 +ae ccf4 +94 cbb2 +d6 db36 +9e cb76 +96 cb36 +dc db72 +ee dc74 +d4 db32 +9c cb72 +ae cc74 +94 cb32 +b6 c7b6 +96 c3b6 +b4 c7b2 +be c776 +b6 c736 +9e c376 +96 c336 +f4 d732 +bc c772 +b4 c732 +9c c372 +4d 98d3 +49 98c3 +d 88d3 +9 88c3 +af ecfd +95 ebbb +97 ebb7 +9d eb7b +9f eb77 +b5 e7b3 +b5 e7bb +b7 e7b7 +bd e77b +97 e3b7 +d7 dbb7 +dd db7b +df db77 +f5 d7b3 +fd d773 +ab cced +91 cbab +99 cb6b +9b cb67 +b1 c7a3 +13 8b27 +31 8723 +f5 d7bb +f7 d7b7 +fd d77b +d7 d3b7 +df d377 +dd d373 +b1 c7ab +b3 c7a7 +b9 c76b +bb c767 +93 c3a7 +9b c367 +57 9337 +33 8727 +13 8327 +97 eb35 +96 eb34 +83 ea2f +9d eb71 +95 eb31 +82 ea2e +9c eb70 +94 eb30 +b6 e7b4 +96 e3b4 +b4 e7b0 +94 e3b0 +b7 e735 +97 e335 +b5 e731 +95 e331 +a2 e62e +bc e770 +b4 e730 +82 e22e +9c e370 +94 e330 +d6 dbb4 +d5 dbb1 +37 737 +d4 dbb0 +d7 db35 +85 ca33 +9f cb75 +97 cb35 +d6 db34 +96 cb34 +c3 da2f +dd db71 +d5 db31 +83 ca2f +9d cb71 +95 cb31 +c2 da2e +dc db70 +d4 db30 +82 ca2e +9c cb70 +94 cb30 +b7 c7b5 +97 c3b5 +f6 d7b4 +d6 d3b4 +b6 c7b4 +96 c3b4 +b5 c7b1 +95 c3b1 +f4 d7b0 +d4 d3b0 +b4 c7b0 +94 c3b0 +f7 d735 +d7 d335 +a5 c633 +bf c775 +b7 c735 +85 c233 +9f c375 +97 c335 +f6 d734 +d6 d334 +b6 c734 +96 c334 +a3 c62f +bd c771 +b5 c731 +83 c22f +9d c371 +95 c331 +e2 d62e +fc d770 +f4 d730 +c2 d22e +dc d370 +d4 d330 +a2 c62e +bc c770 +b4 c730 +82 c22e +9c c370 +94 c330 +69 94eb +49 90eb +29 84eb +9 80eb +6b 94e7 +69 94e3 +4b 90e7 +49 90e3 +2b 84e7 +29 84e3 +9 80e3 +6d 94db +69 94cb +e3 fe8d +4d 90db +49 90cb +c3 f28d +2d 84db +29 84cb +a3 ee8d +d 80db +9 80cb +6d 94d3 +69 94c3 +49 90c3 +29 84c3 +9 80c3 +96 ebb6 +66 b61e +97 ebb5 +9c eb7a +9e eb76 +85 ea33 +9f eb75 +b4 e7b2 +b5 e7b1 +bc e772 +a3 e62f +bd e771 +b4 e7ba +b6 e7b6 +b7 e7b5 +bc e77a +ad 4cfb +be e776 +96 e3b6 +97 e3b5 +95 e3b1 +8d 48fb +9e e376 +85 e233 +9f e375 +9c e372 +83 e22f +9d e371 +d6 dbb6 +d7 dbb5 +dc db7a +dd db79 +de db76 +c5 da33 +df db75 +f4 d7b2 +f5 d7b1 +fc d772 +e3 d62f +fd d771 +f4 d7ba +f5 d7b9 +f6 d7b6 +f7 d7b5 +fc d77a +fd d779 +e5 d633 +ff d775 +d6 d3b6 +d7 d3b5 +d5 d3b1 +c5 d233 +df d375 +dc d372 +c3 d22f +dd d371 +aa ec6c +90 eb2a +b0 e72a +ea dc6c +d0 db2a +98 cb6a +aa cc6c +90 cb2a +b0 c7aa +f0 d72a +b8 c76a +b0 c72a +98 c36a +91 cb0b +ab cc4d +b 2ae7 +89 68eb +8b 68e7 +89 68e3 +1d 29db +19 29cb +d 28db +9 28cb +1f 29d7 +1d 29d3 +1b 29c7 +19 29c3 +d 28d3 +9 28c3 +9b eb6f +9f eb5f +db db4f +17 ab1f +b3 e7af +b7 e79f +bf e75f +f3 d78f +33 a72f +73 970f +d7 db9f +db db6f +df db5f +97 cb9f +ad ccd1 +93 cb8f +9f cb5f +9b cb4f +57 9b1f +17 8b1f +f3 d7af +f7 d79f +fb d76f +ff d75f +d9 d36b +b7 c79f +b3 c78f +d6 fbb4 +bf c75f +73 972f +77 971f +ae ec5c +94 eb1a +98 eb4a +aa ec4c +90 eb0a +b0 e78a +aa e4cc +90 e38a +b4 e71a +b8 e74a +b0 e70a +98 e34a +aa e44c +90 e30a +91 eb29 +8b eac7 +98 eb68 +83 ea87 +90 eb28 +b0 e7a8 +90 e3a8 +b1 e729 +91 e329 +ab e6c7 +b8 e768 +a3 e687 +b0 e728 +8b e2c7 +98 e368 +83 e287 +90 e328 +91 cba9 +90 cba8 +d1 db29 +99 cb69 +91 cb29 +b1 c7a9 +91 c3a9 +f0 d7a8 +d0 d3a8 +b0 c7a8 +90 c3a8 +f1 d729 +d1 d329 +b9 c769 +b1 c729 +99 c369 +91 c329 +29 26eb +2d 26db +29 26cb +a9 64eb +89 60eb +39 25eb +29 24eb +ab 64e7 +a9 64e3 +8b 60e7 +89 60e3 +3b 25e7 +39 25e3 +b1 c50b +2b 24e7 +29 24e3 +1b 21e7 +91 c10b +b 20e7 +3d 25db +39 25cb +2d 24db +29 24cb +3f 25d7 +3d 25d3 +3b 25c7 +39 25c3 +2d 24d3 +29 24c3 +ac ecf0 +92 ebae +9a eb6e +81 ea2b +9b eb6d +9e eb5e +85 ea1b +9f eb5d +82 eaac +8a ea6c +8e ea5c +ec dcd0 +d2 db8e +da db4e +c1 da0b +db db4d +ca da4c +16 ab1e +b2 e7ae +b3 e7ad +b6 e79e +a1 e62b +bb e76d +be e75e +a2 e6ac +aa e66c +2f acff +ae e65c +91 e3a9 +99 e369 +9c e35a +f2 d78e +f3 d78d +fa d74e +e2 d68c +32 a72e +ec dcf0 +d2 dbae +d6 db9e +d7 db9d +da db6e +c1 da2b +db db6d +de db5e +c5 da1b +df db5d +c6 da9c +ca da6c +ce da5c +96 cb9e +97 cb9d +ac ccd0 +92 cb8e +93 cb8d +9e cb5e +85 ca1b +9f cb5d +9a cb4e +81 ca0b +9b cb4d +86 ca9c +82 ca8c +8e ca5c +8a ca4c +56 9b1e +16 8b1e +f2 d7ae +f3 d7ad +f6 d79e +f7 d79d +fa d76e +e1 d62b +fb d76d +fe d75e +e2 d6ac +e6 d69c +ea d66c +6f 9cff +ee d65c +d1 d3a9 +d8 d36a +d9 d369 +b6 c79e +b7 c79d +b2 c78e +b3 c78d +d5 fbb3 +ef fcf5 +be c75e +26 8cbe +a5 c61b +bf c75d +d1 fba3 +eb fce5 +ba c74e +a6 c69c +a2 c68c +2f 8cff +c5 fab1 +ae c65c +72 972e +a7 ccb5 +76 971e +91 eb89 +4f 1af7 +91 eb09 +b1 e789 +91 e389 +b0 e788 +90 e388 +94 cb98 +90 cb88 +d5 d399 +95 c399 +f4 d798 +d4 d398 +b4 c798 +94 c398 +d1 d389 +91 c389 +f0 d788 +d0 d388 +b0 c788 +90 c388 +aa ece4 +90 eba2 +92 eb26 +98 eb62 +92 cba6 +ea dce4 +d0 dba2 +aa cce4 +90 cba2 +d2 db26 +9a cb66 +92 cb26 +5f 3bdf +d8 db62 +1f 2bdf +98 cb62 +b2 c7a6 +92 c3a6 +b0 c7a2 +f2 d726 +d2 d326 +ba c766 +b2 c726 +9a c366 +92 c326 +3f 27df +b8 c762 +1f 23df +98 c362 +16 ab9c +12 ab8c +13 ab2d +12 ab2c +17 ab1d +13 ab0d +16 ab1c +12 ab0c +ef 7c5f +b ae7 +32 a7ac +13 a32d +eb 7ce7 +32 a72c +cb 78e7 +12 a32c +12 8bac +56 9b9c +ed fc73 +16 8b9c +e9 fc63 +12 8b8c +13 8b2d +12 8b2c +57 9b1d +17 8b1d +13 8b0d +56 9b1c +16 8b1c +12 8b0c +13 83ad +32 87ac +12 83ac +57 939d +97 639f +53 938d +76 979c +56 939c +13 832d +96 633e +52 932c +eb 5ce7 +32 872c +76 971c +56 931c +96 631e +52 930c +92 6b8c +12 2b8c +92 6b2c +12 2b2c +96 6b1c +92 6b0c +16 2b1c +12 2b0c +89 48e3 +da f346 +c9 58cb +92 63ac +93 638d +13 238d +b2 678c +96 639c +92 638c +32 278c +12 238c +93 632d +13 232d +b2 672c +92 632c +32 272c +12 232c +17 231d +13 230d +36 271c +32 270c +16 231c +12 230c +8d 48d3 +89 48c3 +5f 19d7 +5d 19d3 +59 19c3 +4d 18d3 +49 18c3 +1f 9d7 +1d 9d3 +1b 9c7 +19 9c3 +d 8d3 +9 8c3 +a3 e627 +92 4bac +e3 f607 +d2 5b8c +a7 e617 +96 4b9c +a3 e607 +92 4b8c +63 b607 +52 1b8c +d2 5b2c +92 4b2c +12 b2c +d6 5b1c +d2 5b0c +96 4b1c +92 4b0c +56 1b1c +52 1b0c +16 b1c +12 b0c +ab eced +91 ebab +99 eb6b +9b eb67 +9d eb5b +9f eb57 +b1 e7a3 +b5 e793 +db db47 +f1 d783 +17 ab17 +53 9b07 +71 9703 +b1 e7ab +b3 e7a7 +b5 e79b +b7 e797 +93 e3a7 +f1 d78b +f3 d787 +fb d747 +db d347 +71 970b +73 9707 +53 9307 +d3 dba7 +ef dcdd +d5 db9b +e4 dc38 +d7 db97 +d9 db6b +db db67 +df db57 +f1 d7a3 +f5 d793 +af ccdd +95 cb9b +a4 cc38 +97 cb97 +ab cccd +91 cb8b +9f cb57 +9b cb47 +b5 c793 +b1 c783 +93 43ad +13 3ad +57 9b17 +75 9713 +17 8b17 +13 8b07 +31 8703 +d2 53ac +b2 47ac +92 43ac +32 7ac +12 3ac +f1 d7ab +f3 d7a7 +f5 d79b +f7 d797 +f9 d76b +fb d767 +d3 d3a7 +db d367 +46 329e +d9 d363 +df d357 +b5 c79b +b7 c797 +b1 c78b +b3 c787 +bf c757 +bb c747 +9f c357 +9b c347 +d3 538d +97 439d +93 438d +53 138d +75 971b +77 9717 +53 9327 +57 9317 +fd 5d73 +37 8717 +f9 5d63 +33 8707 +f2 578c +d6 539c +d2 538c +b6 479c +b2 478c +96 439c +92 438c +72 178c +52 138c +36 79c +32 78c +ed 7473 +16 39c +e9 7463 +12 38c +79 1d63 +ae ecd4 +94 eb92 +aa ecc4 +90 eb82 +96 eb16 +9c eb52 +9a eb46 +92 eb06 +98 eb42 +d3 532d +93 432d +53 132d +13 32d +f2 572c +d2 532c +b2 472c +92 432c +87 e29d +52 132c +32 72c +12 32c +b2 e786 +b0 e782 +a9 4ccb +ba e746 +89 48cb +9a e346 +b8 e742 +98 e342 +ee dcd4 +d4 db92 +ae ccd4 +94 cb92 +ea dcc4 +d0 db82 +aa ccc4 +90 cb82 +d6 db16 +96 cb16 +dc db52 +9c cb52 +d2 db06 +92 cb06 +d8 db42 +98 cb42 +57 131d +53 130d +17 31d +13 30d +37 dbf +b6 471c +76 171c +72 170c +56 131c +52 130c +36 71c +32 70c +16 31c +12 30c +b6 c716 +96 c316 +f4 d712 +b4 c712 +f2 d706 +d2 d306 +b2 c706 +92 c306 +f0 d702 +b0 c702 +7b b74f +92 eba4 +79 b74b +90 eba0 +93 eb25 +92 eb24 +99 eb61 +91 eb21 +98 eb60 +90 eb20 +b2 e7a4 +92 e3a4 +b0 e7a0 +90 e3a0 +b3 e725 +93 e325 +b1 e721 +91 e321 +b8 e760 +b0 e720 +98 e360 +90 e320 +d2 dba4 +d3 db25 +81 ca23 +9b cb65 +93 cb25 +d2 db24 +92 cb24 +d1 db21 +91 cb21 +b3 c7a5 +93 c3a5 +f2 d7a4 +d2 d3a4 +b2 c7a4 +92 c3a4 +b1 c7a1 +91 c3a1 +f0 d7a0 +d0 d3a0 +b0 c7a0 +90 c3a0 +f3 d725 +d3 d325 +a1 c623 +bb c765 +fe fdfe +b3 c725 +81 c223 +9b c365 +de f9fe +93 c325 +f2 d724 +d2 d324 +b2 c724 +92 c324 +f1 d721 +d1 d321 +fc fdfa +b1 c721 +dc f9fa +91 c321 +57 339d +d0 d320 +69 16eb +29 6eb +69 16e3 +2b 6e7 +29 6e3 +ef 745f +b 2e7 +6d 16db +69 16cb +2d 6db +29 6cb +69 16c3 +2d 6d3 +29 6c3 +c9 50eb +89 40eb +79 15eb +69 14eb +59 11eb +49 10eb +39 5eb +29 4eb +19 1eb +9 eb +e9 54e3 +cb 50e7 +c9 50e3 +a9 44e3 +8b 40e7 +89 40e3 +79 15e3 +69 14e3 +59 11e3 +49 10e3 +3b 5e7 +39 5e3 +2b 4e7 +29 4e3 +1b 1e7 +19 1e3 +b e7 +9 e3 +e9 54cb +cd 50db +c9 50cb +a9 44cb +8d 40db +89 40cb +7d 15db +79 15cb +6d 14db +69 14cb +f3 7f8d +5d 11db +59 11cb +e3 7e8d +4d 10db +49 10cb +d3 738d +3d 5db +39 5cb +c3 728d +2d 4db +29 4cb +19 1cb +9 cb +ed 54d3 +e9 54c3 +cd 50d3 +c9 50c3 +ad 44d3 +a9 44c3 +8d 40d3 +89 40c3 +7f 15d7 +7d 15d3 +79 15c3 +6d 14d3 +69 14c3 +59 11c3 +49 10c3 +3b 5c7 +39 5c3 +29 4c3 +1b 1c7 +19 1c3 +9 c3 +91 eba9 +92 eba6 +98 eb6a +99 eb69 +9a eb66 +81 ea23 +9b eb65 +9c eb5a +9e eb56 +b0 e7a2 +b1 e7a1 +b4 e792 +b8 e762 +b9 e761 +bc e752 +da db46 +f0 d782 +f8 d742 +b0 e7aa +b1 e7a9 +b2 e7a6 +b3 e7a5 +b4 e79a +b6 e796 +b9 e769 +a9 4ceb +ba e766 +bc e75a +ad 4cdb +be e756 +92 e3a6 +93 e3a5 +91 e3a1 +81 e223 +9b e365 +98 e362 +99 e361 +9c e352 +f0 d78a +f1 d789 +f2 d786 +d8 d342 +d2 dba6 +d3 dba5 +d5 db99 +fd dd79 +d6 db96 +d8 db6a +d9 db69 +da db66 +c1 da23 +db db65 +de db56 +f0 d7a2 +f1 d7a1 +f4 d792 +7f 37df +f8 d762 +fc d752 +95 cb99 +bd cd79 +96 cb96 +91 cb89 +9e cb56 +9a cb46 +b4 c792 +b0 c782 +d3 fba7 +bc c752 +b8 c742 +f0 d7aa +f1 d7a9 +f2 d7a6 +f3 d7a5 +f4 d79a +f5 d799 +f6 d796 +f8 d76a +f9 d769 +fa d766 +e1 d623 +fb d765 +d2 d3a6 +d3 d3a5 +d1 d3a1 +da d366 +c1 d223 +db d365 +5f 33df +d8 d362 +46 329c +d9 d361 +dc d352 +b4 c79a +b5 c799 +b6 c796 +b0 c78a +b1 c789 +b2 c786 +be c756 +ba c746 +9e c356 +9c c352 +9a c346 +98 c342 +93 eb85 +92 eb84 +91 eb81 +90 eb80 +85 ea13 +9f eb55 +96 eb14 +83 ea0f +9d eb51 +95 eb11 +82 ea0e +9c eb50 +94 eb10 +81 ea03 +9b eb45 +93 eb05 +92 eb04 +99 eb41 +91 eb01 +98 eb40 +90 eb00 +b6 e794 +b5 e791 +b4 e790 +b3 e785 +93 e385 +b2 e784 +92 e384 +b1 e781 +91 e381 +b0 e780 +90 e380 +3d adf3 +a2 e60e +bc e750 +35 adb3 +b4 e710 +b3 e705 +93 e305 +39 ade3 +b8 e740 +97 cb95 +96 cb94 +d5 db91 +95 cb91 +94 cb90 +93 cb85 +92 cb84 +d1 db81 +91 cb81 +90 cb80 +c5 da13 +df db55 +d7 db15 +85 ca13 +9f cb55 +97 cb15 +d6 db14 +96 cb14 +c3 da0f +dd db51 +d5 db11 +83 ca0f +9d cb51 +95 cb11 +c2 da0e +dc db50 +d4 db10 +82 ca0e +9c cb50 +94 cb10 +c1 da03 +db db45 +d3 db05 +81 ca03 +9b cb45 +93 cb05 +d2 db04 +92 cb04 +d9 db41 +d1 db01 +99 cb41 +91 cb01 +d8 db40 +d0 db00 +98 cb40 +90 cb00 +f7 d795 +d7 d395 +b7 c795 +97 c395 +f6 d794 +d6 d394 +b6 c794 +96 c394 +f5 d791 +d5 d391 +b5 c791 +95 c391 +f4 d790 +d4 d390 +b4 c790 +94 c390 +f3 d785 +d3 d385 +b3 c785 +93 c385 +f2 d784 +d2 d384 +b2 c784 +92 c384 +f1 d781 +d1 d381 +b1 c781 +91 c381 +f0 d780 +d0 d380 +b0 c780 +90 c380 +f7 d715 +d7 d315 +b7 c715 +97 c315 +7d 9df3 +e2 d60e +fc d750 +3d 8df3 +a2 c60e +d3 fba5 +bc c750 +82 c20e +1d 89f3 +9c c350 +f3 d705 +d3 d305 +fe fdde +b3 c705 +de f9de +93 c305 +79 9de3 +f8 d740 +39 8de3 +b8 c740 +a6 cc3c +8c cafa +ac c6fa +a6 c43c +8c c2fa +bc c5fa +ac c4fa +9c c1fa +8c c0fa +9f e9ff +a7 e43d +8d e2fb +bf e5ff +ae e4fe +9d e1fb +8c e0fa +a5 cc31 +8b caef +df d9ff +9b c9ef +e7 d43d +cd d2fb +ab c6ef +a3 c42d +89 c2eb +ff d5ff +ee d4fe +dd d1fb +cc d0fa +bb c5ef +aa c4ee +99 c1eb +8d caf9 +8c caf8 +9d c9f9 +8d c8f9 +ad c6f9 +8d c2f9 +ec d6f8 +cc d2f8 +ac c6f8 +8c c2f8 +bd c5f9 +ad c4f9 +9d c1f9 +8d c0f9 +bc c5f8 +ac c4f8 +9c c1f8 +8c c0f8 +8f eafd +8f e8fd +ae e6fe +a6 e43c +8c e2fa +4b 12e7 +8d e2f9 +be e5fe +a5 e4bb +bf e5fd +95 e3bb +af e4fd +94 e3ba +ae e4fc +9c e1fa +5b 11e7 +9d e1f9 +4b 10e7 +8d e0f9 +cf dafd +a4 cc30 +8a caee +8b caed +81 c8ab +9b c9ed +8b c8ed +ee d6fe +e6 d43c +cc d2fa +dc d1f8 +aa c6ee +ab c6ed +fe d5fe +e5 d4bb +ff d5fd +d5 d3bb +ef d4fd +d4 d3ba +ee d4fc +dc d1fa +dd d1f9 +cd d0f9 +cc d0f8 +ba c5ee +a1 c4ab +bb c5ed +91 c3ab +ab c4ed +90 c3aa +aa c4ec +a6 ec34 +8c eaf2 +9c e9f2 +8c e8f2 +8e caf6 +e6 dc34 +cc daf2 +a6 cc34 +8c caf2 +9e c9f6 +8e c8f6 +dc d9f2 +cc d8f2 +9c c9f2 +8c c8f2 +ae c6f6 +8e c2f6 +ac c6f2 +be c5f6 +ae c4f6 +9e c1f6 +bc c5f2 +ac c4f2 +9c c1f2 +8c c0f2 +a7 ec3d +8d eafb +8f eaf7 +ad e6f3 +9f e9f7 +8e e8f6 +bd e5f3 +ac e4f2 +ad e6fb +af e6f7 +8f e2f7 +bd e5fb +bf e5f7 +ac e4fa +ae e4f6 +9f e1f7 +9d e1f3 +8c e0f2 +e7 dc3d +cd dafb +cf daf7 +ed d6f3 +a3 cc2d +89 caeb +8b cae7 +a9 c6e3 +df d9f7 +ce d8f6 +fd d5f3 +ec d4f2 +9b c9e7 +b9 c5e3 +ed d6fb +ef d6f7 +cf d2f7 +a9 c6eb +ab c6e7 +8b c2e7 +fd d5fb +ff d5f7 +ec d4fa +ee d4f6 +df d1f7 +dd d1f3 +ce d0f6 +cc d0f2 +b9 c5eb +bb c5e7 +9b c1e7 +99 c1e3 +8e eaf4 +8d eaf1 +8c eaf0 +8c e8f0 +ae e6f4 +8e e2f4 +ac e6f0 +8c e2f0 +8f caf5 +ce daf4 +8e caf4 +cd daf1 +8d caf1 +8c caf0 +5e 935e +8f c8f5 +8e c8f4 +8c c8f0 +fa fdce +af c6f5 +da f9ce +8f c2f5 +ee d6f4 +ce d2f4 +ae c6f4 +8e c2f4 +f8 fdca +ad c6f1 +d8 f9ca +8d c2f1 +ec d6f0 +cc d2f0 +ac c6f0 +8c c2f0 +a5 c4b3 +bf c5f5 +95 c3b3 +af c4f5 +85 c0b3 +9f c1f5 +75 bfb3 +8f c0f5 +94 c3b2 +ae c4f4 +a3 c4af +bd c5f1 +93 c3af +ad c4f1 +83 c0af +9d c1f1 +92 c3ae +ac c4f0 +82 c0ae +9c c1f0 +a6 ec3c +8c eafa +4b 1ae7 +8d eaf9 +8e eaf6 +8f eaf5 +ad e6f1 +9e e9f6 +5e b35e +8f e8f5 +8e e8f4 +bc e5f2 +a3 e4af +bd e5f1 +93 e3af +ad e4f1 +92 e3ae +ac e4f0 +ac e6fa +6b 16e7 +ad e6f9 +af e6f5 +8f e2f5 +8d e2f1 +82 e0ae +9c e1f0 +bc e5fa +7b 15e7 +bd e5f9 +be e5f6 +a5 e4b3 +bf e5f5 +6b 14e7 +ad e4f9 +95 e3b3 +af e4f5 +94 e3b2 +ae e4f4 +9e e1f6 +85 e0b3 +9f e1f5 +9c e1f2 +83 e0af +9d e1f1 +8f e0f5 +8e e0f4 +8d e0f1 +8c e0f0 +e6 dc3c +cc dafa +cd daf9 +ce daf6 +cf daf5 +ec d6f2 +c5 d8b3 +df d9f5 +16 33e +cd d8f9 +cf d8f5 +ce d8f4 +fc d5f2 +e3 d4af +fd d5f1 +d3 d3af +ed d4f1 +d2 d3ae +ec d4f0 +ec d6fa +ee d6f6 +ef d6f5 +fc d5f8 +ce d2f6 +cf d2f5 +c2 d0ae +dc d1f0 +fc d5fa +fd d5f9 +fe d5f6 +e5 d4b3 +ff d5f5 +ed d4f9 +ec d4f8 +d5 d3b3 +ef d4f5 +d4 d3b2 +ee d4f4 +de d1f6 +c5 d0b3 +df d1f5 +dc d1f2 +c3 d0af +dd d1f1 +b5 cfb3 +cf d0f5 +b4 cfb2 +ce d0f4 +b3 cfaf +cd d0f1 +b2 cfae +cc d0f0 +a2 cc2c +88 caea +a8 c6ea +a2 c42c +88 c2ea +b8 c5ea +a8 c4ea +98 c1ea +88 c0ea +a5 ec31 +8b eaef +9b e9ef +9f e9df +8a e8ee +c3 7207 +ca d8ce +ab e6ef +a3 e42d +89 e2eb +eb d6cf +bb e5ef +bf e5df +aa e4ee +ae e4de +99 e1eb +9d e1db +88 e0ea +fb d5cf +ea d4ce +cf dadf +8f cadf +df d9df +9f c9df +9b c9cf +eb d6ef +e3 d42d +c9 d2eb +ab c6cf +fb d5ef +ff d5df +ea d4ee +ee d4de +d9 d1eb +c8 d0ea +bf c5df +bb c5cf +ae c4de +aa c4ce +a2 ec0c +88 eaca +a8 e6ca +a2 e40c +88 e2ca +8c e0da +b8 e5ca +a8 e4ca +98 e1ca +88 e0ca +88 eae8 +a8 e6e8 +88 e2e8 +89 cae9 +88 cae8 +99 c9e9 +89 c8e9 +a9 c6e9 +89 c2e9 +e8 d6e8 +c8 d2e8 +a8 c6e8 +88 c2e8 +b9 c5e9 +a9 c4e9 +99 c1e9 +89 c0e9 +b8 c5e8 +a8 c4e8 +98 c1e8 +88 c0e8 +a4 ec30 +8a eaee +8b eaed +8e eade +9a e9ee +81 e8ab +9b e9ed +8b e8ed +d3 7307 +da d9ce +aa e6ee +ab e6ed +ae e6de +a2 e42c +88 e2ea +89 e2e9 +a6 e41c +8c e2da +98 e1e8 +ea d6ce +ba e5ee +a1 e4ab +bb e5ed +be e5de +91 e3ab +ab e4ed +90 e3aa +aa e4ec +98 e1ea +99 e1e9 +9c e1da +89 e0e9 +88 e0e8 +fa d5ce +e1 d48b +fb d5cd +d1 d38b +eb d4cd +d0 d38a +ea d4cc +e4 dc30 +ca daee +ce dade +cf dadd +8e cade +8f cadd +8b cacd +c5 d89b +df d9dd +cf d8dd +85 c89b +9f c9dd +81 c88b +9b c9cd +8f c8dd +8b c8cd +ea d6ee +ee d6de +e2 d42c +c8 d2ea +c9 d2e9 +d8 d1e8 +ae c6de +aa c6ce +ab c6cd +fa d5ee +e1 d4ab +fb d5ed +fe d5de +e5 d49b +ff d5dd +d1 d3ab +eb d4ed +d0 d3aa +ea d4ec +d5 d39b +ef d4dd +d4 d39a +ee d4dc +d8 d1ea +d9 d1e9 +c9 d0e9 +c8 d0e8 +be c5de +a5 c49b +bf c5dd +ba c5ce +a1 c48b +bb c5cd +95 c39b +af c4dd +94 c39a +ae c4dc +91 c38b +ab c4cd +90 c38a +aa c4cc +5b 11c7 +9d e1d9 +98 e9e2 +88 e8e2 +8a cae6 +9a c9e6 +8a c8e6 +d8 d9e2 +c8 d8e2 +98 c9e2 +88 c8e2 +aa c6e6 +8a c2e6 +a8 c6e2 +ba c5e6 +aa c4e6 +9a c1e6 +b8 c5e2 +a8 c4e2 +98 c1e2 +88 c0e2 +a3 ec2d +89 eaeb +a9 e6e3 +d8 db68 +cb dac7 +e9 d6c3 +9b e9e7 +9d e9db +9f e9d7 +8a e8e6 +b9 e5e3 +bd e5d3 +a8 e4e2 +db d9c7 +f9 d5c3 +a9 e6eb +ab e6e7 +f8 d768 +eb d6c7 +d8 d368 +cb d2c7 +b9 e5eb +bb e5e7 +bd e5db +bf e5d7 +a8 e4ea +aa e4e6 +ac e4da +9b e1e7 +99 e1e3 +9f e1d7 +9d e1d3 +88 e0e2 +fb d5c7 +db d1c7 +d9 d1c3 +cb dae7 +dc db78 +cf dad7 +e9 d6e3 +ed d6d3 +9c cb78 +8f cad7 +98 cb68 +8b cac7 +ad c6d3 +a9 c6c3 +db d9e7 +cb 726f +df d9d7 +ca d8e6 +f9 d5e3 +fd d5d3 +e8 d4e2 +9b c9c7 +bd c5d3 +b9 c5c3 +e9 d6eb +eb d6e7 +fc d778 +ef d6d7 +cb d2e7 +dc d378 +cf d2d7 +bc c778 +af c6d7 +b8 c768 +ab c6c7 +9c c378 +8f c2d7 +98 c368 +8b c2c7 +f9 d5eb +fb d5e7 +ff d5d7 +e8 d4ea +ea d4e6 +db d1e7 +d9 d1e3 +df d1d7 +dd d1d3 +ca d0e6 +c8 d0e2 +bf c5d7 +bb c5c7 +9f c1d7 +9d c1d3 +9b c1c7 +99 c1c3 +8a eac6 +8e e8d6 +9c e9d2 +8c e8d2 +9a e9c6 +8a e8c6 +98 e9c2 +88 e8c2 +ae e4d6 +8e e0d6 +ac e4d2 +8c e0d2 +ba e5c6 +aa e4c6 +9a e1c6 +8a e0c6 +b8 e5c2 +a8 e4c2 +98 e1c2 +88 e0c2 +ce d8d6 +8e c8d6 +cc d8d2 +8c c8d2 +ca d8c6 +8a c8c6 +d8 d9c2 +c8 d8c2 +98 c9c2 +88 c8c2 +ee d4d6 +ce d0d6 +ae c4d6 +8e c0d6 +ec d4d2 +cc d0d2 +ac c4d2 +8c c0d2 +ea d4c6 +ca d0c6 +aa c4c6 +8a c0c6 +e8 d4c2 +c8 d0c2 +a8 c4c2 +88 c0c2 +8a eae4 +89 eae1 +88 eae0 +98 e9e0 +88 e8e0 +aa e6e4 +8a e2e4 +a8 e6e0 +88 e2e0 +8b cae5 +ca dae4 +8a cae4 +c9 dae1 +89 cae1 +88 cae0 +5a 934e +8b c8e5 +8a c8e4 +98 c9e0 +88 c8e0 +f6 fdbe +ab c6e5 +d6 f9be +8b c2e5 +ea d6e4 +ca d2e4 +aa c6e4 +8a c2e4 +f4 fdba +a9 c6e1 +d4 f9ba +89 c2e1 +e8 d6e0 +c8 d2e0 +a8 c6e0 +88 c2e0 +a1 c4a3 +bb c5e5 +91 c3a3 +ab c4e5 +81 c0a3 +9b c1e5 +71 bfa3 +8b c0e5 +90 c3a2 +aa c4e4 +b9 c5e1 +a9 c4e1 +99 c1e1 +89 c0e1 +b8 c5e0 +a8 c4e0 +98 c1e0 +88 c0e0 +a2 ec2c +88 eaea +89 eae9 +8b eae5 +a6 ec1c +8c eada +8e ead6 +a9 e6e1 +b8 e5e0 +ca dac6 +e8 d6c2 +9a e9e6 +6a b44e +81 e8a3 +9b e9e5 +5b 19c7 +9d e9d9 +9e e9d6 +5a b34e +8b e8e5 +8a e8e4 +b8 e5e2 +b9 e5e1 +bc e5d2 +a9 e4e1 +a8 e4e0 +f8 d5c2 +a8 e6ea +a9 e6e9 +ab e6e5 +ac e6da +b8 e5e8 +8b e2e5 +89 e2e1 +98 e1e0 +ea d6c6 +ca d2c6 +b8 e5ea +b9 e5e9 +ba e5e6 +a1 e4a3 +bb e5e5 +bc e5da +7b 15c7 +bd e5d9 +be e5d6 +a9 e4e9 +a8 e4e8 +91 e3a3 +ab e4e5 +90 e3a2 +aa e4e4 +9a e1e6 +81 e0a3 +9b e1e5 +98 e1e2 +99 e1e1 +9e e1d6 +9c e1d2 +8b e0e5 +8a e0e4 +89 e0e1 +88 e0e0 +fa d5c6 +da d1c6 +d8 d1c2 +e2 dc2c +c8 daea +ca dae6 +cb dae5 +ce dad6 +e8 d6e2 +e9 d6e1 +ec d6d2 +f8 d5e0 +8e cad6 +8a cac6 +ac c6d2 +a8 c6c2 +c1 d8a3 +db d9e5 +cb d8e5 +ca d8e4 +f8 d5e2 +f9 d5e1 +fc d5d2 +e9 d4e1 +e8 d4e0 +9a c9c6 +bc c5d2 +b8 c5c2 +e8 d6ea +e9 d6e9 +ea d6e6 +eb d6e5 +ee d6d6 +f8 d5e8 +ca d2e6 +cb d2e5 +c9 d2e1 +ce d2d6 +d8 d1e0 +ae c6d6 +aa c6c6 +8e c2d6 +8a c2c6 +f8 d5ea +f9 d5e9 +fa d5e6 +e1 d4a3 +fb d5e5 +fe d5d6 +e9 d4e9 +e8 d4e8 +d1 d3a3 +eb d4e5 +d0 d3a2 +ea d4e4 +da d1e6 +c1 d0a3 +db d1e5 +d8 d1e2 +d9 d1e1 +de d1d6 +dc d1d2 +b1 cfa3 +cb d0e5 +b0 cfa2 +ca d0e4 +c9 d0e1 +c8 d0e0 +be c5d6 +ba c5c6 +9e c1d6 +9c c1d2 +9a c1c6 +98 c1c2 +8c ead0 +8b eac5 +8a eac4 +89 eac1 +88 eac0 +8c e8d0 +8b e8c5 +8a e8c4 +89 e8c1 +88 e8c0 +ac e6d0 +8c e2d0 +ab e6c5 +8b e2c5 +aa e6c4 +8a e2c4 +a9 e6c1 +89 e2c1 +a8 e6c0 +88 e2c0 +a3 e48f +bd e5d1 +83 e08f +9d e1d1 +a2 e48e +bc e5d0 +92 e38e +ac e4d0 +82 e08e +9c e1d0 +8c e0d0 +a1 e483 +bb e5c5 +91 e383 +ab e4c5 +81 e083 +9b e1c5 +8b e0c5 +90 e382 +aa e4c4 +8a e0c4 +b9 e5c1 +a9 e4c1 +99 e1c1 +89 e0c1 +b8 e5c0 +a8 e4c0 +98 e1c0 +88 e0c0 +cf dad5 +8f cad5 +ce dad4 +8e cad4 +cd dad1 +8d cad1 +8c cad0 +cb dac5 +8b cac5 +ca dac4 +8a cac4 +c9 dac1 +89 cac1 +88 cac0 +c5 d893 +cb 726d +df d9d5 +cf d8d5 +8f c8d5 +ce d8d4 +8e c8d4 +8d c8d1 +8c c8d0 +c1 d883 +db d9c5 +cb d8c5 +81 c883 +9b c9c5 +8b c8c5 +ca d8c4 +8a c8c4 +99 c9c1 +89 c8c1 +98 c9c0 +88 c8c0 +ef d6d5 +cf d2d5 +af c6d5 +8f c2d5 +ee d6d4 +ce d2d4 +ae c6d4 +8e c2d4 +ad c6d1 +8d c2d1 +ec d6d0 +cc d2d0 +ac c6d0 +8c c2d0 +eb d6c5 +cb d2c5 +f6 fd9e +ab c6c5 +d6 f99e +8b c2c5 +ea d6c4 +ca d2c4 +aa c6c4 +8a c2c4 +e9 d6c1 +c9 d2c1 +e8 d6c0 +c8 d2c0 +a8 c6c0 +88 c2c0 +e5 d493 +ff d5d5 +d5 d393 +ef d4d5 +c5 d093 +df d1d5 +b5 cf93 +cf d0d5 +a5 c493 +bf c5d5 +95 c393 +af c4d5 +85 c093 +9f c1d5 +75 bf93 +8f c0d5 +d4 d392 +ee d4d4 +b4 cf92 +ce d0d4 +94 c392 +ae c4d4 +e3 d48f +fd d5d1 +d3 d38f +ed d4d1 +c3 d08f +dd d1d1 +b3 cf8f +cd d0d1 +a3 c48f +bd c5d1 +93 c38f +ad c4d1 +83 c08f +9d c1d1 +73 bf8f +8d c0d1 +d2 d38e +ec d4d0 +c2 d08e +dc d1d0 +b2 cf8e +cc d0d0 +92 c38e +ac c4d0 +82 c08e +9c c1d0 +72 bf8e +8c c0d0 +e1 d483 +fb d5c5 +d1 d383 +eb d4c5 +c1 d083 +db d1c5 +b1 cf83 +cb d0c5 +a1 c483 +bb c5c5 +91 c383 +ab c4c5 +81 c083 +9b c1c5 +71 bf83 +8b c0c5 +d0 d382 +ea d4c4 +b0 cf82 +ca d0c4 +90 c382 +aa c4c4 +f9 d5c1 +e9 d4c1 +d9 d1c1 +c9 d0c1 +b9 c5c1 +a9 c4c1 +99 c1c1 +89 c0c1 +f8 d5c0 +e8 d4c0 +d8 d1c0 +c8 d0c0 +b8 c5c0 +a8 c4c0 +98 c1c0 +88 c0c0 +9e eb7c +84 ea3a +be e77c +a4 e63a +9e e37c +84 e23a +b4 e53a +94 e13a +9e e17c +84 e03a +9e cbfc +84 caba +de db7c +c4 da3a +8c ca7a +9e cb7c +84 ca3a +9c c97a +8c c87a +9e c97c +84 c83a +be c7fc +a4 c6ba +9e c3fc +84 c2ba +fe d77c +e4 d63a +de d37c +c4 d23a +ac c67a +be c77c +a4 c63a +8c c27a +9e c37c +84 c23a +b4 c5ba +be c5fc +a4 c4ba +94 c1ba +9e c1fc +84 c0ba +f4 d53a +d4 d13a +de d17c +c4 d03a +bc c57a +b4 c53a +ac c47a +be c57c +a4 c43a +9c c17a +94 c13a +8c c07a +9e c17c +84 c03a +97 e9bf +9f e97f +86 e8be +8e e87e +a7 e6bf +9f e3fd +85 e2bb +8d e27b +b7 e5bf +a6 e4be +ae e47e +95 e1bb +9d e17b +9e e1fc +84 e0ba +cf da7f +8b ca6f +df d97f +c6 d8be +ce d87e +9b c96f +9c c9f0 +82 c8ae +8a c86e +e7 d6bf +df d3fd +c5 d2bb +cd d27b +bd c7f1 +a3 c6af +ab c66f +9b c3ed +81 c2ab +89 c26b +f7 d5bf +ff d57f +e6 d4be +ee d47e +d5 d1bb +dd d17b +de d1fc +c4 d0ba +cc d07a +b3 c5af +bb c56f +bc c5f0 +a2 c4ae +aa c46e +91 c1ab +99 c16b +85 cab9 +84 cab8 +c5 da39 +8d ca79 +85 ca39 +cc da78 +c4 da38 +8c ca78 +84 ca38 +95 c9b9 +85 c8b9 +84 c8b8 +1e 37e +d5 d939 +9d c979 +95 c939 +8d c879 +85 c839 +c4 d838 +8f c8d7 +9c c978 +8c c878 +84 c838 +a5 c6b9 +85 c2b9 +e4 d6b8 +c4 d2b8 +a4 c6b8 +84 c2b8 +ad c679 +a5 c639 +8d c279 +85 c239 +ec d678 +e4 d638 +cc d278 +c4 d238 +ac c678 +a4 c638 +8c c278 +84 c238 +b5 c5b9 +a5 c4b9 +95 c1b9 +85 c0b9 +b4 c5b8 +a4 c4b8 +94 c1b8 +84 c0b8 +f5 d539 +e5 d439 +d5 d139 +c5 d039 +96 c396 +bd c579 +b5 c539 +a5 c439 +95 c139 +85 c039 +e7 d497 +f4 d538 +d7 d397 +e4 d438 +c7 d097 +d4 d138 +b7 cf97 +c4 d038 +af c4d7 +bc c578 +a7 c497 +b4 c538 +9f c3d7 +ac c478 +97 c397 +a4 c438 +8f c0d7 +9c c178 +87 c097 +94 c138 +87 eabd +8f ea7d +84 e83a +9e e97c +97 e9bd +9e e97e +85 e83b +9f e97d +87 e8bd +8f e87d +8e e87c +a6 e6be +a7 e6bd +ae e67e +b6 e5bc +a4 e43a +be e57c +9e e3fc +84 e2ba +8c e27a +b6 e5be +b7 e5bd +a5 e43b +bf e57d +a7 e4bd +a6 e4bc +95 e33b +af e47d +94 e33a +ae e47c +94 e1ba +ce da7e +cf da7d +c4 d83a +de d97c +9c cbf0 +82 caae +8a ca6e +8b ca6d +de d97e +c5 d83b +df d97d +cf d87d +ce d87c +9a c96e +81 c82b +9b c96d +82 c8ac +8b c86d +8a c86c +e6 d6be +e7 d6bd +ee d67e +f6 d5bc +e4 d43a +fe d57c +de d3fc +c4 d2ba +c5 d2b9 +cc d27a +d4 d1b8 +cf d0d7 +dc d178 +bc c7f0 +a2 c6ae +a3 c6ad +aa c66e +ab c66d +b2 c5ac +f6 d5be +f7 d5bd +fe d57e +e5 d43b +ff d57d +e7 d4bd +e6 d4bc +d5 d33b +ef d47d +d4 d33a +ee d47c +d4 d1ba +d5 d1b9 +dc d17a +b6 cf96 +dd d179 +c5 d0b9 +c4 d0b8 +bf cfd7 +cc d078 +b2 c5ae +b3 c5ad +ba c56e +a1 c42b +bb c56d +a3 c4ad +a2 c4ac +91 c32b +ab c46d +90 c32a +aa c46c +9e ebf4 +84 eab2 +86 ea36 +9e eb74 +84 ea32 +94 e9b2 +9e e9f4 +84 e8b2 +96 e936 +86 e836 +9c e972 +94 e932 +8c e872 +af 4cfd +95 4bbb +a6 e636 +b4 e532 +94 e132 +86 cab6 +de dbf4 +c4 dab2 +9e cbf4 +84 cab2 +c6 da36 +8e ca76 +86 ca36 +cc da72 +de db74 +c4 da32 +8c ca72 +9e cb74 +84 ca32 +96 c9b6 +86 c8b6 +d4 d9b2 +de d9f4 +c4 d8b2 +94 c9b2 +9e c9f4 +84 c8b2 +c6 d836 +9e c976 +96 c936 +8e c876 +86 c836 +dc d972 +d4 d932 +cc d872 +9c c972 +94 c932 +8c c872 +a6 c6b6 +86 c2b6 +be c7f4 +a4 c6b2 +9e c3f4 +84 c2b2 +c6 d236 +ae c676 +a6 c636 +8e c276 +86 c236 +fe d774 +e4 d632 +de d374 +c4 d232 +ac c672 +be c774 +a4 c632 +8c c272 +9e c374 +84 c232 +b6 c5b6 +a6 c4b6 +96 c1b6 +b4 c5b2 +be c5f4 +a4 c4b2 +94 c1b2 +9e c1f4 +84 c0b2 +f6 d536 +d6 d136 +c6 d036 +be c576 +b6 c536 +ae c476 +a6 c436 +9e c176 +96 c136 +f4 d532 +d4 d132 +bc c572 +b4 c532 +ac c472 +9c c172 +94 c132 +8c c072 +9f ebfd +85 eabb +87 eab7 +bf e7f5 +a5 e6b3 +97 e9b7 +9d e97b +9f e977 +86 e8b6 +8c e87a +8e e876 +b5 e5b3 +bd e573 +be e5f4 +a4 e4b2 +ac e472 +bf e7fd +a5 e6bb +a7 e6b7 +ad e67b +87 e2b7 +9f e3f5 +85 e2b3 +8f e277 +8d e273 +b5 e5bb +b7 e5b7 +bd e57b +be e5fc +a4 e4ba +a6 e4b6 +97 e1b7 +95 e1b3 +9d e173 +9e e1f4 +84 e0b2 +8c e072 +c7 dab7 +cd da7b +cf da77 +ff d7f5 +e5 d6b3 +ed d673 +9b cbed +81 caab +89 ca6b +8b ca67 +bb c7e5 +a1 c6a3 +a9 c663 +d7 d9b7 +dd d97b +df d977 +c6 d8b6 +cc d87a +ce d876 +f5 d5b3 +fd d573 +fe d5f4 +e4 d4b2 +ec d472 +99 c96b +9b c967 +b1 c5a3 +ff d7fd +e5 d6bb +e7 d6b7 +ed d67b +c7 d2b7 +df d3f5 +c5 d2b3 +cf d277 +cd d273 +bb c7ed +a1 c6ab +a3 c6a7 +a9 c66b +ab c667 +83 c2a7 +9b c3e5 +81 c2a3 +8b c267 +89 c263 +f5 d5bb +f7 d5b7 +fd d57b +fe d5fc +e4 d4ba +e6 d4b6 +ec d47a +d7 d1b7 +d5 d1b3 +df d177 +dd d173 +c6 d0b6 +de d1f4 +c4 d0b2 +ce d076 +cc d072 +b1 c5ab +b3 c5a7 +b9 c56b +bb c567 +93 c1a7 +91 c1a3 +9b c167 +87 ea35 +8e ea74 +86 ea34 +8d ea71 +85 ea31 +8c ea70 +84 ea30 +6d b45b +84 e8b0 +97 e935 +87 e835 +96 e934 +86 e834 +83 e82f +9d e971 +95 e931 +8d e871 +85 e831 +82 e82e +9c e970 +94 e930 +8c e870 +84 e830 +a6 e6b4 +86 e2b4 +a4 e6b0 +84 e2b0 +a7 e635 +87 e235 +8e e274 +86 e234 +a5 e631 +85 e231 +ac e670 +a4 e630 +8c e270 +84 e230 +9d e3f3 +b7 e535 +8d e2f3 +a7 e435 +97 e135 +87 e035 +9c e3f2 +b6 e534 +96 e134 +86 e034 +9b e3ef +b5 e531 +8b e2ef +a5 e431 +95 e131 +85 e031 +9a e3ee +b4 e530 +8a e2ee +a4 e430 +94 e130 +84 e030 +87 cab5 +c6 dab4 +c5 dab1 +c7 da35 +8f ca75 +87 ca35 +ce da74 +c6 da34 +8e ca74 +86 ca34 +cd da71 +c5 da31 +8d ca71 +85 ca31 +cc da70 +c4 da30 +8c ca70 +84 ca30 +56 931e +87 c8b5 +d7 d935 +c7 d835 +85 c833 +9f c975 +97 c935 +8f c875 +87 c835 +d6 d934 +c6 d834 +84 c832 +9e c974 +96 c934 +8e c874 +86 c834 +c3 d82f +dd d971 +cd d871 +83 c82f +9d c971 +95 c931 +8d c871 +85 c831 +c2 d82e +dc d970 +d4 d930 +cc d870 +c4 d830 +82 c82e +9c c970 +94 c930 +8c c870 +84 c830 +f2 fd8e +a7 c6b5 +d2 f98e +87 c2b5 +e6 d6b4 +c6 d2b4 +a6 c6b4 +86 c2b4 +e4 d6b0 +c4 d2b0 +a4 c6b0 +84 c2b0 +e7 d635 +c7 d235 +fa fd4e +af c675 +f2 fd0e +a7 c635 +da f94e +8f c275 +d2 f90e +87 c235 +ee d674 +e6 d634 +ce d274 +c6 d234 +ae c674 +a6 c634 +8e c274 +86 c234 +ec d670 +e4 d630 +cc d270 +c4 d230 +ac c670 +a4 c630 +8c c270 +84 c230 +b7 c5b5 +a7 c4b5 +97 c1b5 +87 c0b5 +b6 c5b4 +a6 c4b4 +96 c1b4 +86 c0b4 +b5 c5b1 +a5 c4b1 +95 c1b1 +85 c0b1 +b4 c5b0 +a4 c4b0 +94 c1b0 +84 c0b0 +dd d3f3 +f7 d535 +cd d2f3 +e7 d435 +bd cff3 +d7 d135 +ad cef3 +c7 d035 +a5 c433 +bf c575 +9d c3f3 +b7 c535 +95 c333 +af c475 +8d c2f3 +a7 c435 +85 c033 +9f c175 +7d bff3 +97 c135 +75 bf33 +8f c075 +6d bef3 +87 c035 +dc d3f2 +f6 d534 +cc d2f2 +e6 d434 +bc cff2 +d6 d134 +ac cef2 +c6 d034 +a4 c432 +be c574 +9c c3f2 +b6 c534 +94 c332 +ae c474 +8c c2f2 +a6 c434 +84 c032 +9e c174 +db d3ef +f5 d531 +cb d2ef +e5 d431 +bb cfef +d5 d131 +ab ceef +c5 d031 +a3 c42f +bd c571 +9b c3ef +b5 c531 +93 c32f +ad c471 +8b c2ef +a5 c431 +83 c02f +9d c171 +73 bf2f +8d c071 +da d3ee +f4 d530 +ca d2ee +e4 d430 +ba cfee +d4 d130 +aa ceee +c4 d030 +a2 c42e +bc c570 +9a c3ee +b4 c530 +92 c32e +ac c470 +8a c2ee +a4 c430 +82 c02e +9c c170 +72 bf2e +8c c070 +9e ebfc +84 eaba +86 eab6 +87 eab5 +8f ea75 +84 e832 +9e e974 +be e7f4 +a4 e6b2 +a5 e6b1 +ad e671 +b4 e5b0 +a2 e42e +bc e570 +96 e9b6 +66 b41e +97 e9b5 +9c e97a +9e e976 +85 e833 +9f e975 +56 b31e +87 e8b5 +6f b45f +86 e8b4 +8f e875 +8e e874 +b4 e5b2 +b5 e5b1 +bc e572 +a3 e42f +bd e571 +a5 e4b1 +a4 e4b0 +93 e32f +ad e471 +92 e32e +ac e470 +be e7fc +a4 e6ba +a6 e6b6 +a7 e6b5 +ac e67a +9d 4bfb +b7 4d3d +ae e676 +b6 e5b4 +a4 e432 +be e574 +86 e2b6 +87 e2b5 +9e e3f4 +84 e2b2 +85 e2b1 +8f e275 +8d e271 +96 e1b4 +94 e1b0 +84 e032 +9e e174 +82 e02e +9c e170 +b4 e5ba +b6 e5b6 +b7 e5b5 +a7 e4b5 +a6 e4b4 +96 e1b6 +97 e1b5 +94 e1b2 +95 e1b1 +85 e033 +9f e175 +9c e172 +83 e02f +9d e171 +87 e0b5 +86 e0b4 +85 e0b1 +84 e0b0 +8f e075 +8e e074 +8d e071 +8c e070 +de dbfc +c4 daba +c6 dab6 +c7 dab5 +cc da7a +cd da79 +ce da76 +cf da75 +d6 d9b4 +cf d8d7 +dc d978 +c4 d832 +de d974 +fe d7f4 +e4 d6b2 +e5 d6b1 +ec d672 +f4 d5b0 +e2 d42e +fc d570 +d7 d9b5 +dc d97a +dd d979 +c5 d833 +df d975 +c7 d8b5 +c6 d8b4 +cd d879 +cc d878 +cf d875 +ce d874 +f4 d5b2 +f5 d5b1 +fc d572 +e3 d42f +fd d571 +e5 d4b1 +e4 d4b0 +d3 d32f +ed d471 +d2 d32e +ec d470 +fe d7fc +e4 d6ba +e5 d6b9 +e6 d6b6 +e7 d6b5 +ec d67a +ef d675 +f4 d5b8 +f6 d5b4 +ef d4d7 +fc d578 +e4 d432 +fe d574 +c6 d2b6 +c7 d2b5 +de d3f4 +c4 d2b2 +c5 d2b1 +ce d276 +cf d275 +cc d272 +d6 d1b4 +d4 d1b0 +c4 d032 +de d174 +c2 d02e +dc d170 +f4 d5ba +f5 d5b9 +f6 d5b6 +f7 d5b5 +fc d57a +d6 d396 +fd d579 +fe d576 +e5 d433 +ff d575 +e5 d4b9 +e4 d4b8 +e7 d4b5 +e6 d4b4 +df d3d7 +ec d478 +d5 d333 +ef d475 +d4 d332 +ee d474 +d6 d1b6 +d7 d1b5 +d4 d1b2 +d5 d1b1 +de d176 +c5 d033 +df d175 +dc d172 +c3 d02f +dd d171 +c7 d0b5 +c6 d0b4 +c5 d0b1 +c4 d0b0 +b5 cf33 +cf d075 +b4 cf32 +ce d074 +b3 cf2f +cd d071 +b2 cf2e +cc d070 +8b ea6f +cb da4f +97 e99f +9b e96f +9f e95f +9c e9f0 +82 e8ae +86 e89e +8a e86e +8e e85e +db d94f +ca d84e +bd e7f1 +a3 e6af +9b e3ed +81 e2ab +fd d7d1 +e3 d68f +db d3cd +c1 d28b +b3 e5af +b7 e59f +bb e56f +bc e5f0 +a2 e4ae +a6 e49e +aa e46e +ae e45e +91 e1ab +95 e19b +99 e16b +9d e15b +f3 d58f +fb d54f +fc d5d0 +e2 d48e +ea d44e +d1 d18b +c7 da9f +cb da6f +cf da5f +87 ca9f +9d cbd1 +83 ca8f +8f ca5f +8b ca4f +db d96f +df d95f +ca d86e +ce d85e +9f c95f +9b c94f +8e c85e +8a c84e +fd d7f1 +e3 d6af +e7 d69f +eb d66f +db d3ed +c1 d2ab +df d3dd +c5 d29b +c9 d26b +a7 c69f +bd c7d1 +a3 c68f +9f c3dd +85 c29b +9b c3cd +81 c28b +f3 d5af +f7 d59f +fb d56f +ff d55f +fc d5f0 +e2 d4ae +e6 d49e +ea d46e +ee d45e +d1 d1ab +d5 d19b +d9 d16b +b7 c59f +b3 c58f +d6 f9b4 +bf c55f +a6 c49e +bc c5d0 +a2 c48e +c5 f8b3 +df f9f5 +ae c45e +c1 f8a3 +db f9e5 +aa c44e +95 c19b +91 c18b +9c ebf0 +82 eaae +6b 1c6f +86 ea9e +52 1b2c +87 ea9d +8a ea6e +8b ea6d +8f ea5d +96 e99c +ca da4e +cb da4d +92 e9ae +9a e96e +81 e82b +9b e96d +9e e95e +85 e81b +9f e95d +82 e8ac +8b e86d +8a e86c +8f e85d +8e e85c +da d94e +c1 d80b +db d94d +cb d84d +ca d84c +bc e7f0 +a2 e6ae +a3 e6ad +a6 e69e +72 172c +a7 e69d +ab e66d +ae e65e +b2 e5ac +b6 e59c +fc d7d0 +e2 d68e +e3 d68d +ea d64e +f2 d58c +b2 e5ae +b3 e5ad +b6 e59e +ba e56e +a1 e42b +bb e56d +a5 e41b +bf e55d +a3 e4ad +a2 e4ac +91 e32b +ab e46d +90 e32a +aa e46c +94 e31a +ae e45c +f2 d58e +f3 d58d +fa d54e +5b 33e7 +e1 d40b +fb d54d +e3 d48d +e2 d48c +3f 777 +dc dbf0 +c2 daae +c6 da9e +c7 da9d +ca da6e +cb da6d +ce da5e +cf da5d +86 ca9e +87 ca9d +9c cbd0 +82 ca8e +83 ca8d +8e ca5e +8f ca5d +8a ca4e +8b ca4d +d7 d99d +da d96e +c1 d82b +db d96d +de d95e +c5 d81b +df d95d +c7 d89d +c6 d89c +cb d86d +ca d86c +cf d85d +ce d85c +97 c99d +93 c98d +9e c95e +85 c81b +9f c95d +81 c80b +9b c94d +87 c89d +86 c89c +83 c88d +82 c88c +8f c85d +8e c85c +8b c84d +8a c84c +fc d7f0 +e2 d6ae +e3 d6ad +e6 d69e +e7 d69d +ea d66e +ee d65e +f2 d5ac +f6 d59c +a6 c69e +a7 c69d +bc c7d0 +a2 c68e +a3 c68d +c5 fab3 +df fbf5 +ae c65e +c1 faa3 +db fbe5 +aa c64e +b6 c59c +b2 c58c +f2 d5ae +f3 d5ad +f6 d59e +f7 d59d +fa d56e +e1 d42b +fb d56d +fe d55e +5f 33f7 +e5 d41b +ff d55d +e3 d4ad +e2 d4ac +e7 d49d +e6 d49c +d1 d32b +eb d46d +d0 d32a +ea d46c +b6 c59e +b7 c59d +b2 c58e +b3 c58d +d5 f9b3 +be c55e +1f 23f7 +a5 c41b +bf c55d +d1 f9a3 +ba c54e +1b 23e7 +a1 c40b +bb c54d +a7 c49d +a6 c49c +a3 c48d +a2 c48c +9b ebed +81 eaab +89 ea6b +8b ea67 +bb e7e5 +a1 e6a3 +d0 db28 +c3 da87 +cb da47 +fb d7c5 +e1 d683 +95 e99b +97 e997 +99 e96b +9b e967 +9d e95b +9f e957 +b1 e5a3 +b5 e593 +b9 e563 +db d947 +f9 d543 +bb e7ed +a1 e6ab +a3 e6a7 +9b e3e5 +81 e2a3 +8b e267 +89 e263 +8f e257 +fb d7cd +e1 d68b +f0 d728 +e3 d687 +eb d647 +d0 d328 +c3 d287 +db d3c5 +c1 d283 +cb d247 +b1 e5ab +b3 e5a7 +b5 e59b +b7 e597 +b9 e56b +bd e55b +93 e1a7 +91 e1a3 +97 e197 +95 e193 +9b e167 +99 e163 +27 ebd +9d e153 +f3 d587 +fb d547 +d3 d187 +d1 d183 +db d147 +d9 d143 +c3 daa7 +df dbdd +c5 da9b +d4 db38 +c7 da97 +c9 da6b +cb da67 +cf da57 +fb d7e5 +e1 d6a3 +ff d7d5 +e5 d693 +e9 d663 +9f cbdd +85 ca9b +94 cb38 +87 ca97 +9b cbcd +81 ca8b +90 cb28 +83 ca87 +8f ca57 +8b ca47 +bf c7d5 +a5 c693 +bb c7c5 +a1 c683 +d3 d9a7 +d9 d96b +db d967 +df d957 +f5 d593 +fd d553 +9f c957 +9b c947 +b5 c593 +b1 c583 +bd c553 +b9 c543 +fb d7ed +e1 d6ab +e3 d6a7 +ff d7dd +e5 d69b +f4 d738 +e7 d697 +e9 d66b +eb d667 +c3 d2a7 +db d3e5 +c1 d2a3 +d4 d338 +c7 d297 +df d3d5 +c5 d293 +cb d267 +c9 d263 +cf d257 +bf c7dd +a5 c69b +b4 c738 +a7 c697 +bb c7cd +a1 c68b +b0 c728 +a3 c687 +af c657 +ab c647 +94 c338 +87 c297 +9f c3d5 +85 c293 +90 c328 +83 c287 +9b c3c5 +81 c283 +8f c257 +8b c247 +f3 d5a7 +f5 d59b +f7 d597 +f9 d56b +fb d567 +d3 d1a7 +d1 d1a3 +d7 d197 +d5 d193 +db d167 +46 309e +d9 d163 +df d157 +dd d153 +b5 c59b +b7 c597 +b1 c58b +b3 c587 +bf c557 +bb c547 +97 c197 +95 c193 +93 c187 +91 c183 +9f c157 +9d c153 +9b c147 +99 c143 +ff 7fff +7c bff8 +7d 3ff9 +fc 7ff8 +7c 3ff8 +6e befc +fe 7ffe +77 3fbd +66 3ebc +96 c134 +7c bff2 +7e 3ff6 +fc 7ff2 +b6 6f9c +97 c13d +7d bffb +fd 7ffb +ff 7ff7 +fd dfdb +77 3fb7 +65 3eb3 +7f 3ff5 +96 c13c +7c bffa +7d bff9 +65 beb3 +7f bff5 +fc 7ffa +fd 7ff9 +fe 7ff6 +e5 7eb3 +ff 7ff5 +95 c131 +7b bfef +fb 7fef +ff 7fdf +92 c10c +78 bfca +f0 df22 +77 3f9f +f8 7fca +3d af73 +39 af63 +2d ae73 +d3 7107 +75 9fb3 +cf 70f7 +71 9fa3 +7d 9f73 +79 9f63 +78 bfe8 +79 3fe9 +f8 7fe8 +78 3fe8 +94 c130 +7a bfee +6a beec +6e bedc +2a aecc +fa 7fee +fe 7fde +7c bfd8 +ee 7edc +79 bfc9 +78 bfc8 +73 3fad +76 3f9e +f0 df20 +77 3f9d +62 3eac +f9 df61 +66 3e9c +33 2f8d +22 2e8c +f a0f7 +7d 3fd9 +8e e0f6 +fc 7fd8 +8b e0e7 +f9 7fc9 +b a0e7 +79 3fc9 +8a e0e6 +f8 7fc8 +7a 3fe6 +f8 7fe2 +f8 dfc2 +1e a1fe +e8 dec2 +e a0fe +2e 86fe +f4 ffb0 +5e 91fe +e4 feb0 +4e 90fe +d4 f3b0 +3e 85fe +c4 f2b0 +2e 84fe +b4 efb0 +1e 81fe +a4 eeb0 +e 80fe +93 c12d +79 bfeb +97 c11d +7d bfdb +8c c078 +7f bfd7 +53 b10d +39 afcb +f9 7feb +fb 7fe7 +ff 7fd7 +7a bfc6 +f9 dfcb +73 3fa7 +4b 30cd +31 2f8b +7e 3fd6 +fc 7fd2 +fa 7fc6 +7a 3fc6 +f8 7fc2 +3e 25fe +2e 24fe +79 bfe1 +78 bfe0 +de 51fe +ce 50fe +ae 44fe +9e 41fe +8e 40fe +6e 14fe +61 3ea3 +7b 3fe5 +f9 7fe1 +79 3fe1 +97 e19f +f8 7fe0 +92 c12c +78 bfea +79 bfe9 +61 bea3 +7b bfe5 +96 c11c +7c bfda +7d bfd9 +7e bfd6 +f8 7fea +fa 7fe6 +e1 7ea3 +fb 7fe5 +65 be93 +7f bfd5 +8f e0f7 +fd 7fd9 +fe 7fd6 +63 be8f +7d bfd1 +62 be8e +7c bfd0 +61 be83 +7b bfc5 +79 bfc1 +78 bfc0 +e5 7e93 +ff 7fd5 +65 3e93 +7f 3fd5 +e3 7e8f +fd 7fd1 +63 3e8f +7d 3fd1 +e2 7e8e +fc 7fd0 +62 3e8e +7c 3fd0 +e1 7e83 +fb 7fc5 +61 3e83 +7b 3fc5 +f9 7fc1 +79 3fc1 +f8 7fc0 +78 3fc0 +7 aa3f +8e c07c +74 bf3a +86 e21c +7 a8bf +17 a93f +f a87f +7 a83f +7f bf7f +f7 7f3f +f7 7fbf +ff 7f7f +7f 3f7f +77 3f3f +27 a63f +f a27f +7 a23f +74 bfb8 +75 bf39 +75 3fb9 +f4 7fb8 +74 3fb8 +6f 3ed7 +7c 3f78 +27 a4bf +f1 df83 +17 a1bf +e1 de83 +fb dfc5 +7 a0bf +37 a53f +2f a47f +27 a43f +f9 df43 +1f a17f +f1 df03 +17 a13f +e9 de43 +f a07f +e1 de03 +fb df45 +7 a03f +77 bfbd +7e bf7e +65 be3b +7f bf7d +66 bebc +6e be7c +f6 7f3e +f7 7f3d +e6 7e3c +f6 7fbe +f7 7fbd +fe 7f7e +ee 7e7c +7e 3f7e +76 3f3e +77 3f3d +6e 3e7c +66 3e3c +47 9a3f +7 8a3f +8e c0f4 +74 bfb2 +7c bf72 +8e c074 +74 bf32 +fc dfda +76 3fb6 +f4 7fb2 +7c 3f72 +c6 d21c +47 98bf +86 c21c +7 88bf +57 993f +4f 987f +47 983f +17 893f +f 887f +7 883f +8f c0fd +75 bfbb +7d bf7b +5d bb73 +f5 7f3b +f7 7f37 +f5 7fbb +f7 7fb7 +fd 7f7b +ff 7f77 +7d 3f7b +7f 3f77 +fd df5b +77 3f37 +5d 3b73 +67 963f +4f 927f +47 923f +27 863f +f 827f +7 823f +76 bfb4 +75 bfb1 +74 bfb0 +77 bf35 +76 bf34 +63 be2f +7d bf71 +75 bf31 +62 be2e +7c bf70 +74 bf30 +fd dfd9 +77 3fb5 +f6 7fb4 +fc dfd8 +76 3fb4 +5f 11ff +f5 7fb1 +75 3fb1 +5e 11fe +f4 7fb0 +74 3fb0 +f6 7f34 +fc df58 +76 3f34 +75 3f31 +74 3f30 +67 94bf +57 91bf +47 90bf +27 84bf +17 81bf +7 80bf +77 953f +6f 947f +67 943f +f5 ff31 +5f 917f +ed fef1 +57 913f +e5 fe31 +4f 907f +47 903f +cd f2f1 +37 853f +c5 f231 +2f 847f +27 843f +b5 ef31 +1f 817f +ad eef1 +17 813f +a5 ee31 +f 807f +7 803f +8e c0fc +74 bfba +75 bfb9 +77 bfb5 +7c bf7a +7d bf79 +65 be33 +7f bf75 +f4 7f3a +f5 7f39 +f6 7f36 +f7 7f35 +f4 7fba +f5 7fb9 +f6 7fb6 +f7 7fb5 +fc 7f7a +fd 7f79 +e5 7e33 +ff 7f75 +7c 3f7a +7d 3f79 +7e 3f76 +65 3e33 +7f 3f75 +75 3f39 +fc df5a +76 3f36 +fd df59 +77 3f35 +8a c06c +70 bf2a +f 287f +7 283f +8d c0f1 +73 bfaf +77 bf9f +7b bf6f +7f bf5f +3b af4f +f3 7f2f +f7 7f1f +f3 7faf +f7 7f9f +ff 7f5f +8a c0cc +70 bf8a +8e c05c +74 bf1a +77 9f3d +bb 6f4f +78 bf4a +8a c04c +70 bf0a +7b 3f6f +7f 3f5f +77 3f1f +3b 2f4f +f0 7f8a +f8 7f4a +f0 7f0a +78 3f4a +29 ae63 +2d ae53 +b0 e700 +31 ada3 +39 ad63 +3d ad53 +f 227f +7 223f +6d 9e53 +70 bfa8 +71 bf29 +6b bec7 +78 bf68 +63 be87 +70 bf28 +71 3fa9 +f0 7fa8 +70 3fa8 +eb 7ec7 +f8 7f68 +e3 7e87 +f0 7f28 +6b 3ec7 +78 3f68 +63 3e87 +70 3f28 +27 24bf +f1 5f83 +17 21bf +cc f8f8 +2f 247f +c4 f8b8 +27 243f +f9 5f43 +1f 217f +f1 5f03 +17 213f +e9 5e43 +f 207f +8c c0f0 +72 bfae +73 bfad +77 bf9d +7a bf6e +61 be2b +7b bf6d +7e bf5e +65 be1b +7f bf5d +62 beac +66 be9c +6a be6c +6e be5c +33 af8d +3a af4e +21 ae0b +3b af4d +22 ae8c +2a ae4c +f6 7f1e +f7 7f1d +e6 7e1c +f2 7fae +5d 11fb +f3 7fad +f6 7f9e +f7 7f9d +74 bf98 +fe 7f5e +e6 7e9c +ea 7e6c +ee 7e5c +71 bf89 +70 bf88 +75 bf19 +1d 1db +b3 6f8d +7c bf58 +74 bf18 +76 9f3c +ba 6f4e +aa 6e4c +79 bf49 +71 bf09 +78 bf48 +70 bf08 +7a 3f6e +7e 3f5e +73 3f2d +76 3f1e +77 3f1d +6a 3e6c +6e 3e5c +62 3e2c +66 3e1c +3a 2f4e +33 2f0d +2a 2e4c +22 2e0c +86 e0b6 +f4 7f98 +83 e0a7 +f1 7f89 +8e e076 +fc 7f58 +86 e036 +f4 7f18 +8b e067 +f9 7f49 +83 e027 +f1 7f09 +8f 4a7f +87 4a3f +f a7f +7 a3f +8a c0e4 +70 bfa2 +78 bf62 +f8 dfca +72 3fa6 +f0 7fa2 +78 3f62 +c7 58bf +87 48bf +96 431c +17 9bf +f a2fd +c7 78b7 +e a2fc +e9 dec1 +f a0fd +e8 dec0 +e a0fc +f 8afd +e 8afc +cf 587f +c7 583f +9f 497f +97 493f +8f 487f +87 483f +4f 187f +47 183f +1f 97f +17 93f +f 87f +7 83f +f 88fd +2f 86fd +f 82fd +e7 5cb7 +2e 86fc +8b c0ed +71 bfab +8f c0dd +75 bf9b +84 c038 +77 bf97 +79 bf6b +7d bf5b +7f bf57 +59 bb63 +5d bb53 +4b b0cd +31 af8b +39 af4b +3b af47 +19 ab43 +f1 7f2b +f3 7f27 +f7 7f17 +15 83bb +2f 84fd +f 80fd +e 80fc +f1 7fab +f3 7fa7 +f5 7f9b +f7 7f97 +f9 7f6b +ff 7f57 +8e c0d4 +74 bf92 +dd 7b53 +8a c0c4 +70 bf82 +76 bf16 +cb 70cd +b1 6f8b +7c bf52 +7a bf46 +72 bf06 +78 bf42 +79 3f6b +7b 3f67 +7d 3f5b +7f 3f57 +f9 df4b +73 3f27 +77 3f17 +59 3b63 +5d 3b53 +39 2f4b +3b 2f47 +4b 304d +31 2f0b +33 2f07 +19 2b43 +f4 7f92 +f2 7f86 +f0 7f82 +fc 7f52 +f4 7f12 +7c 3f52 +fa 7f46 +f2 7f06 +7a 3f46 +72 3f06 +f8 7f42 +f0 7f02 +78 3f42 +e 2afc +27 6bf +f 28fd +f 22fd +8e 62fc +2e 26fc +e 22fc +8e 4afc +4e 1afc +e afc +cc daf8 +2f 67f +c4 dab8 +27 63f +f 27f +7 23f +86 e236 +8f 48fd +9f e377 +8e 48fc +4f 12fd +f 2fd +ce 52fc +ae 46fc +8e 42fc +6e 16fc +4e 12fc +2e 6fc +e 2fc +72 bfa4 +71 bfa1 +70 bfa0 +73 bf25 +72 bf24 +79 bf61 +71 bf21 +78 bf60 +70 bf20 +b5 4fbb +cf 50fd +95 43bb +af 44fd +75 3fbb +8f 40fd +35 fbb +4f 10fd +15 3bb +2f 4fd +f fd +74 3fba +8e 40fc +e fc +f9 dfc9 +73 3fa5 +f8 dfc8 +72 3fa4 +5b 11ef +f1 7fa1 +71 3fa1 +70 3fa0 +f8 df48 +72 3f24 +f9 7f61 +79 3f61 +71 3f21 +f a0df +70 3f20 +e7 54bf +c7 50bf +a7 44bf +87 40bf +67 14bf +57 11bf +47 10bf +37 5bf +27 4bf +17 1bf +7 bf +ef 547f +e7 543f +d7 513f +cf 507f +c7 503f +b7 453f +af 447f +a7 443f +97 413f +8f 407f +87 403f +6f 147f +67 143f +f5 7f31 +5f 117f +47 103f +c4 d8b8 +27 43f +7 3f +8a c0ec +70 bfaa +71 bfa9 +73 bfa5 +8e c0dc +74 bf9a +75 bf99 +9d c179 +76 bf96 +78 bf6a +79 bf69 +61 be23 +7b bf65 +7c bf5a +7d bf59 +7e bf56 +f0 7f2a +87 e037 +f5 7f19 +f6 7f16 +f0 7faa +f1 7fa9 +f2 7fa6 +5d 11f3 +f3 7fa5 +77 bf95 +f4 7f9a +87 e0b7 +f5 7f99 +f6 7f96 +f8 7f6a +e1 7e23 +fb 7f65 +76 bf94 +8f e077 +fd 7f59 +fe 7f56 +75 bf91 +74 bf90 +73 bf85 +72 bf84 +71 bf81 +70 bf80 +65 be13 +7f bf55 +77 bf15 +76 bf14 +63 be0f +7d bf51 +75 bf11 +62 be0e +7c bf50 +74 bf10 +61 be03 +7b bf45 +73 bf05 +72 bf04 +79 bf41 +71 bf01 +78 bf40 +70 bf00 +78 3f6a +79 3f69 +7a 3f66 +61 3e23 +7b 3f65 +7c 3f5a +7e 3f56 +71 3f29 +f8 df4a +72 3f26 +f9 df49 +73 3f25 +76 3f16 +f7 7f95 +77 3f95 +f6 7f94 +76 3f94 +5f 11df +f5 7f91 +75 3f91 +74 3f90 +5d 11d3 +f3 7f85 +73 3f85 +72 3f84 +5b 11cf +f1 7f81 +71 3f81 +70 3f80 +e5 7e13 +ff 7f55 +f7 7f15 +65 3e13 +7f 3f55 +77 3f15 +f6 7f14 +76 3f14 +e3 7e0f +fd 7f51 +63 3e0f +7d 3f51 +75 3f11 +e2 7e0e +fc 7f50 +62 3e0e +7c 3f50 +74 3f10 +e1 7e03 +fb 7f45 +61 3e03 +7b 3f45 +73 3f05 +72 3f04 +f9 7f41 +79 3f41 +71 3f01 +f8 7f40 +78 3f40 +70 3f00 +ef 7eff +ff 7dff +f6 771c +77 3dbf +6c bef8 +6d 3ef9 +ec 7ef8 +6c 3ef8 +7d 3df9 +6d 3cf9 +7c 3df8 +6c 3cf8 +6f befd +ee 7efe +67 3ebd +e5 7cbb +ff 7dfd +77 3dbd +67 3cbd +86 c034 +6c bef2 +fb f74f +7c bdf2 +eb f64f +6c bcf2 +f4 df1a +6e 3ef6 +ec 7ef2 +fb 774f +7c 3df2 +eb 764f +6c 3cf2 +92 6bac +96 6b9c +b2 67ac +b6 679c +e3 f627 +d2 5bac +e7 f617 +d6 5b9c +87 c03d +6d befb +6f bef7 +f2 57ac +fc f758 +7d bdfb +c2 f20e +dc f350 +5d b9f3 +f6 579c +ed 7efb +ef 7ef7 +7f 3ffd +65 3ebb +ed dedb +67 3eb7 +6e bef4 +6d bef1 +6c bef0 +63 bcaf +e2 f60c +7d bdf1 +f5 df19 +6f 3ef5 +f4 df18 +6e 3ef4 +57 113f +ed 7ef1 +6d 3ef1 +6c 3ef0 +63 3caf +e2 760c +7d 3df1 +86 c03c +6c befa +6d bef9 +6e bef6 +6f bef5 +7c bdf8 +7d bdf9 +6d bcf9 +6c bcf8 +ec 7efa +ed 7ef9 +ee 7ef6 +36 a79c +fc 7df8 +fe 7df6 +ee 7cf6 +35 a73b +75 973b +71 972b +78 3dea +68 3cea +85 c031 +6b beef +6f bedf +e9 f64b +6a bcee +3b adcf +2a acce +eb 7eef +ef 7edf +82 c00c +68 beca +7d 3ff1 +63 3eaf +68 bcca +ff 7ddf +f2 770c +73 3daf +fb 774d +e1 760b +7c 3df0 +62 3cae +f9 dd63 +66 3c9e +33 2d8f +e8 7eca +e8 7cca +68 3cca +57 9b37 +53 9b27 +77 9737 +75 9733 +73 9727 +71 9723 +68 bee8 +69 3ee9 +5f 11d7 +e8 7ee8 +68 3ee8 +79 3de9 +69 3ce9 +78 3de8 +68 3ce8 +84 c030 +6a beee +6b beed +6f bedd +2b aecd +f9 f74b +7a bdee +65 bc9b +7f bddd +3a adce +21 ac8b +3b adcd +ea 7eee +ee 7ede +ef 7edd +6c bed8 +69 bec9 +68 bec8 +7c 3ff0 +62 3eae +63 3ead +f9 df63 +66 3e9e +76 3d9c +79 bdc9 +69 bcc9 +78 bdc8 +68 bcc8 +23 2e8d +32 2d8c +e5 7c9b +ff 7ddd +a1 6c8b +bb 6dcd +f1 770b +72 3dae +73 3dad +63 3cad +33 2d8d +23 2c8d +22 2c8c +6d 3ed9 +ec 7ed8 +6c 3ed8 +e9 7ec9 +69 3ec9 +e8 7ec8 +68 3ec8 +7d 3dd9 +6d 3cd9 +7c 3dd8 +6c 3cd8 +f9 7dc9 +e9 7cc9 +79 3dc9 +69 3cc9 +f8 7dc8 +e8 7cc8 +78 3dc8 +68 3cc8 +f7 f73f +78 bde2 +e7 f63f +68 bce2 +f0 df0a +6a 3ee6 +e8 7ee2 +f9 7743 +7a 3de6 +83 c02d +69 beeb +6b bee7 +87 c01d +6d bedb +7c bf78 +6f bed7 +43 b00d +29 aecb +f8 f748 +79 bdeb +7d bddb +7f bdd7 +68 bcea +d8 f340 +59 b9e3 +5d b9d3 +3b adc7 +19 a9c3 +e9 7eeb +eb 7ee7 +fc 7f78 +ef 7ed7 +6a bec6 +7b 3fed +61 3eab +e9 decb +63 3ea7 +6e bcd6 +7f 3fdd +f8 df60 +65 3e9b +74 3f38 +67 3e97 +7c bdd2 +6c bcd2 +7a bdc6 +6a bcc6 +f7 f71f +78 bdc2 +e7 f61f +68 bcc2 +3b 2fcd +21 2e8b +31 a72b +ea 7ce6 +75 3d9b +77 3d97 +55 3993 +31 2d8b +33 2d87 +6e 3ed6 +ec 7ed2 +ea 7ec6 +6a 3ec6 +e8 7ec2 +35 a71b +ee 7cd6 +7e 3dd6 +6e 3cd6 +fc 7dd2 +ec 7cd2 +7c 3dd2 +6c 3cd2 +fa 7dc6 +31 a70b +ea 7cc6 +7a 3dc6 +6a 3cc6 +f8 7dc2 +e8 7cc2 +6a bee4 +69 bee1 +68 bee0 +79 bde1 +f7 f73d +78 bde0 +e7 f63d +68 bce0 +f1 df09 +6b 3ee5 +f0 df08 +6a 3ee4 +69 3ee1 +79 3de1 +82 c02c +68 beea +69 bee9 +6a bee6 +6b bee5 +86 c01c +6c beda +6d bed9 +6e bed6 +78 bde8 +7c bdd8 +78 bdea +79 bde9 +7d bdd9 +7e bdd6 +69 bce9 +68 bce8 +6d bcd9 +6c bcd8 +e8 7eea +ea 7ee6 +6f bed5 +ed 7ed9 +ee 7ed6 +6e bed4 +32 a78c +f8 7de8 +6d bed1 +fc 7dd8 +6c bed0 +6b bec5 +6a bec4 +69 bec1 +68 bec0 +65 bc93 +7f bdd5 +55 bb93 +6f bcd5 +63 bc8f +7d bdd1 +53 bb8f +6d bcd1 +62 bc8e +7c bdd0 +52 bb8e +6c bcd0 +61 bc83 +7b bdc5 +51 bb83 +6b bcc5 +79 bdc1 +69 bcc1 +f7 f71d +78 bdc0 +e7 f61d +68 bcc0 +fa 7de6 +fd 7dd9 +fe 7dd6 +ed 7cd9 +ec 7cd8 +ef 7ed5 +6f 3ed5 +ee 7ed4 +6e 3ed4 +6d 3ed1 +6c 3ed0 +6b 3ec5 +6a 3ec4 +69 3ec1 +68 3ec0 +d5 7b93 +ef 7cd5 +65 3c93 +7f 3dd5 +55 3b93 +6f 3cd5 +e3 7c8f +fd 7dd1 +d3 7b8f +ed 7cd1 +63 3c8f +7d 3dd1 +53 3b8f +6d 3cd1 +e2 7c8e +fc 7dd0 +d2 7b8e +ec 7cd0 +62 3c8e +7c 3dd0 +52 3b8e +6c 3cd0 +d1 7b83 +eb 7cc5 +61 3c83 +7b 3dc5 +51 3b83 +6b 3cc5 +f9 7dc1 +e9 7cc1 +79 3dc1 +69 3cc1 +f8 7dc0 +e8 7cc0 +7e bf7c +64 be3a +7e 3ffc +64 3eba +6f be7f +e7 7e3f +f6 f71c +77 bdbf +7f bd7f +e5 f61b +ff f75d +66 bcbe +6e bc7e +f7 7d3f +e6 7c3e +e7 7ebf +ef 7e7f +6f 3e7f +67 3e3f +f7 7dbf +ff 7d7f +e6 7cbe +ee 7c7e +7f 3d7f +77 3d3f +6e 3c7e +66 3c3e +64 beb8 +65 be39 +6c be78 +64 be38 +75 bd39 +65 bc39 +67 bc97 +74 bd38 +65 3eb9 +e4 7eb8 +64 3eb8 +ec 7e78 +e4 7e38 +6c 3e78 +64 3e38 +75 3db9 +65 3cb9 +74 3db8 +64 3cb8 +67 bebd +6e be7e +6f be7d +f5 f719 +76 bdbc +64 bc3a +7e bd7c +e6 7e3e +e7 7e3d +77 bdbd +7e bd7e +65 bc3b +7f bd7d +67 bcbd +55 bb3b +6f bc7d +e6 7ebe +ee 7e7e +ef 7e7d +f6 7dbc +6e 3e7e +6f 3e7d +66 3e3e +67 3e3d +f7 7dbd +fe 7d7e +e5 7c3b +ff 7d7d +e7 7cbd +e6 7cbc +7e 3d7e +65 3c3b +7f 3d7d +5d 3bfb +77 3d3d +4d 3afb +67 3c3d +7e bff4 +64 beb2 +66 be36 +6c be72 +7e bf74 +64 be32 +f3 f70f +74 bdb2 +e3 f60f +fd f751 +7e bdf4 +64 bcb2 +7c bd72 +74 bd32 +6c bc72 +ec deda +66 3eb6 +fe 7ff4 +e4 7eb2 +7e 3ff4 +64 3eb2 +2f edd +ec 7e72 +6c 3e72 +7e 3f74 +64 3e32 +f4 7db2 +fe 7df4 +e4 7cb2 +f3 770f +74 3db2 +7c 3d72 +74 3d32 +6c 3c72 +7f bffd +65 bebb +67 beb7 +6d be7b +6f be77 +4d ba73 +ff 7f7d +e5 7e3b +e7 7e37 +f4 f718 +75 bdbb +7d bd7b +fd f759 +7e bdfc +64 bcba +6c bc7a +5d b973 +fe 7d7c +e4 7c3a +ff 7ffd +e5 7ebb +e7 7eb7 +ed 7e7b +ef 7e77 +cd 7a73 +6d 3e7b +6f 3e77 +7f 3f7d +65 3e3b +ed de5b +67 3e37 +4d 3a73 +f5 7dbb +37 a71f +fd 7d7b +fe 7dfc +e4 7cba +7d 3d7b +7f 3d77 +75 3d3b +fd dd5b +77 3d37 +6c 3c7a +6e 3c76 +7e 3d7c +64 3c3a +5d 3973 +55 3933 +66 beb4 +65 beb1 +64 beb0 +67 be35 +6e be74 +66 be34 +6d be71 +65 be31 +6c be70 +64 be30 +75 bdb1 +f3 f70d +74 bdb0 +e3 f60d +64 bcb0 +5d bbf3 +77 bd35 +4d baf3 +67 bc35 +63 bc2f +7d bd71 +53 bb2f +6d bc71 +62 bc2e +7c bd70 +52 bb2e +6c bc70 +ed ded9 +67 3eb5 +ec ded8 +66 3eb4 +4f 10ff +e5 7eb1 +65 3eb1 +4e 10fe +e4 7eb0 +64 3eb0 +ee 7e74 +e6 7e34 +6e 3e74 +ed 7e71 +4f 107f +e5 7e31 +6d 3e71 +65 3e31 +ec 7e70 +6c 3e70 +64 3e30 +fd ddd9 +77 3db5 +f5 7db1 +75 3db1 +f4 7db0 +e4 7cb0 +f3 770d +74 3db0 +e3 760d +64 3cb0 +63 3c2f +7d 3d71 +5b 3bef +75 3d31 +4b 3aef +65 3c31 +5a 3bee +74 3d30 +4a 3aee +64 3c30 +7e bffc +64 beba +65 beb9 +66 beb6 +6c be7a +6d be79 +6e be76 +6f be75 +74 bdb8 +f5 f711 +76 bdb4 +6f bcd7 +7c bd78 +64 bc32 +7e bd74 +fe 7f7c +e4 7e3a +e5 7e39 +e6 7e36 +e7 7e35 +75 bdb9 +77 bdb5 +7c bd7a +65 bc33 +7f bd75 +65 bcb9 +64 bcb8 +55 bb33 +6f bc75 +e5 7c39 +cd 7af3 +e7 7c35 +fe 7ffc +e4 7eba +e5 7eb9 +e6 7eb6 +ec 7e7a +ed 7e79 +ee 7e76 +ef 7e75 +f4 7db8 +ef 7cd7 +36 a71c +fc 7d78 +6c 3e7a +6d 3e79 +6e 3e76 +6f 3e75 +7e 3f7c +64 3e3a +65 3e39 +6f 3cd7 +7c 3d78 +64 3c32 +7e 3d74 +67 3c97 +74 3d38 +f5 7db9 +36 a71e +fc 7d7a +27 c9f +fe 7d76 +e5 7cb9 +e4 7cb8 +d5 7b33 +ef 7c75 +7c 3d7a +7e 3d76 +65 3c33 +7f 3d75 +75 3d39 +5f 3bd7 +6c 3c78 +55 3b33 +6f 3c75 +65 3c39 +7d bff1 +63 beaf +67 be9f +6b be6f +6f be5f +2b ae4f +fd 7f71 +e3 7e2f +e7 7e1f +f2 f70c +73 bdaf +7b bd6f +7f bd5f +fb f74d +e1 f60b +7c bdf0 +62 bcae +6a bc6e +6e bc5e +3b ad4f +2a ac4e +fd 7ff1 +e3 7eaf +e7 7e9f +eb 7e6f +ef 7e5f +67 9e3d +ab 6e4f +6b 3e6f +6f 3e5f +7d 3f71 +63 3e2f +67 3e1f +2b 2e4f +f3 7daf +f7 7d9f +1b be7 +ff 7d5f +e6 7c9e +b3 6d8f +7b 3d6f +7f 3d5f +73 3d2f +77 3d1f +6a 3c6e +6e 3c5e +7c 3d70 +62 3c2e +66 3c1e +3b 2d4f +33 2d0f +2a 2c4e +7c bff0 +62 beae +63 bead +67 be9d +6a be6e +6b be6d +6e be5e +6f be5d +f1 f709 +72 bdac +76 bd9c +23 ae8d +2a ae4e +2b ae4d +e6 7e1e +e7 7e1d +f1 f70b +72 bdae +73 bdad +77 bd9d +7a bd6e +61 bc2b +7b bd6d +7e bd5e +65 bc1b +7f bd5d +63 bcad +67 bc9d +66 bc9c +51 bb2b +6b bc6d +55 bb1b +6f bc5d +3a ad4e +21 ac0b +3b ad4d +11 ab0b +2b ac4d +fc 7ff0 +e2 7eae +4d 10fb +e3 7ead +e6 7e9e +e7 7e9d +eb 7e6d +ee 7e5e +ef 7e5d +f2 7dac +f6 7d9c +d db +a3 6e8d +66 9e3c +aa 6e4e +ab 6e4d +b2 6d8c +6a 3e6e +6b 3e6d +6e 3e5e +6f 3e5d +7c 3f70 +62 3e2e +63 3e2d +66 3e1e +67 3e1d +2a 2e4e +2b 2e4d +23 2e0d +f2 7dae +f3 7dad +f7 7d9d +e3 7cad +e2 7cac +e7 7c9d +e6 7c9c +b3 6d8d +a3 6c8d +a2 6c8c +7a 3d6e +61 3c2b +7b 3d6d +7e 3d5e +65 3c1b +7f 3d5d +72 3d2e +59 3beb +73 3d2d +5d 3bdb +77 3d1d +49 3aeb +63 3c2d +4d 3adb +67 3c1d +3a 2d4e +21 2c0b +3b 2d4d +19 2bcb +33 2d0d +9 2acb +23 2c0d +7b bfed +61 beab +63 bea7 +7f bfdd +65 be9b +74 bf38 +67 be97 +69 be6b +6b be67 +6d be5b +6f be57 +49 ba63 +4d ba53 +3b afcd +21 ae8b +29 ae4b +2b ae47 +fb 7f6d +e1 7e2b +e3 7e27 +ff 7f5d +e5 7e1b +e7 7e17 +bb 6f4d +a1 6e0b +a3 6e07 +f0 f708 +71 bdab +75 bd9b +77 bd97 +79 bd6b +7d bd5b +7f bd57 +55 b993 +59 b963 +5d b953 +39 ad4b +3b ad47 +fb 7fed +e1 7eab +e3 7ea7 +ff 7fdd +e5 7e9b +f4 7f38 +e7 7e97 +e9 7e6b +eb 7e67 +ef 7e57 +c9 7a63 +cd 7a53 +bb 6fcd +a1 6e8b +69 3e6b +6b 3e67 +6d 3e5b +6f 3e57 +7b 3f6d +61 3e2b +e9 de4b +63 3e27 +7f 3f5d +65 3e1b +67 3e17 +49 3a63 +4d 3a53 +29 2e4b +2b 2e47 +3b 2f4d +21 2e0b +23 2e07 +f1 7dab +f5 7d9b +33 a70f +f9 7d6b +dd 7953 +b1 6d8b +b3 6d87 +79 3d6b +7b 3d67 +7d 3d5b +7f 3d57 +71 3d2b +f9 dd4b +73 3d27 +75 3d1b +77 3d17 +59 3963 +5d 3953 +51 3923 +55 3913 +39 2d4b +3b 2d47 +31 2d0b +33 2d07 +b6 c71c +37 8dbf +b5 c71b +2f 26f7 +36 8dbe +76 3d3c +5c 3bfa +7c 37fa +af 4efd +a7 4ebd +a5 4cbb +b6 e736 +bf 4dfd +ae e6f6 +b7 4dbd +8f 4afd +7f b7ff +77 b53d +5d b3fb +ff 77ff +77 37bf +6f 34fd +55 33bb +2f 8ef7 +27 8c3d +d 8afb +f 8af7 +7c b7f8 +5d 3bf9 +dc 7bf8 +5c 3bf8 +7d 37f9 +5d 33f9 +fc 77f8 +dc 73f8 +7c 37f8 +5c 33f8 +4e bafc +7e b7fe +6e b6fc +76 b53c +5c b3fa +57 3bbd +46 3abc +fe 77fe +ee 76fc +dd 73f9 +76 37be +77 37bd +66 36bc +76 bd34 +5c bbf2 +7e 37f6 +7c 37f2 +77 bd3d +5d bbfb +7d b7f3 +7d b7fb +f7 7d3d +dd 7bfb +fd 77f3 +6f 3cfd +55 3bbb +f7 dd1d +dd dbdb +57 3bb7 +75 37b3 +fd 77fb +ff 77f7 +df 73f7 +75 37bb +fd d7db +77 37b7 +f7 d51d +dd d3db +57 33b7 +43 32af +5d 33f1 +c2 72ae +dc 73f0 +42 32ae +5c 33f0 +76 bd3c +5c bbfa +7c b7f2 +7c b7fa +7d b7f9 +f6 7d3c +dc 7bfa +dd 7bf9 +fc 77f2 +fc 77fa +fd 77f9 +fe 77f6 +de 73f6 +c3 72af +dd 73f1 +2d 8e7b +d3 fb2d +3d 8d7b +72 3d2c +58 3bea +78 37ea +75 bd31 +5b bbef +7b b7ef +73 b52d +59 b3eb +77 b51d +5d b3db +3b a7cf +df 7bdf +72 bd0c +58 bbca +78 b7ca +72 b50c +58 b3ca +fb 77ef +ff 77df +73 37af +f0 d722 +77 379f +6b 34ed +51 33ab +33 278f +76 3d1c +5c 3bda +f2 7d0c +d8 7bca +72 3d0c +58 3bca +7c 37da +f8 77ca +f2 750c +d8 73ca +78 37ca +72 350c +58 33ca +6d 9e73 +69 9e63 +f4 d710 +75 9db3 +7d 9d73 +27 8eb7 +2f 8e77 +2d 8e73 +29 8e63 +b6 c714 +37 8db7 +b4 c710 +35 8db3 +3f 8d77 +d3 fb25 +3d 8d73 +78 b7e8 +59 3be9 +d8 7be8 +58 3be8 +79 37e9 +59 33e9 +f8 77e8 +d8 73e8 +78 37e8 +58 33e8 +74 bd30 +5a bbee +4e badc +7a b7ee +6a b6ec +6e b6dc +72 b52c +58 b3ea +76 b51c +5c b3da +3a a7ce +de 7bde +5c bbd8 +ce 7adc +59 bbc9 +58 bbc8 +eb 764d +6c 3cf0 +52 3bae +e9 dc63 +56 3b9e +d0 db20 +57 3b9d +42 3aac +d9 db61 +46 3a9c +13 2b8d +5d b3d9 +7c b7d8 +5c b3d8 +79 b7c9 +59 b3c9 +78 b7c8 +58 b3c8 +fa 77ee +fe 77de +ea 76ec +ee 76dc +76 97bc +ba 67ce +72 37ae +73 37ad +76 379e +f0 d720 +77 379d +62 36ac +f9 d761 +66 369c +32 278e +33 278d +5d 3bd9 +dc 7bd8 +5c 3bd8 +d9 7bc9 +59 3bc9 +d8 7bc8 +58 3bc8 +dd 73d9 +7d 37d9 +5d 33d9 +fc 77d8 +dc 73d8 +7c 37d8 +5c 33d8 +f9 77c9 +d9 73c9 +79 37c9 +59 33c9 +f8 77c8 +d8 73c8 +78 37c8 +58 33c8 +7a 37e6 +78 37e2 +f4 dfb2 +1a a1ee +fe dff4 +e4 deb2 +a a0ee +f4 df92 +1a a1ce +73 bd2d +59 bbeb +77 bd1d +5d bbdb +6c bc78 +5f bbd7 +79 b7e3 +7d b7d3 +33 ad0d +19 abcb +39 a7c3 +79 b7eb +7d b7db +7f b7d7 +3b a7c7 +f9 77e3 +5a bbc6 +fd 77d3 +6b 3ced +51 3bab +64 3c38 +57 3b97 +71 37a3 +75 3793 +2b 2ccd +11 2b8b +31 2783 +7a b7c6 +5a b3c6 +78 b7c2 +f9 77eb +fb 77e7 +ff 77d7 +db 73e7 +71 37ab +f9 d7cb +73 37a7 +75 379b +77 3797 +f3 d50d +d9 d3cb +53 33a7 +31 278b +33 2787 +5e 3bd6 +5a 3bc6 +7e 37d6 +5e 33d6 +7c 37d2 +fa 77c6 +da 73c6 +7a 37c6 +5a 33c6 +f8 77c2 +78 37c2 +4e 92de +f0 ffa0 +5a 91ee +b0 efa0 +1a 81ee +f4 ff90 +5e 91de +f0 ff80 +5a 91ce +b4 ef90 +1e 81de +b0 ef80 +1a 81ce +e 28de +9a 61ee +8a 60ee +2a 24ee +3a 25ce +2e 24de +2a 24ce +1e 21de +e 20de +e ade +5a 19ce +4a 18ce +1a 9ce +a 8ce +ce 52de +8e 42de +6a 16ce +4e 12de +da 51ee +ca 50ee +9a 41ee +8a 40ee +f0 7fa0 +5a 11ee +78 b7e0 +de 51de +da 51ce +ce 50de +ca 50ce +9e 41de +9a 41ce +8e 40de +8a 40ce +7a 15ce +6a 14ce +f4 7f90 +5e 11de +f0 7f80 +5a 11ce +d8 7be0 +58 3be0 +79 37e1 +59 33e1 +f8 77e0 +d8 73e0 +78 37e0 +58 33e0 +72 bd2c +58 bbea +76 bd1c +5c bbda +5d bbd9 +5e bbd6 +78 b7e2 +79 b7e1 +7c b7d2 +78 b7ea +79 b7e9 +7c b7da +7d b7d9 +7e b7d6 +5e b3d6 +45 ba93 +5f bbd5 +dd 7bd9 +43 ba8f +5d bbd1 +42 ba8e +5c bbd0 +f8 77e2 +f9 77e1 +41 ba83 +5b bbc5 +fc 77d2 +59 bbc1 +58 bbc0 +63 b68f +7d b7d1 +43 b28f +5d b3d1 +62 b68e +7c b7d0 +42 b28e +5c b3d0 +61 b683 +7b b7c5 +41 b283 +5b b3c5 +79 b7c1 +59 b3c1 +78 b7c0 +58 b3c0 +f8 77ea +f9 77e9 +fa 77e6 +fd 77d9 +fe 77d6 +da 73e6 +d9 73e1 +de 73d6 +c5 7a93 +df 7bd5 +45 3a93 +5f 3bd5 +c3 7a8f +dd 7bd1 +43 3a8f +5d 3bd1 +c2 7a8e +dc 7bd0 +42 3a8e +5c 3bd0 +c1 7a83 +db 7bc5 +41 3a83 +5b 3bc5 +d9 7bc1 +59 3bc1 +d8 7bc0 +58 3bc0 +e3 768f +fd 77d1 +c3 728f +dd 73d1 +63 368f +7d 37d1 +43 328f +5d 33d1 +e2 768e +fc 77d0 +c2 728e +dc 73d0 +62 368e +7c 37d0 +42 328e +5c 33d0 +e1 7683 +fb 77c5 +c1 7283 +db 73c5 +61 3683 +7b 37c5 +41 3283 +5b 33c5 +f9 77c1 +d9 73c1 +79 37c1 +59 33c1 +f8 77c0 +d8 73c0 +78 37c0 +58 33c0 +ea d64c +6b 9cef +6f 9cdf +ca d24c +4b 98ef +4f 98df +7 aa37 +8a c24c +b 88ef +f 88df +6e bc7c +54 bb3a +74 b73a +74 37ba +6e 34fc +54 33ba +15 a93b +d a87b +15 a933 +f a877 +d a873 +7 a837 +5f bb7f +d7 7b3f +77 b7bf +7f b77f +5d b37b +78 3de2 +f7 773f +df 7b7f +5f 3b7f +57 3b3f +f7 77bf +ff 777f +dd 737b +7f 377f +77 373f +5d 337b +1f a37d +5 a23b +16 bbc +27 a637 +7 a237 +f9 dfc1 +1f a1fd +5 a0bb +55 bb39 +74 b7b8 +75 b739 +55 b339 +55 3bb9 +d4 7bb8 +54 3bb8 +4f 3ad7 +5c 3b78 +75 37b9 +55 33b9 +f4 77b8 +d4 73b8 +74 37b8 +54 33b8 +75 3f99 +7 a0b7 +35 a53b +2d a47b +ef deff +15 a13b +e7 de3f +d a07b +35 a533 +ef def7 +15 a133 +7d 3f59 +f a077 +e7 de37 +d a073 +75 3f19 +7 a037 +57 bbbd +5e bb7e +45 ba3b +5f bb7d +46 babc +4e ba7c +d6 7b3e +d7 7b3d +c6 7a3c +76 b7be +77 b7bd +7e b77e +65 b63b +7f b77d +66 b6bc +6e b67c +5c b37a +5d b379 +f6 773e +78 3de0 +f7 773d +e6 763c +d5 7339 +d7 7bbd +de 7b7e +ce 7a7c +5e 3b7e +56 3b3e +57 3b3d +4e 3a7c +46 3a3c +f6 77be +f7 77bd +fe 777e +e6 76bc +ee 767c +d5 73b9 +dc 737a +d7 d99f +dd 7379 +7e 377e +76 373e +77 373d +6e 367c +66 363c +5c 337a +55 3339 +5c bb72 +6e bc74 +54 bb32 +5c 3b72 +6e 3c74 +54 3b32 +fc d7da +76 37b6 +f6 d51c +dc d3da +56 33b6 +74 37b2 +94 c310 +15 89b3 +4d 987b +d 887b +4f 9877 +4d 9873 +47 9837 +f 8877 +d 8873 +7 8837 +6f bcfd +55 bbbb +5d bb7b +75 b7b3 +ef 7c7d +d5 7b3b +d7 7b37 +fc ddfa +f5 7733 +75 b7bb +7d b77b +f5 773b +fe ddfe +f7 7737 +de d9fe +d7 7337 +ef 7cfd +d5 7bbb +d7 7bb7 +dd 7b7b +df 7b77 +f5 77b3 +5d 3b7b +5f 3b77 +6f 3c7d +55 3b3b +dd db5b +57 3b37 +7d 3773 +f5 77bb +f7 77b7 +fd 777b +ff 7777 +d7 73b7 +df 7377 +7d 377b +7f 3777 +75 373b +5f 3377 +5d 3373 +1f 83fd +5 82bb +7 82b7 +5f 937d +45 923b +1f 837d +5 823b +47 9237 +f 8277 +d 8273 +7 8237 +5f 91fd +45 90bb +3f 85fd +25 84bb +15 81bb +1f 81fd +5 80bb +57 bb35 +56 bb34 +43 ba2f +5d bb71 +55 bb31 +42 ba2e +5c bb70 +54 bb30 +76 b7b4 +56 b3b4 +74 b7b0 +77 b735 +57 b335 +75 b731 +55 b331 +62 b62e +7c b770 +74 b730 +42 b22e +5c b370 +54 b330 +d6 7b34 +dc db58 +56 3b34 +3f d7f +d5 7b31 +55 3b31 +3e d7e +d4 7b30 +54 3b30 +fd d7d9 +77 37b5 +dd d3d9 +57 33b5 +f6 77b4 +d6 73b4 +fc d7d8 +76 37b4 +dc d3d8 +56 33b4 +75 37b1 +55 33b1 +f4 77b0 +74 37b0 +54 33b0 +fd ddfb +f6 7734 +dd d9fb +d6 7334 +f4 7730 +74 3730 +54 3330 +35 85b3 +27 84b7 +17 81b7 +15 81b3 +6d 947b +eb feed +55 913b +e3 fe2d +4d 907b +c3 f22d +2d 847b +b3 ef2d +1d 817b +ab eeed +15 813b +a3 ee2d +d 807b +6d 9473 +67 9437 +57 9137 +eb fee5 +55 9133 +4f 9077 +47 9037 +2f 8477 +27 8437 +1f 8177 +b3 ef25 +1d 8173 +17 8137 +ab eee5 +15 8133 +5c bb7a +5d bb79 +45 ba33 +5f bb75 +74 b7b2 +75 b7b1 +7c b772 +63 b62f +7d b771 +ee 7c7c +d4 7b3a +d5 7b39 +d6 7b36 +d7 7b35 +fc ddf8 +f5 7731 +74 b7ba +75 b7b9 +77 b7b5 +7c b77a +7d b779 +45 b233 +5f b375 +5c b372 +43 b22f +5d b371 +f4 773a +f5 7739 +f6 7736 +fe ddfc +e4 dcba +f7 7735 +d6 7336 +de d9fc +c4 d8ba +d7 7335 +dc d9f8 +3f 57f +d5 7331 +ee 7cfc +d4 7bba +d5 7bb9 +37 a73d +d6 7bb6 +dc 7b7a +dd 7b79 +c5 7a33 +df 7b75 +f4 77b2 +f5 77b1 +5c 3b7a +5d 3b79 +5e 3b76 +45 3a33 +5f 3b75 +6e 3c7c +54 3b3a +55 3b39 +dc db5a +56 3b36 +dd db59 +57 3b35 +7c 3772 +74 3732 +f4 77ba +f5 77b9 +f6 77b6 +f7 77b5 +fc 777a +f7 dd9f +fd 7779 +d6 73b6 +d7 73b5 +3f 5ff +d5 73b1 +c3 722f +d7 d997 +dd 7371 +7c 377a +7e 3776 +74 373a +75 3739 +fc d75a +76 3736 +5e 3376 +5c 3372 +dc d35a +56 3336 +69 9c6b +6d 9c5b +29 8c6b +c3 fa0d +2d 8c5b +6a bc6c +50 bb2a +70 b72a +70 37aa +6a 34ec +50 33aa +d 287b +f 2877 +d 2873 +8d c85b +7 2837 +57 bb9f +5b bb6f +5f bb5f +1b ab4f +d7 7b1f +73 b7af +77 b79f +96 ebb4 +7f b75f +5d b35b +f9 7deb +33 a78f +3b a74f +f3 772f +78 3dc2 +f7 771f +d7 7b9f +df 7b5f +6a bccc +50 bb8a +6e bc5c +54 bb1a +57 9b3d +9b 6b4f +58 bb4a +6a bc4c +50 bb0a +5b 3b6f +5f 3b5f +6d 3c71 +53 3b2f +57 3b1f +1b 2b4f +70 b78a +6a b4cc +50 b38a +a5 ecb1 +74 b71a +a9 ece1 +78 b74a +89 e8e1 +58 b34a +f3 77af +f7 779f +ff 775f +d9 736b +b3 678f +7b 376f +73 372f +77 371f +59 336b +5d 335b +d8 fbc8 +3b 274f +d0 fb88 +33 270f +19 234b +6e 3cdc +54 3b9a +ea 7ccc +d0 7b8a +6a 3ccc +50 3b8a +d8 7b4a +ea 7c4c +d0 7b0a +58 3b4a +6a 3c4c +50 3b0a +74 379a +6e 34dc +54 339a +f0 778a +ea 74cc +d0 738a +70 378a +6a 34cc +50 338a +f8 774a +f0 770a +d8 734a +ea 744c +d0 730a +f0 d700 +71 9da3 +79 9d63 +7d 9d53 +6b 9c67 +69 9c63 +6f 9c57 +6d 9c53 +b0 c700 +31 8da3 +39 8d63 +d3 fb05 +3d 8d53 +2b 8c67 +29 8c63 +2f 8c57 +3f 25fd +25 24bb +51 bb29 +4b bac7 +58 bb68 +43 ba87 +50 bb28 +70 b7a8 +71 b729 +51 b329 +6b b6c7 +78 b768 +63 b687 +70 b728 +4b b2c7 +58 b368 +43 b287 +50 b328 +51 3ba9 +d0 7ba8 +50 3ba8 +cb 7ac7 +d8 7b68 +c3 7a87 +d0 7b28 +4b 3ac7 +58 3b68 +43 3a87 +50 3b28 +71 37a9 +51 33a9 +f0 77a8 +d0 73a8 +70 37a8 +50 33a8 +eb 76c7 +f2 dd8e +f8 7768 +e3 7687 +f0 7728 +cb 72c7 +d2 d98e +d8 7368 +c3 7287 +d0 7328 +63 3687 +70 3728 +43 3287 +50 3328 +ad c4db +27 24b7 +8d c0db +7 20b7 +cc f8f0 +2f 2477 +ca f8ec +2d 2473 +ad c45b +c4 f8b0 +27 2437 +f 2077 +8d c05b +7 2037 +eb f64d +6c bcf0 +52 bbae +57 bb9d +5a bb6e +41 ba2b +5b bb6d +5e bb5e +45 ba1b +5f bb5d +46 ba9c +4a ba6c +4e ba5c +13 ab8d +1a ab4e +3d d7b +d3 7b2d +d6 7b1e +d7 7b1d +c6 7a1c +93 6b0d +72 b7ae +73 b7ad +77 b79d +61 b62b +7b b76d +95 ebb3 +af ecf5 +7e b75e +65 b61b +7f b75d +62 b6ac +66 b69c +6a b66c +85 eab1 +6e b65c +59 b369 +8d e8f1 +5c b35a +f8 7dea +32 a78e +f9 7de9 +33 a78d +3a a74e +f3 772d +f6 771e +78 3dc0 +f7 771d +67 3cbf +e6 761c +d6 7b9e +d7 7b9d +54 bb98 +de 7b5e +c6 7a9c +ca 7a6c +ce 7a5c +51 bb89 +50 bb88 +55 bb19 +93 6b8d +5c bb58 +54 bb18 +56 9b3c +9a 6b4e +59 bb49 +51 bb09 +58 bb48 +50 bb08 +5a 3b6e +5e 3b5e +6c 3c70 +52 3b2e +53 3b2d +56 3b1e +57 3b1d +4a 3a6c +4e 3a5c +42 3a2c +46 3a1c +1a 2b4e +13 2b0d +55 b399 +74 b798 +54 b398 +71 b789 +51 b389 +70 b788 +50 b388 +75 b719 +5d b359 +55 b319 +93 ebad +7c b758 +74 b718 +5c b358 +54 b318 +79 b749 +71 b709 +59 b349 +51 b309 +78 b748 +70 b708 +58 b348 +50 b308 +f2 77ae +f3 77ad +f6 779e +f7 779d +d1 dba1 +fe 775e +e2 76ac +e6 769c +6f 3cff +ee 765c +d1 73a9 +d8 736a +b2 678e +b3 678d +76 973c +ba 674e +7a 376e +72 372e +73 372d +77 371d +66 361c +58 336a +51 3329 +e9 fcc9 +32 270e +33 270d +55 3b99 +d4 7b98 +54 3b98 +d1 7b89 +51 3b89 +d0 7b88 +50 3b88 +dc 7b58 +d4 7b18 +5c 3b58 +54 3b18 +d9 7b49 +d1 7b09 +59 3b49 +51 3b09 +d8 7b48 +d0 7b08 +58 3b48 +50 3b08 +d5 7399 +75 3799 +55 3399 +f4 7798 +d4 7398 +74 3798 +54 3398 +f1 7789 +d1 7389 +71 3789 +51 3389 +f0 7788 +d0 7388 +70 3788 +50 3388 +5d 3359 +55 3319 +7d 3dfb +fc 7758 +75 3dbb +f4 7718 +5d 39fb +dc 7358 +74 3718 +5c 3358 +54 3318 +72 3dac +f1 7709 +79 3749 +71 3709 +59 3349 +51 3309 +79 3deb +f8 7748 +71 3dab +f0 7708 +59 39eb +d8 7348 +78 3748 +70 3708 +58 3348 +50 3308 +1f bfd +5 abb +7 ab7 +9f 4b7d +85 4a3b +d a7b +1f b7d +5 a3b +87 4a37 +f a77 +d a73 +7 a37 +96 e336 +9f 49fd +85 48bb +58 bb62 +58 3b62 +f8 d7ca +72 37a6 +f2 d50c +d8 d3ca +52 33a6 +70 37a2 +e 82fc +c7 58b7 +87 48b7 +a aacc +95 493b +8d 487b +4d 187b +1d 97b +15 93b +d 87b +e5 deb1 +b a0ed +e4 deb0 +a a0ec +f a0dd +e a0dc +cf 5877 +97 4937 +95 4933 +8f 4877 +8d 4873 +87 4837 +4f 1877 +4d 1873 +1f 977 +1d 973 +17 937 +15 933 +f 877 +d 873 +7 837 +6b bced +51 bbab +6f bcdd +55 bb9b +64 bc38 +57 bb97 +59 bb6b +5d bb5b +5f bb57 +71 b7a3 +75 b793 +2b accd +11 ab8b +19 ab4b +1b ab47 +31 a783 +d3 7b27 +d7 7b17 +f8 ddea +f1 7723 +fc ddda +76 3db6 +f5 7713 +71 b7ab +75 b79b +77 b797 +94 ebb0 +7d b75b +31 a78b +f9 7de3 +33 a787 +f1 772b +fa ddee +f3 7727 +fe ddde +f7 7717 +da d9ee +d3 7327 +de d9de +d7 7317 +ef 7cdd +d5 7b9b +e4 7c38 +d7 7b97 +df 7b57 +6e bcd4 +54 bb92 +f1 77a3 +f5 7793 +7e 3df6 +fd 7753 +6a bcc4 +50 bb82 +56 bb16 +ab 6ccd +91 6b8b +5c bb52 +5a bb46 +52 bb06 +b1 6783 +58 bb42 +59 3b6b +5b 3b67 +5d 3b5b +5f 3b57 +6b 3c6d +51 3b2b +d9 db4b +53 3b27 +6f 3c5d +55 3b1b +57 3b17 +79 3763 +7d 3753 +19 2b4b +1b 2b47 +2b 2c4d +11 2b0b +13 2b07 +d6 fbbc +39 2743 +72 b786 +70 b782 +7f 1ddd +65 1c9b +76 b716 +69 1ccb +7a b746 +7b 1dcd +61 1c8b +72 b706 +49 18cb +5a b346 +78 b742 +58 b342 +f1 77ab +f3 77a7 +f5 779b +f7 7797 +f9 776b +ff 7757 +d3 73a7 +df 7357 +b1 678b +b3 6787 +79 376b +7b 3767 +7f 3757 +71 372b +75 371b +5b 3367 +59 3363 +5f 3357 +5d 3353 +39 274b +d8 fbc0 +3b 2747 +31 270b +b0 e5a8 +eb dccd +d1 db8b +34 712 +be 4ff6 +a0 e4a8 +ae 4ef6 +80 e0a8 +b3 65a5 +8e 4af6 +e0 d4a8 +b8 4d48 +9e e9dc +84 e89a +b7 4d97 +a2 e4a6 +f0 7f88 +82 e0a6 +ed d653 +6e 9cf6 +9a e1e4 +80 e0a2 +ba 476c +a0 462a +eb d64f +6c 9cf2 +f2 d5a6 +c1 50a9 +d0 fb02 +ea fc44 +e2 d4a6 +b1 4fa9 +a2 c4a6 +7a 3d46 +fa d5e4 +e0 d4a2 +b8 4d42 +ba c5e4 +a0 c4a2 +78 3d42 +c7 7817 +e a25c +d2 d1a6 +b2 e724 +a1 4ca9 +ca f844 +92 c1a6 +8a e844 +61 3ca9 +c2 d0a6 +9a 4946 +38 87c2 +5a 3946 +da d1e4 +c0 d0a2 +98 4942 +2e ae5c +9a c1e4 +80 c0a2 +58 3942 +a4 ec38 +97 eb97 +62 1c26 +82 ca24 +f 28d7 +1c 2978 +94 eb38 +87 ea97 +52 1b26 +1e 3de +d5 d999 +e3 dcad +2c 6f2 +95 c999 +b3 e5a5 +a3 e4a5 +50 b1aa +a2 e4a4 +93 e1a5 +b3 472d +91 e1a1 +b1 4729 +e2 d60c +63 9caf +7d 9df1 +83 e0a5 +30 adaa +a3 462d +82 e0a4 +a2 462c +81 e0a1 +a1 4629 +80 e0a0 +a0 4628 +f3 d5a5 +e2 fea6 +b3 c5a5 +58 b948 +a2 eea6 +b1 c5a1 +ba efe4 +a0 eea2 +a3 c4a5 +61 3c03 +7b 3d45 +50 91aa +48 b848 +a2 c4a4 +e0 d4a0 +13 b85 +9e 4bfe +b8 4d40 +a0 c4a0 +5e 3bfe +78 3d40 +93 c1a5 +92 c1a4 +d1 d1a1 +e 2a5e +da fbe4 +c0 faa2 +91 c1a1 +9a ebe4 +80 eaa2 +83 c0a5 +41 3803 +5b 3945 +29 26e3 +30 8daa +82 c0a4 +c1 d0a1 +99 4941 +f 5d +81 c0a1 +59 3941 +96 e996 +b6 4f1e +ba 4f4c +a0 4e0a +b0 e58a +ba e5cc +a0 e48a +90 e18a +75 3713 +7c 9dda +9a e1cc +80 e08a +b3 6587 +b0 c58a +94 c19a +e7 f495 +90 c18a +e3 f485 +9e c1dc +84 c09a +b7 4597 +54 3332 +6e 3474 +9a c1cc +80 c08a +b3 4587 +b7 4f1d +b3 4f0d +a7 4e1d +a3 4e0d +19 2963 +a2 4e0c +4d b871 +93 c985 +1d 37b +c7 d895 +87 c895 +5d 1973 +86 c894 +83 c885 +82 c884 +19 963 +80 c880 +b0 e588 +be 4fd6 +a0 e488 +ae 4ed6 +11 29a1 +97 e397 +a4 e438 +62 1426 +82 c224 +8f 62fd +10 29a0 +90 e188 +9e 4bd6 +f6 5f3c +f 20d7 +1c 2178 +1 28a1 +94 e338 +87 e297 +52 1326 +b7 6595 +0 28a0 +80 e088 +8e 4ad6 +b3 6585 +f0 d588 +b 8047 +95 c199 +11 9a1 +9e ebdc +69 1c6b +84 ea9a +85 c099 +f d7 +1c 178 +1 8a1 +90 e92a +d6 f9bc +39 2543 +9a e96c +80 e82a +c6 f8bc +29 2443 +b3 6d27 +d8 d96a +c9 7263 +d0 d92a +79 1543 +c9 fae3 +e3 fc25 +59 b341 +c8 d86a +9a c96c +80 c82a +c6 d8bc +29 443 +b3 4d27 +b6 e596 +85 6099 +b4 e592 +b2 e586 +81 6089 +a6 e496 +be e5d4 +a4 e492 +a2 e486 +ba e5c4 +a0 e482 +92 e186 +82 e086 +6e 9cd6 +9a e1c4 +80 e082 +6c 9cd2 +f6 d596 +b6 c596 +f2 d586 +a6 c496 +fe d5d4 +e4 d492 +e2 d486 +43 b0a7 +b1 4f89 +d2 d186 +92 c186 +61 3c89 +c2 d086 +82 c086 +8b e8c7 +98 e968 +1f 23ff +39 2541 +80 e828 +f 22ff +29 2441 +68 3e42 +5f 13ff +79 1541 +1f 3ff +39 541 +28 2e42 +88 c868 +a1 4c23 +bb 4d65 +c0 d828 +4f 12ff +69 1441 +80 c828 +f 2ff +29 441 +20 e28 +0 a8a0 +b6 e594 +b3 e585 +b2 e584 +b1 e581 +54 b19a +a4 e490 +d8 f9e8 +3b 256f +a3 e485 +50 b18a +a2 e484 +39 2563 +a1 e481 +38 2560 +a0 e480 +0 a28 +96 e194 +95 e191 +36 dbc +b5 4719 +93 e185 +92 e184 +34 ad9a +26 cbc +37 a737 +a5 4619 +83 e085 +30 ad8a +82 e084 +81 e081 +b7 c595 +0 88a0 +cd f079 +a6 ee96 +b6 c594 +53 b32f +6d b471 +b4 c590 +51 b32b +6b b46d +f3 d585 +e2 fe86 +b3 c585 +a7 c495 +54 919a +a4 c490 +a3 c485 +50 918a +a2 c484 +39 563 +59 b361 +a0 c480 +d7 d195 +ed fc79 +c6 fa96 +97 c195 +6b 1c67 +ad ec79 +86 ea96 +96 c194 +33 af2f +4d b071 +95 c191 +69 1c63 +9e ebd4 +84 ea92 +d3 d185 +93 c185 +92 c184 +49 b061 +d1 d181 +da fbc4 +c0 fa82 +91 c181 +9a ebc4 +80 ea82 +c7 d095 +6d 36d3 +74 9d9a +87 c095 +2d 26d3 +34 8d9a +c5 d091 +85 c091 +c4 d090 +f1 7f21 +5b 116f +84 c090 +21 ae2b +3b af6d +83 c085 +29 26c3 +30 8d8a +c1 d081 +8d e0d1 +58 1160 +fe 7756 +81 c081 +18 160 +c0 d080 +80 c080 +8a e866 +82 e826 +5f 39df +d8 d962 +e4 fe30 +4e 907e +48 9042 +d0 d1a8 +68 36c2 +92 c926 +8 8042 +ca d866 +8a c866 +c0 d0a8 +8f ea77 +f3 55a5 +e2 7ea6 +98 4948 +82 c826 +b0 e52a +ba e56c +a0 e42a +90 e12a +7c 9d7a +9a e16c +80 e02a +b3 6527 +6c 9c7a +f8 d56a +b8 c56a +b aa4d +e8 d46a +1b b4f +d8 d16a +b 84f +98 c16a +9a c16c +80 c02a +b3 4527 +93 e925 +91 e921 +83 e825 +82 e824 +81 e821 +80 e820 +e9 5ecb +45 389b +5f 39dd +d8 d960 +d3 d925 +8a c864 +82 c824 +93 e387 +a0 e428 +ae 4e76 +83 e087 +90 e128 +9e 4b76 +6f 9cd7 +7c 9d78 +80 e028 +8e 4a76 +db d3c7 +e8 d468 +9b c3c7 +a8 c468 +3 82ad +d3 d387 +e0 d428 +13 b0d +93 c387 +a0 c428 +a1 c4a1 +5f 3bff +79 3d41 +cb d0c7 +d8 d168 +b 84d +bb cfc7 +c8 d068 +7b bfc7 +88 c068 +aa 6e66 +b3 cf87 +c0 d028 +73 bf87 +80 c028 +a2 6e26 +cf d855 +8c e85a +9a e94c +80 e80a +c9 fac3 +e3 fc05 +5d 1b79 +92 e126 +4d 1879 +7e 9d76 +f0 7f08 +82 e026 +6e 9c76 +6c 9c72 +fa d566 +ea d466 +b9 4f69 +aa c466 +c2 508c +6f 34df +e8 d462 +43 92a7 +e7 debd +d a0f9 +1b b47 +2f 24df +a8 c462 +e7 f41f +3 82a7 +da d166 +f2 5d8c +9a c166 +b2 4d8c +5f 31df +d8 d162 +f0 5d88 +b 847 +33 8fa7 +ca d066 +99 4b69 +f3 f707 +e2 5c8c +b3 e707 +a2 4c8c +59 3b69 +4f 30df +c8 d062 +23 8ea7 +f 20df +88 c062 +51 3b29 +f3 f5a5 +80 e808 +58 b16a +93 e125 +65 9c33 +7f 9d75 +92 e124 +fb 57cf +e3 dc2d +c9 daeb +2c 672 +91 e121 +63 9c2f +7d 9d71 +83 e025 +ec 56d0 +30 ad2a +55 9b33 +6f 9c75 +82 e024 +eb 56cf +80 e020 +e9 56cb +52 9b2e +6c 9c70 +c4 d818 +e1 d423 +fb d565 +a1 c423 +bb c565 +aa ee66 +56 339c +e9 d461 +6f 34dd +55 339b +e8 d460 +13 b05 +81 c023 +9b c165 +8a ea66 +71 bf23 +8b c065 +38 8d6a +70 bf22 +8a c064 +36 2f9c +c9 d061 +ed fedb +23 8ea5 +47 309d +c0 d020 +9e e956 +9c e952 +9a e946 +98 e942 +fa d76c +e0 d62a +e a05e +92 e906 +8c e852 +8a e846 +81 eaa9 +88 e842 +82 e806 +b4 e51a +b0 e50a +be e55c +a4 e41a +9e c956 +90 e10a +b5 cf13 +cf d055 +7c 9d5a +d0 d188 +92 c906 +ce d856 +cc d852 +9a e14c +80 e00a +6c 9c5a +ca d846 +8a c846 +81 caa9 +c0 d088 +e2 7e86 +8f ea57 +f3 5585 +82 c806 +40 ba20 +18 962 +59 b3e1 +fb 5547 +9a c14c +80 c00a +6c b4da +87 e23d +8 a8e0 +28 e68 +71 1f8b +d5 fb1b +ef fc5d +b 8ae5 +8f 6add +74 bdba +29 86e1 +8b 4865 +ca 5864 +2a aee6 +a aae6 +d5 f11b +b 80e5 +96 c196 +65 3c99 +19 81e1 +85 e813 +9f e955 +83 e80f +9d e951 +81 e803 +9b e945 +89 6ae1 +80 e802 +9a e944 +71 3da9 +88 6ae0 +8 2ae0 +6c 34da +96 e914 +92 e904 +8f e855 +8b e845 +88 e840 +90 c1a2 +87 e815 +31 2d21 +80 e800 +72 3d84 +29 2c61 +a3 4e8d +98 69e0 +97 633d +18 29e0 +21 2c21 +ae 4e56 +11 2921 +9e 4b56 +7c 9d58 +b1 cdab +aa 66e4 +f8 75c2 +c1 d803 +db d945 +d9 d941 +35 8f1b +4f 905d +a8 66e0 +8 22e0 +f6 75be +d8 d940 +97 c915 +8e 4a56 +6c 9c58 +96 c914 +94 eb90 +79 1d61 +d3 d905 +93 eb8f +ad ecd1 +78 1d60 +8e c854 +76 b734 +65 1cb9 +8c c850 +8a c844 +c8 d840 +32 250e +88 c840 +c7 d815 +87 c815 +86 c814 +85 c811 +84 c810 +83 ea8f +9d ebd1 +68 1c60 +47 929d +80 c800 +6 a36 +21 c21 +ce d8de +c7 7217 +95 eb91 +60 1c20 +ea 7e46 +41 1821 +85 c019 +1 821 +40 1820 +e6 7e16 +84 c018 +0 820 +c9 5ae1 +67 bc9f +c8 5ae0 +39 a54b +27 ac9f +88 4ae0 +ec 54da +be ed76 +48 1ae0 +6c 14da +f6 5dbe +e2 7e06 +11 101 +c1 d023 +db d165 +5a 134e +8b 48e5 +1 1 +98 49e0 +5f 31dd +45 309b +d8 d160 +b 845 +fd ffdb +33 8fa5 +88 48e0 +d7 533d +58 19e0 +97 433d +18 9e0 +f4 7dba +a9 46e1 +e8 56e0 +67 b49f +c8 52e0 +a8 46e0 +aa 6ee6 +8a 6ae6 +71 3fa3 +8b 40e5 +38 dea +a9 44e1 +19 1e1 +eb d4c7 +f8 d568 +e aad6 +94 e192 +e5 7cb1 +b4 471a +38 a5e0 +4a 9ac6 +14 a918 +3a f6e +1e a9d6 +66 96be +c8 5842 +3e f5e +39 a5e1 +3c a5d2 +e6 543e +9e e1d4 +84 e092 +3f dff +be 475c +d5 7bb1 +a4 461a +28 a4e0 +5a 99c6 +61 1c83 +7b 1dc5 +78 95c2 +fb 7de7 +28 a6ea +8a 686e +94 e19a +38 a5e8 +96 e196 +e7 7cb5 +b6 471e +f2 dfa4 +18 a1e0 +a1 c689 +2b 6c7 +38 768 +cb 584d +38 a5ea +39 a5e9 +2e 8c7c +14 8b3a +3c a5da +5b 13cd +41 128b +3d a5d9 +2a 8664 +73 9787 +3e a5d6 +97 69bd +e8 5442 +29 a4e9 +1e 8b7c +4 8a3a +9e e1dc +84 e09a +28 a4e8 +36 f36 +2d a4d9 +f3 dfa5 +19 a1e1 +39 769 +f6 df96 +1c a1d2 +6d 3cf1 +53 3baf +3c 75a +c6 503e +b a0e5 +2b 66d +74 1790 +a a0e4 +2a 66c +73 178f +9c e95a +28 668 +71 178b +78 95ca +ad 64db +69 94c9 +58 91c2 +4e 9ad6 +73 b585 +cf 7add +68 96e2 +ca 5866 +85 6019 +cb 5865 +6c 96d2 +c5 5ab9 +ce 5856 +b2 e506 +6d 1c59 +81 6009 +22 8c0c +8 8aca +a 8ac6 +d9 53e1 +a1 e6a9 +8e 4856 +85 4ab9 +82 e006 +6e 9c56 +c3 700d +a9 6ecb +65 9eb9 +6c 9c52 +d9 d343 +5a 99e6 +cf 7adf +4a 326e +5e 99d6 +7c 95d2 +cc f2f2 +e6 f434 +d5 59b9 +de d1d4 +c4 d092 +19 89c9 +1a 89c6 +aa c446 +a1 c6a9 +9 88c9 +6e b6fe +a6 c416 +9e c156 +6d 3c59 +75 15bb +9c c152 +da d146 +9a c146 +69 3c49 +71 15ab +ca d046 +2b ac67 +99 4b49 +c1 d2a9 +8a c046 +81 c2a9 +82 c006 +94 c190 +68 96ea +6a 96e6 +6c 96da +ce 585e +32 2d8e +6d 96d9 +cf 585d +6e 96d6 +c7 5abd +d6 d196 +4e 92d6 +fa 7fec +e0 7eaa +8d ea7b +f1 55a9 +7a 3fec +60 3eaa +8e 485e +8f 485d +87 4abd +8b 484d +38 85c8 +66 9e14 +78 95ea +9b 43e7 +7a 95e6 +49 10e9 +82 c084 +19 163 +39 af61 +7c 95da +7d 95d9 +7e 95d6 +ce f2f6 +d7 59bd +4d 10d9 +ad 64fb +69 94e9 +8b 42e7 +6d 94d9 +5e 91d6 +31 8fa3 +8f 60f7 +4b 90e5 +8d 60f3 +49 90e1 +fa f56e +3d 85d9 +46 b01c +2c aeda +ca 706e +d d9 +8e e2f6 +97 49bd +38 85ca +66 9e16 +39 85c9 +67 9e15 +42 b00c +28 aeca +ea f46e +2d 84d9 +29 84c9 +28 84c8 +4a 32c6 +b2 ef84 +1c 81d2 +18 81c2 +8b 6ae5 +9d 41d9 +b1 e5a1 +98 69e8 +b5 e591 +9c 69d8 +89 4a63 +a8 66e2 +b8 65e0 +f1 d581 +fa ffc4 +e0 fe82 +e9 f443 +d8 59c8 +ff fd5f +1b 8be7 +e8 56c2 +c9 5841 +f a8d5 +e1 dea1 +2f e5d +c8 5840 +e a8d4 +e0 dea0 +2e e5c +c4 5830 +b9 67c3 +75 97b1 +9d 69d9 +8a 4a64 +de d376 +89 68e9 +30 a502 +90 e982 +da 536e +a1 e4a1 +88 68e8 +5a 334e +8b 68e5 +1 2001 +8d 68d9 +de 535e +8c 68d8 +b8 65e2 +f8 55c2 +af 4eff +c9 5041 +f a0d5 +2f 65d +e1 d6a1 +b9 4f41 +ae 4efe +c8 5040 +8c e07a +23 e85 +f5 df39 +e a0d4 +e0 d6a0 +b8 4f40 +3d 759 +ad 4efb +c7 503d +97 e117 +21 e81 +53 3bad +3c 758 +cb 52ef +e5 5431 +16 8b16 +11 a383 +2b a4c5 +6 8a16 +d5 5331 +b a0c5 +2b 64d +b5 4f31 +d4 5330 +f1 df29 +a a0c4 +b4 4f30 +39 a5c1 +28 a4c0 +b2 eda4 +a8 66ea +90 eba8 +e7 5635 +f 8ad5 +4e 9ad4 +42 3086 +e 8ad4 +7b b7e7 +e9 56c9 +ca 52c6 +94 6118 +4b 9867 +b 88c5 +b8 65ea +ba 65e6 +c3 d80d +c 252 +96 4b36 +6f 96d5 +30 a78a +4f 92d5 +e5 d61b +ff d75d +66 9cbe +6e 96d4 +a9 64e9 +9e 4b7c +84 4a3a +cf 5855 +ad 64d9 +8f 4855 +6c 96d0 +ce 5854 +4c 92d0 +cb 5845 +8b 4845 +ac 66d2 +68 96c0 +ca 5844 +f9 55c9 +d8 51c2 +4e bad6 +35 8f93 +4f 90d5 +10 a18a +f 80d5 +d4 d9b8 +37 53f +cd 72f1 +81 c803 +9b c945 +8b c845 +52 938e +6c 94d0 +99 c941 +f 805d +c7 d89f +cd 7279 +89 c841 +4a bac6 +2a aec6 +a aac6 +b 80c5 +a 80c4 +9 80c1 +38 85c0 +18 81c0 +8 80c0 +f1 7589 +f1 d5a1 +fa ffe4 +e0 fea2 +2e 2e5e +e9 f463 +12 838c +d8 59e8 +e8 56e2 +e9 56e1 +ec 56d2 +c9 d841 +25 8e1b +3f 8f5d +9a e3ce +b4 e510 +99 e3c3 +b3 e505 +8e ca56 +7f 1ffd +65 1ebb +98 e3c2 +b2 e504 +7e 1ffc +64 1eba +88 6ac0 +b1 6589 +5c b15a +7a 1f4c +60 1e0a +92 e30e +ac e450 +b5 c591 +be efd4 +a4 ee92 +ad e453 +9c 49d8 +58 b14a +b1 c581 +ba efc4 +a0 ee82 +a9 e443 +98 49c8 +8e e2fe +a8 e440 +ac 46d2 +47 189d +a8 46c2 +43 188d +8a e2ce +a4 e410 +89 e2c3 +a3 e405 +50 b10a +86 e2be +a0 e400 +60 bea8 +93 43a5 +e7 56bd +8f 68d5 +67 16bd +f1 5fa1 +66 16bc +f0 5fa0 +e5 56b9 +e4 56b8 +64 16b8 +e3 56ad +8b 68c5 +63 16ad +62 16ac +98 69c0 +e0 56a8 +e 20dc +88 68c0 +34 ad1a +83 e005 +55 9b13 +6f 9c55 +30 ad0a +81 e001 +53 9b0f +6d 9c51 +80 e000 +52 9b0e +6c 9c50 +37 fbf +51 1101 +ba c5cc +a0 c48a +e1 d4a1 +9f 4bff +b9 4d41 +e1 d403 +fb d545 +db 53ed +c1 52ab +ea fe46 +b7 6fb5 +5e 39fc +dd 7359 +44 38ba +df d3ff +f9 d541 +e8 fe42 +9f c3ff +b9 c541 +a8 ee42 +c 22d0 +7f 375f +96 6bb4 +89 c061 +d2 d184 +b1 cd8b +aa 66c4 +e9 54e1 +1a 8bc6 +e8 54e0 +a8 66c0 +11 ab23 +2b ac65 +8 22c0 +7b 374f +92 6ba4 +f6 759e +94 c312 +ae c454 +d2 d30e +ec d450 +59 1961 +a5 c491 +8d e8d1 +58 1960 +f4 ff32 +37 8f9d +a1 c481 +38 560 +f0 ff22 +33 8f8d +99 e343 +1a a9e6 +88 48c8 +ce d2fe +e8 d440 +b8 45c2 +8c c2d2 +a6 c414 +ca d2ce +e4 d410 +8a c2ce +a4 c410 +88 c2c2 +a2 c404 +c6 d2be +e0 d400 +86 c2be +a0 c400 +f 20d5 +e1 56a1 +75 15b9 +f0 57a0 +61 bea9 +6a bc46 +e0 56a0 +81 c003 +9b c145 +8a ea46 +93 638f +ad 64d1 +96 4b16 +b 20c5 +c8 d06a +fb 5567 +b9 65c1 +a9 64c1 +b8 65c0 +a8 64c0 +b4 cf12 +ce d054 +74 bf12 +8e c054 +65 14b9 +b2 cf0e +cc d050 +27 8e95 +72 bf0e +8c c050 +71 bf03 +8b c045 +ce fafc +38 8d4a +af ceff +c9 d041 +6f beff +89 c041 +ae cefe +c8 d040 +23 8e85 +6e befe +88 c040 +6d bed3 +87 c015 +6c bed2 +86 c014 +ab cecf +c5 d011 +6b becf +85 c011 +aa cece +c4 d010 +6a bece +84 c010 +a7 cebf +c1 d001 +67 bebf +81 c001 +a6 cebe +c0 d000 +66 bebe +80 c000 +e8 56ea +16 211e +e9 56e9 +17 211d +7f bfff +99 c141 +88 ea42 +cf 5ad5 +aa 6ccc +90 6b8a +ec 56da +7f b7f7 +ed 56d9 +b1 ed8b +ee 56d6 +e ad4 +cd 5ad1 +8d 4ad1 +4d 1ad1 +cc 5ad0 +8c 4ad0 +4c 1ad0 +c ad0 +c9 52e1 +cb 5ac5 +91 e98b +ce 52d6 +c9 5ac1 +89 4ac1 +bf ed57 +49 1ac1 +c8 5ac0 +88 4ac0 +8 ac0 +f6 5d9e +ac 46da +ae 46d6 +a8 46ca +8e 42d6 +6c 94d8 +b 8867 +a2 c486 +cf 58d5 +c5 d013 +df d155 +ce fa56 +8f 48d5 +4e 18d4 +cd 58d1 +61 3ea9 +6a 3c46 +8a ea44 +f9 fd63 +22 8c8c +60 3ea8 +c1 d003 +db d145 +ca fa46 +8b 48c5 +4a 18c4 +d9 59c1 +99 49c1 +bf cfff +d9 d141 +c8 fa42 +89 48c1 +ee 76f6 +59 19c1 +be 47f6 +d8 59c0 +98 49c0 +e dc +be cffe +d8 d140 +88 48c0 +d7 531d +58 19c0 +f8 55ea +f9 55e9 +fa 55e6 +fc 55da +fd 55d9 +6f 16d5 +30 278a +4f 12d5 +fe 55d6 +2f 6d5 +f 2d5 +e9 54e9 +ed 56d1 +cd 52d1 +ad 46d1 +8d 42d1 +6d 16d1 +4d 12d1 +d7 5bb5 +b7 4fb5 +ca 7266 +d 2d1 +d8 51e2 +cb 52c5 +78 1fca +2a 6c4 +a 2c4 +b1 4fa3 +cb 50e5 +78 1dea +c6 7216 +20 c20 +c9 50e1 +e9 56c1 +c9 52c1 +c4 d810 +d3 5ba5 +e8 56c0 +af 6c77 +51 9b23 +6b 9c65 +c8 52c0 +8f 6877 +4b 9865 +a8 46c0 +11 8b23 +2b 8c65 +88 42c0 +b 8865 +28 6c0 +b2 4fa4 +8 2c0 +f6 559e +ad 447b +bd 45d9 +be 45d6 +b8 45ca +b9 45c9 +d0 d1a2 +c8 f840 +30 258a +3c 8fd8 +35 f93 +4f 10d5 +10 218a +1c 8bd8 +f d5 +d3 538f +ed 54d1 +93 438f +ad 44d1 +53 138f +6d 14d1 +d2 538e +ec 54d0 +b1 4f83 +cb 50c5 +78 1dca +71 3f83 +8b 40c5 +38 dca +31 f83 +4b 10c5 +18 8bc8 +b c5 +a c4 +e9 54c1 +c9 50c1 +89 40c1 +69 14c1 +49 10c1 +f8 55c0 +e8 54c0 +b8 45c0 +a8 44c0 +78 15c0 +68 14c0 +38 5c0 +e8 deca +62 3ea6 +18 948 +fa 7fe4 +e0 7ea2 +8d ea73 +f1 55a1 +7a 3fe4 +60 3ea2 +fe f7dc +e4 f69a +e7 549f +eb 546f +46 12b4 +85 e0b9 +43 10a7 +62 bea4 +84 e0b8 +42 10a6 +61 bea1 +60 bea0 +4d 10f3 +e3 7ea5 +e9 dec9 +63 3ea5 +4b 10ef +e1 7ea1 +23 8c85 +61 3ea1 +e5 f699 +4a 10ee +e0 7ea0 +22 8c84 +60 3ea0 +e4 f698 +db 516f +36 fb4 +7a bfcc +60 be8a +7e 3fdc +64 3e9a +75 1599 +fa 7fcc +e0 7e8a +8d ea5b +f1 5589 +38 7ca +c7 509f +cb 506f +26 eb4 +ca 506e +64 3e98 +60 3e88 +b7 6597 +73 9585 +7a bfc4 +60 be82 +8a 42e4 +b0 e502 +e6 7e96 +c4 d098 +f7 5595 +40 18a0 +fe 7fd4 +e4 7e92 +f5 5591 +f6 55be +7e 3fd4 +64 3e92 +75 1591 +fa 7fc4 +e0 7e82 +8d ea53 +f1 5581 +62 be84 +84 e098 +42 1086 +a8 64e0 +61 be81 +60 be80 +4f 10df +e5 7e91 +65 3e91 +4e 10de +e4 7e90 +64 3e90 +4b 10cf +e1 7e81 +61 3e81 +4a 10ce +e0 7e80 +60 3e80 +9b 416f +9a 416e +9f 415f +e8 7e6a +fa 7f6c +e0 7e2a +f1 5529 +8c 68d0 +7a 3f6c +60 3e2a +71 1529 +8a 406e +8f 405f +69 be69 +49 106b +68 be68 +97 e11d +48 106a +60 be28 +11 8b0b +2b 8c4d +69 3e69 +60 3e28 +76 159e +f6 7d1c +dc 7bda +12 ba4 +4d b279 +63 14ad +6a 3e66 +2b ecd +e8 7e62 +21 8ea9 +2a 8c46 +68 3e62 +94 e390 +79 1561 +7a 3f64 +60 3e22 +71 1521 +fe f75c +7f bdff +e4 f61a +6a be64 +8c e078 +4a 1066 +69 be61 +49 1063 +62 be24 +84 e038 +42 1026 +ea 7e64 +e9 7e61 +11 8b03 +2b 8c45 +69 3e61 +6e bcfc +54 bbba +ed f659 +b2 4f84 +60 3e20 +65 bcbb +7f bdfd +e4 f618 +8f e0df +f0 7f20 +5a 116e +4a 3a6e +f4 7f10 +5e 115e +6c be5a +7d 9559 +9f 4357 +7a bf4c +60 be0a +93 4307 +43 10ad +fd 5559 +6c 3e5a +8b e2e7 +7d 1559 +e8 7e4a +f9 5549 +68 3e4a +79 1549 +fa 7f4c +e0 7e0a +f1 5509 +6c 94da +7a 3f4c +60 3e0a +71 1509 +ca 52ee +e4 5430 +32 5ae +6d be59 +d7 593f +e3 7e0d +4d 105b +75 97bb +68 be48 +48 104a +70 97aa +89 4ae1 +ed 54db +64 be18 +97 4315 +ce 58fe +60 be08 +36 59e +e0 fc80 +ed 7e59 +f5 57bb +ec 7e58 +f4 57ba +6c 3e58 +74 17ba +69 3e49 +71 17ab +e8 7e48 +f0 57aa +68 3e48 +70 17aa +e4 7e18 +23 4ad +6c be52 +6a be46 +5b 13ed +41 12ab +3c a5fa +68 be42 +62 be06 +34 a5ba +7a bf44 +60 be02 +d5 f91b +b 88e5 +26 49e +b0 4d82 +e9 7e49 +f1 57ab +6e 3e56 +4c 9058 +ec 7e52 +e2 fe2e +fc ff70 +25 8e99 +6c 3e52 +6a 3e46 +3c 25fa +e8 7e42 +f8 ff60 +21 8e89 +df 53ff +f9 5541 +14 a93a +bd cd5b +37 2d37 +34 a53a +ee defe +14 a13a +df 7177 +9b 61e7 +e4 7690 +7e 977c +64 963a +1c a952 +8d e851 +34 85ba +45 3013 +5f 3155 +62 9e06 +2c ac58 +3e 85fc +24 84ba +35 2f13 +4f 3055 +1c ab58 +14 81ba +25 2c13 +3f 2d55 +c a858 +42 9a06 +e a87e +1e 81fc +4 80ba +37 5b7 +ca f2ec +34 853a +c2 f22c +2c 847a +a aae4 +d4 d9b0 +37 537 +3e a7fc +f7 7db7 +24 a6ba +6f be55 +1c 8b5a +4f 1057 +77 97b7 +6e be54 +bb e54f +45 12b9 +4e 1056 +76 97b6 +6d be51 +75 97b3 +6c be50 +1d 837b +d6 5936 +74 97b2 +68 be40 +48 1042 +70 97a2 +66 be14 +e9 7e69 +46 1016 +38 a5c8 +43 1805 +16 19e +72 bf26 +e0 5e08 +3e 27fc +24 26ba +95 c911 +4a 38ec +c9 7249 +1e 23fc +4 22ba +7f 35df +f8 d562 +4b ba45 +f7 dfbd +1d a1f9 +53 93a7 +3e 25fc +24 24ba +49 12c3 +63 1405 +29 ec3 +43 1005 +10 8b08 +15 a1bb +f7 df3f +1d a17b +22 e2c +e6 de3e +c a07a +dc f9f0 +c2 f8ae +3f 2577 +6f 3e55 +1c b5a +b9 e7c9 +77 17b7 +6e 3e54 +b8 e7c8 +76 17b6 +ed 7e51 +f5 57b3 +6d 3e51 +75 17b3 +ec 7e50 +f4 57b2 +6c 3e50 +74 17b2 +2e 6d4 +6b 3e45 +18 b4a +3d 25f9 +b5 e7b9 +73 17a7 +ea 7e44 +f2 57a6 +bc 65f8 +6a 3e44 +b4 e7b8 +72 17a6 +3c 25f8 +a9 cee3 +c3 d025 +39 8741 +ac 46d0 +15 8b33 +2f 8c75 +e9 7e41 +f1 57a3 +2c 6d0 +b6 4fb4 +69 3e41 +71 17a3 +67 3e15 +39 25c9 +e6 7e14 +b8 65c8 +66 3e14 +38 25c8 +8c 4a7a +b1 6529 +c a7a +31 2529 +1e b7c +4 a3a +29 24e9 +9a c966 +4f 9a7f +ab 4c4f +9 aae9 +af e477 +9e 49fc +84 48ba +ea feec +54 913a +b 8a6f +6e 9cd4 +54 9b92 +8c 487a +1c 97a +c 87a +be 47fc +a4 46ba +ef d6df +70 9d82 +9e 43fc +84 42ba +cf d2df +50 9982 +7e 17fc +64 16ba +57 931d +1c 29d2 +8d 68d1 +de 5356 +3e 7fc +24 6ba +1e 3fc +4 2ba +ac 467a +78 9d42 +be 477c +a4 463a +ef d65f +70 9d02 +8c 427a +58 9942 +d6 db94 +2c 67a +e6 deb6 +c a0f2 +1e 37c +4 23a +a 2a6e +f2 d584 +d6 d31c +57 99bf +df d35d +c5 d21b +46 98be +4e 987e +b4 45ba +ac 6c58 +d4 7990 +8b 686d +be 45fc +a4 44ba +9c 6b58 +94 41ba +8c 6858 +c2 5a06 +e7 74b5 +9e 41fc +84 40ba +74 15ba +6c 3c58 +90 e182 +7e 15fc +64 14ba +b7 47b5 +5e 11fc +44 10ba +97 43b5 +34 5ba +2c 2c58 +14 1ba +c 2858 +ed d4d9 +67 34b5 +c7 5a3f +1e 1fc +4 ba +92 c30c +13 89af +a 886e +90 c902 +6 801e +de 737c +c4 723a +8c 407a +90 cb08 +a 2ae4 +24 ae38 +c6 583e +7e 97fc +64 96ba +8f 4255 +1c a9d2 +3c f5a +b2 c5a6 +81 40a9 +90 eb02 +aa ec44 +67 96bf +1f a9d7 +c9 5843 +3f f5f +ba c566 +d2 518c +6f 967f +1d b5b +1f 21df +98 c162 +b0 4d88 +4d 927b +60 be20 +1b 83ed +1 82ab +3d a7d3 +9b 436d +81 422b +55 91bb +4d b859 +5e 91fc +44 90ba +1c 95a +55 3b13 +6f 3c55 +b9 e5c9 +77 15b7 +e2 fe2c +4c 907a +7f 1577 +d1 f321 +3b 856f +11 81ab +9 a849 +19 816b +f7 553f +52 1384 +1 2201 +8 88c8 +21 c83 +3b dc5 +d5 513b +30 f80 +ff 7df7 +2c a6fa +1f 297f +17 293f +70 bdaa +e3 562d +c3 f0a5 +39 a7c1 +f9 f749 +7a bdec +60 bcaa +93 41a7 +29 a6c1 +8b 6845 +8 a860 +71 1f0b +6 9e +90 4982 +26 ae9c +c4 fab8 +27 263f +71 bda9 +b1 cf23 +cb d065 +6b 344f +82 68a4 +9e 61fc +40 90a8 +84 60ba +a9 4ec3 +c3 5005 +70 1d0a +90 e902 +6 a01e +dc f9f8 +3f 257f +f0 7daa +b9 67c1 +70 3daa +c3 70a5 +39 27c1 +b0 c52a +3 aa0d +7a 3dec +f9 7749 +60 3caa +80 eaa8 +1d 1f3 +b3 6fa5 +2a ae6c +b 6d +54 1190 +d7 59bf +c6 58be +8f 42d5 +ce 587e +6c 96fa +8a 486e +57 193f +4e 187e +46 183e +f 255 +1b 96f +84 c890 +13 92f +a 86e +e7 56bf +df 53fd +c5 52bb +e5 d413 +ff d555 +ee fe56 +4a 106e +e0 7e20 +9c c1da +16 21b6 +9b 43ed +81 42ab +a1 c403 +bb c545 +aa ee46 +71 3f89 +3 a0a7 +ff 557f +e6 54be +ee 547e +9d 69f9 +d3 5ba7 +bb 456f +16 3b4 +aa 446e +7f 157f +77 153f +92 e106 +7e 9d56 +4d 1859 +d3 710d +b9 6fcb +75 9fb9 +66 143e +91 c323 +ab c465 +58 916a +eb 7eed +55 113b +97 4335 +d9 d9e9 +3c 570 +22 42e +19 16b +39 af69 +11 12b +31 af29 +f1 7da9 +f0 7da8 +b 2867 +70 3da8 +f a05f +a3 c487 +b0 c528 +60 3ca8 +e3 54a7 +88 484a +c1 7a03 +db 7b45 +f1 f703 +e0 5c88 +72 bda6 +41 38a9 +ef f6ff +70 bda2 +e3 5625 +8f 42d7 +9c 4378 +74 9f10 +68 34c2 +f1 7703 +f8 ddca +72 3da6 +8 a2c0 +f0 7da2 +66 34be +88 e84a +f4 f59a +6 a2bc +64 9e10 +b2 c526 +81 4029 +6b b64f +82 eaa4 +fa 7de4 +e0 7ca2 +3 aa05 +69 b64b +80 eaa0 +fe f5dc +e4 f49a +73 bda5 +f1 f701 +72 bda4 +71 bda1 +60 bca0 +69 34c1 +f9 ddc9 +73 3da5 +40 b8a8 +8e e856 +f1 7da1 +67 34bd +90 c1aa +e3 f4a5 +f4 f598 +f a057 +ea fc46 +e1 fea9 +e5 fe99 +29 86c1 +74 bd9a +c3 f085 +7e bddc +64 bc9a +97 4197 +e7 fe95 +c7 7095 +b a6f +2 2aac +c3 7085 +fa 7dcc +e0 7c8a +7a 3dcc +60 3c8a +80 ea88 +1d 1d3 +b3 6f85 +75 bd99 +74 bd98 +71 bd89 +70 bd88 +65 bc99 +64 bc98 +97 4195 +61 bc89 +75 3d99 +62 1e24 +5d b173 +74 3d98 +5c b172 +71 3d89 +59 b163 +70 3d88 +58 b162 +31 d21 +be ef5c +a4 ee1a +d6 7316 +d9 51e1 +a 88c6 +64 3c98 +4c b072 +e0 7c88 +3e a77e +23 aea7 +d2 7306 +60 3c88 +48 b062 +99 6b49 +ca f046 +c1 f2a9 +da 516e +a3 44a7 +48 384a +2d 84f3 +c3 f2a5 +2a 64c +41 3aa1 +c5 f299 +76 bd96 +45 3899 +66 96bc +aa 66ce +ef f6df +70 bd82 +e3 5605 +66 bc96 +1d ab73 +f1 7d89 +76 3d96 +39 a7cb +f2 7d86 +72 3d86 +ef 76df +70 3d82 +b6 c516 +17 a137 +85 4019 +e1 7c89 +3f a77f +66 3c96 +1d 2b73 +99 6b63 +b2 c506 +62 3c86 +82 ea84 +19 2b63 +fa 7dc4 +e0 7c82 +d 227b +77 bd95 +ab 66cd +76 bd94 +13 ab2f +2d ac71 +aa 66cc +72 bd84 +29 ac61 +a6 66bc +71 bd81 +a5 66b9 +66 bc94 +76 3d94 +f5 7d91 +75 3d91 +74 3d90 +73 3d85 +40 b888 +f1 7d81 +e0 d420 +67 349d +71 3d81 +63 9ca7 +e2 d604 +e7 7c95 +bf e557 +49 12c1 +94 499a +e5 7c91 +e4 7c90 +9a c3ce +b4 c510 +64 3c90 +e3 7c85 +e1 7c81 +e0 7c80 +aa ec46 +a1 eea9 +83 40a7 +d 80f3 +a3 eea5 +2f 2ed7 +3c 2f78 +6e bcdc +54 bb9a +78 bd6a +b1 ef23 +cb f065 +7a bd6c +60 bc2a +93 4127 +29 a641 +32 a70e +f8 7d6a +68 3c6a +88 ea68 +a1 6e23 +bb 6f65 +f9 55c1 +52 bb86 +79 bd69 +9b 6b67 +63 bc87 +70 bd28 +8 2242 +92 6b26 +8b 6a67 +5b bbc7 +68 bc68 +8a 6a66 +53 bb87 +60 bc28 +79 3fe3 +93 4125 +82 6a26 +e3 7c87 +2a a6cc +f0 7d28 +63 3c87 +70 3d28 +d3 7b87 +e0 7c28 +49 3869 +a3 e407 +92 498c +78 bd62 +b0 c5a8 +d2 73a6 +41 3829 +93 e307 +82 488c +68 bc62 +a0 c4a8 +78 3d48 +c2 72a6 +33 8dad +7a 3d66 +78 3d62 +c7 7837 +e a27c +68 3442 +9 a8e3 +88 e240 +66 343e +68 3c62 +88 ea60 +2a a6e4 +58 3342 +28 a6e0 +8a 6864 +61 bc23 +7b bd65 +60 bc22 +7a bd64 +78 bd60 +37 859f +58 bbe2 +72 bd24 +51 bb23 +6b bc65 +18 896a +e8 76c0 +14 93a +50 bb22 +6a bc64 +e7 76bf +68 bc60 +fe f576 +27 849f +ff 77fd +e5 76bb +60 bc20 +b7 459f +54 333a +6e 347c +df f35d +c5 f21b +46 b8be +51 3b23 +6b 3c65 +18 96a +69 3c61 +b2 4d84 +e0 7c20 +82 e804 +7c bd5a +b5 ef13 +cf f055 +b1 ef03 +cb f045 +7a bd4c +60 bc0a +93 4107 +eb 56e5 +a 2a6c +fa 7d4c +e0 7c0a +87 c23d +8 88e0 +7a 3d4c +60 3c0a +80 ea08 +7d bd59 +3c 85d8 +79 bd49 +75 bd19 +74 bd18 +70 bd08 +65 bc19 +46 309c +d9 d161 +64 bc18 +7d 3fd3 +97 4115 +7d 3d59 +c7 72b7 +79 3d49 +a1 c4a9 +c3 72a7 +70 3d08 +f5 55bb +8 ae0 +ec 7c58 +f4 55ba +2f ae77 +5e 9b7c +44 9a3a +92 c9ae +8b 62e7 +b9 ef49 +77 1f37 +69 b4e9 +e8 7c48 +f0 55aa +2b ae67 +65 3c19 +96 c116 +e0 7c08 +7e bd56 +4d 3859 +55 11bb +7c bd52 +7a bd46 +49 3849 +51 11ab +78 bd42 +b0 c588 +90 c92a +89 6263 +72 bd06 +41 3809 +4a 1ae4 +ef f65f +70 bd02 +a0 c488 +62 bc06 +bc 45d8 +f9 7d49 +7e 3d56 +7c 3d52 +fa 7d46 +b8 45c8 +f8 7d42 +31 8d89 +a 2a64 +39 a74b +f2 7d06 +e9 7c49 +f1 55ab +6e 3c56 +8e ea54 +6c 3c52 +8c ea50 +3a a5e6 +a8 44c8 +28 a6c0 +92 41a6 +8a 6844 +96 693c +34 a7b8 +1e a97e +5 a83b +1f a97d +9c 63d8 +f a87d +8c 62d8 +97 693d +c3 788d +3e a556 +35 a7b9 +87 683d +25 a6b9 +1d 9db +2e a456 +86 683c +f7 7db5 +24 a6b8 +86 4a36 +58 31ea +15 a939 +4b 9ae7 +5 a839 +4a 9ae6 +4 a838 +36 a5bc +43 122d +59 bbc3 +73 bd05 +14 a1b8 +42 ba04 +22 c06 +cf f25f +50 b902 +6b 96e7 +4 a038 +3e a57e +27 a4bd +d1 5329 +b1 eda1 +26 a4bc +b0 eda0 +65 3e11 +b9 ed61 +77 1fb5 +4 8ab8 +37 fb5 +15 a1b9 +43 ba05 +f6 df3e +1c a17a +3b f6d +21 e2b +f7 df3d +1d a179 +5 a0b9 +13 b07 +4 a0b8 +bd c5d9 +37 25b5 +12 b06 +e7 de3d +d a079 +43 9227 +6 803c +e8 7c4a +92 c9ac +8b 62e5 +77 1f35 +52 312c +38 2fea +15 89b9 +5 88b9 +6a b6ee +a8 cee2 +c2 d024 +38 8740 +58 11ea +15 8939 +5 8839 +4 8838 +1d bf3 +37 d35 +4e 9a7e +4f 9a7d +60 3ca0 +e4 f498 +da 5166 +3 8aad +9b cbc7 +a8 cc68 +6c 9678 +4c 9278 +cf 58df +a 8a6e +b 8a6d +54 9b90 +91 c309 +b 22e5 +12 89ac +35 85b9 +a7 6e17 +63 9e05 +3e affc +24 aeba +25 84b9 +e7 d41d +cd d2db +47 32b7 +15 81b9 +87 6a17 +43 9a05 +1e abfc +4 aaba +d7 d11d +bd cfdb +37 2fb7 +5 80b9 +c7 d01d +ad cedb +27 2eb7 +f dd +cc 7072 +71 bd29 +93 6b27 +34 85b8 +a6 6e16 +62 9e04 +61 bc29 +83 6a27 +24 84b8 +e6 d41c +cc d2da +46 32b6 +d6 593c +4c 1058 +87 4237 +2c ae7a +3e af7c +24 ae3a +76 959c +1d 8179 +3f 2f77 +86 4236 +2f 84d7 +3c 8578 +21 8ca1 +f 80d7 +1c 8178 +3e 2f76 +4 8038 +1d 3f3 +37 535 +92 492c +8 48 +30 87a8 +65 bc13 +7f bd55 +5f 3bfd +45 3abb +e7 f41d +cd f2db +3 82a5 +d5 d31b +4f 32f7 +ef d45d +56 99be +84 6ab8 +4 2ab8 +45 983b +5f 997d +61 bc03 +7b bd45 +5b 3bed +41 3aab +8b 68cf +47 98bd +60 bc02 +7a bd44 +5a 3bec +40 3aaa +51 11a9 +ae 667c +4f 987d +5e b3f6 +cc 52d8 +4e 987c +5f bbff +79 bd41 +a7 cc9f +ad 6679 +5d bbd3 +77 bd15 +5c bbd2 +76 bd14 +2b 2cef +aa 664c +91 c30b +b 22e7 +ab c44d +12 89ae +55 bb13 +6f bc55 +77 95b7 +1c 895a +13 89ad +8c 6a78 +84 6a38 +4 2a38 +b 886d +a 886c +59 3369 +53 998f +ff 77dd +e5 769b +4d bad3 +67 bc15 +4c bad2 +66 bc14 +4b bacf +65 bc11 +4a bace +64 bc10 +d6 593e +e2 7e0c +4c 105a +6c be58 +74 97ba +ce f276 +d7 593d +4d 1059 +7e 9556 +b9 67cb +75 97b9 +14 29b8 +4 28b8 +c7 583d +3d f59 +c6 583c +3c f58 +3a 8546 +9 49 +31 87a9 +83 482d +2a 8446 +21 86a9 +82 482c +f3 5da5 +20 86a8 +b2 c5a4 +6e 967e +f4 d592 +6 82b4 +60 bc08 +79 3fc3 +93 4105 +82 6a06 +f6 fdb6 +25 26b9 +cb f8cf +2e 2456 +84 62b8 +cf f2dd +50 b980 +24 26b8 +64 943a +7e 957c +a7 6cb7 +63 9ca5 +4c 927a +23 86ad +85 4831 +2d 6d1 +ea 7666 +a6 cc9e +ac 6678 +5e bbfe +78 bd40 +a4 6638 +ef f65d +56 bbbe +70 bd00 +72 15ae +86 c89e +8c 6278 +58 b940 +84 6238 +cf f25d +50 b900 +24 2638 +8 aac0 +4 2238 +32 85ac +35 25b9 +63 3e05 +25 24b9 +1a b4c +0 a0a +15 21b9 +43 3a05 +f0 5522 +5 20b9 +34 25b8 +62 3e04 +24 24b8 +33 8f85 +84 6038 +65 3c13 +7f 3d55 +54 91ba +4c b858 +4 8a38 +37 f35 +e3 7c0f +fd 7d51 +63 3c0f +7d 3d51 +ef f675 +c4 5ab8 +af e675 +84 4ab8 +e2 7c0e +fc 7d50 +57 3b95 +62 3c0e +7c 3d50 +7e 957e +65 943b +7f 957d +c4 f818 +5d 3bd3 +77 3d15 +44 b818 +5c 3bd2 +76 3d14 +5e bbfc +44 baba +f2 ff2c +5c 917a +4c ba7a +89 60cb +45 90b9 +1d 959 +58 3bc2 +72 3d04 +d3 7b0f +ed 7c51 +f5 55b3 +cc 5a78 +d2 51ae +68 b6c8 +c4 5a38 +8c 4a78 +92 41ae +e1 7c83 +fb 7dc5 +28 a6c8 +8a 684c +84 4a38 +52 11ae +44 1a38 +12 1ae +a 284c +4 a38 +51 3b03 +6b 3c45 +18 94a +b5 e5b9 +73 15a7 +92 438e +ac 44d0 +f1 55a3 +cd 7ad3 +e7 7c15 +4d 3ad3 +67 3c15 +14 91a +cb 7acf +e5 7c11 +4b 3acf +65 3c11 +a7 449f +ca 7ace +e4 7c10 +4a 3ace +64 3c10 +8c e2f2 +a6 e434 +95 49b9 +d2 f384 +3c 85d2 +ba 47ee +a5 e433 +bf e575 +94 49b8 +95 e333 +af e475 +84 48b8 +b7 e517 +41 1281 +d4 513a +1d 979 +b2 452e +5 2a11 +84 4838 +4 838 +97 41bd +87 6abd +a4 46b8 +f1 d5ab +8f 6a7d +61 3e29 +24 6b8 +4d b253 +96 69bc +f5 7db3 +3c a7f8 +a2 ec8c +ec 5678 +e4 5638 +82 e88c +cc 5278 +c4 5238 +ac 4678 +f5 579b +a4 4638 +8c 4278 +22 ac8c +6c 1678 +64 1638 +44 b0b0 +44 1238 +a3 e60d +24 acb0 +24 638 +4 a0b0 +c 278 +8 8ac0 +4 238 +a5 44b9 +b4 ef12 +ce f054 +85 40b9 +94 eb12 +ae ec54 +91 e181 +55 11b9 +5e 3bfc +44 3aba +45 10b9 +76 95b6 +54 bb12 +6e bc54 +35 5b9 +63 1e05 +3e 2ffc +24 2eba +15 1b9 +1e 2bfc +4 2aba +43 1a05 +b4 45b8 +6 2a3e +17 13d +a4 44b8 +16 293c +2c 2e7a +1d 179 +c 2a7a +19 89c1 +4d 32f9 +15 139 +1e 2b7c +4 2a3a +c7 5097 +d4 5138 +77 3f97 +84 4038 +f6 7db4 +3d a7f9 +87 68bd +d8 5342 +2e a4d6 +86 68bc +8e 687c +e5 7cb3 +ff 7df5 +2c a6f8 +d7 799f +1e 297e +f 287d +62 96ac +a6 66be +72 bd86 +41 3889 +a7 66bd +73 bd85 +b6 65bc +72 370e +a3 6ca5 +19 23c1 +c3 5205 +cf f2df +50 b982 +70 1f0a +85 62b9 +51 b981 +58 b942 +94 61b8 +31 8f83 +8f 60d7 +4b 90c5 +9c 6178 +6a 344c +50 330a +81 68a1 +d2 5326 +8a 60ce +46 90bc +e5 fc39 +2e 267e +b8 6f62 +dd fbf9 +26 263e +a9 ccc9 +23 2ca5 +1e 237c +4 223a +25 8e11 +d0 7308 +51 39ab +a7 64bd +a6 64bc +5a 39ec +d9 7349 +40 38aa +f7 759f +c2 d0a4 +38 87c0 +c2 7a06 +b9 4fc3 +d3 5105 +3 8a0d +6a 344e +9b 69e5 +81 68a3 +ba cf6c +a0 ce2a +d3 5327 +85 60b9 +84 60b8 +70 1d08 +3e 257e +25 243b +3f 257d +3a 8f6c +20 8e2a +95 e339 +53 1327 +c3 582d +15 29b9 +6a 9446 +39 f49 +9 a8c1 +29 e49 +5 28b9 +5a 9346 +5 2039 +87 e21d +8 a8c0 +28 e48 +4 2038 +e7 f437 +d6 59bc +4c 10d8 +7c 97f8 +f0 5d22 +2a 86c6 +8a 4a6e +a3 e427 +92 49ac +8 c8 +99 c3e3 +b3 c525 +a2 ee26 +f1 5da3 +38 87e8 +4e 1a7e +59 33eb +73 352d +46 1a3e +56 193c +a a6e +f0 7d88 +b 2847 +a1 4e89 +33 afa7 +b a6d +54 1b90 +f9 5d4b +3 a2d +f1 5d0b +12 92c +fa f7c4 +e0 f682 +d6 59be +e2 7e8c +4c 10da +c5 583b +df 597d +7d 97f9 +c7 58bd +6e 94d6 +fa 7f6e +3d fd9 +d7 f337 +c6 58bc +3c fd8 +cf 587d +3a 85c6 +9 c9 +f2 5da4 +39 87e9 +2a 84c6 +e8 76e2 +93 e327 +82 48ac +89 c2e3 +a3 c425 +50 912a +8a 486c +d3 598f +56 193e +bc 4772 +4f 187d +46 183c +1a 96e +12 92e +78 3762 +b 86d +58 b36a +54 1990 +3 82d +68 3662 +f6 55bc +c5 52b9 +ee fe54 +82 e88e +9c e9d0 +cc 527a +83 e88d +cd 5279 +a3 46ad +ab 466d +f4 5790 +38 adea +b2 45ac +15 3b1 +1d 2bf9 +f8 5f62 +63 1ca5 +8c 42f8 +23 62d +fe 557e +e7 54bd +e6 54bc +e 885e +cc 7a7a +d0 d900 +b2 45ae +5 2a91 +ba 456e +d 2a51 +aa 6e6e +da 7bec +c0 7aaa +d1 51a9 +a3 44ad +a2 44ac +7e 157e +76 153e +5d 13fb +77 153d +93 e105 +65 9c13 +7f 9d55 +4d 12fb +67 143d +76 bd9e +2b 86c5 +f1 5d21 +92 e104 +4c 3a7a +17 239f +90 c322 +aa c464 +ea 7eec +54 113a +8d 42f3 +a7 4435 +8d 42f9 +55 1139 +5e 3b7c +44 3a3a +2a 2e6e +c8 72e0 +32 52e +74 1590 +9 2eb +23 42d +fa 77ec +e0 76aa +3c 85d0 +22 848e +5a 33ec +40 32aa +e1 76a9 +ea 7446 +61 36a9 +6a 3446 +b a8e7 +8a e244 +d6 519e +8d 407b +d3 518d +b4 4538 +a7 4497 +c7 f295 +ce 7a5e +d4 7330 +3e 57e +d2 732c +3c 57a +be c5dc +a4 c49a +da 73e4 +c0 72a2 +2 8086 +3 8885 +2 8884 +b3 ef0d +1d 815b +c6 509e +c7 509d +92 e1a4 +dc 51d0 +c2 508e +c3 508d +8 aa48 +21 2e03 +3b 2f45 +62 b6a4 +61 b6a1 +c3 7825 +39 2f41 +60 b6a0 +38 2f40 +23 4a7 +19 2b41 +18 2b40 +5e bbdc +44 ba9a +55 9199 +2e 2e74 +5a bbcc +40 ba8a +2a 2e64 +e3 76a5 +88 6a48 +97 e33d +18 a9e0 +a1 ce89 +2b ec7 +38 f68 +e9 d6c9 +63 36a5 +8 2a48 +fa d5c4 +e0 d482 +e2 76a4 +e1 76a1 +23 8485 +61 36a1 +e0 76a0 +22 8484 +60 36a0 +2d 4f3 +c3 72a5 +da d1c4 +c0 d082 +2b 4ef +c1 72a1 +3 8085 +41 32a1 +2 8084 +40 32a0 +de 7bdc +c4 7a9a +d5 5199 +5e 3bdc +44 3a9a +55 1199 +d3 f325 +3d 8573 +da 7bcc +c0 7a8a +d1 5189 +7a b7cc +60 b68a +49 1861 +5a b3cc +40 b28a +31 8d2b +2a 2664 +6 a09c +45 ba99 +5e 33dc +44 329a +c4 7a98 +44 3a98 +2c ae72 +c0 7a88 +a8 ee62 +40 3a88 +39 8561 +28 ae62 +69 bc69 +42 ba86 +cd d851 +5a bbc4 +40 ba82 +41 3289 +d5 5191 +5e 3bd4 +44 3a92 +55 1191 +d1 5181 +96 419e +93 418d +19 29c1 +61 16a9 +b 88e7 +8a c244 +62 b686 +7a b7c4 +60 b682 +9e 415e +b3 cf0f +cd d051 +5a b3c4 +40 b282 +d2 fb0e +ec fc50 +46 ba94 +42 ba84 +22 c86 +40 ba80 +e6 7696 +40 10a0 +e1 7689 +66 3696 +fe 77d4 +e4 7692 +7e 37d4 +64 3692 +e2 7686 +fa 77c4 +e0 7682 +9a 414e +a4 c498 +20 ca0 +c1 7289 +fb f56f +24 8498 +66 94be +f0 dda2 +de 73d4 +c4 7292 +5e 33d4 +44 3292 +2f cdf +c5 7a91 +45 3a91 +f2 55ae +2e cde +c4 7a90 +44 3a90 +43 3a85 +f0 55a2 +42 3a84 +2b ccf +c1 7a81 +41 3a81 +2a cce +c0 7a80 +40 3a80 +86 409e +3d 2f7b +87 409d +83 408d +4a 92e6 +8e 405e +42 b284 +22 486 +40 b280 +3a 5c4 +20 482 +f0 d5a8 +75 3f1b +8f 405d +e6 7694 +e5 7691 +65 3691 +ca 52e6 +e3 7685 +13 892f +c2 582c +be 67fc +a4 66ba +60 96a8 +38 f48 +8c e2f8 +4a 12e6 +63 3685 +c1 d021 +e2 7684 +e1 7681 +c0 5828 +61 3681 +e0 7680 +49 bae3 +63 bc25 +47 3295 +d2 730e +ec 7450 +f2 dda4 +c6 7294 +ab 64cf +67 94bd +25 c1b +3f d5d +2f 4df +c5 7291 +45 3291 +f0 dda0 +47 b835 +43 3285 +ce 72fe +e8 7440 +41 3281 +40 3280 +71 3f0b +8b 404d +da 7b6c +c0 7a2a +d1 5129 +5a 3b6c +40 3a2a +51 1129 +76 159c +48 ba68 +28 c6a +d7 59b5 +4 82b8 +37 7b5 +41 ba29 +40 ba28 +fc ddd2 +e8 766a +4c 18d0 +14 8932 +b 884d +49 3a69 +a 884c +84 42b8 +71 1d09 +68 b642 +3 880d +4 2b8 +41 3a29 +2 880c +40 3a28 +b2 e5a6 +81 60a9 +67 149d +f1 5d81 +66 149c +f0 5d80 +1d 379 +63 148d +62 148c +19 369 +68 b668 +61 b629 +60 b628 +49 b269 +72 158e +d3 d985 +29 46b +da d9c4 +c0 d882 +36 8f9e +b5 ef11 +1f 815f +41 b229 +3b 56d +21 42b +40 b228 +3a 56c +20 42a +e3 dc8f +fd ddd1 +e9 7669 +63 9c8f +7d 9dd1 +69 3669 +fc ddd0 +e2 dc8e +e8 7668 +b 804d +15 8931 +3 800d +2 800c +54 9990 +40 3228 +8b c24d +c 88f0 +fe 7fdc +e4 7e9a +f5 5599 +4a 3a66 +48 3a62 +59 1161 +51 1121 +52 118c +9 69 +3a 8566 +4e 3a5e +6a 3666 +5 831 +22 8406 +13 238f +2d 24d1 +b7 6db5 +42 ba24 +22 c26 +fe 77dc +e4 769a +4a 3266 +48 3262 +2 8006 +b4 e512 +cb 7a65 +c8 d842 +3e 8f5e +ca 7a64 +3a 7ce +f1 dd89 +c9 7a61 +b 8845 +49 3a61 +92 4b84 +2 8804 +40 3a20 +92 e1a6 +b2 472e +fd d753 +7e 9df6 +47 109d +d1 5981 +43 108d +2b 8467 +42 108c +2a 8466 +68 b660 +48 3a68 +ac ecfa +61 b621 +60 b620 +4a b264 +2a 466 +8e e8fe +43 b225 +42 b224 +d9 d9e1 +22 426 +8c e8fa +41 b221 +40 b220 +3a 564 +20 422 +f2 dd2c +d8 dbea +eb 7665 +e8 d442 +e7 de9d +d a0d9 +f1 dd2b +ea 7664 +48 ba4a +11 8303 +2b 8445 +b2 4784 +5e bb5c +44 ba1a +55 9119 +5a bb4c +40 ba0a +e8 dce8 +e1 7621 +d9 d34b +53 3327 +5a 99ee +e0 7620 +60 3620 +4f 98d5 +f 82d5 +d5 5931 +d2 d92c +cb 7265 +e5 7699 +b4 65b2 +70 95a0 +c8 d042 +55 bbb3 +6f bcf5 +3e 875e +d1 d92b +ca 7264 +f1 d589 +fa ffcc +e0 fe8a +c3 d887 +d0 d928 +c9 7261 +b 8045 +92 4384 +3 8005 +48 98e8 +8c 68fa +41 3221 +2a 46e +c0 7220 +2 8004 +40 3220 +dd 5159 +4c 3a5a +5d 1159 +a7 46b7 +c8 7a4a +d9 5149 +48 3a4a +a3 46a7 +59 1149 +68 3e68 +de 7b5c +c4 7a1a +d5 5119 +5e 3b5c +44 3a1a +55 1119 +d6 f336 +c5 58bb +df 59fd +da 7b4c +c0 7a0a +d1 5109 +5a 3b4c +40 3a0a +51 1109 +36 59c +32 58c +9d ebf1 +83 eaaf +6c b65a +4f 38df +c8 d862 +99 ebe1 +68 b64a +7e b75c +95 ebb1 +64 b61a +4c b25a +5e b35c +44 b21a +5a b34c +40 b20a +48 ba48 +28 c4a +a3 c6a5 +90 e900 +6 a01c +c2 5226 +d5 719b +45 ba19 +44 ba18 +3e d5c +24 c1a +40 ba08 +c4 f298 +f7 7795 +e8 764a +b4 cd12 +7f 3dff +fe 775c +e4 761a +7b 3def +fa 774c +e0 760a +4c 325a +c8 724a +94 c912 +48 324a +14 8912 +b1 478b +5f 39ff +de 735c +c4 721a +5e 335c +44 321a +e8 7c40 +ce 7afe +5b 39ef +da 734c +c0 720a +5e bbd4 +44 ba92 +55 9191 +5a 334c +40 320a +c 88d2 +e4 7c30 +ca 7aee +c1 50ab +db 51ed +cd 7a59 +cc 7a58 +c8 7a48 +c5 7a19 +c4 7a18 +44 3a18 +c0 7a08 +27 a6bf +b1 4d81 +4f 38dd +c8 d860 +32 252e +68 b648 +d2 512e +96 e3be +b0 e500 +e6 7e94 +65 b619 +64 b618 +61 b609 +47 389d +c0 d820 +60 b608 +4c b258 +48 b248 +b2 4d2e +44 b218 +40 b208 +aa 4cee +4c ba52 +4a ba46 +f6 dfbe +1c a1fa +3b fed +21 eab +48 ba42 +42 ba06 +14 a1ba +69 3ceb +e8 7648 +9a cbce +b4 cd10 +66 3cbc +e5 7619 +65 3cbb +7f 3dfd +e4 7618 +7b 3ded +61 3cab +e0 7608 +4d 38fb +cc 7258 +4c 3258 +49 38eb +c8 7248 +94 c910 +48 3248 +14 8910 +45 38bb +5f 39fd +c4 7218 +45 ba91 +41 3209 +35 d3b +cb 7aed +41 38ab +5b 39ed +c0 7208 +44 ba90 +40 3208 +c 88d0 +ce 7a56 +de fb74 +c4 fa32 +7 8a9d +c9 7a49 +4e 3a56 +2c 8c58 +cc 7a52 +c2 fa2e +dc fb70 +5 8a99 +4c 3a52 +ca 7a46 +4a 3a46 +28 8c48 +c8 7a42 +d8 fb60 +1 8a89 +bf 4fff +d9 5141 +48 3a42 +3f fff +59 1141 +3b fcf +55 1111 +16 19c +60 3680 +35 a5b3 +c3 d825 +39 8f41 +65 b439 +89 e869 +e a876 +81 e829 +6 a836 +1c a972 +14 a932 +35 a5bb +3d a57b +42 122c +ee fc5c +d4 fb1a +a 8ae4 +6e 94de +2c a47a +a1 e4a9 +81 e0a9 +1f 355 +5 213 +8f 4af7 +b1 6d0b +6d 9cf9 +74 3f98 +6 a0b6 +dd dbf9 +26 63e +b1 e529 +d0 7980 +a1 e429 +91 e129 +81 e029 +8f 4a77 +74 3f18 +6 a036 +34 a532 +ee def6 +14 a132 +c7 7a3d +7 881d +c5 7a39 +5 8811 +2d c7b +c3 7a2d +5 283b +1f 297d +6d 9673 +b8 e5c8 +76 15b6 +46 9a36 +18 81ea +51 b3a3 +6b b4e5 +e 8a76 +9 8a6b +b 8a67 +52 3b0e +6c 3c50 +74 15b2 +69 b4e1 +c 8a72 +31 a521 +29 8663 +72 9786 +3c a5d8 +b7 e51f +41 1289 +85 c213 +9f c355 +6 88b6 +3b 7ed +21 6ab +1a 36e +d1 d929 +56 9936 +8a 426e +46 9836 +5e 13fc +44 12ba +99 c969 +1e 8976 +91 c929 +16 8936 +89 c869 +e 8876 +81 c829 +77 15b5 +1c 958 +6 8836 +54 9932 +88 426a +4c 9872 +e0 7e88 +c 8872 +b4 6598 +d0 d1a0 +3 885 +d6 d314 +57 99b7 +5f 9977 +c5 d213 +df d355 +46 98b6 +c9 d869 +4e 9876 +75 95b3 +53 bb0f +6d bc51 +e aad4 +7d 9573 +9e 4bdc +af e657 +84 4a9a +90 c308 +a 22e4 +11 89ab +65 b43b +7f b57d +92 c304 +13 89a7 +1b 8967 +2e 8676 +46 129c +d0 5b80 +26 8636 +e 8276 +fd 7f73 +26 e9c +31 85a3 +a6 669c +ab ecc7 +b8 ed68 +b5 4d13 +13 abad +2c 8672 +44 1298 +c 8272 +fb 7f6f +24 e98 +f 82df +d5 593b +26 84b6 +81 c0a9 +59 3949 +a3 6ea7 +49 ba69 +72 1d8e +de 597c +c4 583a +1a a9ce +42 ba0e +5c bb50 +cc f858 +14 81b2 +c a850 +d5 7b13 +ef 7c55 +f7 55b7 +54 11ba +a7 44b5 +4c 3858 +91 492b +c1 d029 +46 9036 +15 b39 +92 c386 +b9 c569 +a8 ee6a +d 79 +56 119c +b1 c529 +ba ef6c +a0 ee2a +36 8536 +5 39 +d2 d92e +cb 7267 +2e 8476 +d0 5980 +46 109c +a1 c429 +ca d8ee +c3 7227 +26 8436 +72 bf86 +99 c169 +88 ea6a +bb 6f67 +1e 8176 +36 d9c +62 be86 +89 c069 +ab 6e67 +fd 7d73 +37 a717 +26 c9c +81 c029 +a3 6e27 +74 9532 +b0 c5a2 +8e eafe +a8 ec40 +1d a9d3 +3d f5b +6a 3cec +50 3baa +e9 7649 +9b cbcf +b5 cd11 +6e 3656 +3f 25df +b8 c562 +d0 5188 +b aa45 +f7 f51f +13 83a7 +6d 967b +7 81d +ea 7646 +85 4811 +6a 3646 +5 811 +e8 7642 +f8 f760 +21 8689 +83 480d +68 3642 +c3 d087 +d0 d128 +3 80d +4f ba55 +eb decd +11 a109 +1f b57 +4e ba54 +2e c56 +56 93b6 +9b e14f +25 eb9 +1d b53 +e6 7616 +40 1020 +62 3cac +e1 7609 +66 3616 +4d ba51 +4c ba50 +c2 7a04 +2c c52 +4f 9277 +67 1e9d +4d 9273 +65 1e99 +4c ba5a +5d 9159 +e2 7606 +62 3606 +4a ba44 +97 e13f +21 ea9 +52 93a6 +2a c46 +f6 dfbc +1c a1f8 +48 ba40 +28 c42 +23 86a7 +46 ba14 +c9 7a69 +26 c16 +f2 df8c +18 a1c8 +2b 8667 +43 128d +40 ba00 +11 2383 +2b 24c5 +6 a16 +29 24c1 +b3 6da5 +af 46d5 +e6 5cbe +b 8267 +9 8263 +97 e11f +f8 7f60 +21 e89 +f6 df9c +1c a1d8 +c 2a72 +15 131 +a 2a4e +4e 3256 +11 8b81 +6e 947e +7f 3ddf +f8 dd62 +53 9ba7 +cc 7252 +c2 f22e +dc f370 +5 8299 +4c 3252 +6c 9472 +ca 7246 +4a 3246 +28 8448 +48 3242 +62 b684 +6 8a1c +46 3216 +95 69b9 +ea d446 +e1 d6a9 +4b b067 +b9 4f49 +b1 4783 +13 a9ad +c2 7206 +c 2872 +83 4227 +a5 643b +bf 657d +61 9429 +96 619c +9b 4365 +81 4223 +94 6198 +75 95bb +6d bc59 +69 bee3 +83 c025 +7d 957b +75 3f13 +8f 4055 +d2 7b0c +3c d5a +e1 d4a9 +b9 4d49 +66 94b6 +3e d56 +35 fb9 +44 ba12 +5e bb54 +6c 947a +3d f79 +92 c924 +8 8040 +55 91b3 +4d b851 +5f 9177 +f3 ff25 +5d 9173 +c1 d0a9 +99 4949 +46 90b6 +1e 956 +4e 9076 +1d b79 +77 b717 +66 1c9c +31 85ab +29 ac49 +39 856b +3b 8567 +53 118d +11 81a3 +9 a841 +86 629c +1b 8167 +19 8163 +f 28ff +8e 625c +f5 553b +50 1380 +f7 5537 +69 b669 +3a a5ce +a 88c4 +d7 5137 +6d b651 +87 c21d +8 88c0 +4 38 +d5 5133 +8a 42c6 +10 8b00 +df f17f +69 1ee9 +cf 7a55 +d7 53b7 +4f 3a55 +fc 5572 +99 e3c9 +57 13b7 +ce 7a54 +d6 53b6 +4e 3a54 +98 e3c8 +56 13b6 +cd 7a51 +4d 3a51 +fa 556e +cc 7a50 +35 a7b3 +4c 3a50 +7b 354f +92 69a4 +8e 42d4 +cb 7a45 +ba cfec +a0 ceaa +9d 61f9 +d3 53a7 +e 2d4 +4b 3a45 +f8 5562 +95 e3b9 +53 13a7 +50 11a0 +f6 7796 +4a 3a44 +94 e3b8 +52 13a6 +f6 5fbc +1c 21f8 +8c 42d0 +f 8875 +c9 7a41 +eb 54e5 +d1 53a3 +c 2d0 +49 3a41 +6b 14e5 +51 13a3 +c8 7a40 +ea 54e4 +d0 53a2 +47 3a15 +f4 5532 +46 3a14 +f2 5f8c +18 21c8 +2f c5f +c5 7a11 +45 3a11 +f2 552e +40 3a00 +cc 5a72 +22 ac06 +f1 7521 +8c 4a72 +b1 6521 +4c 1a72 +71 3521 +69 34e1 +c a72 +31 2521 +29 24e1 +91 4981 +96 49b6 +2c aed0 +b aae5 +86 48b6 +76 3796 +35 8539 +d4 59b2 +7e 37dc +64 369a +ea fee4 +54 9132 +4b ba65 +d 827b +c6 5836 +3c f52 +9e 4976 +3c 87f2 +72 97ae +6a 96ee +49 ba61 +1a a9c6 +3a f4e +54 1932 +4c 1872 +5e 1974 +44 1832 +1c 972 +14 932 +c 872 +1e 974 +4 832 +c3 d8a5 +3d a7f3 +c3 5805 +b8 6de8 +ac 4672 +47 183d +8c 4272 +c3 d82d +c 272 +9d c95b +17 2937 +c 287a +b a2e5 +86 40b6 +29 a6e1 +8b 6865 +8 862 +42 3a0e +5c 3b50 +6c bc5a +9f 4157 +54 11b2 +4c 3850 +c9 f8eb +2c 2472 +18 836a +e6 5436 +96 e93e +4b b265 +c8 d8e0 +2b 467 +c6 5036 +f3 dd0d +d9 dbcb +53 3ba7 +3c 752 +be 4576 +f0 d580 +ae 4476 +b5 6db3 +71 9da1 +9e 4176 +d0 d180 +8e 4076 +b4 ed3a +69 b661 +3a a5c6 +94 e93a +49 b261 +29 463 +1a a1c6 +bc 4572 +ac 4472 +7 2b7 +9c 4172 +8c 4072 +c1 d8ab +db d9ed +3e 574 +24 432 +44 b230 +22 ae2e +3c af70 +34 af30 +2c ae70 +1e 174 +4 32 +24 ae30 +28 ac60 +52 11a4 +b4 c598 +af 46fd +30 da0 +f2 77a6 +36 8d3e +cc faf0 +2f 2677 +2e 8cfe +c4 fab0 +ad c65b +27 2637 +14 893a +d 2273 +34 8598 +80 c008 +a2 6e06 +8d c25b +e 88fe +7 2237 +d1 d323 +eb d465 +a2 6ca4 +18 23c0 +d4 f9b0 +bd c55b +37 2537 +e7 d495 +1f 2177 +e5 d491 +27 cbf +a6 461c +5d 1bd9 +6e b654 +f6 7db6 +3d a7fb +3c a7fa +d6 5136 +67 b615 +35 a7bb +4c 3a58 +f2 7d8e +a7 46b5 +6e 14fc +54 13ba +65 b611 +34 a7ba +64 b610 +ce 50f6 +ae ecde +63 b605 +cf d27d +50 9920 +94 6932 +32 a7ae +8b 4a67 +a9 4663 +bc 65d8 +f2 5786 +4e b254 +cb d8cf +2e 456 +f6 ddb6 +25 6b9 +4c b250 +b6 4d36 +4a b244 +c7 d8bf +2a 446 +48 b240 +c5 d8bb +df d9fd +28 442 +b2 4d26 +46 b214 +c3 d88f +dd d9d1 +c9 7269 +26 416 +1a 896e +f1 dd8b +ea 76c4 +92 6b86 +b9 6d69 +44 b210 +ae 4cf6 +40 b200 +aa 4ce6 +f0 7d82 +88 cac8 +2 2aa4 +f9 d563 +66 349e +b a67 +f0 5fa8 +29 663 +b4 e798 +72 1786 +3c 25d8 +34 2598 +e6 f436 +d5 59bb +17 831f +dd 597b +ce 5876 +6c 96f2 +c2 7a0e +dc 7b50 +a2 e426 +91 49ab +e5 743b +ff 757d +29 aec1 +98 c3e2 +b2 c524 +7d 357b +99 496b +9b 4967 +39 87e3 +b1 45a3 +5f 1977 +55 193b +4c 187a +85 4a33 +9f 4b75 +d 251 +97 4b35 +e 2ad4 +7d 1573 +e6 d494 +6c 1472 +7e 1574 +64 1432 +13 29ad +1b 967 +86 c096 +11 92b +59 99c1 +9d 69d3 +75 9d1b +6e 3654 +74 9d18 +6d 3651 +be 4d7c +a4 4c3a +f2 dd0c +d8 dbca +52 3ba6 +eb 7645 +6b 3ce7 +f1 dd0b +ea 7644 +1f a97f +71 9d0b +6a 3644 +4 a83a +1e a97c +37 8d17 +c0 fa28 +78 3d6a +b1 6f23 +cb 7065 +c7 52b7 +91 6109 +8b eae7 +7d 1d59 +82 e80e +9c e950 +ee dcdc +d4 db9a +e7 7615 +67 3cb7 +ed dcdb +e6 7614 +6d 9cdb +66 3614 +ec dcd8 +66 3cb4 +e5 7611 +57 3317 +5e 99de +6c 9cd8 +65 3611 +b6 4d3c +9c 4bfa +65 3cb3 +7f 3df5 +e4 7610 +64 3610 +98 e940 +e0 d628 +e a05c +4d ba59 +ea dccc +d0 db8a +e3 7605 +48 326a +5c 99d2 +4c ba58 +c2 7a0c +2c c5a +e9 dccb +63 3ca7 +e2 7604 +69 9ccb +62 3604 +e8 dcc8 +62 3ca4 +e1 7601 +53 3307 +5a 99ce +61 3ca3 +7b 3de5 +e0 7600 +60 3600 +72 b70e +a3 eca5 +c3 522d +19 a3c1 +ab 4667 +8b 4267 +89 4263 +f9 5569 +d2 5386 +9c 61d8 +f 88d5 +37 d3f +cd 7af1 +b 4d +95 4931 +33 87ad +86 483e +1c 8972 +a1 6c2b +bb 6d6d +55 933b +6f 947d +66 3c9c +f9 dd61 +7f 3ddd +65 3c9b +f8 dd60 +4d 38f3 +cc 7250 +4c 3250 +9 a2c1 +9a 496c +80 482a +3d d73 +d3 7b25 +4d 98db +46 3214 +99 6969 +39 d43 +cf 7af5 +44 3210 +35 d33 +cb 7ae5 +40 3200 +c8 dae0 +2b 667 +36 259c +e0 7e08 +16 219e +9 263 +f6 5f9c +1c 21d8 +3 227 +16 219c +14 2198 +c6 50b6 +f3 dd8d +3c 7d2 +68 16c0 +b3 45a7 +9a c1ec +80 c0aa +83 4a2d +2a 8646 +91 41a3 +27 a6bd +89 6841 +75 153b +90 e102 +7c 9d52 +d7 5195 +a4 cc98 +7e 157c +64 143a +b7 4735 +5f 1177 +c2 fa2c +2c 8c7a +f3 7f25 +5d 1173 +c6 d094 +3e 8d7c +24 8c3a +eb 7ee5 +55 1133 +e2 7e24 +4c 1072 +5e 1174 +44 1032 +39 56b +31 52b +d0 d9a0 +33 527 +9e e9fe +53 b325 +f0 7d08 +13 127 +33 af25 +11 123 +31 af21 +99 63c1 +90 c12a +c9 f2e3 +e3 f425 +70 b5aa +38 2d4a +5a b1ec +40 b0aa +18 294a +c3 7007 +65 9eb3 +7f 9ff5 +f9 d5cb +73 35a7 +f0 75aa +70 35aa +e6 56be +5a 99c4 +40 9882 +9e 69d6 +7a 35ec +60 34aa +b3 67a5 +80 e2a8 +50 b9a8 +50 31aa +a3 64a5 +5a 31ec +40 30aa +93 63a5 +65 1eb3 +7f 1ff5 +d1 79a9 +78 b5c2 +51 39a9 +50 39a8 +68 b4c2 +91 c129 +9a eb6c +80 ea2a +b3 6f27 +16 8136 +c0 78a8 +83 c087 +90 c128 +df fbfd +c5 fabb +28 2642 +b2 6f26 +40 38a8 +f7 759d +71 b5a9 +66 9c3c +aa 6c4e +4c 9afa +93 63a7 +61 b4a9 +39 2d49 +83 62a7 +51 b1a9 +41 b0a9 +19 2949 +68 3e40 +70 17a2 +f1 75a9 +71 35a9 +80 e002 +9a e144 +a7 ee9f +72 1f2e +e1 74a9 +61 34a9 +8a e044 +c1 70a9 +f2 f5a6 +41 30a9 +e0 5488 +72 b5a6 +54 9b10 +48 30c2 +44 9a10 +92 c126 +61 3c29 +70 b5a2 +3a 2d46 +31 2fa9 +38 2d42 +c0 5088 +52 b1a6 +a7 e69f +72 172e +50 b1a2 +bf e7dd +a5 e69b +70 172a +1a 2946 +18 2942 +f2 75a6 +74 9710 +f8 d5ca +72 35a6 +f0 75a2 +e2 74a6 +64 9610 +e8 d4ca +62 34a6 +82 e2a4 +73 1f2d +71 1f29 +5 8ab9 +e 8856 +d2 71a6 +54 9310 +d8 d1ca +52 31a6 +da d94c +c0 d80a +d0 71a2 +c2 70a6 +44 9210 +38 27c2 +c8 d0ca +42 30a6 +92 6b06 +4e 9af4 +2a 844c +10 830a +41 b8a1 +61 1e29 +4f b0d7 +5c b178 +60 1e28 +f7 f595 +36 27be +49 30c1 +c2 d224 +d5 f199 +39 2fc1 +79 bfe3 +93 c125 +82 ea26 +78 bfe2 +92 c124 +5d 317b +72 b5a4 +71 b5a1 +8 a848 +10 81aa +21 2c03 +3b 2d45 +62 b4a4 +8b c0c7 +98 c168 +ba 6f66 +61 b4a1 +1f 2bff +39 2d41 +60 b4a0 +52 b1a4 +51 b1a1 +71 1729 +5 82b9 +e 8056 +50 b1a0 +63 162d +42 b0a4 +62 162c +6b bcc7 +78 bd68 +9a 6b66 +41 b0a1 +61 1629 +19 2941 +40 b0a0 +18 2940 +60 1628 +f3 75a5 +c0 f0a8 +fa 7f64 +23 e8d +e0 7e22 +22 8c06 +f9 d5c9 +73 35a5 +18 2948 +40 b0a8 +90 eb08 +4e 1af6 +f8 d5c8 +72 35a4 +a5 46b9 +b5 6d93 +71 9d81 +4f b8f7 +ce f254 +f0 75a0 +9 82c1 +54 b99a +e9 d4c9 +63 34a5 +8 2848 +10 1aa +20 2c22 +3a 2d64 +e0 74a0 +d3 71a5 +56 b19e +d1 5121 +2 8806 +d9 d1c9 +53 31a5 +d8 d1c8 +52 31a4 +c0 d808 +85 42b9 +8e 4056 +ae ee54 +d0 71a0 +c9 d0c9 +43 30a5 +c2 70a4 +38 27c0 +c8 d0c8 +42 30a4 +eb 7cc7 +32 a70c +f8 7d68 +c1 70a1 +37 27bd +c0 70a0 +89 42c1 +d4 799a +1 2281 +a7 6c95 +ab c4c7 +b8 c568 +da 79cc +c0 788a +74 b59a +70 b58a +94 e312 +ae e454 +9d 49d9 +7a b5cc +60 b48a +5e b1dc +44 b09a +77 3597 +5a b1cc +40 b08a +65 9e93 +7f 9fd5 +74 359a +10 2ba0 +70 358a +7c 9fd8 +7e 35dc +64 349a +b7 6795 +0 2aa0 +55 b999 +54 b998 +51 b989 +e 287e +50 b988 +54 319a +a7 6495 +50 318a +a3 6485 +5c 9bd8 +d8 d9e0 +3b 567 +8 806a +41 b223 +5b b365 +45 b899 +41 b889 +5e 31dc +44 309a +5a 31cc +40 308a +93 6385 +4c 9ad8 +65 1e93 +7f 1fd5 +d5 7999 +c2 5a24 +55 3999 +42 1a24 +d4 7998 +54 3998 +3c ad72 +51 3989 +72 97ac +b6 67be +d0 7988 +2e a47e +b8 ed62 +50 3988 +38 ad62 +11 921 +9e eb5c +84 ea1a +c4 7898 +7 aab7 +44 3898 +2c ac72 +39 8563 +5a 3bcc +40 3a8a +51 1189 +2f 2cff +ae 665c +c0 7888 +3 aaa7 +a8 ec62 +a 20e4 +90 c108 +6e 9ef4 +b2 6f06 +40 3888 +28 ac62 +75 b599 +71 b589 +66 9c1c +4c 9ada +65 b499 +61 b489 +55 b199 +51 b189 +45 b099 +41 b089 +75 3599 +62 1624 +71 3589 +56 b996 +76 1f1e +65 3499 +51 3189 +39 a563 +45 3099 +76 b596 +41 3089 +72 b586 +2b a6ef +d1 7989 +56 3996 +e2 7ca6 +29 a6eb +8b 686f +e a87c +57 b99f +52 3986 +9 2863 +92 c106 +61 3c09 +42 3886 +8b 4ae5 +ef 54df +74 b592 +2b a46f +66 b496 +35 2f99 +62 b486 +31 2f89 +56 b196 +25 2c99 +a7 4cb5 +76 171e +54 b192 +e5 de33 +ff df75 +b a06f +fe 5ffe +44 ba10 +52 b186 +e3 de27 +9 a063 +fc 5ff2 +46 b096 +15 2b99 +97 4bb5 +66 161e +42 b086 +76 3596 +64 3e10 +b8 ed60 +72 3586 +29 2463 +e1 7489 +66 3496 +1d 2373 +e2 7486 +99 6363 +62 3486 +82 e284 +19 2363 +6e 9ed4 +d6 7196 +d1 7189 +56 3196 +54 3192 +d2 7186 +89 6063 +44 3a10 +52 3186 +8f 687d +e6 7cb4 +2d a6f9 +56 b994 +d a871 +8a 62cc +c1 7089 +f2 f586 +46 3096 +46 b894 +66 1e1c +41 b881 +5c b158 +61 1e09 +c2 7086 +40 b880 +60 1e08 +ea 56e4 +36 279e +57 3995 +56 3994 +d 2871 +d5 7991 +55 3991 +53 3985 +52 3984 +9 2861 +d1 7981 +c7 7895 +c5 7891 +c4 7890 +c3 7885 +79 bfc3 +93 c105 +82 ea06 +43 3885 +78 bfc2 +92 c104 +42 3884 +5d 315b +c1 7881 +dc 7158 +b0 cf20 +37 2f9d +c0 7880 +77 b595 +76 b594 +13 a32f +2d a471 +75 b591 +71 b581 +14 819a +66 b494 +18 960 +81 c881 +65 b491 +64 b490 +10 818a +62 b484 +19 a361 +e2 f6a6 +61 b481 +68 be60 +ed 5cfb +fe f776 +27 869f +57 b195 +77 171d +93 e905 +56 b194 +e7 de35 +d a071 +55 b191 +75 1719 +91 e901 +7 a01d +c3 5227 +54 b190 +e5 de31 +b a06d +74 1718 +53 b185 +73 170d +52 b184 +51 b181 +71 1709 +67 161d +83 e805 +46 b094 +66 161c +7c bd58 +45 b091 +65 1619 +15 2139 +ef 5efd +81 e801 +44 b090 +64 1618 +63 160d +d2 f38c +3c 85da +42 b084 +62 160c +78 bd48 +c2 f2a6 +41 b081 +61 1609 +f7 7595 +c4 f098 +f5 7591 +75 3591 +f3 7585 +c0 f088 +fa 7f44 +e0 7e02 +73 3585 +40 b088 +4e 1ad6 +72 3584 +29 2461 +f1 7581 +70 bd2a +39 a741 +a9 eee3 +c3 f025 +e7 7495 +94 419a +67 3495 +14 19a +34 af98 +e5 7491 +e4 7490 +e3 7485 +90 418a +63 3485 +10 18a +30 af88 +e1 7481 +e0 7480 +87 e01f +e8 7e60 +a7 469f +1e 21f6 +d7 7195 +fe 7776 +27 69f +6d bcfb +ec f658 +57 3195 +24 ac98 +d5 7191 +55 3191 +d4 7190 +8b 606d +d3 7185 +b7 4fbf +d1 5101 +53 3185 +52 3184 +d1 7181 +58 b96a +91 eb23 +ab ec65 +47 3095 +14 ab98 +c6 7094 +c5 7091 +ea 56e6 +7c 3d58 +c6 72b6 +45 3091 +50 b92a +19 a341 +89 eae3 +a3 ec25 +c4 7090 +43 3085 +bc 45da +c2 7084 +42 3084 +f8 7d48 +c1 7081 +b0 c720 +37 279d +c0 7080 +36 279c +9 a241 +58 396a +1a 29e4 +0 28a2 +99 6341 +50 392a +19 2341 +ce 5a7e +3e ad54 +24 ac12 +d9 73eb +f3 752d +48 386a +1b 3e7 +ff 755f +d9 51c1 +59 11c1 +c6 5a3e +d1 73ab +eb 74ed +93 6b25 +78 b56a +78 356a +f0 752a +70 352a +9d 6bf9 +68 346a +88 e268 +3c a7d2 +80 e228 +6c 9e78 +58 316a +59 b969 +d0 712a +50 312a +14 81b8 +86 6a16 +42 9a04 +d6 d11c +bc cfda +36 2fb6 +51 b929 +43 b887 +50 b928 +48 306a +49 b869 +da 716c +c0 702a +e5 5e33 +b 206f +ff 5f75 +5a 316c +40 302a +4c 9a78 +65 1e33 +7f 1f75 +4 80b8 +37 5b5 +c6 d01c +ac ceda +26 2eb6 +41 b829 +40 b828 +59 3969 +14 1b8 +42 1a04 +51 3929 +43 3887 +50 3928 +84 40b8 +68 b442 +c7 5a3d +4 b8 +d5 51b3 +cd 7851 +c0 7828 +52 b386 +79 b569 +9b 6367 +42 b286 +69 b469 +92 c92e +8b 6267 +53 b387 +60 b428 +82 6226 +6e 1e76 +38 2fc0 +41 b029 +4f 1a77 +33 af87 +40 b028 +4e 1a76 +eb 74c7 +f8 7568 +71 3529 +4c 1a7a +63 3487 +70 3528 +61 3429 +d3 7387 +e0 7428 +53 3387 +60 3428 +d1 7129 +1c a950 +2 a80e +51 3129 +e 8ad6 +33 a585 +c3 7087 +d0 7128 +9 8ac9 +43 3087 +50 3128 +41 3029 +b3 6f87 +c0 7028 +33 2f87 +40 3028 +c8 5848 +5a b966 +a3 4627 +b6 659c +58 b962 +90 c1a8 +28 26c2 +b2 6fa6 +4a b866 +48 b862 +80 c0a8 +58 3948 +b3 45a5 +a2 6ea6 +42 b826 +48 3042 +70 b7a2 +cf 727f +50 3922 +a 80e4 +d4 f11a +a a2e4 +7a b7e4 +60 b6a2 +38 2f42 +36 2f3e +e8 5448 +7a b566 +92 418c +78 b562 +90 4188 +82 408c +39 2f69 +68 b462 +c8 5048 +5a b166 +19 2b69 +b0 4f08 +42 b026 +11 2b29 +78 3562 +70 3522 +62 9e2e +7c 9f70 +82 e224 +6e 9e74 +58 3162 +fd 7dfb +37 a79f +44 9a32 +5e 9b74 +50 3122 +ef fcfd +d5 fbbb +38 2742 +c8 d04a +42 3026 +4e 9a74 +ed fcf9 +36 273e +41 b823 +5b b965 +52 b924 +4b b865 +4a b864 +48 b860 +df 73fd +c5 72bb +42 b824 +f5 7d99 +41 3823 +5b 3965 +f4 7d98 +f 2857 +59 3961 +dd f159 +8d 4a79 +97 419f +d a79 +3e 8f76 +67 b617 +56 1b9c +58 3960 +73 bf27 +e1 5e09 +dc f158 +17 19f +37 af9d +5 a39 +36 8f36 +cf 727d +50 3920 +d4 f118 +e5 7c99 +4b 3865 +e4 7c98 +27 aeb7 +4a 3864 +93 4987 +c0 7820 +82 6a8c +18 816a +68 b460 +41 b023 +5b b165 +90 c908 +89 6241 +a 28e4 +9 28e1 +9c e378 +8f e2d7 +5a 1366 +59 b161 +e9 d469 +c2 d286 +e0 dc80 +58 b160 +31 af23 +4b b065 +48 b060 +29 aee3 +43 b025 +d2 f32c +3c 857a +41 b021 +61 3423 +7b 3565 +3b afc7 +48 b068 +ad 4679 +d9 d3c9 +53 33a5 +d9 73e3 +f3 7525 +ce 5a76 +b3 ef87 +c0 f028 +58 33e2 +f8 d548 +72 3524 +f6 dd36 +25 639 +5 a0b1 +70 3520 +18 16a +2b aec7 +38 af68 +10 12a +23 ae87 +30 af28 +e0 7420 +f5 7599 +e2 5624 +41 3023 +5b 3165 +1b abc7 +28 ac68 +59 3161 +8d 4279 +9d 6953 +59 9941 +b9 cfc9 +33 2fa5 +b9 6fe3 +d3 7125 +93 eb87 +a0 ec28 +b 8ac5 +9 8ac1 +d6 d936 +5 239 +36 8736 +50 3120 +e5 7499 +31 2f23 +4b 3065 +a9 6ee3 +c3 7025 +39 2741 +70 3d2a +71 958b +f 2a5d +aa c64c +2b 8cef +c1 faa1 +d5 fbb9 +38 2740 +c1 7021 +37 273d +c0 7020 +78 95e2 +5a b94c +40 b80a +5c 395a +b7 45b7 +4c 385a +a7 44b7 +c8 784a +5b 3bc7 +68 3c68 +b1 4d8b +97 6b15 +f8 55e2 +4b 3ac5 +da 794c +c0 780a +82 e004 +93 e9af +7c b55a +18 ab60 +78 b54a +34 5b2 +12 2b0e +2c 2c50 +4c b05a +7f 3557 +5e b15c +44 b01a +77 3517 +5a b14c +40 b00a +65 9e13 +7f 9f55 +5 ab9 +e 856 +36 8fb6 +d4 f198 +f8 754a +78 354a +59 b961 +74 351a +10 2b20 +f0 750a +18 81e0 +8d 62d9 +74 bd92 +90 eb88 +e7 5615 +64 96b8 +a8 66ca +70 350a +7c 9f58 +fa 754c +e0 740a +66 3e34 +ec de58 +b0 cf22 +ca d064 +37 2f9f +5c 315a +d8 714a +6b 34c7 +78 3568 +e1 f489 +a e4 +d4 711a +90 e302 +99 49c9 +aa e444 +54 311a +93 4325 +de 79fe +d0 710a +54 b992 +c7 5215 +74 1f1a +b a86f +50 310a +5c 9b58 +4c 305a +33 85af +ef f67d +70 bd20 +48 304a +70 b7aa +da 714c +7c 9ff8 +c0 700a +7e 1f5c +64 1e1a +5a 314c +40 300a +4c 9a58 +65 1e13 +7f 1f55 +59 b949 +55 b919 +54 b918 +50 b908 +45 b819 +d4 d192 +bc 65f2 +78 95e0 +ed 76d9 +d6 f9b6 +5 22b9 +e 2056 +40 b808 +59 3bc3 +73 3d05 +ba e54c +a0 e40a +dd 7959 +d5 7919 +d4 7918 +d0 7908 +50 3908 +cd 7859 +d5 51bb +45 3819 +76 bd16 +c4 7818 +f8 55e0 +c0 7808 +3 aa27 +c3 5225 +bf efdd +a5 ee9b +70 1f2a +7d b559 +1e 8bfc +4 8aba +dc 5172 +93 e9ad +7c b558 +61 bc81 +d9 5163 +6f b67d +79 b549 +7a 1f6c +af eedd +60 1e2a +6d b459 +32 d84 +69 b449 +89 40e1 +64 b418 +60 b408 +6e 1e56 +82 6206 +5d b159 +59 b149 +45 b019 +44 b018 +5d 33d3 +77 3515 +41 b009 +4f 1a57 +40 b008 +59 33c3 +73 3505 +4e 1a56 +d4 f19a +7d 3559 +9e 4bfc +af e677 +84 4aba +93 69ad +7c 3558 +61 3c81 +75 3519 +74 3518 +75 bd91 +a9 66c9 +71 3509 +4c 1a5a +74 bd90 +a8 66c8 +11 ab2b +2b ac6d +70 3508 +65 3419 +64 3418 +e0 7408 +12 bac +23 a627 +64 bc90 +60 3408 +dd 7159 +fa df4c +e0 de0a +5d 3159 +59 3149 +a3 66a7 +55 3119 +c5 78bb +df 79fd +d4 7118 +54 3118 +55 b991 +89 62c9 +7 8037 +75 1f19 +51 3109 +d0 7108 +68 96ca +ca 584e +cd 7059 +fe f556 +ef f67f +70 bd22 +4d 3059 +7e b556 +d7 793d +49 3049 +7a b546 +d a2d1 +d3 792d +71 b7a9 +c4 7018 +45 b891 +65 1e19 +41 3009 +72 b506 +c0 7008 +3 a227 +44 b890 +64 1e18 +40 3008 +5e b956 +94 c198 +8f 42fd +10 9a0 +56 b916 +54 b912 +90 c188 +52 b906 +4c b852 +4a b846 +48 b842 +84 c098 +b7 4595 +0 8a0 +46 b816 +80 c088 +b3 4585 +42 b806 +95 4139 +4d 10d3 +e3 7e85 +8 8e0 +87 423d +99 e969 +bd cdd9 +37 2db5 +c1 78a9 +3c a572 +54 3198 +c2 d824 +2c 24f2 +38 8f40 +e6 5e16 +c 2052 +34 a7b2 +d9 7949 +5e 3956 +5c 3952 +74 b792 +78 3d60 +37 59f +d4 7912 +54 3912 +ce 7856 +c4 f832 +de f974 +7 889d +c5 7ab9 +75 1d99 +6c b6d2 +c9 7849 +d1 51ab +4e 3856 +45 3ab9 +cc 7852 +c2 f82e +dc f970 +5 8899 +4c 3852 +31 523 +9c e9fa +51 b321 +ca 7846 +c8 7842 +3e 2f5e +66 b6be +8d 68fb +49 98e9 +34 a5b8 +62 be04 +50 3b22 +6a 3c64 +9a c9cc +93 6305 +80 c88a +b3 4d87 +7e b7d4 +64 b692 +2f a4d7 +3c a578 +21 aca1 +41 1229 +72 9726 +68 3c60 +62 9624 +a6 6636 +41 3801 +35 a5b9 +63 be05 +2a 8c4c +10 8b0a +3c a57a +25 a4b9 +1a 8b4c +0 8a0a +33 f07 +24 a4b8 +32 f06 +63 9627 +76 b59c +f6 df36 +1c a172 +34 2d98 +79 1fe9 +50 9120 +94 6132 +38 d60 +a1 cc81 +de 7356 +b9 efc9 +77 1fb7 +6 80bc +4e 9856 +46 9ab6 +4e 9a76 +59 b3e3 +73 b525 +93 e9a7 +7c b552 +f a2dd +d5 7939 +e aa74 +fc fd52 +6 aa34 +f4 fd12 +6c 9672 +c aa70 +5d 19db +6e b456 +3d 2f59 +65 b6b9 +c7 783d +59 19cb +6a b446 +61 b6a9 +39 2f49 +c3 782d +95 e311 +16 a9b4 +5e b156 +2d 2c59 +35 5bb +de 5b7c +c4 5a3a +e9 74e9 +5a 3964 +40 3822 +de f15c +c4 f01a +f7 7517 +c6 5a36 +98 41ea +d1 73a3 +eb 74e5 +5c b152 +da f9c6 +9 22c9 +7c 3758 +93 6bad +3a a7c6 +5 a833 +1f a975 +17 a935 +94 6390 +f a875 +8c 62d0 +7 a835 +84 6290 +e a874 +77 1f1f +57 b997 +6 a834 +15 a931 +4e 92d4 +5 a831 +82 628c +4c b052 +c a870 +75 1f1b +4 a830 +9b 63cd +81 628b +4a b046 +19 2b49 +46 b016 +15 2b19 +42 b006 +11 2b09 +ec 5e72 +4d 32f3 +ed d459 +67 3435 +54 99ba +d5 d313 +ef d455 +56 99b6 +d9 d969 +5e 9976 +24 a6b0 +86 6834 +77 1db5 +74 95b2 +52 bb0e +6c bc50 +7c 9572 +d 2a7b +15 bb9 +26 a634 +e a274 +fc f552 +6 a234 +f4 f512 +2c a670 +c a270 +4 a230 +bc 457a +27 a4b5 +56 b39e +d1 5321 +2 8a06 +27 63d +8f 68f7 +4b 98e5 +66 149e +f0 5d82 +36 a5b4 +26 a4b4 +6f b4df +d0 5320 +46 109e +16 a1b4 +5f b1df +c0 5020 +6 a0b4 +4f b0df +b0 4f20 +26 63c +e 82de +d4 593a +35 a5b1 +2a 8c44 +10 8b02 +25 a4b1 +c4 5838 +25 a433 +14 9b8 +3f a575 +15 a333 +4 8b8 +2f a475 +e 287c +5 a033 +1f a175 +17 a135 +f a075 +7 a035 +77 b597 +d8 5942 +76 97be +4 a032 +1e a174 +ed 7e5b +23 e25 +e 2de +c5 d899 +4f 18d7 +5c 1978 +16 a134 +e a074 +77 171f +57 b197 +6 a034 +ef def5 +15 a131 +5 a031 +6e 16dc +1e 21fc +4 20ba +f8 5fc0 +42 3a04 +14 21b8 +e6 de34 +c a070 +75 171b +ff 5fff +55 b193 +4 a030 +1d 21fb +f7 5fbf +7 8ab5 +99 e9e1 +68 b44a +a1 cc03 +bb cd45 +70 3d20 +ef 767d +f9 7549 +7e 3556 +6c 967a +f7 f51d +dd f3db +13 83a5 +b8 65ca +74 95b8 +96 43b6 +f1 7509 +cc 5a5a +76 3516 +4e 9276 +66 1e9c +4c 9272 +64 1e98 +74 3512 +4f 9a75 +ea 7c46 +e1 7ea9 +22 ac2c +8 aaea +19 81e9 +f0 7508 +f 8a75 +89 c04b +3 2027 +e 8a74 +fc dd52 +6 8a34 +f4 dd12 +50 39aa +e9 7449 +6e 3456 +31 8d81 +f a8f7 +8e e254 +65 36b9 +4d 9a71 +d 8a71 +56 9b94 +d a8f3 +8c e250 +c 8a70 +1a 2164 +0 2022 +6f 9cd5 +55 9b93 +e1 7409 +66 3416 +7 a8b7 +86 e214 +4c b85a +e2 7406 +ee de54 +62 3406 +6e 9e54 +8b 42ed +17 89b5 +7 88b5 +ec de50 +8a 42ec +95 c311 +16 89b4 +de 7156 +d4 f132 +17 819d +d9 7149 +5e 3156 +58 9940 +9c 6952 +7a 3564 +60 3422 +80 e220 +6c 9e70 +a9 eceb +e6 5636 +7e 375c +64 361a +95 6bb1 +b 22cd +1e abdc +4 aa9a +d2 f12e +15 8199 +5c 3152 +d1 7109 +56 3116 +5a 3164 +40 3022 +4c 9a70 +89 e8eb +c6 5236 +c1 f203 +db f345 +42 b8a6 +1a a3c6 +fe dd76 +2d 679 +e7 deb5 +d a0f1 +76 179c +78 3560 +e1 f481 +54 3112 +25 a43b +3f a57d +de 79f6 +52 3106 +45 9833 +5f 9975 +8b 426d +d4 5390 +5 8833 +1f 8975 +17 8935 +94 4390 +7 8835 +84 4290 +8a 426c +16 8934 +e 8874 +43 322f +5d 3371 +57 9997 +6 8834 +c9 7049 +fa f546 +4e 3056 +11 8981 +45 32b9 +89 4269 +d 8871 +42 322c +56 9994 +8a 42cc +4c 3052 +6c 3c5a +14 8930 +c 8870 +4 8830 +9b 43cd +81 428b +c1 72a9 +33 85a7 +11 ab03 +2b ac45 +c1 7009 +f2 f506 +46 3016 +ce da54 +42 3006 +4e 9a54 +24 e12 +3e f54 +4 a12 +1e b54 +63 be25 +85 e039 +43 1027 +26 86b4 +43 ba25 +74 95ba +6c bc58 +b9 65cb +75 95b9 +f1 d583 +8f 6a55 +c7 5835 +88 68ea +70 bd8a +25 86b1 +87 4835 +50 b98a +5 82b1 +68 bee2 +82 c024 +7c 957a +d2 d386 +f9 d569 +e8 fe6a +7e 9576 +d 8279 +c6 5834 +61 be21 +5b 1165 +41 1023 +24 86b0 +86 4834 +41 ba21 +4 82b0 +a9 64cb +65 94b9 +3d d59 +87 42b7 +86 42b6 +3c d58 +8f 4277 +8e 4276 +54 91b2 +4c b850 +b2 cf86 +d9 d169 +c8 fa6a +5e 9176 +76 1d9c +f2 ff24 +5c 9172 +74 1d98 +ba cd4e +6f 9675 +30 a72a +9a c94e +4f 9275 +92 c90e +8b 6247 +47 9235 +7a bd4e +2f 8675 +72 bd0e +27 8635 +5a b94e +f 8275 +5e b154 +44 b012 +ee 5e7e +6e 9674 +58 3962 +37 af9f +26 8634 +e 8274 +fc d552 +6 8234 +f4 d512 +4c 1a78 +88 686a +e8 7442 +78 bd4a +2d 8671 +76 9794 +c a78 +56 9394 +6c 9670 +4c 9270 +38 27e2 +2c 8670 +75 9793 +24 8630 +c 8270 +4 8230 +1c 8958 +c 8858 +25 c13 +3f d55 +d2 7b0e +ec 7c50 +47 3a95 +f4 55b2 +46 bab6 +27 84b5 +7 80b5 +24 c12 +3e d54 +93 c925 +9 8041 +83 c825 +59 bbe3 +73 bd25 +f0 7780 +36 85b4 +f3 ff2d +5d 917b +26 84b4 +53 b925 +3a 5ce +d0 7380 +16 81b4 +43 b825 +2a 4ce +c0 7280 +6 80b4 +b9 65c3 +75 95b1 +2d 4d3 +c3 7285 +a9 64c3 +65 94b1 +23 c0f +3d d51 +5e bbf4 +44 bab2 +15 81b1 +5 80b1 +71 bd21 +34 85b0 +51 b921 +14 81b0 +41 b821 +4 80b0 +91 cb09 +b 2ae5 +9 2ae1 +d4 5132 +65 9433 +7f 9575 +86 4a9c +44 183a +5e 197c +55 9333 +6f 9475 +30 a52a +4e 187c +28 a4ea +45 9033 +5f 9175 +4e ba76 +35 8f33 +4f 9075 +ea deee +10 a12a +fc dff0 +e2 deae +8 a0ea +25 8433 +3f 8575 +2e ae76 +1d 83f3 +37 8535 +26 ae36 +15 8333 +2f 8475 +c 827a +df 5977 +7d 97f3 +d 82f3 +27 8435 +5 8033 +1f 8175 +17 8135 +6 aa36 +f 8075 +7 8035 +f aad5 +64 9432 +7e 9574 +c5 7899 +5f 3bdd +d8 db60 +45 3a9b +77 9597 +16 8134 +e 8074 +6 8034 +d2 d90c +cb 7245 +ba c7ec +a0 c6aa +53 932f +6d 9471 +4c ba72 +4b 1ac5 +15 8131 +4b 38e7 +d1 d90b +ca 7244 +52 932e +6c 9470 +1a 83ee +34 8530 +4a 1ac4 +8 2a60 +d a8fb +4a 1246 +8c e258 +14 8130 +4 8030 +85 6ab9 +45 b813 +5f b955 +87 6ab5 +8d 6a79 +43 b80f +5d b951 +94 69b8 +5a 13c4 +40 1282 +41 b803 +5b b945 +6e 365c +85 6ab1 +59 b941 +87 c89f +8d 6279 +57 b915 +56 b914 +b 28ef +8a 624c +55 b911 +4f b855 +57 91b7 +f1 d5a3 +8f 6a75 +8c c852 +b5 cd99 +e 80d4 +4b b845 +53 91a7 +8d 6a71 +d6 7b94 +d 2a71 +56 3b94 +5 2a31 +4a b844 +52 91a6 +c 80d0 +49 b841 +51 91a3 +48 b840 +50 91a2 +df 73dd +c5 729b +47 b815 +46 b814 +45 b811 +44 b810 +dc d952 +d 2a79 +71 95a3 +f 2a75 +da 73ec +c0 72aa +1c 81d0 +2 808e +8d c8d9 +7 28b5 +ef defd +15 a139 +4b 92e7 +1b a3ef +35 a531 +63 3e85 +2a ccc +3b a747 +10 b8a +8c eaf8 +4a 1ae6 +8b 4ac5 +5 28b1 +e3 fc2d +c9 faeb +2c 2672 +1e 235e +d5 f919 +97 6935 +e7 5e15 +d 2051 +35 a7b1 +96 6934 +e6 5e14 +c 2050 +34 a7b0 +95 6931 +33 a7ad +49 3a49 +94 6930 +37 af37 +a5 4e19 +14 2930 +c 2870 +c6 d216 +c2 d8a4 +ca d864 +d1 f9ab +34 2532 +3c a7f2 +5a b146 +29 2c49 +31 5ab +da d346 +85 68b9 +2c a4d2 +be c5d4 +a4 c492 +a6 66b4 +9e c1d4 +84 c092 +3b af6f +28 84e0 +86 62b4 +72 1f04 +56 331e +87 68b5 +54 b990 +b a86d +88 62c8 +6 8036 +74 1f18 +50 3108 +8d 6879 +c3 5a27 +d6 799c +a5 66b1 +25 26b1 +40 b800 +5 22b1 +a4 66b0 +84 62b0 +70 1f00 +be c554 +a4 c412 +ad ccfb +a6 6634 +8c c052 +95 c93b +8e 6274 +31 da1 +b5 c599 +be efdc +a4 ee9a +ac ccf8 +a5 6631 +8c c8f8 +85 6231 +76 3794 +84 6230 +4 2230 +5 2833 +1f 2975 +15 2939 +ad c4d9 +27 24b5 +2 a06 +7b 3fef +95 4131 +9d c1d9 +17 21b5 +8d c0d9 +7 20b5 +d0 71aa +d 2879 +85 ea39 +43 1a27 +56 399c +b a2ef +25 a431 +c 2878 +84 ea38 +42 1a26 +55 399b +4b 18e5 +bc c5d8 +36 25b4 +ac c4d8 +26 24b4 +f 2875 +e 2874 +57 3997 +98 41c2 +e7 7c97 +f4 7d38 +2e a6dc +4 2838 +da 71ec +c0 70aa +75 1711 +e5 5eb3 +b 20ef +ff 5ff5 +8c c858 +6 2834 +b5 65b1 +6 21e +a5 64b1 +c6 d814 +5 20b1 +d9 f9eb +3c 2572 +b4 65b0 +a4 64b0 +aa 46c4 +24 24b0 +54 313a +54 3992 +b 286f +9b 63ef +b5 6531 +93 632f +ad 6471 +f6 7594 +8b 62ef +a5 6431 +13 232f +2d 2471 +76 3594 +9d c3d9 +17 23b5 +65 be11 +5f 1155 +45 1013 +92 632e +ac 6470 +8d c2d9 +7 22b5 +f5 7593 +8a 62ee +a4 6430 +49 3249 +15 8911 +94 6130 +84 6030 +c9 f8e9 +12 232e +2c 2470 +75 3593 +70 9502 +b4 65b8 +4c 1052 +e2 7e04 +cb 58e5 +27 ebf +41 1001 +b6 65b4 +70 370a +a1 6ca1 +f2 5726 +17 23bd +5d 1b59 +3c 2d58 +b4 ef18 +72 1f06 +64 b4b8 +70 1f02 +85 62b1 +71 1f01 +44 3812 +5e 3954 +c3 780f +dd 7951 +e8 744a +c6 5ab4 +96 c93c +8f 6275 +3c 2f7a +87 c897 +94 c938 +8d 6271 +d6 7394 +c2 780e +dc 7950 +42 380e +5c 3950 +c5 5ab1 +96 61b4 +84 c818 +94 61b0 +c4 5ab0 +35 a51b +75 b791 +57 3915 +56 3914 +d5 7911 +55 3911 +1e 2956 +f0 5f22 +cf 7855 +d7 51b7 +6d b6d1 +f4 7d90 +f a75 +4e 3854 +98 e1c8 +56 11b6 +48 3860 +e5 7413 +ff 7555 +cc f058 +91 4983 +27 ae9d +ce 5a74 +40 3820 +dd 73d3 +f7 7515 +c4 f018 +c6 5a34 +98 41e8 +4e 1a74 +6c bc52 +46 1a34 +18 1e8 +e a74 +19 3eb +33 52d +cc 7850 +d4 51b2 +cd 5a71 +9 aac3 +23 ac05 +8d 4a71 +d6 5b94 +d2 5ba4 +ed 547b +85 4a31 +4d 1a71 +45 1a31 +d a71 +56 1b94 +59 bbe9 +fb 5d4f +5 a31 +da 51e6 +70 b700 +cc 5a70 +8c 4a70 +4c 1a70 +c a70 +58 bbe8 +fa 5d4e +47 3815 +d6 fb94 +2c 267a +64 b690 +46 3814 +5f b3dd +45 b29b +3e 277c +24 263a +87 409f +c4 7810 +44 3810 +ac ced2 +c6 d014 +76 95b4 +ba 65c6 +2f 24d7 +3c 2578 +21 2ca1 +b4 e738 +a7 e697 +72 1726 +c0 7800 +56 131e +87 48b5 +4b 3a65 +f2 dfae +18 a1ea +46 ba36 +7 8b5 +fd 7553 +c3 f82d +c 2272 +56 3394 +15 8139 +1e ab7c +4 aa3a +bd cf5b +37 2f37 +c d0 +5 8039 +ad ce5b +27 2e37 +f5 7513 +d5 59b1 +d4 59b0 +a cc +40 1082 +5a 11c4 +97 4935 +d 51 +35 87b1 +5 833 +1f 975 +f 875 +5 839 +36 8d36 +7e 9dd6 +6a 366e +84 4832 +9e 4974 +22 86ae +3c 87f0 +16 934 +e 874 +57 1997 +6 834 +55 1931 +ba 4766 +11 2ba9 +66 9636 +15 931 +7a 3766 +d 871 +5a b36e +56 1994 +12 2384 +64 3618 +82 482e +9c 4970 +54 1930 +d4 f9b8 +37 253f +4c 1870 +44 1830 +2 82e +1c 970 +85 c891 +14 930 +e2 f686 +c 870 +55 1993 +4 830 +b7 65b5 +92 4b06 +da d36c +c0 d22a +f3 5727 +c2 d226 +a5 64b9 +9a 4b4c +80 4a0a +a4 64b8 +74 b590 +e3 5627 +f6 759c +65 be19 +5f 115d +45 101b +e2 5626 +7 22bd +f5 759b +5c 317a +67 be15 +39 a5c9 +47 1017 +de f1dc +c4 f09a +f7 7597 +d2 532e +ec 5470 +97 61b5 +85 c819 +95 61b1 +60 34a0 +e6 56b4 +40 30a0 +c6 52b4 +26 6b4 +6 2b4 +f4 5592 +ab 446f +a2 ce26 +3 8a05 +f0 7d8a +a5 46b1 +c0 d800 +d0 798a +85 42b1 +bb e547 +90 498a +45 12b1 +87 60b5 +59 1bc3 +73 1d05 +34 2dba +86 60b4 +85 60b1 +84 60b0 +e4 56b0 +c4 52b0 +9d c1db +17 21b7 +a4 46b0 +2b c6f +c1 7a21 +3 8805 +84 42b0 +61 3e21 +66 bcbc +e5 f619 +24 6b0 +e9 f669 +41 3a21 +4 2b0 +a9 446b +f2 558e +3c 2d7a +47 ba15 +f3 df8d +19 a1c9 +be eddc +a4 ec9a +d7 7197 +8d 6071 +d6 7194 +45 ba11 +8c 6070 +d5 7193 +61 3421 +41 3021 +c7 5235 +74 1f3a +7a 3d4e +2f 675 +5f 93dd +45 929b +72 3d0e +27 635 +f4 7590 +5a 394e +f 275 +52 390e +7 235 +53 b3ad +f5 5513 +60 3420 +a9 ece9 +e6 5634 +40 3020 +89 e8e9 +c6 5234 +15 a33b +2f a47d +ce 78f6 +1b 896f +f2 dd8c +eb 76c5 +83 e8a7 +6c b452 +c5 7839 +9 a8e9 +46 1234 +c3 daad +26 634 +e5 7e19 +e 274 +3e 8fdc +24 8e9a +e5 5631 +63 1e2d +c5 5231 +d1 f30b +eb f44d +52 b9ae +f8 7d4a +ad 4671 +f6 5794 +b9 e74b +3a adee +b a8e5 +2b e6d +74 1f90 +99 e34b +1a a9ee +d6 5394 +76 1794 +58 394a +d 271 +59 b3e9 +fb 554f +56 1394 +51 b3a9 +f3 550f +7d b57b +e4 5630 +82 e884 +cc 5270 +62 1e2c +5d b17b +c4 5230 +86 e896 +a6 4e1e +ac 4670 +f5 5793 +2a e6c +73 1f8f +8c 4270 +46 b896 +66 1e1e +19 896b +64 1630 +44 1230 +3f a57f +c1 daa9 +24 630 +c 270 +58 b3e8 +fa 554e +6f 14d5 +55 1393 +4 230 +50 b3a8 +f2 550e +ad ced3 +c7 d015 +77 95b5 +bb 65c7 +b5 e739 +73 1727 +35 2539 +b7 45b5 +5c 3958 +97 41b5 +87 40b5 +34 dba +5 2033 +1f 2175 +22 8e26 +35 ad9b +67 14b5 +c 858 +1a abc6 +e9 74e1 +57 11b5 +24 8cb8 +e6 dc1c +cc dada +46 3ab6 +e4 d490 +96 e39e +7b 156f +47 10b5 +14 8bb8 +27 4b5 +a9 64e1 +17 1b5 +a6 cc1c +8c cada +6 2ab6 +7 b5 +4f 32ff +69 3441 +c0 f828 +b6 45b4 +2f 2eff +49 3041 +71 b7a1 +96 41b4 +86 40b4 +16 1b4 +6 b4 +f 2075 +e 2074 +57 3197 +b5 45b1 +cf 50d7 +dc 5178 +c1 58a1 +a5 44b1 +95 41b1 +56 3194 +85 40b1 +65 14b1 +55 3193 +45 10b1 +4d b059 +12 984 +35 5b1 +4f 10d7 +5c 1178 +c5 d099 +41 18a1 +25 4b1 +4d 32fb +67 343d +b4 45b0 +e1 7c21 +a4 44b0 +c1 7821 +37 2f3d +84 40b0 +41 3821 +c5 f019 +c7 5a35 +99 41e9 +4 b0 +a2 ee86 +c9 f069 +bd 4ff3 +d7 5135 +98 61ea +c6 7a36 +9d 43f3 +b7 4535 +62 9e24 +a6 6e36 +7d 3ff3 +97 4135 +42 9a24 +86 6a36 +6d 3ef3 +87 4035 +ca 7aec +34 d3a +55 919b +c 8078 +2e 2e76 +60 bca8 +93 41a5 +82 6aa6 +15 333 +2f 475 +d 2f3 +27 435 +e 2a76 +17 135 +f 75 +7 35 +9 88c1 +d4 d19a +4f 9ad5 +7c 3ff2 +96 4134 +6c 3ef2 +86 4034 +16 134 +e 74 +e0 7ea8 +57 1197 +6 34 +db 53ef +f5 5531 +6 8816 +9b 43ef +b5 4531 +8b 42ef +a5 4431 +6b 3eef +85 4031 +2c 2e72 +ce 7854 +d6 51b6 +6c b6d0 +1b 3ef +35 531 +3e 2f74 +24 2e32 +13 32f +2d 471 +76 1594 +b 2ef +25 431 +d 71 +56 1194 +5 31 +a2 442e +bc 4570 +17 3b5 +8a 42ee +a4 4430 +6a 3eee +84 4030 +14 130 +c 70 +55 1193 +4 30 +de 5bfc +ef f677 +c4 5aba +aa eee4 +14 8132 +6d 3459 +90 6908 +11 8983 +5f 33fd +45 32bb +91 c121 +9a eb64 +80 ea22 +c5 f099 +4f 30d7 +5c 3178 +c7 5ab5 +6 8ab6 +8f c2df +10 8982 +5e 33fc +44 32ba +cc 5a7a +f1 7529 +cd 5a79 +48 3862 +27 ae9f +ff 7557 +49 3861 +92 4984 +cd f059 +cf 5a75 +e7 fc1f +3 8aa7 +d1 f303 +eb f445 +52 b9a6 +c0 5888 +2f 2cdf +a8 cc62 +5b 33ed +41 32ab +5e bb7c +44 ba3a +b5 c713 +36 8db6 +5f b3ff +79 b541 +d 879 +67 b417 +56 199c +3e 8d76 +c4 5832 +de 5974 +62 96ae +7c 97f0 +e5 56b1 +11 2309 +a3 ec85 +ed 5671 +f4 55b0 +8d 6ad1 +57 13b5 +b2 ef0c +1c 815a +1b 89c5 +1 8883 +1a 89c4 +0 8882 +4a b2ce +64 b410 +f 28d5 +e1 5ea1 +45 b013 +5f b155 +3f 2ffd +25 2ebb +4f 1a75 +e 28d4 +e0 5ea0 +5e 1b7c +44 1a3a +69 34e9 +d 28d1 +5e 1356 +43 b00f +5d b151 +47 1a35 +19 1e9 +c 28d0 +42 b00e +5c b150 +61 1e01 +b 28c5 +41 b003 +5b b145 +3b 2fed +21 2eab +a 28c4 +3a 2fec +20 2eaa +31 5a9 +9 28c1 +8f 4ad5 +56 1934 +13 2bad +d 2ad1 +ba 45ee +35 af13 +4f b055 +32 af0e +4c b050 +31 af03 +4b b045 +21 4a9 +d3 f925 +d2 f924 +2d aed3 +47 b015 +2c aed2 +46 b014 +2b aecf +45 b011 +ef 5e7d +2a aece +44 b010 +eb 5e6d +ff f577 +d4 59ba +83 428f +9d 43d1 +e2 7e84 +4c 10d2 +ba c56c +a0 c42a +17 831d +dd 5979 +de 5976 +7c 97f2 +a2 c426 +d5 f333 +ef f475 +c4 58b8 +6 821c +cc 5878 +ce 5874 +f5 55b1 +e5 54b1 +e4 54b0 +7a 37e4 +60 36a2 +d3 532f +ed 5471 +1e 8b56 +54 193a +8d 4af3 +a7 4c35 +4c b272 +55 1939 +ba 476e +57 1935 +4c 1878 +4e 1874 +44 1838 +46 1834 +7c 1572 +74 1532 +53 132f +6d 1471 +4d b251 +ca d8cc +c3 7205 +2d 453 +87 e29f +52 132e +6c 1470 +4b 12ef +65 1431 +45 b211 +4a 12ee +64 1430 +65 3413 +7f 3555 +4c b058 +11 983 +a2 ec8e +bc edd0 +ec 567a +a3 ec8d +ed 5679 +68 3462 +88 e260 +b1 ed2b +ee 5676 +d7 5935 +4 8238 +37 735 +e3 740f +fd 7551 +25 ae99 +63 340f +7d 3551 +69 3461 +b2 4584 +98 ebe8 +ef 5675 +b0 672a +e2 740e +fc 7550 +24 ae98 +57 3395 +f4 55b8 +2f ae75 +f 77 +e1 7403 +fb 7545 +c8 f048 +61 3403 +7b 3545 +48 b048 +25 4b9 +f6 55b4 +8d 6ad9 +97 c33d +18 89e0 +e1 5ca1 +57 13bd +c6 52b6 +90 6108 +8a eae6 +7c 1d58 +6b 3cc7 +78 3d68 +2a 26ce +e1 fc89 +41 30a1 +c7 52b5 +74 1fba +c5 52b1 +5c 33d2 +76 3514 +48 3062 +ed 7cfb +27 a69f +91 e92b +ce 5276 +49 3061 +92 4184 +cf 5275 +aa 646c +90 632a +7c 1f7a +db f34f +5c b9f2 +db 73cf +f5 7511 +5b 33cf +75 3511 +83 e885 +cd 5271 +d9 f34b +5a b9ee +da 73ce +f4 7510 +5a 33ce +74 3510 +5 b9 +36 85b6 +d6 51b4 +5d b959 +d9 73c3 +f3 7505 +ce 5a56 +d7 73bf +f1 7501 +cc 5a52 +9b 49c5 +81 4883 +b2 6f0c +1c 15a +1b 9c5 +1 883 +3c af58 +46 9236 +1a 9c4 +0 882 +53 330f +6d 3451 +d1 7303 +d8 d9ca +52 39a6 +eb 7445 +38 af48 +94 411a +aa 6ecc +14 11a +34 af18 +cb 72cf +e5 7411 +4b 32cf +65 3411 +ca 72ce +e4 7410 +4a 32ce +64 3410 +c9 72c3 +d0 d98a +e3 7405 +90 410a +9a 49ee +30 af08 +c7 72bf +e1 7401 +c6 72be +e0 7400 +4c b0f2 +7 aa9d +30 272a +44 3012 +5e 3154 +1c 2952 +5 aa99 +43 300f +5d 3151 +28 26ea +4 aa98 +37 2f95 +42 300e +5c 3150 +6f 14d7 +7c 1578 +61 1ca1 +e5 d499 +b0 c5aa +3 aa8d +f 2ad5 +bc 45f2 +1 aa89 +b3 6d85 +c8 dae8 +2b 66f +74 1792 +3d 2fd3 +57 3115 +24 ac18 +78 3fc8 +a a0e6 +39 2fc3 +53 3105 +20 ac08 +8f 6875 +2d a6f1 +38 2fc2 +52 3104 +3a fee +54 1130 +6a 96c6 +34 a518 +7c 3d5a +b0 c5a0 +68 96c2 +ca 5846 +33 2f0f +4d 3051 +37 85b5 +26 aeb6 +27 2e95 +32 2f0e +4c 3050 +66 96b6 +30 a508 +3e f56 +2d 671 +78 3d4a +ba c5ec +a0 c4aa +2d 2ed3 +47 3015 +14 ab18 +97 431d +18 9c0 +2c 2ed2 +46 3014 +2a 2ece +44 3010 +37 271d +f7 55b5 +9c 4958 +67 b4b5 +fc 557a +4f 3a5d +fe 5576 +e5 54b9 +16 8b9e +7a 37ec +60 36aa +1e 8b5e +d1 5ba1 +bd e553 +47 12bd +e8 d6ca +62 36a6 +18 148 +b0 652a +d7 51b5 +40 3828 +d5 51b1 +c 2270 +6 8896 +7 8abd +ce 7a76 +c7 50b5 +74 1dba +c6 50b4 +e2 d40c +c8 d2ca +42 32a6 +20 84a8 +b5 4f33 +cf 5075 +90 612a +7c 1d7a +5a 33e4 +e0 d408 +40 32a2 +27 eb5 +74 153a +75 1539 +5d 13f3 +77 1535 +38 25ea +30 252a +4f b255 +cc d8d0 +2f 457 +91 e101 +63 9c0f +7d 9d51 +c7 7a95 +65 1439 +90 e100 +62 9c0e +7c 9d50 +c6 7a94 +45 b219 +3f 55d +25 41b +57 1397 +64 1438 +28 24ea +47 b215 +4e 3a76 +f2 7f24 +5c 1172 +4c 3a72 +3d ff3 +57 1135 +ea 7ee4 +54 1132 +3b fef +55 1131 +5e 3b74 +44 3a32 +35 f33 +4f 1075 +ea 5eee +10 212a +2d ef3 +47 1035 +2b eef +45 1031 +ba 4fec +a0 4eaa +c0 d002 +da d144 +7a 1fec +60 1eaa +e9 546b +3f a5ff +4a 3844 +94 e1b8 +52 11a6 +21 2ea9 +2a 2c46 +20 2ea8 +a1 4ea9 +8 aae0 +aa 4c46 +ca fa44 +d7 f13f +61 1ea9 +ac ec58 +6a 1c46 +8a ca44 +5c b1f8 +e 28dc +d6 f13e +60 1ea8 +96 e13e +20 ea8 +37 a5bf +48 1242 +d2 5b26 +6 aa9e +81 4a21 +87 6835 +25 a6b1 +22 8ea6 +5 20b3 +1f 21f5 +88 40c0 +43 30af +5d 31f1 +12 a92e +85 6831 +23 a6ad +a2 4ea6 +58 1948 +8a 62e6 +b8 ef48 +76 1f36 +68 b4e8 +a4 eeb8 +62 1ea6 +2c 2cf8 +e 28d6 +74 1f32 +fe d7dc +e4 d69a +1a 1e4 +0 a2 +a6 6eb6 +62 9ea4 +7d 977b +ec feda +22 8ea4 +21 8ea1 +20 8ea0 +c9 506b +b ef +a1 6ea1 +21 2ea1 +a ee +a0 6ea0 +20 2ea0 +a3 4ea5 +63 1ea5 +44 b0b2 +5e b1f4 +91 c9ab +8a 62e4 +76 1f34 +62 1ea4 +33 ad8f +7d 177b +5d b1f3 +ec 7eda +22 ea4 +f7 dfb7 +1d a1f3 +a1 4ea1 +75 1f31 +d7 f137 +61 1ea1 +e5 d699 +6f 16d7 +32 ad8c +7c 1778 +42 b0ae +5c b1f0 +a0 4ea0 +88 62e0 +74 1f30 +d6 f136 +60 1ea0 +e4 d698 +96 e136 +20 ea0 +a4 c698 +b0 e582 +3a afcc +20 ae8a +31 8589 +f8 7542 +11 892b +a 2264 +b9 456b +14 3b0 +7e 9fdc +64 9e9a +c5 7819 +4a 1264 +5d 31d9 +3e 2fdc +24 2e9a +ba 6fcc +a0 6e8a +b1 4589 +7e 1fdc +64 1e9a +fa 5fcc +e0 5e8a +ba 4fcc +a0 4e8a +7a 1fcc +60 1e8a +b1 452b +20 ae88 +de 73fe +f8 7540 +53 3385 +6a 3c66 +8a ea64 +20 8e88 +bc e57a +53 1385 +de 53fe +f8 5540 +25 2e99 +d a273 +24 2e98 +15 939 +c a272 +21 2e89 +9 a263 +1e 8974 +4 8832 +a0 6e88 +3a 25ee +20 2e88 +d7 f11f +61 1e89 +49 9263 +5c b1d8 +72 bfa6 +e0 5e88 +4f 32df +c8 d262 +f 22df +88 c262 +d6 f11e +60 1e88 +48 9262 +96 e11e +20 e88 +8 8262 +5e bb74 +44 ba32 +5 8b1 +f0 d70a +6a 36e6 +bb 456d +a1 442b +49 b069 +22 ae86 +33 8585 +f1 77a1 +31 8581 +c7 7815 +65 b691 +cd 707b +62 9e86 +2c acd8 +45 3093 +5f 31d5 +c5 7811 +43 308f +5d 31d1 +e8 d4c8 +62 34a4 +99 416b +a1 6e89 +37 595 +db f16f +4 8098 +b5 4591 +f2 7526 +35 591 +1c 952 +b1 4581 +b0 c502 +7a 3dc4 +60 3c82 +80 ea80 +c 20d2 +e6 5e96 +be e576 +48 12e0 +ac 6cd8 +c9 5069 +a2 4e86 +2c 2cd8 +91 412b +20 ae80 +1a 1c4 +0 82 +fc 75f2 +c6 7096 +94 cbb0 +7d 975b +8d 6079 +22 8e84 +a8 44e0 +93 cbad +7c 9758 +fa 75ee +21 8e81 +ba 65ee +f5 5d13 +53 bbad +3c 8758 +20 8e80 +5 8831 +82 428c +26 2e94 +f df +a5 6e91 +25 2e91 +e de +a4 6e90 +24 2e90 +89 60c3 +45 90b1 +d d3 +a3 6e85 +23 2e85 +b cf +a1 6e81 +3d 2dfb +bc 6758 +21 2e81 +3c 2758 +a ce +a0 6e80 +20 2e80 +26 e94 +b 20cd +e5 5e91 +a5 4e91 +94 c998 +8d 62d1 +de 79fc +c4 78ba +65 1e91 +a4 4e90 +64 1e90 +fb 7f67 +24 e90 +e9 fe49 +c9 504b +a3 4e85 +7b b747 +6a 1ccc +50 1b8a +92 c98c +8b 62c5 +44 9a18 +77 1f15 +52 310c +38 2fca +cc f07a +63 1e85 +44 b092 +5e b1d4 +91 c98b +8a 62c4 +76 1f14 +69 bc61 +b2 cd84 +62 1e84 +5d b1d3 +7d 1dfb +fc 5758 +a1 4e81 +3d dfb +d3 7bad +bc 4758 +90 c988 +89 62c1 +75 1f11 +d7 f117 +61 1e81 +42 b08e +5c b1d0 +a0 4e80 +88 62c0 +b a865 +74 1f10 +68 9e6a +54 b910 +9 28eb +88 6248 +28 8e6a +9d e379 +5b 1367 +a8 6e6a +ba 6f6c +a0 6e2a +b1 4529 +d7 519f +74 3f3a +8e 407c +d4 7910 +a8 4e6a +54 3910 +28 e6a +87 e23f +8 a8e2 +e 285e +da f9e4 +c0 f8a2 +33 5ad +ba 4f6c +a0 4e2a +9a e9e4 +80 e8a2 +82 c00e +9c c150 +3a f6c +20 e2a +99 e341 +1a a9e4 +0 a8a2 +29 ae69 +9 6b +52 118e +21 ae29 +1b 16d +1 2b +4 22b8 +29 8e69 +28 8e68 +41 1223 +5b 1365 +cf 70df +71 9f8b +21 8e29 +20 8e28 +9e 49fe +53 1325 +a1 6e29 +57 b3b5 +ec 547a +21 2e29 +20 2e28 +a9 4e69 +d7 519d +c6 7a9e +97 419d +a8 4e68 +f1 5f8b +5f b3d7 +6c b478 +e1 5e29 +f 285d +37 afbd +a1 4e29 +6a b44c +50 b30a +81 e8a1 +21 e29 +f6 df3c +f a0d7 +1c a178 +1 a8a1 +52 9326 +e 285c +16 1be +a0 4e28 +69 b44b +80 e8a0 +2a 8e66 +42 1a8c +3d addb +de 73dc +c4 729a +2a 2e66 +51 918b +8 8068 +21 423 +3b 565 +28 2e62 +39 561 +ac e65a +60 36a8 +0 8028 +31 521 +ac ee78 +6a 1e66 +b7 ef15 +68 1e62 +28 e62 +be c75c +d5 fbb1 +3f 8dff +a4 c61a +2a ae64 +a 66 +23 ae25 +3 27 +22 ae24 +2 26 +21 ae21 +20 ae20 +1a 164 +0 22 +2b 8e65 +29 8e61 +ed fe5b +23 8e25 +ec fe5a +22 8e24 +35 ad99 +96 6b94 +21 8e21 +20 8e20 +aa 6e64 +d1 d189 +29 2e61 +ee 5476 +c 72 +a2 6e24 +ec 5472 +89 e2c9 +47 12b7 +21 2e21 +a 6e +a0 6e20 +20 2e20 +ab 4e65 +a7 ec1d +8d eadb +58 1b6a +36 2f1e +ea 5e64 +18 21c0 +f2 5f84 +69 1e61 +29 e61 +72 1f84 +66 16b4 +a3 4e25 +a2 4e24 +b5 6d99 +48 b042 +ec 7e5a +22 e24 +35 2d99 +f7 df37 +1d a173 +66 be9e +e1 5e21 +f 2855 +17 1b7 +64 16b0 +21 e21 +26 8cbc +a5 c619 +51 3323 +6b 3465 +58 99ea +7 82bf +21 8401 +7f bfdf +e0 5e20 +e 2854 +16 1b6 +36 afb4 +20 e20 +25 8cbb +3f 8dfd +a4 c618 +f5 df33 +1b a16f +2c ae5a +3e af5c +24 ae1a +47 329f +da d364 +c0 d222 +3a af4c +20 ae0a +d0 5982 +31 8509 +6c 9e5a +2c 8e5a +68 9e4a +b6 cdbe +6b 96e5 +af 66f7 +28 8e4a +7e 9f5c +64 9e1a +3e 8f5c +24 8e1a +2e 8c56 +bd 4559 +2c 2e5a +3d 559 +b9 4549 +28 2e4a +39 549 +b5 4519 +3e 2f5c +24 2e1a +35 519 +ba 6f4c +a0 6e0a +b1 4509 +3a 2f4c +20 2e0a +31 509 +a1 4cab +b2 e726 +bb 4ded +ec 5e5a +ac 4e5a +6c 1e5a +1b 29ef +9a 634c +80 620a +4c b8d2 +2c e5a +c a8d2 +e8 5e4a +a8 4e4a +68 1e4a +c7 f21f +48 b8c2 +28 e4a +87 e21f +8 a8c2 +77 159d +fe 5f5c +e4 5e1a +de f9d4 +c4 f892 +3e f5c +24 e1a +fa 5f4c +e0 5e0a +da f9c4 +c0 f882 +b1 4f21 +36 af9e +f0 7522 +33 58d +3a f4c +20 e0a +2c ae58 +a2 6e0c +c 5a +34 87ba +36 a5be +28 ae48 +92 492e +8 4a +30 87aa +25 ae19 +1f 15d +5 1b +24 ae18 +1e 15c +4 1a +20 ae08 +1a 14c +0 a +8a 48ee +6d 9e59 +2d 8e59 +6c 9e58 +29 8e49 +76 95be +ac 6e5a +68 9e48 +b6 cdbc +af 66f5 +be 6f5c +a4 6e1a +60 9e08 +a7 66b5 +a3 ec8f +bd edd1 +ed 567b +20 8e08 +2e 24fc +14 23ba +ac 6e58 +b4 47ba +2c 2e58 +34 7ba +29 2e49 +31 7ab +a8 6e48 +b0 47aa +28 2e48 +30 7aa +c8 58c0 +a5 6e19 +af c67d +30 8d20 +c7 521d +48 18c0 +25 2e19 +a4 6e18 +a0 6e08 +7f bf77 +ed 5e59 +de 7bfe +f8 7d40 +53 3b85 +3f af77 +ad 4e59 +7e bf76 +ec 5e58 +3e af76 +ac 4e58 +6c 1e58 +4c b8d0 +2c e58 +c a8d0 +69 1e49 +49 b8c1 +7a bf66 +e8 5e48 +68 1e48 +c7 f21d +48 b8c0 +25 e19 +24 e18 +21 e09 +f6 df1c +1c a158 +52 9306 +20 e08 +2c ae52 +2a ae46 +1b 3ed +1 2ab +f3 dd87 +f9 7761 +28 ae42 +1f 83ff +39 8541 +1b 83cf +35 8511 +23 427 +c0 d8a0 +e 85c +60 b688 +22 ae06 +f8 dde8 +f1 7721 +17 83bf +31 8501 +bf 4d57 +1d abf1 +3 aaaf +a8 ec6a +7 aa9f +ac ec5a +df 7157 +4b 9a4f +d7 d915 +12 8926 +2e 8e56 +6c 9e52 +2c 8e52 +2a 8e46 +7e b7de +f9 5761 +68 9e42 +28 8e42 +d3 f1a5 +f3 572d +92 e30c +13 a9af +1f a95f +9e 437c +84 423a +cf d25f +50 9902 +2c 678 +e6 deb4 +c a0f0 +75 179b +d5 71b9 +a a86e +89 e8c9 +47 18b7 +c6 5214 +e a85e +b6 6f96 +72 9f84 +dd 7179 +8c 4878 +4a 984e +48 3a48 +a3 46a5 +ac ecda +61 b601 +93 692f +31 a7ab +35 a79b +46 9ab4 +8a 6ac6 +3a a7ec +f3 7da7 +20 a6aa +3e a7dc +f7 7d97 +24 a69a +a9 6e49 +b1 47ab +2e 2e56 +c2 d88c +25 413 +3f 555 +c 8058 +ad 647b +69 9469 +42 9286 +e6 de9c +c a0d8 +e3 d4a5 +a1 4c03 +bb 4d45 +2c 2e52 +75 b713 +64 1c98 +d3 590f +71 978b +16 b16 +e2 de8c +8 a0c8 +9f 43ff +b9 4541 +64 3698 +c4 58b0 +a1 6e09 +26 2e16 +1d 3d3 +37 515 +4 8018 +92 e924 +fb 5fcf +7a 97cc +be 67de +60 968a +e0 7688 +5e 9174 +44 9032 +3a 2f44 +20 2e02 +17 3bf +31 501 +a1 4ca3 +bb 4de5 +3d a7f1 +23 a6af +3e 5fc +24 4ba +1c 2b58 +1f a3dd +5 a29b +e1 7ca9 +9f 63df +5b 93cd +41 928b +76 3d3e +2b 665 +94 6918 +49 924b +32 8526 +1 29 +68 3c42 +88 ea40 +ee 5e56 +e0 f408 +f7 55bf +7b bf67 +e9 5e49 +25 ae9b +3f afdd +3b af67 +a9 4e49 +2e e56 +d9 79c3 +20 a408 +4c 90d8 +ec 5e52 +c 80d8 +2e 2ed6 +ac 4e52 +6c 1e52 +2c e52 +c1 70a3 +db 71e5 +d1 fb03 +eb fc45 +5a 3166 +ea 5e46 +bc 45fa +aa 4e46 +7c 35fa +b3 45af +2a a6c4 +f0 7d20 +2a e46 +d 2ad9 +3e afd6 +8 80c8 +2a 2ec6 +a8 4e42 +b1 45ab +a9 6c49 +28 e42 +7a 3d44 +60 3c02 +80 ea00 +38 2d62 +bc e55a +7a 3f44 +60 3e02 +57 13bf +71 1501 +e1 5ca3 +28 86e8 +fb 5de5 +2a a46e +11 a1ab +15 a19b +2 8226 +d5 d331 +9a 69e6 +2 8a0e +1c 8b50 +f3 df2f +19 a16b +f7 df1f +1d a15b +4c 1270 +2 a884 +22 e0c +9f 43d7 +ac 4478 +7a 174c +60 160a +91 4ba1 +f5 559b +7 2bd +65 9e19 +73 958f +92 c9a4 +7b 954f +7c 95d0 +62 948e +d2 5926 +19 836b +81 c8a3 +9b c9e5 +6a 944e +bc 6570 +a2 642e +8b e865 +91 612b +7d 1d7b +e8 dc6a +47 9a9f +66 3c36 +ec dc5a +9a c946 +4f 9a5f +5a b946 +f3 5d0f +51 bba9 +f 8a5f +b 8a4f +19 a34b +d2 7906 +87 4a1f +d2 d30c +53 99af +5d 3379 +57 999f +e 887c +4a 986e +4f b2ff +69 b441 +d9 fbe3 +f3 fd25 +1f 895f +8 2ac0 +c 2278 +6 889e +d5 51b9 +de 7bfc +c4 7aba +c 2a70 +a 884e +c8 7a6a +69 be49 +49 104b +71 97ab +d7 591f +75 979b +97 e31f +18 a9c2 +38 f4a +c6 581e +7e 97dc +64 969a +97 491f +35 879b +56 119e +2d ae79 +93 490f +31 878b +86 481e +3e 87dc +f7 5d97 +24 869a +15 a1b3 +7d 97f1 +63 96af +3b f4f +1b a9c7 +b2 c586 +67 969f +2f 2edd +a8 ce60 +7d bfd3 +97 c115 +86 ea16 +47 3895 +6b 966f +ba c546 +86 cab4 +6f 965f +19 b4b +90 c182 +5b bbcf +75 bd11 +49 926b +98 c142 +4d 925b +64 be10 +60 be00 +50 b182 +a1 4ca1 +70 170a +fa 5fee +dc f372 +1f 83dd +5 829b +1b 83cd +d8 f362 +1 828b +58 b142 +8a 4ac6 +98 c96a +d1 fb23 +eb fc65 +57 9935 +9b 6947 +d5 fb13 +ef fc55 +9 80e1 +5e 3176 +5c b95a +95 eb13 +af ec55 +f2 7506 +1e 2176 +d 8271 +58 b94a +91 eb03 +ab ec45 +1a 2166 +77 959f +f7 fd1d +dd fbdb +13 8ba5 +3f 2ddd +25 2c9b +b8 cd60 +96 c9b4 +7f 955f +da 73c4 +c0 7282 +c8 7242 +d8 f360 +1 8289 +51 91ab +49 b849 +25 8e19 +33 858f +d1 f301 +52 b9a4 +3b 854f +28 26c0 +26 849e +b0 cd82 +fe 77fc +e4 76ba +24 26b0 +c9 fae9 +26 8c96 +2c 2670 +15 819b +11 818b +9a 49c6 +af eefd +19 814b +55 b3b9 +f7 551f +54 391a +9 241 +93 4b25 +fc 5570 +e2 542e +e6 d436 +91 69a9 +38 a5c2 +cb d865 +d2 f9ac +35 2533 +44 b2b8 +e6 541e +82 4a24 +95 6999 +4 88b8 +37 db5 +d1 512b +d9 db49 +53 3b25 +4a bae6 +8b c865 +91 410b +c3 722d +2d 47b +d7 d995 +6c 365a +9d 6bf1 +83 6aaf +2a ae44 +a 46 +32 87a6 +f6 553e +8 260 +6b 14c5 +51 1383 +c5 d813 +df d955 +8b 6a6f +b1 e5a9 +d2 db8c +35 713 +28 ae40 +8 42 +92 4926 +30 87a2 +27 ae15 +7 17 +26 ae14 +a9 6e69 +6 16 +2a eee +44 1030 +25 ae11 +20 ae00 +8a 48e6 +cb 5a4f +2f 8e55 +c0 f880 +23 2407 +2e 8e54 +d9 f9c1 +22 2406 +2d 8e51 +2c 8e50 +e5 d631 +aa 6ce6 +61 b681 +c3 7805 +15 8b19 +91 c981 +c4 f032 +de f174 +7 809d +c5 72b9 +2b 8e45 +3c 85f8 +2a 8e44 +d5 f9b1 +be c55c +a4 c41a +1e 23f6 +c2 f02e +dc f170 +5 8099 +29 8e41 +f1 d5a9 +fa ffec +e0 feaa +ac 6e52 +68 9e40 +b1 c5a9 +ba efec +a0 eeaa +28 8e40 +de d1dc +c4 d09a +f7 5597 +27 8e15 +4 8a18 +37 f15 +26 8e14 +ff 7dd7 +2c a6da +23 8e05 +b7 c51d +9d c3db +17 23b7 +b2 ed2c +98 ebea +ef 5677 +22 8e04 +b6 c51c +9c c3da +16 23b6 +68 3460 +b1 4583 +21 8e01 +ba 656e +20 8e00 +80 e822 +9a e964 +1f 295f +13 292f +17 291f +2 2086 +e9 5eeb +a 286e +6 281e +53 190f +2f 2e55 +1e 83fc +d7 59b7 +4 82ba +b7 65bd +2e 2e54 +36 7b6 +f7 d595 +e6 fe96 +2d 2e51 +1 a9 +32 85a6 +b5 65b9 +4d 1053 +e3 7e05 +2c 2e50 +34 7b2 +bf cdd7 +ab 666f +f4 7792 +33 8f2f +4d 9071 +ab 6e45 +9a c3ec +80 c2aa +b3 47a7 +2b 2e45 +33 7a7 +32 8f2e +4c 9070 +aa 6e44 +b2 47a6 +a9 cec3 +c3 d005 +69 3643 +70 9d0a +a9 6e41 +b1 47a3 +29 2e41 +31 7a3 +fa 77cc +e0 768a +27 2e15 +26 2e14 +d5 d191 +de fbd4 +c4 fa92 +9b 63ed +81 62ab +a1 e403 +90 4988 +bb e545 +8 a840 +10 81a2 +25 2e11 +c3 d00f +dd d151 +cc fa52 +8d 48d1 +f2 7706 +9d c9d3 +89 626b +66 3694 +64 3690 +20 2e00 +5a 31e6 +db 53cd +c1 528b +c5 50b9 +d4 fb12 +ee fc54 +2a 2e46 +8 8048 +b3 65af +6f 1e55 +8a c8cc +83 6205 +4a 304c +30 2f0a +e8 56ca +2f e55 +cc 58d0 +dc d9d2 +c8 726a +f7 55bd +6e 1e54 +2e e54 +bb 656f +9c c3d8 +16 23b4 +ad 4e51 +6d 1e51 +2d e51 +9d c9d9 +17 29b5 +8 286a +ba 45c6 +ac 4e50 +f5 55b9 +6c 1e50 +b5 45b9 +be 6ffc +60 9ea8 +a4 6eba +2c e50 +8a 6866 +28 4c0 +b2 4da4 +eb 5e45 +bd 45f9 +ab 4e45 +58 1b4a +7d 35f9 +6b 1e45 +3d 5f9 +46 303c +2c 2efa +fe 57fc +e4 56ba +d7 d31d +58 99c0 +9c 69d2 +2b e45 +ea 5e44 +bc 45f8 +aa 4e44 +7c 35f8 +b3 45ad +2a e44 +25 ae93 +3f afd5 +6c 16d0 +3 a025 +a9 4e41 +69 1e41 +29 e41 +f6 55b6 +c1 7029 +1e 1d6 +b6 45b6 +a8 4e40 +c3 d085 +69 36c3 +70 9d8a +8f e0d5 +40 1022 +5a 1164 +51 9189 +95 619b +82 4226 +e6 d496 +47 b0b7 +b5 4f99 +99 616b +26 e14 +b1 cf03 +cb d045 +78 9d4a +59 9149 +9d 615b +82 6884 +25 e11 +23 e05 +a2 4e04 +74 35b8 +22 e04 +f7 df17 +1d a153 +c2 f00e +dc f150 +21 e01 +51 3303 +6b 3445 +58 99ca +36 af94 +20 e00 +f5 df13 +1b a14f +77 bf37 +e5 5e19 +f3 558f +b2 e5a4 +99 436b +d1 518b +7b 3fc7 +88 4068 +dc f9d8 +3f 255f +2a 246e +7f 9fdf +1 88a1 +2e 245e +fa f5e4 +e0 f4a2 +1d 8b79 +66 9c9c +73 150f +e7 7ebd +51 110b +34 a51a +c7 5a9f +cb 5a6f +cf 5a5f +89 6863 +87 4a9f +8b 4a4f +4b 1a6f +3e ad7e +f a5f +94 41b8 +c2 5a04 +b a2cd +d1 7929 +78 b542 +b a4f +1d b51 +3 a0f +d7 599f +8e 487c +df 595f +7d 97db +c6 589e +cf 72ff +50 39a2 +e9 7441 +97 499f +4e 387c +93 498f +4a 386c +9f 495f +9b 494f +7 aabd +c5 7033 +df 7175 +f aa7d +8a 484e +5f 195f +53 192f +57 191f +4a 186e +4 a832 +1e a974 +4e 185e +d4 7112 +76 9fbe +5c 1970 +42 182e +16 a934 +46 181e +1f 95f +1b 94f +17 91f +13 90f +e 85e +da d9e4 +c0 d8a2 +a 84e +6 81e +57 99b5 +9b 69c7 +e7 569f +5e 31f6 +a1 ec83 +bb edc5 +eb 566f +df 53dd +c5 529b +77 973d +bb 674f +3c 2df2 +db 7bcf +f5 7d11 +99 e9c1 +c9 526b +4e 18fe +cd 525b +4e 105e +e4 7e10 +f2 7586 +a9 6463 +4a 104e +e0 7e00 +1a 21e6 +fa 7546 +f1 77a9 +7 ab5 +d0 7182 +9f 43dd +85 429b +8 8840 +11 8189 +1a abcc +0 aa8a +e 8fe +8d 425b +55 9913 +5b 3bcf +75 3d11 +19 a9c1 +49 126b +7a 3546 +72 3506 +64 9e12 +7e 9f54 +ee defc +14 a138 +7 a097 +d2 73ac +3c 5fa +f3 55af +f7 559f +54 399a +9 2c1 +8c 6ad0 +fb 556f +56 13b4 +57 19b5 +5d b3f9 +ff 555f +97 e91d +48 186a +e6 549e +ea 546e +ee d476 +99 69e9 +d5 519b +7f 3fd7 +8c 4078 +d6 7bbe +f0 7d00 +b3 458f +50 332a +6a 346c +17 9b5 +8 86a +41 3a23 +5b 3b65 +a6 449e +c8 dac8 +2b 64f +42 3aa4 +4a 3a64 +95 419b +3f 2fd7 +4c 3078 +91 418b +3b 2fc7 +48 3068 +62 3484 +99 414b +c 2ad0 +96 49b4 +7f 155f +77 151f +dd 7b5b +13 b25 +9f e3df +6a 146e +6e 9476 +19 29e9 +24 a432 +3e a574 +8e 48fe +43 1225 +a a64 +f8 5d42 +1d 29d9 +97 e39f +7c 1570 +62 142e +86 e09c +51 112b +91 c303 +12 89a6 +ab c445 +ee fefc +58 914a +eb 7ecd +55 111b +df 59ff +cf faff +e9 fc41 +4c 9a72 +71 b521 +b4 4d32 +39 af49 +ae eefc +18 814a +ab 6ecd +15 11b +9f 49ff +35 af19 +9b 49ef +31 af09 +d9 d1cb +53 31a7 +4b 3845 +95 e1b9 +53 11a7 +b0 6daa +4b 12cf +65 1411 +30 2daa +83 60a5 +55 1311 +70 b52a +70 1daa +c3 50a5 +39 7c1 +30 daa +83 40a5 +f0 d52a +74 3d9a +29 6c1 +80 caa8 +b3 4fa5 +6d 16f3 +30 ada8 +eb 5467 +21 8ca9 +82 4aa6 +20 8ca8 +53 11a5 +e2 dc0c +c8 daca +42 3aa6 +e9 5463 +ad 44d9 +3f a5f7 +b1 6da9 +31 2da9 +b0 6da8 +30 2da8 +42 baa4 +2b 864f +f6 d736 +a1 6ca9 +d2 f1a6 +f2 572e +39 a7e9 +f2 7da4 +68 34c0 +71 b529 +4c 9a7a +9a c9ee +93 6327 +7f 1f77 +63 b487 +70 b528 +92 6326 +7e 1f76 +20 2ca8 +28 a6e2 +31 da9 +40 b802 +5a b944 +8e 627c +f0 5da8 +b 867 +f 2a7f +70 1da8 +cd 727b +f2 f724 +e1 5ca9 +6d bc79 +46 ba96 +57 9195 +f1 f723 +e0 5ca8 +41 ba89 +71 b723 +60 1ca8 +4b b2ed +ed 5453 +fb 5565 +e1 5423 +22 a6ae +3c a7f0 +82 682e +9c 6970 +b1 e703 +a0 4c88 +32 ada6 +af e6ff +30 ada2 +ee 7cfe +a3 4625 +ef d6ff +70 9da2 +af c6ff +30 8da2 +e1 d603 +fb d745 +62 9ca6 +a1 c603 +bb c745 +22 8ca6 +ed d6f1 +b2 6da6 +28 24c2 +34 8f10 +eb d6ed +b0 6da2 +26 24be +b4 e59a +a2 6ca6 +24 8e10 +70 b522 +be e5dc +a4 e49a +9a 61e6 +8c 6a70 +8 82c0 +b0 4da2 +26 4be +98 61e2 +f4 d59a +6 82bc +48 b84a +b4 c59a +f2 d526 +c1 5029 +f1 df81 +17 a1bd +a2 4ca6 +6b 964f +82 caa4 +fe d5dc +e4 d49a +33 ada5 +b1 e701 +32 ada4 +31 ada1 +51 1329 +b1 e70b +32 adae +a4 4630 +4b b8c7 +58 b968 +cb 5067 +eb fe65 +fc fdda +b1 c701 +32 8da4 +a7 6e9d +31 8da1 +6e 3476 +af c6fd +30 8da0 +a5 6e99 +6f 94d7 +7c 9578 +9e 4376 +20 8ca0 +ed f479 +c6 f296 +c9 5063 +e9 fe61 +b9 cdc9 +33 2da5 +31 2da1 +a2 c624 +72 1586 +b4 e598 +58 b3e2 +72 b524 +3d 257b +4b 38c7 +58 3968 +70 b520 +20 2ca0 +ef fe55 +73 1da5 +fd 7ddb +33 da5 +0 88a8 +d6 d93e +cf 7277 +8e c856 +f1 5da1 +67 14bd +4e b856 +e7 5c1f +45 bab9 +b1 4da1 +27 4bd +f0 5da0 +66 14bc +98 61e0 +f 2a77 +2f 26d5 +36 8d9c +f4 d598 +f 8057 +d4 d93a +cd 7273 +e3 5ca5 +e4 543a +fe 557c +59 13c1 +8b 60e5 +5d 1bf3 +77 1d35 +8a 60e4 +8e 6a74 +f0 d5a2 +ce fafe +e8 fc40 +7d 157b +89 60e1 +5b 1bef +75 1d31 +42 3206 +e0 5ca0 +88 60e0 +41 ba81 +60 1ca0 +e4 d498 +ed fe51 +db 5165 +c1 5023 +85 4099 +17 a1b7 +bb 4567 +88 c06a +a 2064 +5e 315c +44 301a +9e c9dc +84 c89a +97 6315 +b9 4563 +a 64 +95 e199 +53 1187 +d5 5b93 +ef 5cd5 +30 2d8a +83 6085 +55 1b93 +6f 1cd5 +8b 4a6d +d4 5b90 +74 b51a +10 ab20 +3e 2ddc +24 2c9a +ba 6dcc +a0 6c8a +70 b50a +3a 2dcc +20 2c8a +74 1d9a +c7 5095 +34 d9a +87 4095 +f0 5d8a +2 aac +65 16b1 +b0 4d8a +70 1d8a +c3 5085 +30 d8a +83 4085 +2 822c +af e6fd +30 ada0 +7a 1dcc +60 1c8a +80 ca88 +b3 4f85 +b1 4523 +34 ad98 +25 ac99 +21 ac89 +e5 f433 +ff f575 +d4 59b8 +ab 4467 +cb f265 +29 26c1 +30 8d88 +9 2a63 +79 3d69 +52 3b86 +b1 6da1 +1c b50 +2 a0e +27 24bd +64 9c98 +a9 4463 +f2 5586 +c9 f261 +31 2d89 +f3 df27 +19 a163 +30 2d88 +f2 df26 +18 a162 +75 b519 +99 41e1 +74 b518 +24 2c98 +e6 de36 +c a072 +cf f255 +a9 6c61 +71 b509 +4c 9a5a +7f 1f57 +a0 6c88 +88 e062 +70 b508 +7e 1f56 +20 2c88 +e2 de26 +8 a062 +f2 7d2e +2c a6d2 +35 d99 +34 d98 +b2 ef24 +1c 8172 +68 b6c2 +71 1d89 +59 9163 +f 2a5f +70 1d88 +58 9162 +30 d88 +18 8162 +49 9063 +7a bfec +60 beaa +b5 65bb +71 95a9 +8b 6a45 +93 43a7 +71 b703 +60 1c88 +48 9062 +b a2ed +ad 4453 +a3 4427 +2d 8473 +c3 f225 +bb 4565 +a1 4423 +c7 f215 +9b 4167 +68 bc6a +ee 7cde +a3 4605 +26 ac96 +46 121e +f5 75b1 +a6 46be +66 36be +af c6df +30 8d82 +7e 37fc +64 36ba +68 3660 +62 9c86 +19 8b63 +e8 dec8 +62 3ea4 +a8 cec8 +22 2ea4 +e7 fc95 +3d 277b +ed d6d1 +b2 6d86 +6d 96d1 +32 2d86 +eb d6cd +b0 6d82 +b9 c563 +26 249e +76 b516 +45 3019 +cf 78fd +f6 d716 +a1 6c89 +d2 f186 +f2 570e +26 2c96 +a2 6c86 +22 2c86 +ca 52e4 +4a 12e4 +70 b502 +3 a28d +c9 78e9 +cd 5079 +a6 4e96 +40 10a2 +5a 11e4 +b6 4d96 +a8 e6c2 +b1 4d89 +d9 7be9 +36 d96 +d aa71 +56 bb94 +f4 5d92 +6 ab4 +58 11e0 +b4 4d92 +74 1d92 +4b ba6d +34 d92 +b aa6d +54 bb90 +39 87cb +f2 5d86 +c9 fa61 +b2 4d86 +89 ea61 +32 d86 +9 aa61 +52 bb84 +50 bb80 +48 10e0 +ee 76d6 +f6 fd36 +25 2639 +a4 ec98 +62 1c86 +82 ca84 +19 b63 +75 9d91 +61 3629 +d 27b +80 ca80 +8e 6ad6 +4a 9ac4 +83 e8af +9d e9f1 +6c b45a +8 aa60 +91 4123 +c6 7ab6 +27 a63d +66 163e +36 ad94 +c6 5abe +e0 5c00 +51 1309 +8b 4067 +ab ee65 +58 bb6a +d1 5b01 +47 121d +26 ac94 +d0 5b00 +46 121c +40 1208 +ca 5aec +33 8d85 +67 36bd +32 8d84 +af c6dd +30 8d80 +64 36b8 +93 c9ad +7c 9558 +9e 4356 +20 8c80 +8f 4057 +af ee55 +5c bb5a +48 3a60 +4d b8fb +cc f258 +de 7376 +7 29f +37 2d95 +4 a898 +36 2d94 +b5 6d91 +5e 997c +44 983a +d 8251 +35 2d91 +27 8cb7 +a6 c614 +b4 6d90 +34 2d90 +33 2d85 +32 2d84 +da d96c +c0 d82a +69 1443 +b1 6d81 +a0 c420 +27 249d +65 3e99 +4d b273 +b0 6d80 +b9 c561 +26 249c +5d b3d3 +77 b515 +27 2c95 +5c b3d2 +76 b514 +26 2c94 +dc 7958 +a5 6c91 +b9 edcb +f6 5716 +5b b3cf +75 b511 +25 2c91 +39 adcb +76 1716 +a4 6c90 +5a b3ce +74 b510 +24 2c90 +a3 6c85 +5 2b1 +50 398a +59 b3c3 +73 b505 +4e 9a56 +23 2c85 +f0 f5a0 +58 b3c2 +72 b504 +22 2c84 +3d 255b +d8 7948 +a1 6c81 +bc 6558 +b5 edbb +f2 5706 +90 c320 +17 239d +57 b3bf +71 b501 +4c 9a52 +a7 4e95 +6e 1cdc +7f b757 +54 1b9a +21 2c81 +3c 2558 +a0 6c80 +a9 c461 +16 239c +8d 4053 +ad ee51 +c9 d069 +a2 ce86 +7b b76f +77 1d95 +44 9898 +ad 6473 +69 9461 +7a b76e +76 1d94 +f9 f76b +f5 5d91 +b9 e76b +b5 4d91 +ba cfc4 +a0 ce82 +79 b76b +75 1d91 +f8 f76a +f4 5d90 +b3 4d85 +80 c888 +4d 3251 +54 9918 +73 1d85 +33 d85 +0 8888 +72 1d84 +d 22f3 +14 89ba +27 2435 +ad c459 +29 c61 +d6 d91e +cf 7257 +99 61c1 +71 1d81 +4f 38f7 +d5 d91b +ce 7254 +31 d81 +95 c91b +f 28f7 +8e 6254 +b0 4d80 +6a b66e +66 1c94 +3 a2f +1d b71 +e9 f66b +e5 5c91 +8d 60d1 +81 4a2b +9b 4b6d +8c 60d0 +7a 376c +74 9d92 +60 362a +ab c64f +c2 faa4 +2c 8cf2 +8b 60c5 +5d 1bd3 +77 1d15 +44 9818 +38 2dca +63 1c85 +8a 60c4 +62 bea6 +b7 65b7 +73 95a5 +18 8948 +62 1c84 +19 b61 +94 49b0 +7d 155b +e1 5c81 +fc 5558 +57 139d +89 60c1 +5b 1bcf +75 1d11 +61 1c81 +8a e2e6 +93 49ad +7c 1558 +e0 5c80 +de d976 +d 279 +c3 5aad +3e 8776 +56 139c +d6 d3be +f0 d500 +a0 4c80 +88 60c0 +5a 1bce +74 1d10 +7a bfe4 +60 bea2 +b5 65b3 +71 95a1 +60 1c80 +5c 31da +38 ad6a +8b e065 +75 1db3 +f4 5710 +28 ac6a +5b 3167 +d5 5393 +ef 54d5 +b0 658a +f2 7f8c +5c 11da +7d 3ffb +97 413d +28 8c6a +9d e179 +5b 1167 +67 9cb7 +e6 d614 +b8 6d6a +c2 d806 +8b 6065 +b0 6d2a +30 2d2a +83 6025 +28 2c6a +b9 45c1 +39 5c1 +89 62e1 +90 c9a8 +32 870e +63 bca5 +f8 5d6a +23 aca5 +b8 4d6a +22 ae84 +2 86 +ad ecdb +78 1d6a +b1 4f23 +cb 5065 +38 d6a +71 3f23 +8b 4065 +b0 4d2a +79 3741 +30 d2a +69 3ee3 +83 4025 +bd e75b +3e adfe +13 aba5 +a8 4c6a +b7 ed1d +9d ebdb +68 1c6a +88 ca68 +a1 4e23 +bb 4f65 +b5 e71b +36 adbe +5e b3dc +44 b29a +35 8d3b +cb faed +2e 2674 +6e 1674 +31 ad29 +23 ac87 +6d 1673 +30 ad28 +9e e95c +84 e81a +21 ac29 +13 ab87 +20 ac28 +9b 4b67 +99 e169 +23 e27 +36 2d9c +9 243 +93 4b27 +31 8d29 +8b e0c7 +98 e168 +8 242 +92 4b26 +d8 db4a +52 3b26 +4 20b8 +87 6a3d +87 cabf +a1 cc01 +4b 3a67 +de d95c +c4 d81a +8a 4a66 +59 99c9 +9d 69db +9e c95c +84 c81a +15 abb1 +b7 4d17 +86 cabe +a0 cc00 +89 e069 +7c 3f58 +e a076 +83 4a27 +96 699c +21 8c29 +c9 da4b +43 3a27 +88 e068 +51 9989 +95 699b +82 4a26 +39 fe3 +53 1125 +b1 6d29 +31 2d29 +a3 6c87 +b0 6d28 +23 2c87 +30 2d28 +ef f6dd +70 bd80 +a4 66b8 +e0 7c22 +23 c8d +fa 7d64 +fb ff6f +24 8e98 +57 1395 +3 2aad +a1 6c29 +4e 32fe +68 3440 +21 2c29 +93 6b87 +a0 6c28 +b4 45b2 +7 2a95 +13 2b87 +20 2c28 +33 870d +d2 5b86 +f9 5d69 +74 15b8 +a6 c494 +4e ba56 +b9 ed43 +43 1aad +e1 5c29 +37 adbd +90 e180 +31 dab +b0 4708 +64 14b8 +45 b299 +a1 4c29 +d2 d126 +46 ba16 +3d 8fd3 +57 9115 +f2 df8e +18 a1ca +d3 5b87 +e0 5c28 +9 2869 +70 b5a8 +92 63a6 +c0 f008 +7e 1ff6 +1 2829 +60 b4a8 +38 2d48 +82 62a6 +b0 ef08 +6e 1ef6 +9a e166 +d9 5941 +bb 67cf +77 97bd +72 9d26 +41 1829 +8a 42e6 +2a 8c66 +53 b307 +42 188c +28 8c62 +51 b303 +6b b445 +40 1888 +f8 7f48 +8a e066 +62 9c26 +ba 6d66 +d4 719a +3a 2d66 +f0 75a8 +b 2067 +ed d671 +b2 6d26 +c5 f8bb +df f9fd +28 2442 +70 35a8 +eb d66d +b0 6d22 +dd f9f9 +26 243e +de 71dc +c4 709a +2a 2c66 +28 2c62 +ac e45a +cd 78fb +7 a29f +e0 74a8 +a2 6c26 +cf f8fd +18 2342 +60 34a8 +af ecd7 +bc ed78 +7a 1d66 +ad ecd3 +78 1d62 +c7 5837 +e 827c +38 d62 +58 bb60 +30 da8 +b0 4d22 +dd d9f9 +26 43e +50 bb20 +ea 5c66 +e8 54e8 +aa 4c66 +ca fa64 +d4 5932 +e 82d6 +ac ec78 +9f ebd7 +6a 1c66 +8a ca64 +85 eab9 +43 1aa7 +d 28f9 +a8 4c62 +e7 7c1f +3 aa7 +c8 fa60 +f 2add +88 ca60 +47 929f +28 c62 +48 ba60 +de f376 +cd 58fb +7 829f +28 86e0 +8a 4864 +5e b3d4 +44 b292 +ba 4d64 +a0 4c22 +21 ac23 +3b ad65 +b8 67c0 +96 e916 +20 ac22 +3a ad64 +73 97ad +b7 67bf +39 ad61 +b0 6780 +31 ad21 +86 e816 +22 e84 +f7 df97 +1d a1d3 +54 3bb0 +3d 75b +a0 6680 +b2 ef2c +1c 817a +55 b333 +44 18b8 +6f b475 +8 aae2 +22 ac24 +21 ac21 +7 2a3d +f7 57bf +d4 d198 +cf 52fd +50 19a0 +96 c916 +af 6e5d +d4 d912 +5 2a39 +38 8d60 +ad 6e59 +81 e023 +9b e165 +f0 5780 +b0 4780 +80 e022 +9a e164 +8a 404e +a7 6e1d +99 e161 +b5 6d33 +71 9d21 +31 8d21 +8c c0d8 +6 20b4 +c6 d816 +86 c816 +a7 46bf +8a 42c4 +4 20b0 +ad 6c73 +69 9c61 +e6 56bc +87 6a35 +29 8c61 +a6 46bc +9e c954 +84 c812 +86 6a34 +28 8c60 +bf 47fd +a5 46bb +e0 5680 +a0 4680 +42 3284 +21 8c21 +20 8c20 +a1 6c23 +bb 6d65 +88 e868 +a0 6c22 +ba 6d64 +85 c2b1 +d0 f98a +39 2d61 +b8 6d60 +99 cbc9 +13 2ba5 +f0 d522 +77 359f +0 a828 +c5 f8b9 +e 22fe +28 2440 +b0 6d20 +af 667d +30 2d20 +c5 7099 +f6 f596 +26 8e94 +7 a0b5 +9c 417a +dc d9fa +d5 7333 +ef 7475 +88 6ae2 +a2 6c24 +18 2340 +b2 6f2c +1c 17a +8 2ae2 +a8 cc48 +22 2c24 +ea 56c4 +a1 6c21 +6a 16c4 +71 bd09 +44 9830 +88 6842 +26 a6be +16 914 +85 4039 +21 c03 +3b d45 +8 8848 +a7 64b7 +63 94a5 +63 b685 +f8 574a +75 3d19 +e3 56a7 +88 4a4a +ad 64f9 +f0 570a +d0 f182 +55 3919 +c3 52a7 +79 1d49 +8d 60f9 +a0 e420 +fa 7d4e +af 4675 +bb e74f +3c adf2 +f2 7d0e +a7 4635 +b3 e70f +34 adb2 +21 2421 +98 e140 +4d b259 +12 b84 +5f 13d7 +6c 1478 +51 1ba1 +1f 2977 +da 794e +8f 4275 +9b e34f +1c a9f2 +3c f7a +d2 790e +87 4235 +93 e30f +ad e451 +14 a9b2 +1 2021 +3f fd7 +4c 1078 +2d ae59 +a3 6e0d +d 5b +84 4238 +be e554 +a4 e412 +58 b1c2 +31 8da9 +6e 347e +c9 f2c3 +e3 f405 +5a 39cc +40 388a +93 6b85 +c6 5a9e +12 a32e +2c a470 +75 b593 +e6 5c3e +3c add2 +d3 530d +54 19b0 +67 b495 +fc 555a +37 ad1f +98 4b60 +e 27c +6 201e +3a 256e +f0 550a +2 22c +aa 4e64 +bd 6dd9 +19 21c3 +f3 5f87 +4c 125a +d6 5b3e +2c acd2 +e1 74ab +fb 75ed +c3 520d +44 18b0 +21 2e09 +27 ac1f +88 4a60 +47 129f +49 98cb +42 3204 +3 2805 +d0 f120 +7 aa15 +22 2e04 +93 430d +14 9b0 +d9 f969 +2b 8ccf +c1 fa81 +58 3b60 +1c a352 +b5 6539 +eb 56e7 +d7 f9bf +3a 2546 +31 27a9 +95 6b39 +7e 1d56 +75 1fb9 +7c 1d52 +b3 6f0d +1d 15b +2 884 +3d af59 +9 2ac3 +23 2c05 +f0 f520 +ea 7ce4 +d0 7ba2 +31 a729 +c2 788e +dc 79d0 +9b 6bcd +81 6a8b +8d 6af9 +1 8a29 +16 114 +c9 f8cb +2c 2452 +d0 d982 +46 909e +1 2aa9 +a 2846 +a0 4e88 +32 afa6 +33 785 +42 92a6 +1a b46 +25 24b3 +3f 25f5 +e6 debc +c a0f8 +91 6b01 +7 221d +d5 f191 +c1 7801 +37 2f1d +84 4090 +80 4080 +e6 f41e +2 82a6 +29 4c1 +b3 4da5 +80 c8a8 +1e 9fc +2f a477 +4 8ba +9d 4359 +62 94a6 +3a d46 +31 fa9 +40 ba02 +5a bb44 +74 3590 +d 22db +27 241d +69 1ceb +7a b766 +e8 5648 +65 1cbb +7f 1dfd +76 b736 +e4 5618 +c4 f090 +8b e2cf +a5 e411 +79 1749 +59 b1c1 +7b 1ded +61 1cab +72 b726 +e0 5608 +c0 f080 +c6 d096 +86 e89c +51 192b +3b 2d6f +30 a582 +88 62ea +a2 642c +80 e080 +52 9b8e +6c 9cd0 +4c 98d0 +22 84a6 +b3 452f +68 1648 +bb c54f +d2 f9a4 +48 b0c0 +f7 df1d +1d a159 +64 9e90 +60 1608 +40 b080 +e2 56ac +f3 df0d +19 a149 +4f 92f7 +4c 9ad0 +48 1248 +a7 e61d +28 acc0 +d3 53a5 +a0 cea8 +76 959e +f6 fd1c +dc fbda +12 8ba4 +44 1218 +ce 5afc +27 8637 +28 648 +7b b54f +92 e9a4 +7e 1dfc +64 1cba +fd 5759 +24 618 +4 a090 +fb ff67 +24 8e90 +a8 6c60 +89 cac9 +3 2aa5 +20 608 +0 a080 +aa 4eec +c 8ad0 +1a 21c4 +0 2082 +8 248 +36 859e +36 a536 +a4 4418 +68 bc48 +72 95a6 +41 10a9 +50 bb02 +6a bc44 +e7 769f +a7 4e15 +79 35c9 +76 9596 +45 1099 +48 1240 +d2 5b24 +ce 52fe +e8 5440 +ac e47a +43 1285 +8b 60cf +47 90bd +83 42a7 +39 d49 +22 8c26 +82 42a6 +38 d48 +87 e89f +52 192e +d8 d148 +52 3124 +38 2fe2 +5 aa11 +5a 9166 +72 1d8c +70 9782 +28 2c60 +6a 1446 +ac e458 +e2 d606 +47 9815 +5f 1357 +8 a8ca +f2 f52e +35 8599 +15 893b +e 2274 +3e afdc +24 ae9a +fc 7552 +d1 53a9 +b1 c589 +91 c92b +8a 6264 +ba efcc +a0 ee8a +d9 f3e9 +36 8596 +8e 42fe +a8 4440 +3 285 +2e a45e +8 240 +54 b3b8 +f6 551e +92 4b24 +32 8586 +d8 7160 +1 89 +f0 77a2 +a9 6e61 +d0 5902 +46 101e +af e4df +7a 156e +91 c189 +9a ebcc +80 ea8a +b9 efe9 +16 8196 +43 302f +5d 3171 +6e 3efe +88 4040 +14 8192 +8b 6a6d +3e dde +d4 7b90 +10 8182 +5f bbf7 +cd 5ad9 +b2 ef04 +1c 8152 +5b bbe7 +c9 5ac9 +ae eef4 +18 8142 +f8 5742 +1d 23d9 +a7 6cbd +54 1912 +60 bc88 +93 4185 +a9 6c69 +82 6a86 +46 9a16 +18 81ca +51 b383 +6b b4c5 +14 912 +ba 4746 +55 1911 +65 9c99 +3 2087 +ea 5eec +10 2128 +26 243c +c 22fa +68 94e8 +ac 64fa +b0 4702 +42 b206 +4b 18cd +d1 f181 +6a 9e44 +ae 6e56 +8 860 +6 2034 +8c c058 +51 1983 +29 a4c1 +b3 eda5 +62 3426 +e8 d44a +9a 43ec +80 42aa +ed dcd1 +d3 db8f +36 716 +1a 2b66 +1b 23ef +35 2531 +99 49e1 +68 144a +2c 86d2 +f2 5d2e +11 a1a9 +ad e6db +78 176a +58 b1e2 +d0 71a8 +cd d271 +92 6926 +e2 5e06 +8 2042 +30 a7a2 +20 ac88 +77 1715 +44 9218 +38 27ca +2c 8e58 +e5 d639 +aa 6cee +43 b2ad +e5 5413 +ff 5555 +ee 7e56 +48 1860 +fe ff74 +e4 fe32 +27 8e9d +e3 dc0d +c9 dacb +43 3aa7 +2c 652 +b6 4f36 +0 a888 +57 1315 +24 8e18 +60 be88 +93 4385 +37 715 +79 35c1 +52 bbac +f4 5d12 +6 a34 +22 8e06 +76 b79e +f1 5721 +c2 580c +40 ba88 +3a dcc +20 c8a +17 315 +50 31a8 +4d 9271 +98 c94a +12 2926 +95 4199 +7d b573 +44 b238 +e7 fcbd +30 2702 +ba 6fe6 +2 aaa4 +f0 fd82 +d6 7936 +1d a37b +c0 5880 +1e 8376 +1c 2972 +72 b526 +e0 5408 +36 a59c +23 8627 +9d 4159 +e7 76b7 +a3 4c07 +1 aaa1 +94 6998 +9b 4b65 +81 4a23 +9b e3cf +b5 e511 +9c 6958 +d2 5b06 +f7 75b5 +f3 57ad +a8 6c62 +a3 cc0d +89 cacb +3 2aa7 +d5 f939 +1e 237e +1a a1c4 +0 a082 +c4 5030 +aa 4eee +0 aa80 +ef dcfd +d5 dbbb +38 742 +c2 5026 +4c 9072 +e2 fe24 +6f 9655 +30 a70a +2c 24fa +38 8f48 +a5 6439 +c7 f8bf +2a 2446 +f2 fda6 +21 26a9 +a aa64 +f8 fd42 +85 e8b3 +9f e9f5 +6e b45e +28 6e2 +42 322e +5c 3370 +56 9996 +2 aa24 +f0 fd02 +53 b1a5 +73 172d +c0 5800 +6 8014 +69 3ce1 +38 74a +f2 df86 +18 a1c2 +dc 5170 +c2 502e +48 1062 +97 e115 +c8 5a4a +ed 74f9 +25 2439 +38 adca +a aa44 +f6 f51e +12 83a6 +4 8010 +f8 dfc0 +1e a1fc +4 a0ba +bd c5db +37 25b7 +c7 581d +65 9699 +95 e111 +69 1449 +91 e1a9 +7d 9df9 +8 aa40 +c6 581c +64 9698 +0 aa00 +53 13a5 +20 8ea8 +f8 5560 +c5 7299 +94 61b2 +50 91a0 +2b 2e65 +50 11aa +a3 44a5 +48 3848 +6 b6 +26 aeb4 +5c 1952 +61 14a9 +70 bf02 +8a c044 +45 3811 +ea 5e66 +f 2afd +d8 d942 +9 2a69 +a8 4e48 +3a af66 +a7 ec9f +72 1d2e +13 a9a5 +33 f2d +91 e30b +ab e44d +12 a9ae +23 e2d +f8 df40 +4 a03a +1e a17c +c 2a5a +1d 159 +ed d6db +67 36b7 +84 4230 +62 9406 +31 f09 +a8 e44a +6 a894 +26 e1c +13 a925 +90 6380 +d2 7126 +15 191 +3 a825 +80 6280 +6c 1ed0 +38 56a +48 1a4a +6d 34f9 +94 4930 +a 4c +49 1a49 +7a 9f46 +d3 51af +69 b6c9 +c5 5a39 +83 caa7 +6c 9652 +8c ea58 +4a 1a46 +b2 6fac +1c 1fa +de 515e +3c aff8 +45 9099 +e1 5ea9 +4a 184e +a4 e698 +62 1686 +2c 24d8 +b6 6dbc +2 a824 +41 1a09 +3c ad58 +72 9f06 +11 a921 +f9 dfe9 +12 a184 +1 a821 +e9 dee9 +2 a084 +22 60c +ac 4ef0 +b4 6db8 +f0 5782 +0 a820 +6 2b6 +81 42a9 +8a 4046 +aa ee44 +c2 da8c +25 613 +3f 755 +76 1d3e +5c 1b52 +3 a27 +16 299c +b7 e53f +41 12a9 +6a be44 +72 97a6 +3c a5f8 +a8 e4c8 +66 14b6 +44 3a12 +5e 3b54 +e1 54a9 +90 41a8 +2f a45f +32 d26 +52 bb24 +c aa7a +70 15a8 +41 b289 +60 14a8 +c0 5080 +85 6899 +b6 ed96 +21 a681 +83 6805 +20 a680 +82 6804 +0 a280 +23 2485 +22 2484 +d 8079 +56 919c +2f 2e77 +43 1a0d +f2 7f2c +5c 117a +95 4333 +af 4475 +4a 12c4 +87 4a35 +59 31e9 +1 2821 +43 1007 +85 e019 +0 8000 +60 3c20 +ca 78ec +4 a290 +fb f547 +d0 598a +b3 458d +a3 4e05 +75 35b9 +2b cef +c1 7aa1 +aa 464c +36 a594 +40 1200 +ca 5ae4 +3b a54f +93 6ba7 +7c 3752 +74 3d12 +ad 64d3 +69 94c1 +f3 dda5 +c7 7295 +50 1908 +a a244 +29 2cc1 +5c 9972 +c8 7860 +2 a204 +1 a221 +82 e00e +9c e150 +a1 4e01 +3a 2f6c +20 2e2a +31 529 +30 f08 +28 a640 +92 4126 +9d 6979 +d3 5b27 +20 a600 +8a 40e6 +8 a240 +0 a220 +78 1742 +27 2cbd +a 8a66 +1d a9db +3 2005 +7a 1dec +60 1caa +f9 5749 +7e 1756 +13 2b8f +2d 2cd1 +7 a35 +da 53ec +c0 52aa +d9 7b6b +2d a651 +97 4137 +5f 997f +b0 45a2 +3 2a85 +28 ae4a +8 2a40 +14 2918 +8c ead8 +4a 1ac6 +8e 4274 +be cfdc +a4 ce9a +e4 5438 +d7 5397 +e2 5e24 +8 2060 +43 9a8f +5d 9bd1 +84 4a30 +2 2026 +88 c04a +70 15a2 +4e 3afe +68 3c40 +cf 5255 +7c 1f5a +5c b9d2 +68 bc4a +9b 4147 +50 11a2 +48 3840 +96 4136 +26 8c1c +c 8ada +31 a589 +a2 4686 +22 6a6 +2 aa04 +48 b04a +30 5a2 +84 e238 +42 1226 +55 319b +8f e25d +10 a900 +a2 44a6 +4 a30 +50 bba8 +f2 5d0e +12 1a6 +a 2844 +32 afa4 +fe 7f54 +e4 7e12 +db 53cf +f5 5511 +77 3595 +44 b098 +81 c089 +b c7 +18 168 +a9 eee9 +6 8096 +de 73f4 +c4 72b2 +1e 2b5c +4 2a1a +15 119 +ee ded4 +14 a110 +1d 2959 +95 eb19 +53 1b07 +45 b0b9 +7 8095 +2f 4ff +c5 72b1 +8b 68c7 +98 6968 +47 98b5 +32 a584 +f9 ffe9 +56 9196 +a a264 +f8 f542 +22 a4a4 +c8 7060 +a9 cec9 +23 2ea5 +44 9290 +5a b3e4 +40 b2a2 +18 2b42 +40 1080 +2 a0a4 +22 62c +15 2919 +31 a581 +11 a1a1 +31 729 +1a 83cc +d3 5987 +0 828a +33 787 +b8 6d62 +2e 247e +b3 cd0d +99 cbcb +13 2ba7 +10 a182 +d4 5130 +ba 4fee +2e 2efe +48 3040 +70 b7a0 +0 8008 +19 3c3 +33 505 +22 2e06 +94 41b2 +8c 6850 +0 2828 +20 a480 +ca 52ec +0 a0a0 +20 628 +45 1211 +cf 5af5 +ca 7ae6 +2b a66d +74 b790 +5 2819 +36 ad16 +48 b260 +28 462 +37 a595 +1 aa21 +df 515d +c5 501b +e5 fe19 +42 1286 +84 e298 +69 1469 +30 2522 +22 8e2e +3c 8f70 +8f 68d7 +9c 6978 +4b 98c5 +4a b246 +53 190d +b8 4742 +43 180d +a8 4642 +cd 7ad9 +65 1631 +b0 4d0a +fd dff9 +16 a194 +a6 4ebe +c0 5000 +53 3b05 +1e 8b5c +4 8a1a +29 a4c9 +37 f17 +ed def9 +6 a094 +26 61c +b0 4f00 +b2 cdac +ab 66e5 +a8 6e4a +64 9e38 +45 1231 +90 490a +c9 7ac3 +e3 7c05 +22 a484 +cc 52f0 +8d 68d3 +49 98c1 +45 1039 +76 9536 +34 a590 +48 b242 +51 1909 +eb 56c5 +8f 40d7 +9c 4178 +55 9b19 +d0 79aa +24 a490 +41 1809 +72 9d06 +a6 463e +90 c900 +6 801c +c4 7238 +45 9a19 +14 a190 +34 718 +d2 712e +15 199 +f6 dd1c +dc dbda +56 3bb6 +ef 7655 +28 8e48 +d6 d91c +cf 7255 +7 abf +21 c01 +6f 3cf7 +f5 dd1b +ee 7654 +26 ebe +40 1000 +94 e918 +52 1906 +e1 74a1 +39 ad69 +12 ab86 +56 9b16 +61 b483 +7b b5c5 +1 a021 +6a 16cc +54 9910 +1a 2b6c +0 2a2a +11 129 +d6 73be +f0 7500 +12 32e +c9 d8e9 +2c 470 +75 1593 +d a251 +8a e064 +c a250 +5a bb6c +40 ba2a +95 613b +51 9129 +40 1808 +e7 fc1d +cd fadb +3 8aa5 +67 949f +70 3fa2 +8a 40e4 +ce dafe +e8 dc40 +dc 71f2 +53 938f +6d 94d1 +54 1918 +d aad1 +29 ccb +3a a746 +93 6b2d +3 8a85 +8e cafe +a8 cc40 +aa 66e6 +eb 76ed +14 918 +3 228d +c aad0 +7b 956f +8 2248 +86 6ab6 +42 9aa4 +5d 937b +4 2ab0 +c 28d8 +3e a57c +24 a43a +e6 fc1c +cc fada +2 8aa4 +66 949e +f0 dd82 +b0 65a8 +0 8a80 +e5 5eb9 +52 1984 +9 861 +7 2035 +8d c059 +b6 cd3c +9c cbfa +af 6675 +1 21 +ae ccde +63 9605 +a7 6617 +58 1942 +64 b498 +5 a231 +a 804e +a0 ee00 +b0 e520 +9f e3ff +b9 e541 +41 1881 +5c 1158 +a6 46b6 +ee 7c56 +e4 fc32 +fe fd74 +27 8c9d +8f 6a57 +4b 9a45 +26 ac3c +c aafa +1d 81f9 +cd fa5b +3 8a25 +67 941f +77 bfbf +91 c101 +9a eb44 +80 ea02 +41 3881 +5c 3158 +c7 5a95 +3e a55e +92 e92c +cf 5277 +2 8a04 +cc fa5a +2 8a24 +15 a999 +f0 dd02 +97 c9b5 +66 941e +a8 c442 +ec 7c52 +e2 fc2e +fc fd70 +25 8c99 +54 3b38 +47 3a97 +3d a55b +6c 1670 +22 ac84 +cc 5af0 +42 120c +20 e80 +96 e116 +9b 49e5 +81 48a3 +6a 144e +6e 9456 +19 29c9 +2e 86d6 +f4 5d32 +3e ffe +58 1140 +6 a2b6 +43 3805 +6 94 +1 8a21 +c5 5a91 +c8 58e0 +2 8284 +8 a60 +f6 5d3e +1e ab54 +4 aa12 +15 8111 +0 8a20 +c4 5a90 +9e 43dc +84 429a +9a 43cc +80 428a +1d 8971 +3 882f +7e 17dc +64 169a +5d 1979 +3a 856c +20 842a +be edfe +73 b725 +5a 13cc +40 128a +50 118a +a3 4485 +d8 716a +a7 c695 +db dbed +c1 daab +24 632 +3e 774 +b 22c5 +12 898c +3e ad56 +d 2859 +15 1bb +3e 7dc +24 69a +f7 559d +51 b323 +6b b465 +40 18a8 +28 26e0 +11 b01 +ff 5ddf +19 89e1 +34 59a +90 e1a2 +b0 472a +fb d74f +7c 9df2 +41 1089 +72 9586 +29 8463 +90 c180 +a2 6e86 +c9 7069 +45 9299 +23 a4ad +1e 3dc +4 29a +7d 3df3 +e2 760e +fc 7750 +1a 3cc +0 28a +79 3de3 +f8 7740 +c0 f288 +f3 7785 +a7 cc97 +b4 cd38 +ad 6671 +f6 7794 +cd f8db +3 88a5 +4 803a +1e 817c +a8 c46a +7a 3fc4 +60 3e82 +f0 d502 +cc f25a +2 8224 +15 a199 +a 8a44 +5c 9158 +80 4a28 +1 8881 +1c 8158 +3e 2f56 +66 b6b6 +4e 3076 +5f 1957 +6c 3658 +83 6aad +f0 7d22 +33 d8d +2a a6c6 +c3 f80d +c 2252 +46 9894 +5e 935c +44 921a +3a adcc +20 ac8a +77 1717 +e 8a54 +3a a56c +20 a42a +45 9233 +5f 9375 +cd f85b +3 8825 +80 4280 +cb f26f +c7 5895 +4b 3a6f +e2 7e26 +25 e91 +b0 4f0a +ca 504c +da f1ec +c0 f0aa +f3 75a7 +f1 5501 +d7 53bf +cf 7a5d +cd d8d1 +16 316 +fd d759 +77 3735 +64 9cba +7e 9dfc +f1 d58b +8f 6a5d +f 8d7 +1c 978 +85 c899 +1b 83ef +35 8531 +87 6a1d +31 5a1 +be e7dc +a4 e69a +56 13be +70 1500 +e0 5ca2 +fa 5de4 +62 3e84 +85 6833 +9f 6975 +41 9821 +14 219a +22 2e84 +3d 275b +1 8821 +39 afe3 +53 b125 +89 c8c9 +3 28a5 +c9 f26b +c5 5891 +30 8588 +10 892a +9 2263 +79 3569 +52 3386 +9a 436c +80 422a +cb d24f +4c 98f2 +68 3c4a +e3 f6a5 +a1 6e03 +bb 6f45 +8f c27d +10 8920 +85 6a19 +98 c9c8 +91 6301 +12 29a4 +d8 f36a +d4 5990 +8b 486d +8 8860 +a5 c413 +bf c555 +ae ee56 +9f 43fd +85 42bb +51 9983 +79 97eb +0 8820 +9d c3d3 +b7 c515 +a6 ee16 +88 c8c8 +81 6201 +2 28a4 +9a 4146 +91 43a9 +d0 7180 +35 f11 +61 1689 +1a 146 +11 3a9 +20 ae02 +3a af44 +e4 5e90 +a 20cc +1e 8356 +f9 55e1 +2a 8cc6 +c1 7809 +46 3816 +16 a1b6 +84 4098 +6c b472 +a 22c4 +11 898b +65 b41b +7f b55d +42 3806 +70 3580 +95 693b +51 9929 +42 1006 +84 e018 +0 2820 +32 a5ac +5a b3ec +fc 5552 +40 b2aa +18 2b4a +2 806 +40 1088 +28 8462 +22 a4ac +51 3909 +72 972c +b6 673e +14 198 +11 189 +1a 2bcc +0 2a8a +83 422d +b0 65aa +d5 53b3 +ef 54f5 +76 3f96 +9d 4179 +bc cf5a +36 2f36 +e4 5cba +fe 5dfc +e6 7496 +59 9361 +9d 6373 +54 1118 +ef f477 +c4 58ba +de 59fc +89 eac3 +a3 ec05 +9e c3fe +b8 c540 +d9 59e1 +13 8385 +32 2d0e +6d 9659 +f0 5702 +15 2399 +6d 9edb +72 1da4 +f1 5701 +d4 7192 +8b 606f +8a 4a46 +5c 31fa +86 62b6 +42 92a4 +ec f6da +22 86a4 +e6 f41c +cc f2da +2 82a4 +8e 6a54 +f0 d582 +29 aec9 +65 9499 +9b 636f +c3 5825 +39 f41 +19 b41 +21 86a1 +83 4825 +92 49ae +8 ca +28 aec8 +e2 f40e +fc f550 +ec 545a +57 b395 +d4 f932 +17 899d +de 7956 +38 f40 +40 3088 +28 a462 +2 2806 +12 838e +2c 84d0 +d8 59ea +44 b298 +77 3795 +93 4b87 +a0 4c28 +6 aa16 +17 8115 +b7 4db5 +90 c302 +aa c444 +ea 7ecc +54 111a +de 59fe +20 8680 +82 4804 +18 b40 +20 86a0 +82 4824 +84 6a30 +92 61a6 +98 e96a +eb f66f +e7 5c95 +9a c94c +80 c80a +90 61a2 +45 1a91 +ef fefd +59 914b +43 1085 +10 8b88 +b0 4582 +2a 4c4 +10 382 +fa d5cc +e0 d48a +e0 76a8 +f9 f563 +22 848c +b0 6f22 +ca 7064 +fa fdcc +e0 fc8a +18 29ea +46 9294 +42 b2a6 +1a 2b46 +42 1084 +7d b759 +90 c1a0 +1d b59 +4e 9056 +e9 7461 +1a ab46 +65 9691 +c7 5815 +44 98b8 +88 68ca +61 1481 +f2 5fa4 +18 21e0 +c9 7061 +25 263b +3f 277d +45 9291 +c9 d2c9 +43 32a5 +64 9690 +60 1480 +ee 7456 +e4 f432 +fe f574 +27 849d +b1 cd81 +e5 76b9 +96 c91e +8f 6257 +4b 9245 +23 2c0f +3d 2d51 +65 b4b1 +56 b91e +b 8245 +12 8984 +ed f65b +6e bcfe +23 8625 +89 c2c3 +a3 c405 +e6 febc +50 910a +8a 484c +59 bbe1 +fb 5d47 +28 864a +39 a561 +56 b93e +b 8265 +dc f9da +91 c301 +12 89a4 +e4 7498 +27 a6b7 +93 4187 +7a bdcc +60 bc8a +c4 7098 +7 a2b7 +5a b9cc +40 b88a +7c 1f72 +60 3480 +e6 5694 +a9 e64b +2a acee +38 8548 +5a 3346 +ed f4db +23 84a5 +40 3080 +c6 5294 +89 e24b +a a8ee +9b 496f +fe 7554 +e4 7412 +35 27b3 +88 624a +44 9238 +20 aca8 +77 1735 +38 27ea +b0 6782 +23 2685 +b4 cd3a +ad 6673 +69 9661 +84 6292 +40 9280 +9e 63d4 +94 c93a +8d 6273 +49 9261 +72 3526 +f8 d54a +cf d2fd +94 69b2 +50 99a0 +c5 7a99 +74 bd3a +29 8661 +87 c2bf +a1 c401 +e3 54a5 +e5 5691 +4e b2fe +68 b440 +d8 fbe2 +f2 fd24 +a7 c41d +8d c2db +7 22b7 +9a 496e +38 87ea +a2 ee0c +c 805a +45 b213 +5f b355 +0 a800 +f3 fdad +3c 27f2 +28 8640 +d8 5b42 +4e 125e +2e acd6 +e3 74af +fd 75f1 +20 8600 +8 8240 +a7 ec97 +b4 ed38 +72 1d26 +9 2a61 +52 3b84 +cb 5a4d +85 6a39 +37 8595 +8c 6272 +48 9260 +cf 5a7d +28 8660 +71 9783 +5 a011 +e4 5690 +7f 1777 +28 acea +7e bf54 +64 be12 +5b 93cf +75 9511 +8 8260 +7e 3ddc +64 3c9a +69 1c69 +42 1a86 +84 ea98 +b7 6f95 +cb 5a6d +7 aabf +21 ac01 +4f 9255 +31 f03 +4b 1045 +18 8b48 +f 8255 +46 983e +a a866 +4e 9254 +49 b241 +94 e91a +2b acc7 +38 ad68 +1 a0a1 +cc 5870 +6 8214 +e2 5686 +ac 64d8 +48 10c0 +f6 fd16 +25 2619 +19 14b +af 6efd +d0 5182 +4a 9a4e +55 b3bb +6f b4fd +8 a862 +73 b7a7 +e1 5689 +58 31e0 +66 1696 +74 1592 +c8 d8e8 +c1 7221 +2b 46f +75 959b +4e 3276 +70 1582 +31 85a9 +74 9792 +2b 866f +bd 4d59 +e6 5496 +45 1833 +5f 1975 +22 8426 +98 69c8 +a8 c4ca +22 24a6 +2e 8ef4 +50 1182 +1 20a9 +11 81a9 +d3 d10d +b9 cfcb +33 2fa7 +b 826f +83 caa5 +6c 9650 +ea 74e6 +3 882d +4e 12fe +68 1440 +52 9926 +c7 7a1f +0 82a +1a 96c +22 ae26 +22 686 +75 9599 +7e bfdc +64 be9a +a4 4438 +97 4397 +aa 64e6 +2d cf3 +c3 7aa5 +ac 4650 +dc 5b52 +a2 64a6 +5 8031 +26 2694 +34 592 +b a26d +54 b390 +24 e10 +1f a15f +32 586 +9 a261 +52 b384 +8 22ea +22 242c +77 9f9d +29 ce3 +a8 4640 +c 8250 +8a 60e6 +cf 5a5d +48 1842 +29 469 +2 286 +a2 4486 +14 192 +34 af90 +9 8e3 +88 4240 +2c 2c5a +10 182 +30 af80 +70 1702 +fa 5fe6 +90 c102 +c2 d006 +23 ac27 +91 4b09 +38 256a +f1 d703 +72 9da6 +88 c2e2 +a2 c424 +51 9109 +95 611b +18 942 +fd f5db +33 85a5 +22 aea6 +30 858a +f4 5590 +29 8461 +96 6196 +52 9184 +9 8061 +32 8584 +f0 77a0 +55 1b91 +c 8af0 +12 8184 +99 c9c9 +13 29a5 +bd cfd9 +37 2fb5 +c2 702e +5 99 +dc 7170 +d9 f36b +d5 5991 +11 8921 +51 3921 +d5 f119 +14 1b0 +b2 ef86 +d9 f169 +17 935 +d0 51aa +c8 7848 +b aa67 +d0 f1a8 +3 288d +52 b3ac +f4 5512 +6 234 +e7 769d +34 25ba +59 13c3 +73 1505 +62 3e06 +20 c02 +3a d44 +14 21ba +39 fc3 +53 1105 +42 3a06 +20 8c08 +86 60b6 +42 90a4 +0 802 +1a 944 +40 1a00 +73 b5a5 +18 a948 +4e 9af6 +fc f5da +32 85a4 +a7 669d +e6 7eb4 +50 1102 +59 916b +ec f4da +22 84a4 +dc f1da +12 81a4 +6a 946e +87 629d +8c 4872 +2a 86ee +2c 265a +72 9f2c +b6 6f3e +cc f0da +2 80a4 +f0 5582 +c5 5833 +df 5975 +c 8278 +6a 14c4 +50 1382 +7a b74c +91 eba1 +60 b60a +2f ac77 +9d 4b59 +ce d056 +35 2519 +a5 6cbb +61 9ca9 +bf 6dfd +b1 e521 +a 8244 +cd 5a51 +80 4228 +cb d24d +90 6902 +4c 98f0 +11 8181 +66 be16 +38 a5ca +a 8264 +f8 d542 +f7 df9d +1d a1d9 +9a 4b66 +19 941 +31 85a1 +7f 1557 +e2 fe0c +4c 905a +cd fad3 +e7 fc15 +2b 8cc7 +38 8d68 +5a 3b66 +1 80a1 +f5 fd31 +db fbef +3e 2776 +d8 73e2 +f2 7524 +21 2429 +6b b647 +5a 1bcc +40 1a8a +ab 64c7 +67 94b5 +b8 6568 +13 23ad +c8 5042 +1e a1d6 +e8 fe40 +38 a5e2 +42 3824 +c8 d848 +1e bfe +38 d40 +18 940 +e6 f696 +e9 5e69 +2 2004 +20 84a0 +0 80a0 +a6 e696 +fa d56c +e0 d42a +a6 649c +3e 8576 +c3 58ad +6a 94c6 +39 fc9 +fd dffb +16 a196 +ed dcd9 +67 3cb5 +36 71e +53 1b05 +2 4 +ac c6d8 +26 26b4 +3e 85d4 +24 8492 +fc 77f0 +e2 76ae +0 aa20 +2d a671 +76 b794 +a8 4442 +49 b8e3 +c8 f240 +83 c805 +46 9094 +53 b905 +8 28e0 +87 623d +16 8194 +43 b805 +6 8094 +2e 4fe +c4 72b0 +c 2a78 +25 a631 +0 a0 +8a 4ae6 +4d 18d1 +b2 4706 +e 8254 +13 92d +a a266 +a 44 +fc f7da +32 87a4 +c2 d826 +38 8f42 +64 9490 +81 c801 +44 9090 +41 b801 +db f167 +4 8090 +fe d55c +5e 33f6 +e4 d41a +7 2a9d +80 ca20 +e7 7e95 +ef 76dd +70 3d80 +fc 7f70 +e2 7e2e +25 e99 +7d 9579 +56 9396 +3a 2dce +75 9719 +f3 75af +c7 589d +98 e16a +e7 5495 +47 1295 +d2 530e +ec 5450 +20 2e08 +b4 4792 +ce f2fc +38 854a +a aa66 +dd f15b +13 8125 +2 aa26 +87 c01f +e8 5e60 +1e 1f6 +9e ebfe +b8 ed40 +a5 c499 +2f 4d7 +3c 578 +21 ca1 +d4 7338 +c7 7297 +b 8065 +cd f05b +3 8025 +19 8341 +89 cae3 +a3 cc25 +68 1442 +9 88e3 +88 c240 +17 29bd +4a 1a6e +19 96b +83 4a05 +55 31b9 +46 1294 +dc f15a +12 8124 +1f a3fd +5 a2bb +d6 5194 +73 3f2f +8d 4071 +52 3906 +7 a1f +66 bc16 +a 8064 +53 9187 +cc f05a +2 8024 +c6 5094 +1b 89e5 +1 88a3 +80 c200 +31 872b +ea 5ce6 +68 9e60 +ac 6e72 +3a 8f4c +20 8e0a +95 e319 +16 a9bc +53 1307 +30 27a2 +a a86c +96 e31c +17 a9bf +62 3684 +85 6033 +9f 6175 +41 9021 +b0 65a2 +61 3c23 +7b 3d65 +48 b868 +6 281c +8e c8fc +87 6235 +4e 307c +34 2f3a +96 691c +34 a798 +a9 c4c9 +23 24a5 +45 1291 +d0 530a +ea 544c +e5 5491 +4c 9252 +61 1c03 +7b 1d45 +3c 2dfa +1 aa81 +d5 7913 +1c a358 +19 a96b +ac 6472 +68 9460 +59 91c1 +9d 61d3 +9c 6172 +58 9160 +4e 38fc +cd 7259 +97 eb1d +48 1a6a +ff 575f +5a 1b6c +8f eadd +40 1a2a +94 c112 +38 8560 +2e 2cfc +14 2bba +ad 6659 +9e c154 +84 c012 +8d c8fb +86 6234 +28 8460 +f 2a55 +71 9583 +c3 d805 +73 9da5 +b7 6db7 +74 bd12 +64 9638 +a8 664a +18 8160 +e 28fc +8d 6259 +8 a6a +d6 7bb4 +bf 475f +7e bd54 +64 bc12 +8 8060 +51 9183 +eb 7e65 +11 2381 +85 4a39 +f ad5 +3a a54c +20 a40a +48 9a6a +bd ef79 +7b 1f67 +aa 664e +66 963c +35 719 +15 a191 +74 3598 +48 3842 +6a 364c +81 6aa1 +1 2aa1 +43 1287 +50 1328 +85 e299 +f 22d7 +16 899e +1c 2378 +0 8280 +0 2a80 +83 e887 +90 e928 +cd 5273 +0 8a00 +a8 c6ca +22 26a6 +da 53ee +f4 5530 +68 9c62 +85 6a91 +5a 13ee +74 1530 +65 9c91 +61 1409 +c 2850 +14 1b2 +fb 7dc7 +28 a6ca +e7 dcbd +30 702 +ba 4fe6 +6c 3ed2 +86 4014 +f a855 +e1 de21 +46 3294 +83 6a05 +8c 60f0 +9a 4bcc +ab e647 +80 4a8a +79 9563 +8 20c0 +e2 5e84 +7e 1dfe +fd 575b +8f c2dd +10 8980 +44 32b8 +3 2285 +d a8d9 +e8 dc42 +b 2a45 +b8 4562 +f7 751f +13 3a7 +f 8fd +6 a236 +6 14 +46 92b6 +ea decc +10 a108 +1e b56 +66 3494 +9d 415b +82 4884 +5 231 +50 390a +ce 5076 +e a854 +16 81b6 +67 3e9d +e0 de20 +8c 4a5a +e7 56b7 +b1 6509 +8a 6a44 +92 43a6 +b4 65ba +70 95a8 +a0 4ea8 +d a851 +88 c842 +8a 6a64 +b1 cd89 +2b 86ef +9a c944 +80 c802 +82 6a24 +2 2a6 +e6 741e +0 8220 +a2 64a4 +c4 5290 +da 51c6 +cc 5a50 +9 8243 +32 2786 +d2 d106 +33 ad27 +a1 4c09 +82 4886 +44 3290 +81 6a01 +fa d546 +74 3790 +9 2a41 +4 10 +8e 48f4 +1c b52 +64 3490 +cc 5072 +1 2a21 +2 2284 +c a8d8 +ad 6c7b +69 9c69 +42 9a86 +25 2c93 +3f 2dd5 +a2 6484 +0 8200 +90 41aa +88 6848 +e3 74a5 +2e 45c +45 38b1 +14 31a +92 4986 +a a844 +12 81a6 +43 928d +87 629f +88 6a60 +d8 5940 +17 395 +35 8519 +d4 5992 +e2 5ca6 +29 86eb +8b 486f +58 b148 +a2 e6a6 +21 a481 +cb 52ed +80 6a00 +6d 1cfb +7e b776 +ec 5658 +cd f0db +3 80a5 +b9 cfe3 +d3 d125 +f5 5791 +7c bdfa +31 8721 +54 1992 +b 86f +6 221e +36 ad96 +5 2899 +dd f1db +13 81a5 +2 aaa6 +88 e06a +bb 6567 +51 3181 +d7 5395 +9a e34c +80 e20a +1b a9ef +a4 ce98 +7 a815 +28 2ec2 +42 3004 +1b 894f +6 a814 +5 a811 +4 a810 +61 3c21 +24 4b0 +cb 78ed +5 a291 +67 b41f +c8 5260 +44 1a30 +3f ad7f +50 3180 +c2 722e +dc 7370 +d6 d996 +5 299 +50 b12a +89 e2e3 +a3 e425 +14 a910 +2 a804 +22 4a6 +42 b2a4 +0 2a02 +1a 2b44 +b5 6d39 +a8 e448 +66 1436 +15 29b1 +e1 5429 +37 a5bd +89 6ac3 +a3 6c05 +45 9ab1 +4e 1076 +4c 9258 +88 e2c8 +46 12b6 +ea 5ecc +10 2108 +80 68aa +9a 69ec +c1 52a9 +ea fe44 +b0 e580 +65 b699 +c7 781d +dd f9d9 +26 241e +da d966 +9 269 +3a 8766 +c3 d8af +dd d9f1 +26 436 +46 b234 +a1 4429 +5 8ab1 +d2 d926 +1 229 +32 8726 +1 a801 +6d 9459 +8f 4257 +8f 685d +97 41bf +e 76 +2e ae74 +d3 d30f +ed d451 +54 99b2 +9f e1dd +85 e09b +50 112a +e3 f407 +d2 598c +89 4869 +ba cd66 +f3 df05 +19 a141 +60 1600 +10 2120 +ea 5ee4 +57 119d +d9 db63 +46 3a9e +3f 8577 +68 1e68 +c7 f23d +48 b8e0 +de 5b5c +c4 5a1a +e9 74c9 +56 9914 +b 8ef +8a 424c +13 12d +38 a5c0 +97 6395 +7a 9566 +70 3d02 +ef 765f +89 68c3 +45 98b1 +30 a580 +41 1029 +72 9526 +68 3cc2 +e7 761f +f2 df04 +18 a140 +8 2a4a +19 149 +e9 d6cb +63 36a7 +5 8a39 +cf f27d +50 b920 +64 9c38 +57 9b97 +a8 6c4a +7b 9567 +52 330e +6c 3450 +83 68a5 +97 e3bf +b1 e501 +8c ca52 +90 41a2 +88 6840 +26 a6bc +73 3f8f +8d 40d1 +19 2943 +47 1a9f +e7 7e15 +b9 65c9 +24 2490 +77 bd37 +e5 5c19 +91 6981 +40 b2a0 +e2 5406 +80 c020 +7 209d +28 ae6a +81 6881 +95 e9bb +d2 5306 +9c 6158 +67 349f +e0 d422 +fa d564 +51 b121 +87 4ab5 +0 8080 +fa d54c +5a 33e6 +e0 d40a +42 1806 +84 e818 +1f a3ff +39 a541 +eb dec5 +11 a101 +e a254 +4d 9871 +ca 52cc +1e a3fe +38 a540 +46 3a94 +ea dec4 +10 a100 +1a 2b4c +0 2a0a +11 109 +ba 6d4c +5c 9bf8 +a0 6c0a +ff 5fdf +82 4086 +f7 75bf +83 6885 +48 986a +bd ed79 +96 eb96 +7b 1d67 +31 a701 +9b 41e7 +95 6991 +47 1a3d +92 4924 +8 40 +30 87a0 +b a045 +a 20ce +e4 5e92 +fe 5fd4 +a a044 +c a5a +31 2509 +a9 e6c9 +67 16b7 +a1 6cab +bb 6ded +2 800e +1c 8150 +a4 6630 +7d 97d9 +88 4268 +de 7b76 +7 a9f +cc fa58 +40 b820 +dd f3d3 +f7 f515 +da f3ec +c0 f2aa +6c 3652 +83 6aa7 +61 bca9 +5a 936c +40 922a +9e 637e +ce fa54 +a5 4eb9 +f4 d590 +ba 67ec +a0 66aa +7 8a95 +92 cb0e +ac cc50 +83 6825 +21 a6a1 +8 a4a +a5 e6b9 +63 16a7 +2d 24f9 +4e 1ad4 +18 8140 +7 a015 +40 32a8 +dd 715b +13 125 +47 1ab5 +70 1d02 +ef 565f +11 8121 +bd c7d9 +37 27b5 +51 3121 +2c a458 +62 9606 +11 ab81 +c3 7887 +a a2cc +d0 7928 +a a4e +b9 6de1 +12 8104 +3a 56e +d0 7320 +4 aa38 +bd cf59 +37 2f35 +d5 5911 +22 2684 +1 8021 +67 3695 +f2 5f0c +18 2148 +9 8861 +86 42bc +a5 6c99 +d6 f196 +f6 571e +b 2865 +a 8044 +c2 d886 +c8 7260 +5a 3bc4 +40 3a82 +51 1181 +2f 2cf7 +b5 cd1b +ae 6654 +cd 5851 +c5 5811 +a7 669f +63 968d +46 b294 +18 340 +38 a7e0 +92 6924 +e2 5e04 +8 2040 +30 a7a0 +6 8a94 +82 6824 +20 a6a0 +d4 5190 +71 3f2b +8b 406d +98 c1c8 +12 21a4 +62 bc0e +7c bd50 +1a b6c +0 a2a +49 1049 +7a 9546 +b4 6d98 +98 c948 +12 2924 +d4 5910 +11 832b +ca 58e6 +2b 846d +74 9590 +0 8020 +88 c0c8 +2 20a4 +c4 5090 +a4 6c98 +a 2864 +cc 5850 +88 c848 +2 2824 +c4 5810 +26 4b6 +46 b2b4 +a1 44a9 +b0 ef02 +ca f044 +ac e452 +a5 4e11 +c5 d8b9 +e 2fe +28 440 +81 c823 +9b c965 +d8 db48 +52 3b24 +92 6184 +d0 7122 +13 18d +ca 52c4 +81 6821 +82 6084 +54 1b92 +6e 1cd4 +39 8fe3 +97 6137 +53 9125 +42 ba26 +cd 78db +3 8a5 +4 3a +1e 17c +75 1791 +ef ded5 +15 a111 +da 7bce +f4 7d10 +98 e9c0 +c8 526a +3 a005 +ad 4e71 +39 2f6b +6e bede +e9 5e61 +25 aeb3 +3f aff5 +10 a1aa +33 a5af +c0 fa08 +8f 4a75 +84 6832 +9e 6974 +40 9820 +dd d3d3 +f7 d515 +e6 fe16 +3 aa85 +98 4b4a +eb 7e45 +da d3ec +c0 d2aa +bd 65f9 +f3 57a7 +22 a40e +3c a550 +cb 5ae5 +a4 4e10 +1b 2967 +b2 4586 +89 e261 +f a2ff +29 a441 +99 ebe3 +b3 ed25 +58 9960 +9c 6972 +3a a7ee +98 6960 +e8 5e40 +e 207c +90 6920 +e0 5e00 +6 203c +88 ea4a +1e 8156 +15 83b9 +61 1489 +b0 c580 +c2 7286 +e9 7469 +80 6820 +1 a001 +ab 4e6d +18 2960 +33 af27 +a1 4e09 +ed 7edb +23 ea5 +c8 5060 +8f 627d +10 2920 +d2 590c +8e 4a74 +8 2860 +4a 1046 +8c e058 +c2 d206 +ae ccfe +a7 6637 +63 9625 +86 4a34 +58 31e8 +e6 5416 +44 b2b0 +e a2fe +28 a440 +98 ebe2 +b2 ed24 +a8 e462 +c0 7088 +3 a2a7 +5d 1359 +54 3190 +91 6901 +7 201d +42 90a6 +1a 946 +f a055 +e1 d621 +f1 7da3 +38 a7e8 +c1 5229 +70 b70a +a1 eca1 +d3 d9af +36 536 +6 236 +81 4229 +85 e011 +59 1349 +da 7946 +d1 7ba9 +8f 4a5f +40 1228 +20 aca0 +17 91d +e a256 +9b 43c7 +a8 4468 +f1 558b +3 2ad +7e 3f5c +64 3e1a +83 e2a7 +75 1519 +e5 5cbb +f6 f736 +ff 5dfd +8 268 +13 898f +19 2369 +38 2dea +66 9694 +19 29e1 +9f e3d7 +ac e478 +6a 1466 +8a c264 +62 b6a6 +18 8148 +3a 2f46 +62 1484 +19 361 +4 8ab0 +0 228 +6 2a3c +61 34a1 +e7 56b5 +8b 40c7 +98 4168 +9c 41d2 +7 a2bf +21 a401 +81 e881 +cb 526d +58 b9ea +b8 6dc8 +4f 30fd +35 2fbb +76 9594 +13 832f +2d 8471 +99 69c3 +55 99b1 +89 42e9 +69 364b +80 6aa0 +ca 5a46 +9c 41fa +d5 73b3 +ef 74f5 +f3 552f +84 6a90 +36 8594 +15 89b1 +49 32e9 +b1 edab +ee 56f6 +c5 5291 +d aad9 +a3 6685 +7a bfce +94 c110 +44 3890 +68 966a +2a 866e +6 2894 +4 8810 +ba c5c4 +a0 c482 +a2 66a4 +68 94c2 +21 6a9 +f2 dda6 +65 1c13 +7f 1d55 +4c 9858 +81 c021 +a2 6684 +d9 7b61 +d4 5912 +72 978e +3b afcf +55 b111 +5 2891 +a1 66a1 +21 26a1 +e3 568d +40 30a2 +5a 31e4 +4e 1876 +29 669 +fa dd66 +a1 6681 +19 8941 +21 2681 +8c 60d8 +a0 66a0 +d8 7148 +a1 6481 +80 62a0 +6c 1ef0 +61 1421 +a 286c +20 26a0 +0 22a0 +a0 64a0 +88 ea48 +46 1a36 +18 1ea +da 514e +38 afe8 +c1 5a29 +68 9642 +17 abbd +68 be4a +9b 4347 +48 3a40 +20 2680 +a0 6480 +7 2295 +92 630e +4e 92fc +ac 6450 +cb 586d +5 8211 +83 60a7 +af 6e57 +6b 9e45 +3d 85f9 +46 b03c +2c aefa +6 2294 +a6 6494 +82 60a6 +13 8905 +a5 6691 +25 2691 +3d 2d59 +43 92a5 +87 62b7 +b5 ef19 +73 1f07 +65 b4b9 +5 2291 +a5 6491 +c4 5032 +de 5174 +6 8abc +11 8901 +45 3239 +13 2985 +ac 4c5a +17 ab95 +c1 5a01 +a2 ec0e +bc ed50 +a4 6690 +ad 6e53 +69 9e41 +4 2290 +65 9439 +a9 644b +a4 6490 +57 bb37 +c5 5a19 +3 2a2d +96 4996 +b0 4d02 +fb 75e7 +e 8854 +cc 7a70 +d3 5127 +ba cd6c +a0 cc2a +25 c19 +f9 ff69 +56 9116 +96 6936 +52 9924 +c7 7a1d +26 694 +b2 458c +30 8580 +83 4a07 +55 31bb +53 b3a7 +c1 5289 +10 8180 +ce 587c +ee fcde +a3 c605 +ca f2c6 +d3 598d +5c 3970 +42 382e +56 1914 +58 314a +42 b2ae +5c b3f0 +fe 5556 +f5 57b9 +26 8e9e +27 a49d +54 b3b0 +f6 5516 +62 bc84 +19 ab61 +94 e9b0 +7d b55b +18 8962 +50 b90a +5 8231 +8c 425a +c 8850 +30 2502 +f5 d731 +ba 6de6 +22 8e0e +3c 8f50 +b6 4716 +85 e891 +cf 527d +50 1920 +2b 2ce7 +b1 cd0b +aa 6644 +32 8506 +1 9 +d6 d336 +81 68a9 +28 a4c2 +b2 eda6 +29 8ccb +22 2604 +5c 1972 +9 88cb +2 2204 +a8 ccc8 +22 2ca4 +a1 6601 +80 6220 +6c 1e70 +13 ab85 +a8 4c4a +c8 fa48 +aa 446c +90 432a +ea dcec +d0 dbaa +e3 7625 +db d34f +5c 99f2 +7a 3d6c +60 3c2a +80 ea28 +49 12cb +63 140d +98 c148 +12 2124 +4 aa30 +80 4088 +12 a1a6 +e9 dce9 +32 72e +54 1910 +dc 73d2 +f6 7514 +25 2419 +5b 196f +27 417 +c4 d890 +0 2220 +ee 74fe +47 1095 +eb fc6f +14 8b98 +2e 4d4 +14 392 +cd 5af1 +43 120d +1e 837c +d7 5937 +4 823a +2c 2458 +25 acbb +a4 e618 +3f adfd +62 1606 +11 2b81 +11 121 +be cdde +73 9705 +b7 6717 +b1 e589 +cc 5078 +bf 4fd7 +36 a596 +5 2099 +24 2418 +f 8d5 +85 4839 +1 8aa9 +5e b1de +d9 5161 +a 8846 +ac ce50 +44 9810 +73 970d +b7 671f +38 2dc2 +e a5e +da dbe4 +c0 daa2 +19 23cb +33 250d +a3 6caf +bd 6df1 +8a 4266 +97 6b35 +11 1a9 +73 978d +b7 679f +93 630f +4f 92fd +14 29b2 +ad 6451 +5f 33ff +79 3541 +c3 f887 +d0 f928 +48 1a60 +28 662 +aa 4466 +ca f264 +3f 2fff +59 3141 +47 181d +8 262 +a8 4462 +e7 741f +3 2a7 +c8 f260 +86 4a9e +c 2250 +96 6b34 +d3 51a5 +a0 cca8 +ba 45e4 +a0 44a2 +98 6b40 +e 225c +60 3e00 +10 a32a +2a a46c +2 aa84 +d6 7916 +1d a35b +8d e8db +58 196a +35 539 +3e 2f7c +24 2e3a +5e 39dc +44 389a +a 8c4 +3a fec +20 eaa +2d 651 +b7 4f35 +4a 1a4e +34 a59a +23 2ead +44 9298 +77 1795 +91 63a1 +5c 195a +36 a59e +b1 4521 +b9 c5c9 +33 25a5 +0 a0a8 +e af6 +55 1391 +e0 540a +fa 554c +4f 1ad5 +2a 2ccc +10 2b8a +19 8141 +8 aa42 +30 582 +50 b380 +b8 6548 +67 9495 +b8 65e8 +87 e897 +94 e938 +52 1926 +82 60a4 +77 1597 +f a255 +4c b252 +55 1919 +d1 7ba3 +eb 7ce5 +ba 474e +90 6322 +aa 6464 +da f1cc +c0 f08a +4d 18f3 +cc 5250 +a8 c4c8 +22 24a4 +e4 5490 +81 4a01 +44 1290 +e9 544b +43 1a2d +3e 8554 +24 8412 +d1 f323 +eb f465 +c0 58a8 +93 6185 +45 3211 +4c 98d8 +65 1c93 +7f 1dd5 +5e 13dc +44 129a +12 a924 +ac e6f8 +6a 16e6 +49 1869 +97 6b95 +7a 9d66 +47 1a95 +11 8101 +1a ab44 +0 aa02 +36 5b4 +a2 e40e +bc e550 +46 1a1e +16 8114 +6 a1e +b5 6db1 +11 238b +2b 24cd +17 a9bd +c1 5829 +68 9442 +21 629 +f2 dd26 +b0 478a +e3 548d +50 1308 +40 1828 +80 c808 +8d ca5b +7 2a37 +90 61a0 +15 2bb1 +a0 6c2a +ba 6d6c +e1 5629 +99 6941 +c1 f0a1 +f 205d +37 a7bd +90 e922 +fa df44 +e0 de02 +6 a03e +45 1819 +c1 7aa3 +db 7be5 +aa 464e +76 9d16 +44 1818 +22 2486 +2e 8ed4 +13 8105 +2 aa06 +d8 d9e8 +3b 56f +d1 7321 +54 1192 +2b ae6d +a1 6e21 +b 6f +f6 753e +a6 46b4 +20 24a0 +41 1a29 +2f acd7 +3c ad78 +72 9f26 +e2 548c +99 4369 +98 e948 +56 1936 +78 9542 +da d16c +c0 d02a +f3 5527 +b1 6581 +48 3048 +c a2d0 +d2 792c +2d c73 +c3 7a25 +86 42b4 +0 20a0 +65 be99 +88 e848 +46 1836 +91 6181 +63 1c8f +7d 1dd1 +1e 83fe +38 8540 +98 e160 +e8 fe48 +71 3d21 +34 5b0 +d2 f386 +f9 f569 +c1 78ab +db 79ed +15 a391 +25 611 +af 4ef5 +a2 ee2c +c 807a +dc d9f0 +c2 d8ae +3f 577 +6e 9c54 +54 9b12 +79 b5c1 +a8 6e60 +97 6195 +d0 7902 +29 e69 +9 a8e1 +5a 9366 +1 881 +1c 158 +ec d6da +66 36b6 +27 2495 +65 3699 +c5 58b1 +b0 6580 +d 8a51 +9 841 +8f 60f5 +31 8fa1 +6e 3676 +7 2095 +45 3299 +76 b796 +7 2a17 +90 6180 +80 6080 +45 18b1 +aa 46e6 +ed 545b +d2 5b84 +30 2580 +51 1b09 +cd 505b +ed fe59 +10 2180 +56 1916 +cb 58c5 +b6 6594 +96 6194 +a5 6433 +bf 6575 +61 9421 +99 cb63 +6 2a9e +9e 4b5c +84 4a1a +a9 64c9 +35 8f13 +4f 9055 +ea dece +10 a10a +c2 f824 +38 af40 +f 8055 +c7 d897 +d4 d938 +cd 7271 +4b 18c5 +36 2594 +1a 83ce +34 8510 +5a 1b4c +40 1a0a +8e e054 +65 34b9 +23 60d +98 414a +3 a085 +ad 4ef1 +fa 5d4c +e0 5c0a +26 2494 +dc 5952 +7a 97ce +5b bb67 +c9 5a49 +e 8054 +c6 d896 +cc 7270 +e2 7e2c +4c 107a +b5 6591 +4c 3058 +d6 793c +15 2391 +39 25c1 +48 906a +96 e396 +bd e579 +7b 1567 +95 6191 +33 8f0f +4d 9051 +35 2591 +e6 54b6 +c4 7a12 +de 7b54 +49 3243 +50 990a +89 cac3 +a3 cc05 +25 2491 +50 31a2 +3 2085 +f3 7f2d +5d 117b +c1 5201 +42 18a4 +10 92a +26 4b4 +1 8001 +15 2191 +6f b477 +5e 19fc +44 18ba +dd 5359 +55 1b11 +32 a5ae +32 8f0e +4c 9050 +c 8050 +ef 7e55 +f7 57b7 +eb f647 +da 5bcc +c0 5a8a +a3 468d +0 20a2 +1a 21e4 +97 43bf +b1 4501 +33 2585 +0 a088 +e ad6 +f 8a55 +ea 5e6c +3 2007 +c9 58c1 +b4 6590 +67 bc37 +d5 5b19 +6c 1c7a +a5 4e33 +bf 4f75 +94 6190 +93 692d +31 a7a9 +e3 5e0d +9 2049 +3a a546 +72 3706 +2 2884 +e6 debe +c a0fa +18 8342 +a2 cc26 +b 8a45 +a5 6eb9 +57 b937 +c5 5819 +1b 29c5 +1 2883 +3e 25f6 +1d 23fb +37 253d +18 2b62 +1e b5c +4 a1a +29 24c9 +b3 6dad +14 8110 +ce f256 +d7 591d +75 9799 +28 a660 +74 9592 +c1 f221 +2b 846f +1d a979 +53 b985 +87 62bd +73 1f0d +5a 134c +40 120a +d1 d1a9 +56 91b6 +4e b854 +3c a55a +5b 134d +41 120b +93 c987 +99 6361 +e2 7484 +d a879 +43 9a27 +56 b99c +63 1e0d +46 18b4 +c5 5211 +9 a86b +a7 6e15 +6e 3c5c +54 3b1a +8d 6859 +c3 5a07 +48 1ac0 +be ed56 +95 41bb +99 e141 +46 1296 +6d 1479 +a5 4639 +19 949 +9f 61fd +85 60bb +41 90a9 +fe 7dde +b3 4705 +1d 8b59 +9b 69ef +eb 5ecf +11 210b +4d 1251 +d7 5b35 +7a 37c4 +60 3682 +d3 530f +54 19b2 +ed 5451 +88 e0c8 +46 10b6 +cb 52cf +e5 5411 +54 133a +6e 147c +53 1ba5 +a6 463c +54 b11a +8e 685c +e5 7c93 +ff 7dd5 +2c a6d8 +96 41be +e3 dc0f +fd dd51 +86 423c +a8 cc42 +a6 6e14 +d2 d924 +3c 25f2 +c3 5aa7 +34 a512 +8d 68f9 +c a252 +15 919 +d5 7199 +c2 5224 +21 2c23 +3b 2d65 +8 a868 +53 9905 +97 6917 +fb 7def +35 a793 +53 930f +6d 9451 +82 6024 +aa 46c6 +f6 7d9e +ab 46c5 +25 24b1 +1a b44 +0 a02 +2d 8ed3 +47 9015 +fc dfd0 +e2 de8e +8 a0ca +7 8015 +cc d8f8 +2f 47f +c5 7231 +88 c048 +2 2024 +e2 dc0e +fc dd50 +50 1188 +38 8562 +12 906 +cb 70c7 +d8 7168 +33 2fad +a1 64a1 +10 1a8 +72 978c +b6 679e +d4 7198 +85 4299 +17 a3b7 +52 9904 +96 6916 +fa 7dee +34 a792 +a9 4c69 +82 4a86 +c aa50 +52 930e +83 c8a5 +6c 9450 +3a 25c6 +ff 75ff +88 e040 +70 3d8a +25 6b1 +54 31b2 +f7 753f +52 3384 +73 152f +4 2a90 +1b 9ef +9a 434c +80 420a +4c 98d2 +81 6021 +86 489e +65 1691 +9 c1 +5e 9bfc +a2 6c0e +44 9aba +bc 6d50 +17 2b95 +de 7bf4 +c4 7ab2 +25 a639 +62 968c +a6 669e +0 a8 +38 25c2 +dd dbf1 +c3 daaf +26 636 +ce d8dc +c7 7215 +d9 dbe3 +f3 dd25 +1c a9da +e 5e +a4 6e10 +b2 6586 +80 6020 +79 956b +5 8a19 +6c 345a +9d 69f1 +83 68af +86 4234 +0 2020 +55 9b91 +96 4934 +c 50 +34 87b0 +4b 1a4d +5e 1b5c +44 1a1a +69 34c9 +58 33c2 +72 3504 +27 61d +9c 415a +7 a095 +b1 4f01 +bc c5da +36 25b6 +2a 2c64 +10 2b22 +17 a99f +35 25b3 +b0 6582 +a a064 +73 170f +4c 92d2 +66 9414 +ea 5c4c +d0 5b0a +f5 75b9 +89 e841 +3a 876c +f3 5d27 +20 862a +a8 4c60 +67 149f +fb 576d +b1 ed81 +e1 562b +55 9931 +99 6943 +37 a7bf +f 205f +84 6812 +40 9800 +9e 6954 +e6 563c +e8 7cea +22 a68e +3c a7d0 +d9 dbc1 +22 606 +c3 5a85 +83 4a85 +ac ec7a +43 1a85 +e3 5c85 +d9 d3c3 +f3 d505 +e2 fe06 +bc 67da +78 97c8 +a3 4c85 +7b b547 +50 198a +c1 5aa1 +72 958c +b6 659e +49 b861 +92 c984 +0 2a20 +5 a8bb +42 1206 +1f a9fd +84 e218 +42 1a84 +5d 135b +83 eaad +6c b658 +d6 513e +4b 1a6d +ca f2cc +34 851a +7b 1def +fa 574c +e0 560a +da f1c4 +c0 f082 +c0 5aa0 +31 a50b +b7 ed17 +41 1a81 +5c 1358 +cc 5afa +e6 5c3c +22 ac8e +6c 167a +3c add0 +c8 f060 +3 a7 +b8 c5c8 +32 25a4 +30 8520 +7e b7dc +64 b69a +c6 781e +f0 df82 +16 a1be +54 3918 +c2 52a6 +78 1d48 +8c 60f8 +6a 16e4 +ed 5479 +c6 5296 +1 2881 +94 e318 +15 a9bb +52 1306 +f6 5f1c +1c 2158 +87 4a95 +68 16e0 +75 3f33 +8f 4075 +d2 7b2c +3c d7a +a2 c40c +88 c2ca +2 22a6 +19 a9e1 +39 f69 +19 8361 +34 259a +0 a80 +8b c2cf +a5 c411 +1e a976 +8c 4858 +e7 54b5 +42 982e +5c 9970 +f3 550d +d9 53cb +21 421 +13 2905 +17 ab15 +85 4a91 +15 ab11 +8f 625d +10 2900 +c1 d821 +a 266 +be 47dc +a4 469a +74 b512 +7 a29d +cd 78f9 +8b 4a65 +83 4a25 +26 696 +f0 7d80 +17 899f +1d 2379 +f9 d561 +66 349c +37 afb7 +a5 4e99 +b a65 +86 48b4 +6f 145f +89 6a41 +ab 44e5 +91 43a3 +6a 3ece +84 4010 +67 141f +d2 f30e +ec f450 +66 14b4 +19 341 +85 e89b +9f e9dd +50 192a +58 3142 +d 25b +b1 cd2b +aa 6664 +da f3cc +c0 f28a +f6 5596 +ad 4473 +e9 dcc1 +32 706 +ca 5a64 +dd 79d9 +8 60 +51 1183 +19 bc3 +33 d05 +0 8808 +38 2d60 +cf d25d +50 9900 +94 6912 +8 c0 +7b 154f +92 49a4 +cb 5a45 +9d 41f9 +5 2831 +8b 4a45 +5d 31f9 +52 91a4 +96 61b6 +88 cac2 +a2 cc04 +88 6a40 +aa 44e4 +90 43a2 +2c ae78 +8f c25f +10 8902 +5e 337c +44 323a +4d 92d1 +12 2986 +b0 4da8 +e5 74b1 +4b 1a45 +1d 1f9 +61 3621 +68 9ce8 +ac 6cfa +35 8d9b +2e 26d4 +d 8071 +56 9194 +de 53fc +c4 52ba +e4 d412 +fe d554 +b a45 +58 194a +ca 5a44 +9c 41f8 +75 9591 +7e bfd4 +64 be92 +81 e823 +9b e965 +4b 32c7 +58 3368 +52 998e +9 886b +82 4a04 +54 31b8 +4a 1a44 +1c 1f8 +11 89a1 +3e 255e +f0 f5a2 +c 8070 +55 9193 +24 a630 +c2 fa0c +2c 8c5a +e8 7e68 +5f 1157 +89 e061 +d2 f184 +96 6994 +82 4284 +34 25b2 +e0 7e28 +57 1117 +88 4262 +2 a04 +9 a61 +e 88fc +7 2235 +8d c259 +52 1b84 +f7 5d3f +1 a21 +25 c93 +3f dd5 +d9 514b +34 f90 +a2 4484 +45 b291 +3f 5d5 +25 493 +64 14b0 +26 ac9e +a1 4c21 +aa 4ee6 +c9 5a41 +ae c456 +cc 705a +2 24 +79 9549 +bd 655b +a2 6c84 +76 bd1e +2b 8645 +48 9a42 +53 b3af +6d b4f1 +ac 66d0 +15 ab33 +2f ac75 +31 afa3 +4b b0e5 +d0 f18a +4c 12d0 +d6 5bb4 +3 282d +89 4a41 +d aa51 +8a e864 +1f bff +39 d41 +5f bbdf +c0 5a20 +16 abb4 +49 1a41 +e4 54b8 +2b ae65 +8c ea7a +f0 55a8 +b 67 +3 88d +c0 7822 +da 7964 +2c 26d0 +b6 6fb4 +41 1a01 +22 ac0e +3c ad50 +c7 5abf +e1 5c01 +fe 555c +e4 541a +88 e060 +51 9981 +95 6993 +9 a41 +55 bbb9 +f7 5d1f +52 138e +6c 14d0 +da 53ce +f4 5510 +cb d845 +d2 f98c +35 2513 +9c e3d2 +b6 e514 +68 1eca +1 a01 +8d 60d3 +49 90c1 +d3 d9a5 +a7 6e95 +98 436a +3 a2a5 +a8 e460 +2e aed6 +0 a20 +b7 4715 +1f 89fd +5 88bb +84 c218 +ee 5cfe +44 b290 +3e 5d4 +24 492 +c8 5a40 +94 e998 +52 1986 +9 863 +f5 fdb1 +3e 27f6 +1c 235a +62 9c2c +48 9aea +a6 6c3e +7d 9d79 +56 9b96 +d 8a73 +2a 8644 +ed 5e51 +98 6362 +c0 5a00 +16 ab94 +15 293b +87 6837 +43 9825 +c0 5280 +a0 6420 +8b 4a6f +31 2f29 +85 4833 +9f 4975 +23 86af +3d 87f1 +b2 cd0c +98 cbca +ab 6645 +12 2ba6 +a 844 +fc ffda +32 8fa4 +ee 7efc +58 114a +1 2801 +87 4a15 +59 31c9 +8c e2d8 +4a 12c6 +84 68ba +40 98a8 +9e 69fc +0 a00 +ee 5cde +47 1a15 +19 1c9 +22 2c0c +8 2aca +92 6126 +7e 1d76 +54 191a +50 3102 +1f 35d +5 21b +a9 4c41 +8f 4aff +99 e9cb +d6 5316 +85 6891 +ba c544 +a0 c402 +c aa78 +25 2e33 +3f 2f75 +a9 cceb +a2 6624 +18 162 +38 af60 +c0 d0a0 +98 4940 +e 5c +6 2814 +46 1a14 +18 1c8 +6 a14 +8c 4ad2 +a6 4c14 +be e75c +a4 e61a +3f adff +ec 565a +e0 5ea8 +13 2185 +9a 6b44 +80 6a02 +77 3fbf +91 4101 +cc 525a +c8 524a +15 2911 +d8 5160 +0 8aa8 +fd 7fdb +33 fa5 +da 736c +c0 722a +d4 d992 +5 2ab9 +e 2856 +d0 510a +4 ab0 +26 8c16 +c9 fa69 +f2 5d8e +7c 3dda +31 701 +b7 ed9d +68 1cea +a1 4ea3 +bb 4fe5 +7 283d +8d 4a51 +85 4a11 +5 a11 +68 3e6a +94 e398 +52 1386 +79 1569 +94 6910 +b5 e539 +73 1527 +31 2581 +b7 4795 +e4 7c18 +0 aa0 +84 c298 +d3 5b85 +65 141b +7f 155d +e2 740c +c8 72ca +45 9a91 +41 1209 +3c a558 +6b 166d +21 ac81 +72 9706 +cb 5aed +57 b137 +c5 5019 +f6 d516 +48 124a +a7 e61f +28 acc2 +c2 fa26 +5 8a91 +89 6861 +d2 d906 +1 209 +8b 4aed +0 808 +1a ab6c +0 aa2a +11 8129 +b9 cf4b +33 2f27 +3a a56e +44 3090 +81 6801 +90 e122 +7c 9d72 +45 1019 +76 9516 +c6 f236 +cf 58fd +45 9891 +41 1009 +72 9506 +76 bfbe +90 c100 +40 3880 +c6 5a94 +a 28ce +45 9219 +c 25a +83 420d +4 8b0 +c9 f869 +83 4027 +d 8073 +a3 ee25 +8 24a +ac 4c70 +92 4b2e +12 186 +32 af84 +4 a10 +37 a5b5 +66 b49e +e1 5421 +12 8b06 +22 ae04 +2 6 +80 6800 +4c 1258 +12 ab8e +2c acd0 +b5 4791 +d1 5b81 +47 129d +2f 8677 +10 232a +2a 246c +65 9e9b +7f 9fdd +22 2c26 +a8 cc4a +26 a496 +35 8f3b +4f 907d +f4 551a +2f acdf +90 4b20 +6 23c +a a2ee +24 a430 +a7 4615 +1a a36e +16 994 +db f96f +4 8898 +43 1aa5 +a3 e60f +bd e751 +24 acb2 +3e adf4 +c3 58a5 +c4 503a +de 517c +39 fc1 +d7 d3bf +f1 d501 +fa ff44 +e0 fe02 +a1 4c81 +d8 5948 +1b 8b67 +97 e137 +21 ea1 +a5 c699 +2f 6d7 +3c 778 +4c b0d2 +90 41a0 +d4 511a +6b 14c7 +ad e4d9 +78 1568 +e1 d489 +57 999d +9 8261 +54 b93a +d1 59a1 +47 10bd +61 3689 +96 4316 +74 9518 +5a b144 +40 b002 +ea 5e6e +7d b5d9 +19 2bc3 +33 2d05 +0 a808 +5b 33c7 +68 3468 +b1 458b +de 715c +c4 701a +21 8e09 +2f 24fd +15 23bb +31 27a1 +d 8879 +b9 4569 +92 4386 +50 992a +49 3263 +60 3688 +c0 58a0 +49 b8eb +c8 f248 +1d 3d1 +da 7366 +3 28f +e1 7603 +e8 dcca +62 3ca6 +fb 7745 +71 170b +fb 5fef +93 4985 +87 6895 +e1 7c2b +fb 7d6d +35 a711 +13 985 +fb fd6f +24 8c98 +57 1195 +6d 3c79 +46 3a96 +f1 550b +3 22d +38 8fc0 +96 6114 +8 2840 +10 1a2 +8e 4a54 +bb cfcf +d5 d111 +de fb54 +c4 fa12 +95 c993 +9b 636d +81 622b +6d 1e7b +89 e26b +85 4891 +ea 76c6 +5e 937c +44 923a +51 3b81 +35 2791 +80 e2a0 +e6 56b6 +b0 6508 +a3 ce05 +75 b5b9 +4 2830 +8a 4a44 +5c 31f8 +c1 5881 +dc 5158 +f4 7f32 +37 f9d +98 4b6a +3 aaa5 +a8 ec60 +34 27b2 +b7 cfbf +d1 d101 +da fb44 +c0 fa02 +81 4881 +9c 4158 +e6 76b6 +a a44 +90 4b02 +aa 4c44 +48 b86a +11 981 +88 e8e8 +c5 5233 +df 5375 +8e 4a7e +ba 6566 +dc 5352 +8b 68cd +70 9788 +b4 679a +7c 9f78 +6f 9ed7 +90 692a +cf 5875 +b8 cdca +b1 6703 +32 2da6 +6d 96f1 +b6 cfbe +d0 d100 +80 4880 +55 1319 +f5 5519 +fe 7f5c +e4 7e1a +5c 1352 +b 28cd +da 5946 +78 97c2 +9e e3fe +b8 e540 +5a bbe4 +40 baa2 +95 61b3 +51 91a1 +6d b659 +32 f84 +40 1880 +44 9a90 +98 4b62 +c5 d839 +e 27e +10 292a +4f 1875 +0 208 +8a 4aec +88 6860 +db fb67 +4 8a90 +c7 fabd +31 8d0b +2a 2644 +39 ad6b +b8 4d62 +f7 7d1f +13 ba7 +c4 7230 +2e 47e +18 b62 +c8 70ea +17 a997 +37 f1f +cd 785b +3 825 +6 8236 +13 230f +2d 2451 +61 be29 +5b 116d +41 102b +f7 5db5 +24 86b8 +86 483c +e 2054 +7f b7df +e0 5620 +36 a7b4 +4f 1a7d +38 87e0 +83 4aa5 +6c 1650 +f5 f713 +76 bdb6 +e4 5c98 +4a 1864 +b5 e713 +36 adb6 +a4 4c98 +a 864 +95 e999 +53 1987 +ce 52d4 +91 e989 +16 a996 +36 f1e +cc 785a +2 824 +c3 f8a7 +ac c452 +c9 f8c9 +12 230e +2c 2450 +2c 8ed0 +a6 4696 +c5 fab9 +28 2640 +1c 172 +b2 6f24 +81 c889 +b 8c7 +18 968 +7a 9f4c +60 9e0a +be 6f5e +82 4084 +39 2f61 +31 589 +3a 2fcc +20 2e8a +9 24b +9d e9fb +da 5346 +89 68c1 +db f9ef +3e 2576 +f2 f72e +35 8799 +fc 7752 +bc 65d2 +78 95c0 +fc 7df2 +36 a796 +23 84ad +13 38f +2d 4d1 +ea 7466 +81 6281 +6d 1ed1 +80 4288 +12 a3a6 +12 184 +17 2395 +42 9826 +34 f3a +4e 107c +46 3896 +20 8422 +3a 8564 +6 8ab4 +fa 776c +e0 762a +f4 dd92 +28 a44a +86 c016 +55 3b19 +73 9587 +72 3d06 +b0 4588 +64 be90 +60 3608 +12 8b8e +2c 8cd0 +45 1a39 +76 9f36 +90 4920 +6 3c +77 95bf +4 2298 +22 84ac +e3 5427 +38 a54a +77 9595 +8d c079 +66 be96 +9 8869 +d 22d3 +14 899a +27 2415 +8 8868 +21 c23 +3b d65 +d 8fb +1e a376 +8c 4258 +55 3313 +6f 3455 +5c 99da +a 204c +e4 5e10 +5b 3967 +b 22cf +25 2411 +87 68b7 +43 98a5 +44 903a +5e 917c +21 8c81 +5e 3356 +a2 cea6 +85 60b3 +41 90a1 +9f 61f5 +3 8d +c0 7022 +da 7164 +a5 c6b1 +f0 fd8a +a8 4448 +3a a566 +80 4a80 +bc cfd2 +d6 d114 +8a e26e +86 4894 +5 8019 +27 2e17 +a 22ce +c1 f889 +24 2410 +31 ad0b +6e 1656 +3 2a8f +1d 2bd1 +6a 1cec +7b b767 +50 1baa +e9 5649 +79 1741 +b0 65a0 +d1 5b29 +78 9742 +27 acbd +bc 455a +d3 79af +27 a495 +95 c99b +8e 62d4 +66 1e94 +16 83be +30 8500 +47 18b5 +32 2584 +ca 726c +de d9d4 +c4 d892 +90 e120 +f9 57cb +62 9c2e +7c 9d70 +e0 fe08 +87 e89d +52 192c +a5 ec13 +bf ed55 +c9 facb +e3 fc0d +2c 2652 +a 8a64 +1d a9d9 +85 c8b3 +9f c9f5 +6e 945e +f8 dd42 +4f 90d7 +5c 9178 +4 2010 +8e 68f4 +cc d850 +36 251e +63 b4a5 +f8 556a +a7 e41d +8d e2db +58 136a +f6 7d3e +ab 4665 +b7 e73f +38 ade2 +0 aa88 +33 2f85 +e 8fc +1f a377 +8d 4259 +a 8864 +59 3361 +53 9987 +a1 4401 +87 42bf +5e 13fe +78 1540 +c 878 +b 82ef +25 8431 +95 4119 +7 aa35 +82 4806 +d 2a5b +f4 5518 +9a 43ee +b4 4530 +69 1649 +49 b0c1 +65 9e91 +10 2320 +fe 75fe +88 4842 +26 86be +95 41b9 +c3 5a05 +d4 5118 +9 e1 +b6 cd9e +6b 96c5 +bc 6778 +af 66d7 +52 b126 +c0 5008 +16 a19c +3 8227 +b4 4518 +dc 7952 +d2 f92e +15 8999 +a a2ce +d0 792a +24 a410 +4d 9ad1 +7a 3fee +94 4130 +49 1249 +d3 5b2d +29 acc1 +6e 1456 +65 16b9 +f 88f7 +8e c254 +b0 4508 +3c ad7a +61 3481 +e7 5695 +98 4148 +e2 76a6 +43 1207 +85 e219 +6 a8bc +42 b80e +5c b950 +7 22bf +21 2401 +58 39ea +ab 4c47 +9 aae1 +94 4118 +b0 452a +3 2a0d +20 2482 +3a 25c4 +54 9930 +98 6942 +da f1e4 +c0 f0a2 +36 a7be +e 205e +b3 4f2d +56 33be +70 3500 +f6 dd16 +25 619 +5 a091 +80 6a80 +1 a081 +ab 4eed +7c 9dd2 +68 366a +ca fae4 +34 8d32 +36 8fbc +94 6110 +93 69af +7c 355a +81 ea81 +18 2b60 +54 1b12 +6e 1c54 +e 2a56 +5 13 +1f 155 +89 6a49 +50 b9aa +e9 f449 +3e 2ffe +58 3140 +de d956 +d 259 +60 3628 +74 9d90 +11 8b2b +2b 8c6d +70 1508 +9d c15b +17 2137 +d7 f317 +c6 589c +ca fa66 +d 8ad1 +3a 8746 +93 4b2d +8 848 +63 14a5 +8e 60fc +30 8fa8 +8 aa6a +19 8169 +3b 2f67 +31 8f81 +8f 60d5 +3c 2dda +18 a96a +97 631d +18 29c0 +60 16a8 +d4 d312 +ee d454 +8e 40f6 +cc d2d2 +e6 d414 +8f 62dd +10 2980 +93 61af +dc d9d0 +c2 d88e +3f 557 +c8 7268 +a 804c +b7 659d +6f 9477 +0 28a8 +8 842 +c 8a52 +17 a3bf +31 a501 +4c 9250 +ca 70e6 +37 a597 +fb 5545 +e1 5403 +2e efe +48 1040 +86 e016 +86 e014 +23 685 +2f cf7 +ae 4654 +23 8ead +81 6001 +44 1018 +df f377 +ce 58fc +d3 7925 +0 a228 +4f 985d +5a b36c +40 b22a +44 9890 +40 1008 +98 c9e8 +91 6321 +46 10b4 +b2 ef0e +cc f050 +90 6182 +7c 1dd2 +15 21b3 +d0 d902 +46 901e +37 afb5 +cc 507a +1 2a29 +a0 4e08 +32 af26 +88 e860 +f1 5f0b +d3 5905 +0 8208 +7e 3dde +33 705 +26 ae94 +14 21b2 +76 3d1e +2b 645 +48 1a42 +53 33af +6d 34f1 +e8 5c42 +d 28d9 +3e add6 +78 97ea +97 e317 +86 489c +8 2842 +80 ca28 +b3 4f25 +9 2841 +11 1a3 +8f 4a55 +c2 700e +7e 9ffc +dc 7150 +64 9eba +29 ae61 +94 e198 +52 1186 +9 63 +2f ae55 +f4 5598 +f 57 +2e ae54 +e 56 +d6 d9b6 +5 2b9 +36 87b6 +2d ae51 +a3 6e05 +d 53 +af 4e55 +5c 1b5a +f 2aff +29 2c41 +31 5a3 +f3 5507 +51 b3a1 +27 e15 +fa 57cc +e0 568a +85 4231 +d0 790a +c3 52a5 +70 1faa +6b be65 +18 8b6a +8d e079 +4b 1067 +1e a1d4 +4 a092 +3e 75c +55 3bb1 +24 61a +90 c9a0 +79 954b +9 2a49 +3a af46 +6e 16d4 +31 ad89 +5e 997e +6d 16d3 +30 ad88 +ba 47cc +a0 468a +69 3649 +9a 63ee +b4 6530 +5 a8b3 +84 e210 +1f a9f5 +d3 590d +ca f246 +e a054 +c3 5aaf +dd 5bf1 +f4 f71a +2a 86e4 +29 866b +e2 5c26 +58 1342 +7 28bd +e9 7c61 +23 a605 +19 161 +8 2a62 +8c e25a +25 ac13 +3f ad55 +7a 17cc +60 168a +99 4b63 +b a8c7 +18 a968 +43 12a5 +e8 5460 +24 a4b2 +3e a5f4 +f8 5548 +3b 8767 +b0 47a2 +a8 6e40 +50 b108 +5e 1b56 +5d 197b +9c 4b5a +7 aa95 +92 eb0e +ac ec50 +fe dddc +f7 7715 +e4 dc9a +1 8801 +39 afc3 +53 b105 +3 2885 +d0 f1a0 +4 201a +1e 215c +e3 5687 +f0 5728 +5c 197a +95 4b33 +af 4c75 +6 aa94 +0 8800 +3a 7cc +20 68a +8f ea5f +f3 558d +7a 37cc +60 368a +54 19ba +7f b577 +ed 5459 +14 118 +62 9484 +a6 6496 +c9 58e1 +3 8285 +8e c2fe +a8 c440 +f2 55a6 +3e 2d56 +35 2fb9 +b9 6d49 +e1 f4a9 +d3 7ba7 +bc 4752 +71 9789 +b5 679b +55 9393 +6f 94d5 +30 a58a +56 191c +a9 6461 +f2 7584 +87 4815 +9a 63ce +56 93bc +b4 6510 +f aa55 +d4 5198 +b 2845 +1 8089 +d8 f160 +13 1a7 +c8 506a +33 afa5 +14 a99a +8 8c0 +87 421d +e aa54 +16 83b6 +98 63c0 +a 8a4e +d9 5369 +4e 38de +3 205 +3 aa25 +92 638e +ac 64d0 +c1 5801 +47 123f +17 a995 +37 f1d +f2 dfa6 +18 a1e2 +8 8a4a +63 96a7 +2d a4f9 +3b f47 +b7 e51d +9d e3db +68 146a +a1 4623 +bb 4765 +88 c268 +b8 4548 +f7 579f +ae 467c +e4 f418 +0 82a0 +23 2e05 +35 2599 +ec 765a +22 624 +70 3f82 +8a 40c4 +62 9ea6 +45 30b3 +5f 31f5 +25 84b1 +83 6285 +6f 1ed5 +4a 30cc +30 2f8a +5a bbce +74 bd10 +29 2ceb +a8 6648 +48 926a +bd e779 +7b 1767 +43 1825 +77 95bd +bb 65cf +e7 f615 +68 1642 +17 2bbd +9b 43cf +b5 4511 +37 2595 +4 a098 +ec 5652 +4f 32d7 +5c 3378 +56 999e +58 9962 +3 a25 +cd 7a5b +2a ae66 +31 2589 +16 136 +36 af34 +91 4129 +9a 6b6c +80 6a2a +35 a799 +97 691d +56 b334 +45 18b9 +aa 46ee +f5 d713 +76 9db6 +30 2588 +d8 59ca +20 8488 +69 3469 +42 3286 +e7 fe15 +38 762 +60 3c08 +6e 14d4 +54 1392 +b 26f +b8 c5ca +32 25a6 +8d e8fb +ca 5246 +17 23bf +31 2501 +c a52 +a1 6ca3 +bb 6de5 +f9 75c1 +98 cb4a +12 2b26 +8c 4870 +e3 5ca7 +2a 86ec +2c 2658 +d0 7b22 +13 b8d +ea 7c64 +66 9e94 +4c 9870 +e3 540d +c9 52cb +5c 39fa +11 321 +ff 55ff +d9 d341 +5a 99e4 +40 98a2 +9e 69f6 +43 120f +5d 1351 +cd 5af3 +e7 5c35 +a8 6cea +e5 7c33 +ff 7d75 +2c a678 +d7 791f +e3 7487 +f0 7528 +f1 df2b +a a0c6 +ce 5074 +b4 4f32 +62 bc86 +19 ab63 +59 3be1 +28 64a +92 e9a6 +cc 5070 +b2 4f2e +5b 194f +7a bdc4 +60 bc82 +c2 f086 +a3 64ad +b2 4786 +70 9d2a +69 3663 +18 142 +ae 6ef4 +d1 7901 +94 4190 +d8 d142 +29 ceb +a8 4648 +3a a766 +6f 3cdd +55 3b9b +e8 dc60 +cb f8ef +2e 2476 +70 b580 +e2 f62e +fc f770 +25 8699 +ec 7652 +87 481d +5e 197e +4 2210 +8e 6af4 +95 e9b1 +64 b41a +7e b55c +2d cdb +c3 7a8d +3e a756 +97 6b3d +43 1885 +44 101a +5e 115c +cb 7865 +b4 edba +69 b6e1 +d2 db0e +ec dc50 +7f 957f +c 2258 +83 e8ad +6c b458 +4a 9244 +8e 6256 +51 bb81 +e1 7cab +fb 7ded +35 a791 +97 6915 +16 a19e +91 4121 +a0 e402 +ba e544 +9a 63ec +80 62aa +6c 1efa +42 baa6 +97 61b7 +53 91a5 +42 1884 +f3 7f0d +5d 115b +ac 6458 +a5 ecbb +bf edfd +e2 5606 +91 6b81 +ca 7864 +68 b6e0 +46 9a94 +6 8814 +2e c7e +c4 7a30 +13 2385 +9e 63fe +5a 93ec +40 92aa +b8 6540 +c0 f808 +b6 4594 +53 332f +6d 3471 +e5 7c13 +ff 7d55 +2c a658 +96 413e +34 2d9a +87 6095 +a6 6c96 +62 9c84 +94 c9b0 +7d 955b +19 8b61 +d5 5391 +98 e348 +56 1336 +19 a9eb +11 8183 +99 c3c9 +13 23a5 +b8 6560 +70 9520 +b4 6532 +5c b9fa +11 8321 +24 a4b0 +a7 4695 +bc cdd0 +a2 cc8e +a8 6668 +30 852a +42 b884 +62 1e0c +5d b15b +8 a86a +3b 2d67 +86 689c +71 b781 +96 4194 +93 61a5 +65 1cb3 +e4 5610 +7f 1df5 +92 692c +30 a7a8 +c5 7813 +df 7955 +c a258 +4d 32d1 +54 9998 +7 a15 +da 53cc +c0 528a +5d 9971 +43 982f +21 2403 +3b 2545 +e2 de0c +8 a048 +4e b056 +1d 2b59 +e7 541f +45 b2b9 +b1 45a1 +29 ae49 +93 492f +9 4b +31 87ab +3b fc7 +48 1068 +c4 fa18 +f5 7519 +11 3a1 +8 a260 +86 4094 +19 2361 +13 8987 +ec 747a +5e bb76 +cc 5a58 +93 4927 +29 ae41 +9 43 +31 87a3 +48 1a40 +e a76 +0 a028 +dd 7bf3 +f7 7d35 +24 a638 +4d 9079 +26 8e96 +f5 57b1 +aa 6c66 +d9 dbc9 +53 3ba5 +3c 750 +22 60e +e9 deeb +2 a086 +c6 5034 +ac 4ef2 +f5 7d93 +3c a7d8 +48 3a6a +47 12bf +61 1401 +d1 5ba3 +eb 5ce5 +fc 7d52 +f2 fd2e +35 8d99 +70 95a2 +e 2a74 +5f 197f +c 88d8 +5 2211 +8f 6af5 +16 a39e +91 4321 +dc 79fa +13 927 +7e 3f54 +64 3e12 +5b 13cf +75 1511 +e5 5cb3 +2c 86f8 +ff 5df5 +a5 6e13 +bf 6f55 +61 9e01 +a8 4460 +f1 5583 +43 322d +57 9995 +8b 42cd +53 19a5 +b9 6d61 +31 709 +11 a181 +a1 4eab +bb 4fed +cc 5852 +6a 96ce +96 6914 +fa 7dec +e0 7caa +34 a790 +9c 4152 +a5 ec9b +bf eddd +70 1d2a +39 741 +a9 4ee3 +c3 5025 +fc 575a +67 b695 +47 90b7 +98 616a +7a 3fcc +60 3e8a +7f bd77 +ed 5c59 +61 1681 +8 8a48 +a7 66b7 +63 96a5 +21 e03 +3b f45 +d6 5196 +8d 4073 +88 c042 +12 8126 +47 38b7 +cd d8db +c6 7214 +d8 dbe2 +f2 dd24 +ee 5efe +14 213a +1a 8966 +e d4 +6a 9c46 +86 6094 +70 15aa +68 3c48 +86 c896 +8c 6270 +b4 e5b8 +72 15a6 +c2 7226 +5 291 +ca 5a6c +27 e95 +b2 4f0e +cc 5050 +77 9715 +38 a7ca +21 2e01 +4a b2ec +ec 5452 +4c 1252 +d6 5b36 +e1 74a3 +fb 75e5 +97 eb15 +48 1a62 +46 3236 +cc d25a +a8 ecca +ff 5757 +49 1a61 +6e bcde +23 8605 +e9 5c61 +38 856a +a 2a44 +4c 9af0 +90 6b02 +aa 6c44 +b2 45a6 +76 1536 +b8 e548 +5a bbec +fc 5d52 +40 baaa +44 9a98 +77 1f95 +2 a024 +31 8521 +84 c898 +bb e76f +b7 4d95 +af e4d7 +bc e578 +7a 1566 +d 2a59 +3e af56 +ad 6c59 +b5 45bb +b 2a67 +2b 26c5 +32 8d8c +cc 5252 +be 67dc +60 9688 +a4 669a +31 858b +8c 6a50 +83 4aa7 +6c 1652 +1c 2172 +f6 5f36 +30 8522 +4f 1a7f +9a 4966 +38 87e2 +6 214 +8c 42d2 +a6 4414 +66 1496 +86 c294 +e8 7ce0 +22 a684 +ad e4db +78 156a +4c 9850 +ff 55df +97 e99d +48 18ea +5a b16c +40 b02a +65 9e33 +7f 9f75 +f9 d54b +73 3527 +72 3f8e +8c 40d0 +c9 7841 +d1 51a3 +67 b6bd +d5 511b +8f e055 +3c ad5a +11 ab21 +8c 6072 +48 9060 +45 1831 +aa 4666 +79 95c9 +bd 65db +16 23be +30 2500 +a0 6ca2 +ba 6de4 +73 1785 +5 a019 +86 6894 +58 b3e0 +fa 5546 +f1 57a9 +d 88f3 +8c c250 +3d 877b +f6 5d36 +90 4902 +6 1e +ca 72ec +34 53a +41 3aa9 +4a 3846 +db f9cd +c1 f88b +24 2412 +3e 2554 +80 60a8 +7d b773 +6c 1cf8 +a7 e497 +b4 e538 +72 1526 +a5 e493 +bf e5d5 +70 1522 +61 bc21 +24 84b0 +82 6284 +6e 1ed4 +81 6a21 +3e ad5c +24 ac1a +57 3117 +42 1824 +76 95bc +ba 65ce +ec d452 +61 be01 +5b 1145 +41 1003 +12 832c +cb 58e7 +a8 6460 +89 c2c9 +3 22a5 +86 4814 +1d 2973 +a6 4e1c +cc faf8 +2f 267f +87 e097 +94 e138 +52 1126 +a4 4490 +41 3a01 +db 7367 +4 290 +82 6806 +3a a7c4 +20 a682 +d3 59a5 +0 82a8 +fd 77db +33 7a5 +75 1533 +6 2a94 +48 1260 +ca 5266 +b 2acf +25 2c11 +6d 145b +84 48b0 +24 2698 +45 3a19 +90 6900 +6 201c +ce f2fe +e8 f440 +62 14a4 +9 8e1 +9d 6173 +59 9161 +48 ba62 +64 1490 +d0 512a +c2 d026 +38 8742 +91 4b29 +97 6997 +53 9985 +87 42bd +d6 7914 +1d a359 +b1 47a9 +66 1e16 +38 5ca +c1 582b +db 596d +15 8311 +93 61a7 +1b 296f +36 af36 +a4 4e18 +ce 527c +84 e890 +13 298d +35 873b +ee 5cf6 +d9 7161 +a a846 +2c a452 +85 6839 +57 3bbf +71 3d01 +34 590 +f9 f549 +20 ae28 +fd df79 +16 a114 +6 2a1e +17 11d +98 e162 +b0 6d88 +ef d67f +70 9d22 +b1 c703 +32 8da6 +10 8122 +c0 da28 +19 2161 +f3 5f25 +f8 7548 +9a 4bec +ab e667 +80 4aaa +8 20e0 +e2 5ea4 +b3 ed8f +fd 577b +19 a941 +80 6880 +9e c15c +84 c01a +1a 964 +0 822 +56 b3be +70 b500 +a6 4e94 +20 2c80 +54 b198 +16 a916 +eb 54c7 +f8 5568 +53 13ad +35 2d19 +ac 6cf8 +68 3448 +31 2781 +9e 4956 +a6 e634 +95 4bb9 +3c 87d2 +cf 52f5 +7c 1ffa +cf 725d +50 3900 +5 a19 +36 8f16 +37 ad37 +a5 4c19 +d6 d116 +53 b127 +c1 5009 +f2 d506 +17 a19d +97 c3bf +b1 c501 +ba ef44 +a0 ee02 +d 20d1 +e7 5e95 +d2 f126 +15 8191 +1e abd4 +4 aa92 +96 c3be +b0 c500 +60 3c80 +2a 2cce +65 9619 +c 20d0 +e6 5e94 +d0 79a8 +87 e895 +52 1924 +4a 18e4 +c9 5241 +cb 78ef +5 a293 +1f a3d5 +2a e66 +de 53dc +c4 529a +28 2448 +98 6bea +b2 6d2c +d0 dba0 +33 727 +9a 4be6 +db 79c7 +22 a40c +8 a2ca +72 9784 +b6 6796 +10 1a0 +5c 3172 +3 88ad +d 8d1 +ca 7866 +71 1da9 +68 b6e2 +d0 5120 +12 38e +2c 4d0 +b6 4db4 +4f 3aff +69 3c41 +71 15a3 +ef 5e55 +ca 704c +6c 9ef8 +b0 6f0a +9a 6366 +6b b4c7 +78 b568 +5 19 +d9 f369 +36 8516 +90 4b0a +aa 4c4c +d3 71a7 +23 2c2f +3d 2d71 +ee 5e54 +cb 78c7 +12 a30c +d8 7968 +2b 8647 +c3 5aa5 +1a a3ce +34 a510 +e0 fca8 +48 9240 +8c 6252 +c 2852 +41 b8a9 +e2 d406 +c1 522b +db 536d +91 e981 +7 a09d +43 b027 +b1 4f09 +b9 4d69 +92 4b86 +86 e814 +5d 9979 +6b 1ce7 +ad ecf9 +ea 5644 +f0 5720 +f5 dd3b +ee 7674 +42 9aa6 +49 1243 +c a8f8 +c 8d0 +21 ac03 +3b ad45 +1b 2bed +1 2aab +d2 5186 +89 4063 +a9 ee61 +c4 5a10 +39 2561 +37 8fbf +51 9101 +95 6113 +39 749 +f3 df85 +19 a1c1 +57 1915 +18 29ca +5d 99d9 +4d 9879 +38 a548 +6e 96f6 +b0 4722 +d 88db +6 2214 +48 92c0 +8c 62d2 +a6 6414 +3d 87db +f6 5d96 +16 893e +f 2277 +36 859c +c aa72 +70 15a0 +98 4362 +96 c99c +8f 62d5 +56 311c +3c 2fda +c6 78be +67 1e95 +18 ab6a +c3 5885 +fa d544 +e0 d402 +5 a099 +e9 dceb +e2 7624 +6 29e +7f 3df7 +e4 7612 +fe 7754 +84 e810 +95 c9b1 +64 941a +7e 955c +e0 7402 +fa 7544 +16 bbe +30 d00 +af 465d +5b 1bcd +41 1a8b +3c adda +29 2449 +2 226 +15 219b +c 887a +a2 4426 +ce 72fc +38 54a +3 a805 +42 b204 +d9 d9c1 +22 406 +6d bcf1 +53 bbaf +3c 875a +69 9463 +76 1d96 +4d ba71 +e8 f6c2 +f1 5d89 +3 2825 +89 c849 +99 61e1 +71 1da1 +f5 d599 +fe ffdc +e4 fe9a +d5 d93b +ce 7274 +62 b604 +51 1b89 +0 88 +c3 5a0d +d1 5183 +88 4060 +67 b69d +b9 c7cb +33 27a7 +2f eff +49 1041 +6d 3cf3 +ec 7650 +86 6896 +42 9884 +f3 ff0d +5d 915b +14 299a +2c 26fa +e5 f419 +1 82a1 +9d 697b +59 9969 +ec 5e70 +24 2e18 +1e a3de +99 4361 +e2 5484 +81 4081 +dd 517b +38 fc0 +6 a94 +7f 1dff +fe 575c +e4 561a +de f1d4 +c4 f092 +9c 61f2 +58 91e0 +cd 72d9 +b0 450a +e3 7caf +fd 7df1 +37 a795 +62 1ca4 +e1 5601 +25 8e33 +3f 8f75 +18 8940 +95 61b9 +2d c53 +c3 7a05 +c 2a50 +17 81b5 +6 aab6 +f5 7d31 +db 7bef +2f a6d5 +13 2387 +20 2428 +75 9f99 +da db44 +c0 da02 +90 e98a +e7 5417 +45 b2b1 +4 ab8 +2f a675 +8 a2e0 +aa 4446 +a1 46a9 +4b b8e7 +ca f244 +55 9999 +35 f13 +4f 1055 +d5 d339 +9a 69ee +1c 8b58 +bb 67c7 +77 97b5 +77 3d9f +f0 dd22 +66 943e +d3 d90d +1c 352 +a6 4c36 +e1 d429 +11 29a9 +66 9436 +15 a9b1 +35 f39 +eb dee5 +11 a121 +88 e862 +1a 834c +d3 5907 +0 820a +d0 db80 +33 707 +ff f557 +d4 599a +88 6a68 +87 4017 +a7 ee15 +97 e915 +48 1862 +43 b2af +5d b3f1 +ff 5557 +fe ff76 +27 8e9f +44 9832 +5e 9974 +ed 745b +9 2e3 +23 425 +55 b3b1 +f7 5517 +8d c2d3 +a7 c415 +ea fecc +54 911a +8e 485c +32 5a6 +10 2b02 +2a 2c44 +52 b3a4 +cc 5272 +82 e886 +a8 4660 +f1 5783 +12 a904 +28 ae68 +8 6a +59 1b41 +52 9984 +96 6996 +37 a5b7 +a5 4499 +b 65 +fe dffc +e4 deba +17 a197 +db 5145 +c1 5003 +d4 db98 +37 71f +cd 705b +3 25 +a5 4439 +2e 8656 +87 4a3d +42 b20e +5c b350 +a8 e6e2 +b1 4da9 +94 491a +49 1241 +d3 5b25 +cf 52ff +50 19a2 +e9 5441 +a6 4436 +7e 1576 +59 1369 +39 ade1 +70 950a +c8 5a6a +38 ad40 +1e abfe +15 a911 +1d 297b +97 49b5 +66 141e +15 2999 +f0 5d02 +cc 7a5a +2 a24 +18 8b62 +12 986 +9a 6966 +38 a7e2 +56 1996 +29 a4e1 +96 4916 +b8 6562 +b3 c50d +99 c3cb +13 23a7 +18 2362 +1c 297a +8e 6876 +4a 9864 +e1 5401 +c7 52bf +3f 2ddf +b8 cd62 +f7 fd1f +13 8ba7 +c4 f230 +2e 847e +59 1341 +b6 4536 +82 4aa4 +6b 164f +8 e0 +6a 96c4 +ae 66d6 +b4 c512 +7e 3dd4 +64 3c92 +69 1c61 +84 ea90 +1b 2b6f +cb 5a65 +5f 135d +45 121b +e9 5c41 +cf 5aff +90 4102 +ec 7c78 +df 7bd7 +26 a61c +38 85c2 +bd 6579 +52 9384 +96 6396 +74 b598 +9 ac3 +23 c05 +d7 fb95 +2d 267b +85 e093 +9f e1d5 +50 1122 +49 3841 +6a 9664 +ae 6676 +51 11a3 +cf 5a55 +aa 6c4c +4c 9af8 +90 6b0a +13 a125 +69 1e69 +17 bbf +31 d01 +ce 5a54 +b5 4539 +be 6f7c +60 9e28 +a4 6e3a +d7 d9bf +3a 546 +31 7a9 +a ac4 +2c 245a +a0 6ca0 +3a fcc +20 e8a +86 4ab6 +a8 6cca +64 9cb8 +50 b122 +2d 2459 +d4 51b8 +f aa75 +96 4936 +2c ae50 +34 87b2 +4b 1a4f +96 e9b4 +7f b55f +b8 edc0 +e8 566a +3e a7fe +1c 2958 +94 eb18 +52 1b06 +fd d5d9 +77 35b5 +44 b0b8 +1b a3cf +35 a511 +db d9cf +3e 556 +35 7b9 +4d 18d9 +44 b212 +5e b354 +3d 5f3 +d3 73a5 +40 3800 +c6 5a14 +98 41c8 +6a be66 +bf 6577 +61 9423 +7b 9565 +82 4a8c +0 a020 +19 21eb +f3 5faf +a9 44c1 +ad e4d3 +78 1562 +8a c8ee +83 6227 +61 b429 +6f 1e77 +6d 9e51 +2 a8a4 +81 e201 +4f 1aff +69 1c41 +c7 d8bd +10 302 +2a 444 +65 9491 +7a 95c6 +ca f2e6 +d3 59ad +49 10c9 +6c 9e50 +1b a9e5 +1 a8a3 +80 e200 +68 be62 +bd 6573 +79 9561 +84 6090 +cc 52d0 +4f 9875 +10 a92a +83 682d +21 a6a9 +19 9cb +2a a446 +42 1224 +55 3199 +3d a573 +4c 9878 +93 6125 +65 1c33 +7f 1d75 +11 329 +c8 52e2 +e2 5424 +9b 69e7 +78 1548 +48 b24a +30 7a2 +28 2e40 +a2 c406 +c5 5813 +c 8258 +df 5955 +63 968f +7d 97d1 +cc fa50 +13 8187 +5 8a11 +6c 3452 +83 68a7 +8d e0db +58 116a +39 85c1 +42 b004 +28 aec2 +d9 d14b +53 3127 +89 4863 +aa 4e66 +5 2811 +a8 cce8 +a1 6621 +7e bfde +f9 5f61 +35 afb3 +4f b0f5 +d0 d18a +33 8fad +91 6101 +63 1c0f +7d 1d51 +66 be94 +e9 7ee9 +46 1096 +a0 4480 +0 280 +70 150a +34 8792 +fa 5dee +3e fdc +24 e9a +a5 ccb1 +74 971a +19 349 +d0 5302 +ea 5444 +f 88d7 +1c 8978 +67 163d +76 bf9e +f1 5f21 +b6 ed36 +40 1aa0 +f7 5795 +c4 d298 +e8 564a +cb 5265 +c7 f01d +ad eedb +78 1f6a +d7 f33f +58 b9e2 +2 a806 +d1 7121 +36 afb6 +a4 4e98 +3a 87cc +20 868a +f3 5d87 +b9 e561 +67 3e95 +f0 7580 +f9 5543 +ca fac4 +34 8d12 +9d e353 +8c 48d8 +1e a9f6 +48 9a62 +76 bd3e +2b 8665 +4b 18c7 +8d e8d9 +58 1968 +12 a124 +d2 592e +c 82d2 +26 8414 +e4 7630 +de 5956 +e6 f634 +d5 5bb9 +7c 97d2 +6b 164d +31 af83 +4b b0c5 +59 13e3 +73 1525 +84 603a +9e 617c +33 8f87 +40 9028 +ab e66f +a7 4c95 +7c 97d8 +78 3fe2 +92 4124 +13 a325 +ec dc52 +f4 7d30 +da 7bee +2e a6d4 +70 3f22 +8a 4064 +d3 5187 +ba cdcc +b3 6705 +a0 cc8a +49 12e3 +63 1425 +30 722 +7c 157a +ec 7cf0 +d2 7bae +26 a694 +68 3ee2 +82 4024 +ef f657 +de 5bdc +c4 5a9a +12 a1a4 +fc 5752 +8a 4866 +28 86e2 +ad ecfb +ea 5646 +99 6bc1 +0 2a8 +d3 d98f +d9 7369 +36 516 +eb 7c6d +d1 7b2b +25 a611 +c 2a58 +2e 4fc +14 3ba +ed d6d9 +b2 6d8e +67 36b5 +d1 7b23 +eb 7c65 +3 a8a5 +98 496a +39 8d61 +14 a1b2 +34 73a +4 8218 +d7 5915 +75 9791 +2d efb +47 103d +c9 d8c1 +12 306 +60 1e20 +82 c886 +88 6260 +78 15e0 +40 3808 +d4 5192 +8b 406f +4 2a30 +98 c1ca +12 21a6 +2a 8c4e +c0 fa00 +b 8247 +61 9c03 +bf 6d57 +7b 9d45 +3c adfa +30 d20 +af 467d +e4 7698 +b0 4502 +d8 7940 +1f ab77 +8d 4a59 +ef 7efd +59 114b +22 484 +22 a6a6 +fa 55c4 +e0 5482 +8d 68f3 +49 98e1 +34 a5b0 +1 809 +32 8d06 +66 363e +a 8246 +69 344b +80 68a0 +9 aa69 +32 d8e +c 8878 +25 c33 +3f d75 +3e ad5e +13 ab25 +14 2190 +ca f86e +d 88d9 +d 88f1 +36 f9e +d2 7326 +15 391 +d1 79a1 +a0 440a +ba 454c +d2 598e +89 486b +b6 cd3e +af 6677 +6b 9665 +3b a56f +40 1220 +34 792 +e 885c +cc 7a78 +11 909 +8 a242 +91 e189 +7d 9dd9 +1a 3ec +0 2aa +e3 5c0f +41 baa9 +fd 5d51 +77 9d15 +5d 9bd3 +d0 5900 +61 be09 +5b 114d +41 100b +9b 63c7 +57 93b5 +a8 6468 +3 22ad +fb f76f +f7 5d95 +24 8698 +86 481c +a5 e49b +bf e5dd +70 152a +ff d577 +90 69a8 +32 2506 +24 8e12 +3e 8f54 +17 915 +d0 518a +b aa47 +a 80ee +a0 eea0 +28 c48 +45 3a99 +90 6980 +cd f2d3 +e7 f415 +2b 84c7 +38 8568 +5a 3366 +f2 fd26 +21 2629 +35 8d91 +69 36c9 +af 66dd +30 2d80 +87 489d +ec 76d2 +67 bc1f +c8 5a60 +4 aab2 +1e abf4 +28 4e0 +eb 5e65 +9e 41d4 +84 4092 +e 80de +a4 ee90 +3b 2f6f +32 506 +41 1889 +52 b304 +61 3e89 +49 b263 +d6 d916 +5 219 +43 b085 +d8 514a +4d 10d1 +33 f8f +88 4062 +a8 ee60 +67 b69f +e8 d6c8 +62 36a4 +1e 2b56 +46 b2b6 +c2 d00c +a8 ceca +22 2ea6 +0 80a8 +58 13c2 +72 1504 +8c 4052 +ac ee50 +b1 67a9 +4 8a12 +1e 8b54 +df 5177 +21 8c01 +7 8abf +12 8904 +6b 3ecf +85 4011 +f5 5fb9 +44 3818 +3e adde +b9 4d61 +15 33b +2f 47d +4e b0de +c9 5061 +5 a0b3 +1f a1f5 +3c ad52 +5b 1b45 +41 1a03 +93 6985 +b5 4f93 +cf 50d5 +90 618a +7c 1dda +58 996a +91 cb23 +ab cc65 +45 1239 +21 2ca9 +76 9736 +25 acb1 +90 4120 +f5 f71b +76 bdbe +2b 86e5 +38 ad4a +8b e045 +1e 954 +4 812 +44 3018 +ce 78fc +a8 6e62 +77 1537 +b9 e549 +90 c980 +dd f173 +6 809c +c4 72b8 +45 9a99 +84 68b2 +40 98a0 +9e 69f4 +d 8a59 +90 4322 +aa 4464 +da d1cc +c0 d08a +f3 5587 +30 85a0 +a5 6699 +7e 1556 +75 17b9 +7 835 +57 9197 +e0 fea8 +1e 895e +29 ee3 +43 1025 +3 8a87 +10 8b28 +1d 2953 +b0 4522 +3 2a05 +e5 5439 +4 a010 +1d 21db +f7 5f9f +1 22a1 +93 49a7 +7c 1552 +e8 f6e2 +f1 5da9 +9c e958 +5a 1946 +65 b419 +e0 7422 +23 48d +fa 7564 +7e 3756 +9a 4b46 +c2 d2a6 +a5 64b3 +61 94a1 +bf 65f5 +11 2121 +eb 5ee5 +59 33e3 +73 3525 +f9 d549 +e8 fe4a +eb 7ced +d1 7bab +25 a691 +87 6815 +6 a09e +81 4021 +59 b36b +55 1991 +ba 47c6 +41 10a3 +5b 11e5 +ea dc4c +d0 db0a +4a 3ae6 +c8 5a62 +eb 5665 +60 b488 +a9 6469 +82 6286 +6e 1ed6 +1a abec +0 aaaa +bc 4d52 +a0 6c20 +7 95 +6e b4de +e9 5461 +1a 8b46 +25 a4b3 +3f a5f5 +94 49b2 +a6 6696 +62 9684 +34 279a +4c 1850 +6 294 +dd 515b +a6 4494 +80 e888 +d7 5315 +1b a96f +a4 ce18 +6c 1672 +22 ac86 +f1 75a1 +b9 4fe3 +d3 5125 +5 a91 +c2 7a26 +93 cb87 +a0 cc28 +ba 4564 +a0 4422 +4c 1250 +d6 5b34 +10 81a0 +68 946a +85 6299 +78 3548 +ee fcfe +a3 c625 +b7 479f +e 8d4 +3e ffc +24 eba +4e 1a5e +b aac5 +56 3bbe +70 3d00 +ef 765d +86 4896 +ba 4de6 +50 b300 +8d 4af9 +92 61a4 +13 1ad +9a 4b6c +80 4a2a +51 1921 +de fb5c +c4 fa1a +96 e314 +17 a9b7 +85 4899 +7e 155c +64 141a +95 49b1 +35 2799 +1 801 +ec d65a +66 3636 +af 6e55 +5c 3b5a +16 8b36 +3 a85 +83 4aad +6c 1658 +32 af8e +4c b0d0 +0 2a00 +18 296a +46 9214 +4b 18ed +42 b226 +bd cfd3 +d7 d115 +c6 fa16 +8b e26f +87 4895 +18 2b6a +37 2795 +d7 7995 +4 a298 +b3 65a7 +9a e1ec +80 e0aa +64 b698 +c6 781c +26 ae16 +f0 df80 +16 a1bc +47 1a1f +92 4906 +30 8782 +f6 5f3e +1c 217a +1b 29e5 +80 6200 +1 28a3 +47 1835 +8 28ea +18 23c2 +32 2504 +c1 52a1 +b2 4da6 +28 4c2 +48 b2c0 +23 c85 +f0 d5a0 +7 bd +31 2789 +1a 23ec +0 22aa +9 aa6b +9 849 +3a 8d46 +8f 60fd +31 8fa9 +6e 367e +c0 5802 +da 5944 +9d 6b59 +ce f056 +bf 45fd +a5 44bb +95 4191 +5a 19e4 +40 18a2 +d9 5341 +c2 d00e +dc d150 +8c 48d0 +9c c9d2 +88 626a +54 b932 +1e 976 +99 4969 +3c 27fa +8e 6854 +f2 7d2c +d8 7bea +2c a6d0 +96 41b6 +25 ac93 +3f add5 +6f 167f +ba 4566 +cb d26d +90 6922 +4a 104c +30 f0a +c2 5284 +8c e05a +8 2862 +4 22b0 +a9 646b +ad 6e73 +69 9e61 +40 9a80 +84 6a92 +9e 6bd4 +39 87e1 +6d 1651 +d2 712c +b8 6fea +28 8ce8 +21 2621 +8c 6a58 +ae 44fc +94 43ba +e7 76b5 +9b c3cf +b5 c511 +be ef54 +a4 ee12 +65 3c91 +ca 7844 +d2 51a6 +68 b6c0 +cb 50c7 +d8 5168 +33 fad +a1 44a1 +e5 7419 +1 2a1 +2d 479 +6 296 +f1 7d21 +2b a6c5 +a6 4496 +c6 f294 +4c 1852 +d2 7106 +74 9fb2 +1e bfc +4 aba +2f a677 +8 2e0 +ec 7458 +3b dcf +d1 7b81 +15 313 +2f 455 +67 1e1d +e8 5662 +de f9f6 +d 22f9 +91 63a9 +7d 1ff9 +8c 4852 +f0 5d2a +2a 86ce +d9 fbcb +f3 fd0d +3c 2752 +16 394 +f1 552b +f a55 +ac c6da +26 26b6 +a1 66a9 +49 3069 +22 2e86 +33 585 +0 8088 +47 1a3f +d0 51a8 +b aa65 +67 9c95 +b8 6d48 +e0 f4a8 +34 253a +a6 6436 +48 92e2 +62 9424 +53 9185 +97 6197 +bc 65fa +78 95e8 +9a 43e6 +3a 8d66 +9 869 +63 b407 +52 198c +56 11b4 +93 4925 +31 87a1 +9 41 +16 2994 +d a51 +31 581 +23 8cad +66 9e96 +be 6574 +a4 6432 +60 9420 +51 9181 +95 6193 +38 8d62 +61 b403 +7b b545 +50 1988 +69 9e49 +ad 6e5b +34 859a +2e ae56 +1f 3fd +5 2bb +99 e143 +23 ead +91 43a1 +88 4840 +26 86bc +91 c989 +cd 52f1 +35 a591 +82 6a04 +23 a4a5 +b8 456a +b 2a4d +42 12a4 +3d a5f3 +97 4915 +e1 5cab +f2 f726 +fb 5ded +35 8791 +db d9ef +3e 576 +20 4a2 +3a 5e4 +96 4196 +4d 32d3 +67 3415 +54 999a +a7 cc95 +8 aa68 +21 2e23 +3b 2f65 +43 1a25 +38 ad48 +74 1518 +82 e2a6 +6e 9ef6 +84 e010 +21 681 +96 4914 +e0 5caa +fa 5dec +34 8790 +c 8a7a +d0 79a2 +31 a529 +a2 4626 +71 9589 +b5 659b +20 8420 +e4 7e30 +4e 107e +ae ccfc +94 cbba +a7 6635 +8c 485a +c5 7a13 +df 7b55 +e7 54b7 +9c 4972 +3a 87ee +ae 6656 +6a 9644 +3c 275a +d4 d190 +a6 c41c +8c c2da +6 22b6 +81 62a9 +6d 1ef9 +9e 6bdc +84 6a9a +40 9a88 +dc f17a +73 1f85 +3d afd3 +57 b115 +7 2895 +ab 6e65 +58 3b6a +49 1061 +f6 dd1e +ef 7657 +d7 7915 +4 a218 +d5 f9bb +38 2542 +be 4756 +15 8b13 +2f 8c55 +b a065 +74 1710 +f aff +29 c41 +41 1021 +ee dcde +e7 7617 +3f a5df +a0 4420 +0 220 +ee 54fe +54 1138 +47 1097 +aa 4c64 +90 4b22 +6 23e +1 281 +0 880 +11 81a1 +e2 d40e +fc d550 +a8 6448 +57 9395 +bc cdd2 +a8 666a +e8 5642 +de f9d6 +d 22d9 +97 6bbd +3e a7d6 +1d 8979 +46 103e +4e 9874 +5e 39fe +dd 735b +13 325 +e1 5481 +2b ce7 +aa 4644 +83 c20d +4 88b0 +d1 5123 +0 28 +62 960c +a6 661e +c1 7803 +db 7945 +8 a248 +86 6814 +1f a1df +80 4020 +28 ac4a +f0 5daa +b9 47c1 +be 657c +60 9428 +53 9387 +a4 643a +1 8229 +60 1680 +c7 5a1f +4d 1a59 +7e 9f56 +b7 ef1d +68 1e6a +c7 f23f +48 b8e2 +94 4910 +22 8c0e +3c 8d50 +cc 585a +c5 5831 +a7 66bf +63 96ad +e2 d426 +b1 4f29 +92 4ba6 +22 404 +8 2c2 +39 2549 +b7 6d97 +73 9d85 +a7 46bd +c8 d2c8 +42 32a4 +5a 91c4 +9e 61d6 +40 9082 +5b b36f +57 1995 +1 2a01 +ec fe50 +3f a55f +44 1210 +ce 5af4 +65 1e11 +9 8a61 +84 c8b0 +6d 945b +d 2851 +15 1b3 +a3 cc87 +b0 cd28 +a9 6661 +f2 7784 +71 3d09 +34 598 +74 1712 +f0 f5a8 +79 3f49 +b a067 +fe 5ff6 +21 ae89 +df 73ff +f9 7541 +40 10a8 +e6 769e +2 826 +57 bbb7 +c5 5a99 +aa eec4 +14 8112 +21 c81 +43 182d +a8 4662 +51 1901 +b6 4736 +55 9b11 +1a a3ec +0 a2aa +bc 4552 +d3 79a7 +7 215 +40 30a8 +88 c84a +2 2826 +0 a88 +2b a645 +4 a2b0 +a6 4416 +47 b8b7 +c6 f214 +4d 98d1 +6 216 +16 a914 +31 ad2b +6e 1676 +3 2aaf +1d 2bf1 +e9 5669 +3a ad4c +20 ac0a +92 4306 +43 9aa5 +87 6ab7 +bd 4fd3 +d7 5115 +c6 7a16 +54 91b8 +98 61ca +46 b09e +c1 5021 +17 a1b5 +91 e301 +12 a9a4 +3 a225 +a7 4ebf +c1 5001 +e4 deb8 +17 a195 +37 71d +6f 96f7 +3e 275c +24 261a +3d 2559 +5a 934c +9e 635e +40 920a +b5 e719 +36 adbc +73 1707 +59 13cb +73 150d +ab 46cd +56 bbb6 +ef f655 +c4 5a98 +a2 448e +bc 45d0 +df 7bff +f9 7d41 +55 331b +6f 345d +0 8a28 +fd 7f5b +33 f25 +46 9216 +1 a09 +32 8f06 +ea 7ec4 +54 1112 +80 4002 +9a 4144 +8c c0da +6 20b6 +12 8b04 +48 3ac2 +62 3c04 +f2 7f0c +5c 115a +5b 19c5 +41 1883 +50 11a8 +d0 5122 +4d b27b +62 9426 +11 a9a1 +31 f29 +c a50 +92 4b0e +ac 4c50 +7 a95 +ba 47ce +ae 4656 +c7 dabd +2a 644 +ae 6e5c +c1 f8ab +db f9ed +3e 2574 +24 2432 +aa 4646 +45 1811 +13 a30d +d9 7969 +55 9b99 +0 2028 +c3 580d +f4 dfb0 +1a a1ec +0 a0aa +b9 c5cb +33 25a7 +91 4901 +7 1d +4a b266 +53 192d +b8 4762 +97 4bbf +b1 4d01 +d 2db +27 41d +c5 5a11 +13 a1a5 +33 72d +80 4800 +a4 4690 +d 8af3 +27 8c35 +68 36ca +c6 72bc +30 50a +ba 4dee +65 bcb1 +34 871a +13 2b85 +e0 f4a0 +a 244 +90 4302 +aa 4444 +44 b812 +5e b954 +2c a6f2 +35 db9 +9 22c3 +10 898a +23 2405 +ed 7c71 +d3 7b2f +27 a615 +4a ba64 +2a c66 +1b 23cf +35 2511 +c2 daa6 +a5 6cb3 +61 9ca1 +bf 6df5 +64 1412 +7e 1554 +2f 867f +b2 6f86 +d9 7169 +a a84e +55 9399 +36 2d96 +b1 6d89 +99 6b61 +f 227d +19 3e3 +33 525 +fd 755b +61 b409 +8a c8ce +83 6207 +6f 1e57 +88 42e8 +85 e099 +43 1087 +50 1128 +8 8248 +c1 5803 +db 5945 +91 41ab +89 6849 +44 1ab0 +ba ed46 +8 a42 +13 23af +2d 24f1 +a8 4c42 +c8 fa40 +a7 4495 +54 119a +7 295 +92 430e +c3 78a5 +ac 4450 +90 6120 +97 eb9f +62 1c2e +7c 1d70 +b1 ed0b +ee 5656 +88 40e0 +fe 7dd4 +e4 7c92 +9b 6b6f +5c 9358 +35 a59b +22 8626 +44 b898 +77 3d95 +3a 566 +15 311 +4c 18fa +94 e11a +d2 590e +70 978a +81 62a1 +6d 1ef1 +3e a5dc +24 a49a +a aa46 +d3 d987 +d9 7361 +4a 1a64 +5d 39d9 +76 bfb6 +e4 5e98 +dc 59da +61 bc09 +83 6a07 +8e 6a76 +4a 9a64 +5d b9d9 +1b 3cf +35 511 +a5 4cb3 +bf 4df5 +52 190e +5e 9b54 +44 9a12 +f3 fda5 +69 b4c1 +3 a05 +66 1494 +50 190a +d9 f9e9 +3c 2570 +22 242e +77 9f9f +48 9860 +8c 6872 +2a a6ee +1e 35c +4 21a +b3 65ad +a8 4c40 +8e 4afe +28 6e0 +2 2a84 +1d 235b +a6 e494 +71 1523 +92 49a6 +8 c2 +28 aec0 +a5 441b +bf 455d +8 868 +1a 296e +c2 da06 +80 c0a0 +58 3940 +27 4b7 +66 1694 +b9 65e1 +ae 4c74 +94 4b32 +9b e36f +97 4995 +c3 78a7 +a a2ec +ac 4452 +4d b8f3 +cc f250 +6 ab6 +64 3412 +7e 3554 +8f 42df +10 982 +81 4aa9 +28 86c2 +8a 4846 +60 1402 +7a 1544 +f4 571a +d4 f192 +89 4841 +27 86bd +ee 7676 +a 2046 +32 584 +66 163c +8f c0df +f0 5f20 +46 b0b4 +1f a977 +8d 4859 +be cd56 +b cd +c8 7062 +1 80a9 +f5 fd39 +3e 277e +c3 d00d +a9 cecb +23 2ea7 +7a b564 +60 b422 +83 482f +9d 4971 +21 86ab +3b 87ed +3d 2759 +4f 1a5f +30 2d0a +3c 255a +77 b537 +e5 5419 +65 b413 +7f b555 +54 1998 +d2 fb24 +3c 8d72 +30 250a +f5 d739 +ba 6dee +3c 8f58 +f0 7520 +75 9d99 +4e 3a74 +75 3f93 +8f 40d5 +d2 7b8c +3c dda +1b 2947 +50 b3a0 +f2 5506 +90 c120 +17 219d +5c 995a +95 cb13 +af cc55 +de 51d6 +49 b2c3 +63 b405 +a6 eebc +10 810a +aa 46e4 +9a 61c4 +80 6082 +6c 1cd2 +86 4036 +b a265 +68 9e62 +48 b262 +51 1929 +6f 3eff +89 4041 +28 8e62 +a0 eea8 +17 8197 +7 a03d +e1 de01 +bb 65e7 +81 6a81 +1d 29fb +9c 6358 +f0 5520 +98 e3e2 +b2 e524 +a5 6499 +b 2065 +5a 33c4 +40 3282 +9e 6956 +40 9802 +5a 9944 +8e 427c +28 24ca +ed d6f9 +b2 6dae +34 8f18 +f2 ff04 +5c 9152 +9e e154 +84 e012 +89 48e1 +58 134a +f6 7d1e +12 ba6 +ab 4645 +b7 e71f +38 adc2 +28 a460 +74 3f12 +8e 4054 +1b 8947 +91 4181 +a7 6c97 +63 9c85 +b4 6d38 +10 2122 +ea 5ee6 +46 b2be +60 b400 +cd 5a5b +c0 f220 +2a 846e +44 9230 +88 6242 +e3 fc87 +f0 fd28 +c 8a58 +c5 d239 +8a 68ee +25 e13 +3f f55 +23 ca7 +a2 4604 +fa 5566 +52 1184 +9 61 +b6 cd1e +6b 9645 +af 6657 +20 c80 +f4 559a +2f ae57 +6 2bc +8f e0d7 +9c e178 +5a 1166 +16 8914 +c8 fae2 +e2 fc24 +58 b340 +55 9313 +6f 9455 +30 a50a +95 4b39 +ad 4ed3 +c7 5015 +74 1d1a +44 90b8 +88 60ca +82 4206 +1 8281 +9d 695b +59 9949 +ba 67c6 +76 97b4 +e5 dc31 +cb daef +2e 676 +a9 4669 +db fb6f +4 8a98 +37 f95 +6a 3c6c +50 3b2a +7e 95d4 +64 9492 +a1 6ea9 +4c 9af2 +66 9c34 +aa 6c46 +a a864 +43 92ad +a1 6401 +87 62bf +73 1f0f +ff d557 +90 6988 +3d fd3 +57 1115 +46 3a16 +24 8c18 +b2 e526 +81 6029 +4d 12db +67 141d +92 c90c +8b 6245 +7a b7ec +60 b6aa +38 2f4a +5 819 +d9 fb69 +36 8d16 +1d 8359 +d6 5914 +ca 5ae6 +2b 866d +74 9790 +96 43be +b0 4500 +90 e1a8 +9e 4bf6 +75 3731 +7c 9df8 +2d ed3 +47 1015 +cd d2f9 +92 69ae +14 8b18 +12 a3ac +b4 4512 +2 206 +a 2a46 +88 4848 +1a a966 +4f 3277 +56 993e +8a 686c +e1 7ca3 +fb 7de5 +28 a6e8 +ea f66e +2d 86d9 +e6 5c94 +83 4a2f +9d 4b71 +65 9419 +87 4217 +ec 7c70 +d2 7b2e +26 a614 +15 b99 +e9 fee9 +46 9096 +be 4ffe +d8 5140 +0 8a88 +9c e17a +33 f85 +3e 257c +24 243a +55 3333 +6f 3475 +5c 99fa +a2 6626 +74 159a +f4 7d18 +10 ba0 +b9 cf61 +26 2e9c +e 54 +36 87b4 +bf e577 +94 49ba +49 12e1 +47 ba95 +dc 5b5a +4d 1259 +d7 5b3d +13 ab8f +2d acd1 +7e 3ffe +98 4140 +24 ae10 +8e 48f6 +f0 7d02 +97 69b5 +66 341e +49 1269 +7a 9766 +29 ace1 +6e 1476 +c2 5286 +e9 5469 +25 a4bb +3f a5fd +1a 8b4e +8e e8de +43 b205 +c0 d880 +23 407 +23 8c0f +3d 8d51 +99 6b41 +2b 84ef +c1 f2a1 +bb 45e5 +a1 44a3 +f 225d +7a 35cc +60 348a +b3 6785 +80 e288 +6c 9ed8 +e6 569e +65 1639 +45 b0b1 +b0 4520 +4e b2de +c9 5261 +2a 8e64 +3d add9 +e 2a7e +d 2d3 +27 415 +11 b21 +75 151b +ff 5dff +74 15b0 +36 ad9e +b1 4d21 +d 2fb +27 43d +f 8f7 +8e 4254 +72 3f0e +8c 4050 +42 9226 +c2 f8ac +25 2433 +3f 2575 +e6 de3c +c a078 +55 b19b +dd f35b +13 8325 +5e b9fe +52 b986 +9 a863 +72 1f0e +de fb76 +7 8a9f +83 688d +1c a97a +b2 6526 +c1 580b +db 594d +bd 67db +79 97c9 +a0 6620 +4 2810 +85 e8bb +9f e9fd +c2 5206 +8c 6058 +56 b314 +45 1899 +aa 46ce +a8 e6c8 +66 16b6 +30 2508 +a0 6caa +ba 6dec +e1 56a9 +99 69c1 +88 c060 +f 20dd +9a 4166 +54 1312 +6e 1454 +22 ea6 +ed de79 +6 a014 +57 33bf +71 3501 +4c 1a52 +a0 ec88 +f7 5715 +40 1a20 +74 97b8 +b8 67ca +3b ad6f +20 2600 +14 132 +aa 6ee4 +d3 5185 +a0 cc88 +ba 45c4 +a0 4482 +9 88e1 +d0 5920 +a 82c4 +9 824b +c2 5806 +0 8288 +d3 5985 +1e 29fc +4 28ba +9d 6359 +1 82a9 +a 8046 +25 8631 +70 bd0a +a9 eec3 +c3 f005 +6d 3e59 +75 17bb +9a 43ce +b4 4510 +2 204 +32 2526 +b8 c54a +8f c2fd +10 89a0 +68 9c6a +85 6a99 +1a 3ce +d1 d989 +34 510 +70 3f88 +2 a0a6 +e5 5639 +5c 9b52 +3b 745 +21 603 +ab 4ee7 +22 ae0e +3c af50 +62 9e26 +45 3033 +5f 3175 +70 3f02 +8a 4044 +ba 6564 +a0 6422 +b7 ed3f +41 1aa9 +8c e858 +4a 1846 +72 9fa6 +c1 d88b +db d9cd +24 412 +3e 554 +c8 d84a +42 3826 +80 40a8 +80 40a0 +f1 5729 +d1 f1a1 +b9 cf63 +26 2e9e +be 4f5c +a4 4e1a +48 1268 +a7 e63d +28 ace0 +25 2c19 +56 b116 +9a 4346 +95 6131 +d0 5328 +c3 5287 +8d 60d9 +19 2961 +75 9d19 +97 4b17 +5 a8b9 +42 1204 +3d a553 +4b 32c5 +52 998c +96 699e +2 a224 +f0 f502 +b4 e590 +c5 5039 +4e 9256 +9 a49 +3a 8f46 +f2 7f04 +5c 1152 +48 9840 +8c 6852 +f0 7d2a +2a a6ce +4f 9855 +10 a90a +1c 8b52 +6c 1ef2 +4d 98d9 +e3 5485 +b0 4782 +30 5aa +28 2c48 +a6 64b4 +cb 5245 +78 1f4a +d7 f31f +58 b9c2 +ca 5a66 +13 9a5 +dd 79db +ce 7af6 +2f a67d +99 4163 +3d af79 +a2 c40e +d3 f9a5 +bc c550 +57 1937 +99 e949 +5f 9355 +45 9213 +1e a956 +66 963e +bb e567 +90 49aa +b2 470e +e3 7ca5 +7a bf6c +60 be2a +93 4327 +a6 66b6 +62 96a4 +20 e02 +3a f44 +45 1219 +21 2c89 +76 9716 +6f 167d +25 ac91 +cf 5afd +76 3fbe +90 4100 +31 252b +3d 8f79 +4f 98d7 +5c 9978 +83 6a85 +21 429 +42 180e +5c 1950 +91 c1a9 +9a ebec +80 eaaa +b3 6fa7 +8 8a40 +e0 d480 +f2 5f26 +18 2162 +30 8702 +89 4ae9 +11 2981 +74 1512 +fe 5df6 +d a59 +3e 8f56 +21 84a9 +e3 d40d +c9 d2cb +43 32a7 +d 825b +c6 5816 +7a 15c4 +60 1482 +80 c280 +57 399f +d0 d922 +46 903e +de d156 +99 c149 +13 2125 +d7 7935 +4 a238 +a4 6498 +42 b886 +68 1660 +c a87a +3f 2d77 +a2 6426 +5d 13d3 +77 1515 +66 3e16 +44 9018 +38 25ca +8d e8d3 +58 1962 +37 8f9f +73 b527 +e1 5409 +37 a59d +66 1416 +15 2991 +7 88b7 +86 c214 +e2 f4a4 +9f 49f7 +35 af11 +14 110 +84 48b2 +9e 49f4 +e1 f4a1 +51 b901 +85 6239 +14 8190 +ce f2d6 +d7 599d +46 383e +7b b567 +e9 5449 +50 19aa +72 170e +a3 4ca5 +19 3c1 +49 1841 +ae 4676 +4d 9a51 +21 aca9 +df dbfd +c5 dabb +28 642 +b2 4f26 +53 130f +6d 1451 +1a a164 +0 a022 +8f 42ff +10 9a2 +a9 4441 +7f 3fff +99 4141 +44 9a30 +88 6a42 +4b 1acf +65 1c11 +2f 8efd +8d 6051 +0 a802 +1a a944 +a6 663e +62 962c +19 141 +8 2a42 +18 a962 +84 6a98 +63 b42f +7d b571 +dd 73db +f7 751d +13 3a5 +b8 4560 +46 10be +10 122 +30 af20 +7 8a15 +3 805 +57 319d +d0 d120 +1 22a9 +d2 f9a6 +48 b0c2 +28 ae60 +8 62 +14 910 +fc 5550 +e2 540e +40 b2a8 +18 2b48 +f9 d7c9 +73 37a5 +2 804 +6 8a14 +91 4189 +9a 6bcc +80 6a8a +79 b563 +a a8c4 +2a e4c +d 8f3 +8c 4250 +8d e2d3 +a7 e415 +58 1362 +be 457c +a4 443a +26 8ebc +84 6010 +69 bec3 +83 c005 +c6 fabc +29 2643 +30 8d0a +80 60a0 +5a 13ec +40 12aa +cc 5872 +6 8216 +9e 49f6 +34 af10 +d8 d9ea +d1 7323 +eb 7465 +3 a0a5 +98 416a +63 1485 +30 782 +ae 6e7c +5 2011 +5e 335e +8f 68f5 +18 a362 +30 2f88 +18 140 +13 105 +2 2a06 +50 b1a8 +a0 ec08 +5e 1bf6 +cd d25b +47 3237 +4e 98fe +78 3fc2 +92 4104 +7a 97ee +a 82ee +24 8430 +a6 6694 +0 200 +8a 4ae4 +ee 54de +2c a4fa +18 ab62 +66 3ebe +80 4000 +28 84c0 +b2 cda4 +86 6294 +88 eac8 +46 1ab6 +10 2908 +cf 52df +50 1982 +e9 de6b +2 a006 +ac 4e72 +51 1381 +a0 4e00 +33 a7a5 +b 2045 +15 111 +1e 2b54 +4 2a12 +85 48b3 +6e 145e +9f 49f5 +3 5 +2 a826 +89 6ae9 +44 1012 +5e 1154 +2b a665 +0 aa8 +b7 479d +65 3619 +fe d556 +2f 267d +b9 6f61 +17 115 +6 2a16 +8c e8da +41 b201 +3b 545 +21 403 +95 e391 +60 1420 +8 804a +d8 d9c0 +3b 547 +41 b203 +5b b345 +f 82ff +29 8441 +99 cbe3 +b3 cd25 +8e c8dc +87 6215 +4e 305c +34 2f1a +b 2cf +25 411 +5c 19fa +f4 5d9a +6 abc +8f e8d7 +9c e978 +5a 1966 +95 e31b +af e45d +16 a9be +d4 791a +a 8e4 +89 4241 +84 6032 +9e 6174 +40 9020 +a5 e61b +bf e75d +26 acbe +6e 9676 +19 2be9 +7c 3fd2 +96 4114 +53 b927 +c1 5809 +17 a99d +21 609 +f2 dd06 +46 1816 +6e 9656 +19 2bc9 +4a b0c6 +9c 4952 +3a 87ce +82 480c +d8 51e0 +a 846 +1 aa9 +32 8fa6 +e 8a56 +19 a3c3 +33 a505 +2a ece +44 1010 +51 1b21 +53 1925 +68 b46a +e aa56 +72 1584 +29 461 +1e a176 +8c 4058 +64 1610 +14 2130 +ee 5ef4 +19 894b +c5 f091 +42 302e +5c 3170 +a2 4c86 +c5 f831 +e 2276 +35 859b +83 c88f +9d c9d1 +89 6269 +55 b931 +8f e8d5 +40 1822 +5a 1964 +f4 5d98 +f 857 +5 211 +8f 4af5 +8b 42cf +a5 4411 +16 839e +dc 59fa +c4 52b8 +1a 2964 +0 2822 +9e e15c +84 e01a +ff 5577 +49 1a6b +d1 f98b +34 2512 +ca d844 +c3 da8d +26 614 +8c e272 +95 4939 +15 1b1 +3a fce +54 1110 +c4 58b2 +25 8439 +de 59f4 +1c a9d8 +9 8a63 +c7 58b5 +fa 7f66 +23 e8f +3d fd1 +b2 6584 +11 2b21 +75 351b +b 2ac5 +b8 45e2 +34 afb0 +d6 5116 +e3 d485 +ad 4ef3 +c7 5035 +88 60ea +74 1d3a +19 8969 +c8 5062 +e8 fe60 +23 ea7 +41 1a23 +5b 1b65 +67 3ebf +81 4001 +29 84c1 +b3 cda5 +10 908 +6a 9466 +87 6295 +4e 30dc +34 2f9a +4 8a30 +8d 6073 +49 9061 +f3 7d2d +d9 7beb +2d a6d1 +8f 6855 +97 41b7 +e a0de +89 4061 +d2 5184 +3a d66 +d4 519a +9c c9d0 +82 c88e +88 6268 +54 b930 +10 812a +a0 64a8 +e 854 +36 8fb4 +0 22a8 +c5 d831 +e 276 +81 6aa9 +31 d89 +28 a6c2 +3c fd2 +56 1114 +8a 4844 +28 86c0 +82 480e +9c 4950 +52 9b26 +f a8d7 +1c a978 +18 2962 +9c e15a +da 594e +78 97ca +ed 7c5b +9 ae3 +23 c25 +c a2d2 +d2 792e +26 a414 +15 999 +b2 65a4 +81 4a81 +1d 9fb +2e a476 +9c 4358 +7 2015 +55 33b3 +6f 34f5 +f5 d519 +71 1d21 +fe ff5c +e4 fe1a +b 865 +cf 72dd +50 3980 +1a 29ce +55 9319 +5d 9179 +36 8f96 +26 a49e +a1 4421 +52 9186 +9 8063 +16 2914 +87 c09f +e8 5ee0 +7e 3d5c +64 3c1a +42 1a06 +84 ea18 +b7 6f15 +8 68 +51 118b +b6 ed1e +40 1a88 +6b b645 +49 b8e1 +18 834a +9 a8cb +46 1216 +f1 7d83 +38 a7c8 +c aa58 +2e 84fc +14 83ba +25 2e13 +3f 2f55 +a9 cccb +23 2ca7 +a2 6604 +98 c14a +12 2126 +4 8a32 +1e 8b74 +3c 2d5a +4d 1ad3 +67 1c15 +28 2cca +b0 4daa +5a 13ce +74 1510 +4d 1a5b +38 2d6a +66 9614 +9e 4156 +95 43b9 +96 493e +4b 1265 +77 b7b7 +e5 5699 +42 30ae +5c 31f0 +a2 4ea4 +13 8b0f +2d 8c51 +d2 71a4 +f2 5784 +13 a905 +d 851 +93 6105 +35 8fb1 +4e 92fe +68 9440 +ac 6452 +54 b91a +9 8241 +b5 ed39 +73 1d27 +47 1215 +56 9316 +1 2889 +b4 4712 +46 b216 +4f 18dd +64 94b8 +a8 64ca +a3 4c0f +1 aaa9 +bd 4d51 +20 8688 +f3 5d85 +2 84 +dc 7bd2 +f6 7d14 +3d a759 +2 a2a6 +bc ed7a +53 1b85 +de 5bfe +f8 5d40 +da 51c4 +c0 5082 +4a 90ce +e0 fe80 +36 79e +3a 8d64 +20 8c22 +5b 9947 +10 922 +8f 427f +de dbfe +f8 dd40 +e3 5caf +fd 5df1 +37 8795 +11 89a9 +20 a420 +4b 18e7 +8d e8f9 +ca 5244 +b9 65e9 +ae 4c7c +94 4b3a +95 e939 +53 1927 +12 832e +2c 8470 +75 9593 +62 1e04 +34 5b8 +5d b153 +91 c90b +b 28e7 +8a 6244 +3e 8756 +c3 5a8d +12 8106 +13 38d +ea 7464 +d0 7322 +51 31a9 +99 c94b +13 2927 +68 1640 +18 2160 +f2 5f24 +ee 7e54 +f6 57b6 +5a bb64 +40 ba22 +95 6133 +51 9121 +40 1800 +43 b0a5 +d8 516a +40 1002 +5a 1144 +ee 545c +d4 531a +b2 652e +79 3d61 +4 2818 +15 911 +7a 3746 +c5 dab9 +28 640 +b2 4f24 +d2 7386 +f9 7569 +aa 64ee +9c 49fa +51 1321 +99 e3e3 +b3 e525 +64 1e10 +d 28f3 +8c 6250 +8c 405a +6a 1e46 +ac ee58 +33 85ad +76 9796 +45 1299 +2d 8673 +c2 5084 +65 be91 +61 3609 +17 a9b5 +3a 2fce +54 3110 +d2 db84 +28 66a +e1 542b +fb 556d +db d3cf +f5 d511 +fe ff54 +e4 fe12 +a9 e66b +a5 4c91 +dc 5958 +0 282a +1a 296c +aa cccc +90 cb8a +a3 6605 +8 226a +1c 89d2 +d aa59 +c0 7802 +da 7944 +8f 4a5d +9 2849 +3a ad46 +11 1ab +c8 7a60 +a 8844 +87 429f +b7 6d95 +11 901 +aa eecc +14 811a +28 24e0 +9e 61d4 +40 9080 +84 6092 +15 8b11 +7c 3552 +93 69a7 +4 12 +1e 154 +da f96e +1d 89d9 +4e 98d4 +13 385 +9e 43fe +b8 4540 +e0 5e28 +13 2105 +cd f25b +3 8225 +4e b8fe +87 60b7 +43 90a5 +21 8481 +a2 c6a6 +38 25c0 +e1 7c09 +66 3c16 +86 ea14 +36 a5b6 +a4 4498 +41 3a09 +db 736f +4 298 +d1 5383 +eb 54c5 +55 9919 +2b 2ecf +45 3011 +9e 435e +cf 78f5 +ea 5666 +68 364a +99 6be1 +33 5a5 +fd 75db +27 2695 +52 130e +83 48a5 +6c 1450 +c 78 +55 119b +dd 5173 +1f 8bfd +5 8abb +8f c25d +10 8900 +44 3238 +12 2984 +1 2a81 +1c 2358 +a5 e491 +70 1520 +1e a3f4 +4 a2b2 +db 7167 +4 90 +c9 f049 +c7 5a15 +99 41c9 +a2 6c0c +44 9ab8 +88 6aca +6f 9e55 +83 e205 +4a b04c +30 af0a +d8 5142 +1a 8bcc +0 8a8a +0 2 +1a 144 +9 8eb +88 4248 +1a a366 +2c 2cfa +6e 9654 +6d 1659 +1d 2179 +f7 5f3d +33 af8f +4d b0d1 +ae 4674 +de d3dc +c4 d29a +f7 5797 +ca 7a66 +d ad1 +db 71e7 +a0 6ca8 +41 baa3 +5b bbe5 +2a 864e +9a 6166 +4f 1a55 +2a 2c4c +10 2b0a +89 4069 +62 3e86 +f aa57 +73 1585 +9e 61dc +84 609a +40 9088 +f a277 +cc 7058 +f 55 +37 87b5 +ea 56c6 +b4 6518 +a7 ce15 +79 b5c9 +bc 6d58 +90 cb20 +17 2b9d +18 2142 +f2 5f06 +85 6091 +48 b06a +7b 3567 +11 181 +8f e2ff +a9 e441 +10 a9a2 +ce 78fe +83 4225 +7e 9fd4 +64 9e92 +1a 8146 +11 83a9 +6a 3466 +8a e264 +5 2031 +57 bbbf +71 bd01 +a5 6639 +34 8590 +e4 fe98 +fa fdc4 +e0 fc82 +af c67f +30 8d22 +d 59 +c3 588d +3e 8556 +35 87b9 +8e e276 +97 493d +dd fb73 +6 8a9c +8 aa62 +19 8161 +8 840 +1a 81c6 +c 8a50 +1a 2144 +0 2002 +c5 d231 +8a 68e6 +5 2a99 +e1 7423 +fb 7565 +bb efc7 +c8 f068 +77 9795 +65 1611 +15 2131 +ef 5ef5 +ca 70ec +b0 6faa +9 22eb +23 242d +aa 646e +ec 5cfa +26 869e +f 7d +39 2749 +4c 1a50 +99 43c1 +b 28c7 +18 2968 +81 e889 +fa 55c6 +ec 5e50 +29 8643 +69 3449 +8c 68f8 +b 22ef +25 2431 +41 1083 +5b 11c5 +21 2601 +28 8cc8 +4a 3ac6 +c8 5a42 +1e abd6 +d3 73af +ed 74f1 +1 a0a9 +88 c86a +bb 4d67 +51 1981 +ae 6e54 +b6 47b6 +50 1180 +32 a5a4 +72 9fae +d0 7102 +c9 f269 +26 8416 +93 c9a7 +7c 9552 +c0 5022 +da 5164 +e9 76c3 +f0 dd8a +2 8aac +d0 718a +17 2915 +e9 5ee1 +95 e139 +53 1127 +11 2181 +97 4395 +64 be98 +5d 9959 +13 8927 +d8 5162 +1a 8bec +0 8aaa +33 fa7 +52 1904 +ef 5ef7 +15 2133 +7 8ab7 +0 8828 +19 be3 +fd 7d5b +33 d25 +38 562 +58 b360 +71 1781 +84 6890 +68 36c8 +30 508 +a0 4caa +ba 4dec +dc 53d2 +f6 5514 +25 419 +9a c1c4 +80 c082 +82 62a4 +6e 1ef4 +17 a915 +ca 5a6e +3a ad44 +20 ac02 +10 8100 +46 1a94 +3a 7ec +20 6aa +0 aa08 +33 2f05 +6a 1e44 +3c 5f8 +8f ea7f +f3 55ad +a5 6eb3 +61 9ea1 +bf 6ff5 +49 3ac3 +63 3c05 +10 90a +26 a6b6 +26 494 +40 1802 +5a 1944 +4f 1255 +62 3686 +36 253e +52 11ac +d2 5126 +68 b640 +d9 f949 +5b 19ef +da 534c +c0 520a +23 e0d +6 8b4 +85 4211 +5c 137a +12 a98e +9a 6964 +80 6822 +ac 6e50 +b4 47b2 +9c 495a +7 a895 +27 e1d +74 3592 +c8 f8e8 +2b 246f +c1 5223 +db 5365 +a8 ce68 +50 b188 +5e 1bd6 +12 a906 +95 c9b3 +7e 955e +c 852 +97 6995 +0 800 +df 5155 +23 aead +c5 5013 +84 e290 +69 1461 +ba 47ec +a0 46aa +8a 42ce +a4 4410 +4 210 +8e 4af4 +2 80e +1c 950 +e0 5480 +b6 e516 +40 1280 +3b a5cf +ff 557d +e5 543b +98 43c2 +b2 4504 +b a8c5 +2b e4d +85 4291 +a6 ecbc +e3 5607 +9a 69e4 +80 68a2 +2 8a0c +dd 5959 +4a 12ce +64 1410 +ba cd46 +ee 767e +91 c901 +7 801d +c5 7239 +d6 519c +54 9190 +b 806d +c5 52b3 +df 53f5 +26 6b6 +be 4574 +a4 4432 +29 a661 +72 b784 +e3 5e05 +9 2041 +93 6925 +31 a7a1 +9 2ac1 +32 afac +d4 5112 +93 e38f +ad e4d1 +78 1560 +e1 d481 +8a c8ec +83 6225 +6f 1e75 +4a 306c +30 2f2a +5f bb77 +cd 5a59 +9 249 +da d946 +b 2a6d +54 3b90 +8b 6ac5 +9f 415d +85 401b +63 1e07 +a5 ee19 +e0 5422 +fa 5564 +80 6000 +22 8eac +cb daed +2e 674 +5e 93dc +44 929a +77 1797 +1e 2154 +4 2012 +f0 758a +2 22ac +1d 2bd3 +37 2d15 +4 a818 +35 711 +6c 1cfa +a5 4eb3 +bf 4ff5 +6e 3cde +23 605 +74 1d30 +5a 1bee +65 34b1 +c0 52a0 +a6 4616 +2a 24c4 +10 2382 +48 9a40 +8c 6a52 +71 3f03 +8b 4045 +ce 7afc +38 d4a +6 2a14 +c9 5263 +dc 71d8 +48 9ac0 +8c 6ad2 +a6 6c14 +7a 3fce +94 4110 +48 b0e2 +d7 7bbf +f1 7d01 +4d 32db +67 341d +b4 4590 +51 332b +6b 346d +30 270a +5c 997a +f2 5526 +7 a8bd +58 9342 +e2 dc26 +55 9911 +c5 d811 +e 256 +b1 65a9 +a6 4c3c +8c 4afa +e2 74a4 +11 23a9 +1a 2146 +e7 74bd +9a 41c6 +6 283c +8c 4a50 +88 e2c2 +a2 e404 +91 4989 +16 996 +11 8929 +43 18a5 +44 103a +5e 117c +ad 64f3 +69 94e1 +85 e899 +43 1887 +50 1928 +57 9315 +32 a50c +18 a3ca +98 e148 +56 1136 +e1 7421 +12 ab06 +6f b655 +44 1a98 +2c 8e72 +1a 34c +0 20a +88 6862 +d3 53ad +36 594 +11 2b01 +3a afec +20 aeaa +dc 5152 +1e 8bdc +4 8a9a +44 1038 +37 f97 +76 1516 +a2 4c26 +cf d8fd +18 342 +e0 54a8 +a2 ce06 +74 b5ba +59 1969 +d 22fb +27 243d +b1 6d21 +66 3c14 +4c 3ad2 +ca 5a4e +d5 73bb +ef 74fd +3 aad +f1 5d8b +b5 e519 +73 1507 +1c 295a +5e b1fc +44 b0ba +fd d5db +77 35b7 +8e 6856 +4a 9844 +c7 529f +5 8011 +49 b049 +35 a713 +fb 7d6f +24 c98 +f0 5d20 +2a 86c4 +27 495 +dd 79f3 +24 a438 +17 a397 +5e 19d4 +44 1892 +1c 29fa +78 3f48 +a a066 +be 6fdc +60 9e88 +a4 6e9a +b5 4599 +a 2a66 +1 23 +1b 165 +2a 26c4 +31 8d8b +45 1093 +5f 11d5 +25 2611 +2c 8cd8 +4e 3ad6 +c0 5a80 +45 9019 +ad 6479 +86 6296 +42 9284 +99 6949 +c1 f0a9 +cf 5af7 +98 4162 +e7 7c37 +2e a67c +34 27ba +12 904 +e4 7c12 +fe 7d54 +6 89e +cf f27f +50 b922 +27 615 +78 1d40 +5e 1bfe +8e 4854 +69 be41 +1a 836c +0 822a +d3 5927 +49 1043 +71 97a3 +ce d854 +11 2b89 +66 9616 +15 ab91 +6c 96f8 +b0 670a +74 3792 +6e be56 +5f 13fd +45 12bb +ee 5ef6 +14 2132 +45 9899 +4e 38fe +cd 725b +3 225 +f1 5503 +ec f458 +8 82e0 +7a 374c +91 6ba1 +60 360a +e1 54ab +fb 55ed +c2 fa84 +2c 8cd2 +95 e313 +af e455 +84 4898 +16 a9b6 +7a b76c +60 b62a +64 9c90 +60 1408 +fb f767 +d0 5baa +24 8690 +ea 5cec +ac e6d2 +b5 4d99 +f4 5712 +c0 50a8 +82 4826 +e9 fce1 +b8 c74a +32 2726 +4f b077 +bd 4f59 +23 ae05 +3 7 +69 16c1 +b4 4d9a +1a 966 +45 3219 +32 8fac +90 6100 +1 8221 +4c b8fa +28 2460 +26 8ebe +40 9000 +9e 6154 +84 6012 +17 2b15 +77 bfb7 +e5 5e99 +db 734d +c1 720b +42 38ae +5c 39f0 +ca f2c4 +34 8512 +9e e374 +84 e232 +8d 48f9 +4b 1a65 +46 12be +60 1400 +71 1589 +d aa5b +8d 6871 +2b a6ed +36 2fbe +50 3100 +37 a537 +a5 4419 +19 1c1 +c9 d063 +36 2f9e +ad 667b +69 9669 +90 69aa +2b ecf +45 1011 +cf 58f5 +27 a4b7 +95 4399 +17 b95 +d3 5305 +a0 ce08 +61 1c21 +89 6061 +d2 7184 +4f 9a55 +2a ac4c +10 ab0a +18 b42 +3a 8544 +20 8402 +1e 2954 +4 2812 +f2 df84 +18 a1c0 +38 748 +e0 5c22 +fa 5d64 +83 c8ad +6c 9458 +8e 4256 +b9 6561 +c6 7a14 +98 61c8 +5c 31f2 +d9 7be1 +a8 464a +74 9d12 +db 594f +79 97cb +27 695 +97 6937 +53 9925 +d0 5380 +f0 552a +2f 4d5 +15 393 +26 ae96 +4d b079 +41 1a21 +46 98bc +8a 68ce +c5 d219 +22 ac2e +3c ad70 +42 1284 +3d a5d3 +66 bc9e +e1 5c21 +37 adb5 +c 250 +96 4b34 +19 961 +17 2135 +9d c159 +62 9626 +1f a3d7 +2c a478 +b3 4d07 +11 aba1 +75 b59b +d3 79a5 +a2 440e +bc 4550 +0 a2a8 +b9 c7c9 +33 27a5 +d3 5325 +a0 ce28 +28 a448 +80 4a00 +a8 446a +13 a3a5 +b8 e560 +12 238e +2c 24d0 +b6 6db4 +41 1801 +a6 4636 +72 3f2e +8c 4070 +d5 5193 +45 9a11 +1a 894e +2c a650 +d8 7b6a +c4 7812 +de 7954 +e5 fc31 +cb faef +2e 2676 +a3 cc8f +bd cdd1 +a9 6669 +2a accc +10 ab8a +67 1617 +1c 8952 +22 a6a4 +5d 9359 +8a 6a46 +46 9a34 +18 81e8 +da d14c +c0 d00a +3a 2fe6 +f6 7dbe +ab 46e5 +fa f5ec +e0 f4aa +8c 4850 +e3 5c87 +f0 5d28 +2a 86cc +42 9a26 +c a878 +25 2c33 +3f 2d75 +55 b99b +df f15f +69 1ec9 +61 b423 +7b b565 +50 19a8 +69 9e69 +ad 6e7b +12 106 +32 af04 +41 3a89 +dc 5972 +16 8316 +4a ba66 +42 9224 +86 6236 +57 b397 +64 b438 +8e e0dc +59 116b +ec 74da +22 4a4 +5 2a19 +36 af16 +b7 459d +11 a323 +2b a465 +0 8a8 +c8 5268 +b9 4561 +3e a5de +66 3e94 +89 6843 +45 9831 +aa c666 +c2 528c +16 a3be +30 a500 +ca 5264 +fa dfcc +e0 de8a +dd 71d9 +da 73cc +c0 728a +7 2a15 +b4 4532 +2f 655 +66 1c3e +4a baec +ec 5c52 +1c 897a +ec 76d0 +37 ad95 +67 163f +1f 2957 +b2 4526 +c8 d868 +e1 5c23 +28 8668 +fb 5d65 +c9 d8e1 +12 326 +20 8c88 +53 1185 +69 3c69 +42 3a86 +93 41af +e2 7c84 +29 a6c9 +8b 684d +a a2c4 +d0 7920 +a a46 +b5 c519 +15 23b3 +2f 24f5 +d6 d194 +2b 86e7 +15 319 +cc 52d2 +e6 5414 +5a 91e6 +1d 3db +37 51d +26 2e1e +3 85 +5a b964 +40 b822 +bd 6d59 +da db4c +c0 da0a +c5 5239 +63 b485 +f8 554a +a 26c +2d cd3 +c3 7a85 +61 1429 +17 2917 +51 b9a9 +f8 5542 +a 264 +3a 8fcc +20 8e8a +d7 d9bd +20 402 +3a 544 +68 3ec2 +82 4004 +93 49af +7c 155a +81 ca81 +18 b60 +2e ac54 +14 ab12 +c9 72c9 +91 4109 +9a 6b4c +80 6a0a +36 af14 +b0 4f22 +ca 5064 +fa ddcc +f3 7705 +e0 dc8a +da 71cc +c0 708a +e5 5e93 +b 20cf +ff 5fd5 +7 2815 +d 28fb +8c 6258 +8e 6af6 +4a 9ae4 +5a 19c4 +40 1882 +1 88a9 +3e 2f7e +5e 9bd4 +44 9a92 +6 823c +27 261f +c4 fa98 +a 866 +19 2bc1 +e 254 +18 8960 +8d 6a59 +ae 4cfe +63 1625 +eb 5cc7 +32 870c +f8 5d68 +53 1bad +c1 50a1 +8a 4264 +9d 61d9 +ba cfcc +a0 ce8a +e0 5428 +d3 5387 +d0 5322 +ea 5464 +fa 75cc +e0 748a +d 2ad3 +27 2c15 +44 1090 +81 4801 +e6 7636 +25 439 +6a 9c66 +87 6a95 +a1 6ea3 +bb 6fe5 +85 e239 +43 1227 +98 6162 +4d 1a51 +12 2904 +2e 8c5e +c4 fa10 +48 1060 +ee 7656 +25 8c13 +3f 8d55 +ac e678 +6a 1666 +19 2be1 +ed 74db +23 4a5 +b8 e562 +d0 7188 +81 4289 +13 a3a7 +6d 1459 +96 4336 +51 b109 +5f 1b57 +87 4a9d +53 3905 +16 194 +89 6ac1 +9 26b +da 5366 +58 334a +89 68e1 +1b 2bcf +35 2d11 +6c 3cda +21 601 +ab 4ee5 +99 41c1 +9a 696c +80 682a +a5 e413 +bf e555 +94 4998 +58 1940 +83 62a5 +6f 1ef5 +4a 30ec +30 2faa +5c 9bf0 +ba 6d44 +a0 6c02 +42 9aae +8c e2d2 +a6 e414 +95 4999 +6f 165d +35 af93 +4f b0d5 +a0 4402 +ba 4544 +84 eab8 +42 1aa6 +c 28f8 +42 1804 +46 9a14 +18 81c8 +3a 2fc6 +9f e35d +85 e21b +6 a8be +88 424a +54 9912 +68 b662 +71 1d29 +d2 f32e +ec f470 +15 8399 +dc 7352 +99 6169 +5e b3fe +78 b540 +5d 1bf9 +6e b674 +6c 1e72 +e2 7426 +25 491 +46 9a9e +59 1941 +be 4776 +aa 4664 +bd 65d9 +da d3cc +c0 d28a +f3 5787 +6c 1c52 +8c ca50 +6 8a9e +d5 53b9 +13 2b05 +e0 f420 +23 ac8d +6d 1679 +b0 4580 +65 1699 +99 4149 +44 9a38 +88 6a4a +e3 76a7 +1e 156 +15 3b9 +24 ae12 +3e af54 +be c556 +1f a177 +8d 4059 +dc 715a +12 124 +d9 5b41 +4f 125d +77 1595 +8d 4079 +66 3e96 +44 9098 +9c 4352 +b5 4f13 +cf 5055 +7c 1d5a +1e 2174 +4 2032 +8a 4246 +d1 d901 +1a 346 +89 c861 +a9 eccb +e6 5616 +a4 ee38 +62 1e26 +1f 2bd7 +2c 2c78 +d 8051 +8a 6266 +5b b3c7 +68 b468 +8f c27f +10 8922 +2 aaac +a4 4c12 +be 4d54 +49 1ae3 +63 1c25 +53 bb27 +c1 5a09 +17 ab9d +46 1a16 +18 1ca +38 afc8 +51 3383 +6b 34c5 +6b be45 +18 8b4a +3d a5f9 +8d e059 +4b 1047 +73 97a7 +66 b694 +d5 dbb9 +38 740 +8 2868 +21 a421 +6d 3ed3 +87 4015 +ca 7acc +34 d1a +c 850 +92 6104 +34 8fb0 +68 1e40 +51 13a9 +9c e158 +5a 1146 +22 684 +29 a461 +72 b584 +4d 9859 +53 1985 +5a b164 +40 b022 +1d 2359 +38 2562 +d1 5301 +52 19a4 +f8 55e8 +ba 4d66 +1a b66 +44 1a90 +a0 4c20 +3f addf +ce 7256 +28 c60 +49 18e3 +c8 5240 +44 1a10 +3f ad5f +13 81ad +64 3c18 +26 e16 +d1 7983 +18 a3c8 +64 1698 +5a 3bce +74 3d10 +97 e31d +18 a9c0 +48 126a +a7 e63f +28 ace2 +3e a5fe +23 ac8f +3d add1 +6d 167b +31 f23 +4b 1065 +18 362 +25 631 +70 3d0a +63 16a5 +8 a48 +e6 5494 +a9 6449 +10 29aa +4f 18f5 +f9 df69 +12 a104 +7e bffe +98 c140 +ce 5ad4 +61 3e01 +fb 7767 +24 690 +6a bcec +50 bbaa +e9 f649 +2a 8444 +10 8302 +4d 1851 +b9 6fc3 +d3 7105 +75 9fb1 +25 a6b3 +3f a7f5 +8e 6276 +4a 9264 +5d b1d9 +4d 1a79 +7e 9f76 +98 4960 +57 119f +e 7c +90 4980 +45 1a99 +76 9f96 +d3 f9ad +bc c558 +1c 23f2 +36 2534 +4b 1865 +f6 f714 +77 bdb7 +e5 5c99 +90 61aa +b5 4fb3 +cf 50f5 +7c 1dfa +b a245 +10 322 +2a 464 +b5 e599 +73 1587 +86 4016 +a6 ee14 +b8 cfe2 +d2 d124 +1 8029 +a9 ce4b +23 2e27 +9d 417b +da 536c +90 e980 +c0 522a +ba ede4 +a0 eca2 +88 4868 +85 e213 +9f e355 +6 a8b6 +88 4242 +e3 dc87 +e9 7661 +f0 dd28 +7c 9570 +62 942e +c1 5081 +f4 7732 +37 79d +f8 5d48 +53 1b8d +3b 8f67 +d9 f9cb +3c 2552 +29 a6e9 +e2 7ca4 +fd 757b +65 1491 +88 42c2 +a2 4404 +cb dacd +2e 654 +5b 9147 +96 e91e +4b b245 +c8 d8c0 +2b 447 +9f e3d5 +85 e293 +50 1322 +6a 1464 +e6 fe14 +d3 f9a7 +bc c552 +10 120 +9c 497a +7 a8b5 +85 6a11 +c4 5a30 +0 aa82 +1a abc4 +ac c4da +26 24b6 +32 8f04 +a1 64a9 +73 15a5 +f aa77 +cc 7858 +d4 51ba +ef d6fd +b4 6db2 +70 9da0 +e5 7e99 +f 855 +d0 d182 +8c 4252 +46 32be +60 3400 +67 1cb7 +a9 ecc9 +e6 5614 +2a ac6e +95 4911 +fa 7746 +46 1094 +83 4805 +21 8681 +d3 7905 +0 a208 +52 398e +7 2b5 +f5 5593 +92 432e +ac 4470 +14 291a +86 6816 +42 9804 +1b 8147 +b7 e715 +68 1662 +b0 45a0 +5 11 +5e 135e +8f 48f5 +a4 e638 +62 1626 +11 2ba1 +1f 23d7 +2c 2478 +75 359b +89 4049 +1b a167 +21 ae01 +1b 145 +1 3 +8b 48e7 +c9 5a69 +d1 fb8b +eb fccd +34 2712 +ca da44 +2e 245c +14 231a +86 6216 +42 9204 +bc c7da +36 27b6 +99 e36b +95 4991 +b1 652b +88 e04a +70 35a2 +b9 cfc3 +d3 d105 +c2 fa06 +83 4885 +33 2785 +d3 7985 +0 a288 +7c bdda +31 8701 +b 2a6f +6e 3cd4 +54 3b92 +a1 4681 +19 a949 +4f 9af7 +f5 5799 +d0 5ba2 +ea 5ce4 +31 8729 +c2 588e +dc 59d0 +2f cfd +15 bbb +26 a636 +d0 f320 +3a 856e +fe fd76 +27 8c9f +2d 2679 +85 e091 +50 1120 +f6 7716 +c 2a52 +16 2996 +c1 7023 +db 7165 +a5 4c13 +bf 4d55 +3 aaad +9b ebc7 +a8 ec68 +1d bfb +37 d3d +2e a676 +3a dce +d0 7b80 +cb d8cd +14 312 +2e 454 +4b b86d +c8 72c8 +90 4108 +c9 fac9 +2c 2650 +b6 6f34 +14 293a +86 6836 +42 9824 +20 8480 +b7 6f35 +16 a136 +84 4018 +59 1361 +8d e0d3 +58 1162 +67 161f +6f 1cdd +55 1b9b +66 b616 +28 2662 +1a 234e +d1 f909 +42 1aa4 +ea 564e +e9 74c1 +72 95a4 +b6 65b6 +a8 cec2 +c2 d004 +4b b0c7 +58 b168 +21 a4a1 +80 6a20 +30 a522 +91 e90b +ce 5256 +3f a7fd +25 a6bb +f6 5594 +93 432f +ad 4471 +d2 5384 +4b 12c5 +96 499e +c9 d8cb +43 38a7 +2c 452 +c2 7204 +4a 92ee +a8 6442 +64 9430 +ed 7cf1 +d3 7baf +27 a695 +bc 475a +28 2e6a +12 386 +39 569 +46 3094 +93 49a5 +7c 1550 +62 140e +15 ab99 +97 cbb5 +66 961e +33 a5a7 +a1 4489 +d2 d986 +d8 7360 +1 289 +17 2995 +65 9e99 +3 2287 +10 2328 +13 2925 +99 c949 +1e 8956 +15 8bb9 +10 188 +9a c9ce +93 6307 +4f 92f5 +87 6abf +a1 6c01 +43 9aad +db dbc7 +e8 dc68 +f 2055 +37 a7b5 +d6 599e +22 8404 +8 82c2 +f6 559c +86 609c +0 8880 +b5 45b3 +70 9582 +e 2a54 +16 3b6 +1a a964 +0 a822 +1b 34d +1 20b +14 2910 +2 2804 +6 aa14 +9b 696f +f2 7da6 +39 a7eb +93 698d +d9 f9e1 +a8 c44a +22 2426 +2e 8e74 +c8 704a +77 9f97 +92 41a4 +69 b461 +b2 c584 +62 1684 +9 8a4b +64 1418 +4 2a98 +2b c4f +c1 7a01 +b0 e522 +65 1419 +45 bab3 +5f bbf5 +2e 865e +d3 5385 +a0 ce88 +c2 5a26 +8c 6878 +d5 799b +8 a62 +bf 4757 +8f 4875 +78 bdca +2d 86f1 +2f 265d +e1 f6a1 +b9 6f41 +a2 cc0c +88 caca +2 2aa6 +dd 71db +13 1a5 +58 3148 +a2 66a6 +21 2481 +ae 4e54 +1a 8b64 +0 8a22 +da 5966 +78 97e2 +81 42a1 +77 b795 +5e 1976 +13 830d +d9 5969 +c8 5242 +1e a3d6 +53 b9a5 +22 840e +3c 8550 +ef d477 +80 68a8 +66 b4b4 +aa 64e4 +90 63a2 +7c 1ff2 +c3 5a25 +84 6838 +5a 994e +e4 7e98 +48 9842 +2f 8cf7 +ae c654 +23 2c8f +3d 2dd1 +c7 7a15 +99 61c9 +4 2090 +e6 569c +27 263d +1b 16f +b1 6f21 +a6 4c96 +c6 fa94 +9 2061 +e3 5e25 +1 a281 +20 4a0 +5c 9952 +1a a1e4 +0 a0a2 +3a 76c +20 62a +af 6c57 +51 9b03 +6b 9c45 +2c acfa +17 abb7 +85 4a99 +15 8313 +db 596f +2f 8455 +e7 dc97 +f4 dd38 +ed 7671 +4c 9852 +8 a2e2 +22 a424 +11 9a9 +5 ab1 +f3 5d8f +90 4b2a +aa 4c6c +21 ae81 +f5 7d13 +3c a758 +31 a5a1 +26 8c34 +c 8af2 +9 22e3 +10 89aa +23 2425 +a9 c449 +2e 8456 +25 86b9 +87 483d +84 6898 +71 1789 +47 b01f +a8 4e60 +67 169f +e1 5681 +1e a1f4 +4 a0b2 +3e 77c +24 63a +88 4048 +1a a166 +ce 7af4 +38 d42 +58 bb40 +41 b281 +3b 5c5 +21 483 +60 14a0 +33 a5a5 +e 8af6 +55 9391 +e8 5cea +3c 87d0 +22 868e +9e 4954 +84 4812 +4d 9ad9 +87 4097 +94 4138 +e3 5685 +62 340c +48 32ca +dd f95b +13 8925 +90 4380 +57 93bf +71 9501 +b5 6513 +21 ae09 +1b 14d +1 b +8b 48ef +33 f87 +40 1028 +e6 761e +a0 44a0 +2e cdc +14 b9a +3f a757 +26 a4be +ea 546c +d0 532a +b0 eda2 +e4 7418 +0 2a0 +16 293e +9c 4b52 +2d 659 +fe dd56 +b 286d +54 3990 +e0 54a0 +b6 e536 +40 12a0 +3b a5ef +83 4285 +4a 10cc +30 f8a +96 c93e +4b 9265 +8f 6277 +fc 7dda +b1 4701 +32 da4 +46 b296 +6d b479 +8c ea72 +f0 55a0 +1 2081 +87 4295 +4e 10dc +34 f9a +16 3be +30 500 +a0 4ca2 +ba 4de4 +95 6b91 +78 9d62 +fb 554d +e1 540b +48 1048 +11 381 +9e c35c +1f 89ff +84 c21a +b7 4717 +1e 8bf4 +4 8ab2 +62 b40e +93 e9a5 +7c b550 +1a 36c +0 22a +ba 456c +a0 442a +19 169 +8 2a6a +e8 5660 +24 a6b2 +3e a7f4 +9c c158 +16 2134 +fc 7770 +25 699 +e2 762e +f6 dd96 +1 8829 +1a 896c +0 882a +33 d27 +53 bb25 +a7 449d +47 9215 +19 296b +ca fa6e +d 8ad9 +a3 4685 +98 4342 +b1 4f03 +cb 5045 +78 1d4a +d 8a79 +58 b960 +e1 de09 +35 f3b +4f 107d +41 b209 +3b 54d +21 40b +95 e399 +53 1387 +60 1428 +88 6a62 +1e a1de +99 4161 +c8 5262 +d 8851 +cb 7a6d +2c 84d8 +4e 32d6 +69 bc49 +f 2a7d +71 95ab +47 9a35 +8b 6a47 +51 11a1 +b5 cd3b +ae 6674 +de f3dc +c4 f29a +a4 e6b8 +62 16a6 +2c 24f8 +8c 6a72 +48 9a60 +19 3cb +33 50d +a3 4caf +bd 4df1 +23 262d +37 8d95 +71 150b +9a 434e +cb 78e5 +b 88c7 +18 8968 +3e 8d54 +24 8c12 +61 1e21 +42 b02e +5c b170 +83 c887 +90 c928 +89 6261 +a 8ac4 +2c a45a +79 15e1 +8e c8de +87 6217 +43 9205 +bd c7db +37 27b7 +db 716f +4 98 +c7 5a1d +95 411b +1a 166 +20 ae22 +3a af64 +b4 459a +ef 567d +70 1d20 +a5 ec91 +e5 f413 +ff f555 +d4 5998 +e8 7c42 +a0 e422 +ba e564 +ee d456 +99 69c9 +68 14e0 +de 51d4 +c4 5092 +4e 90de +e4 fe90 +1a 8164 +0 8022 +88 c0ca +2 20a6 +e 8af4 +98 c940 +e 805c +c6 d89e +cc 7278 +4d 9a59 +34 2518 +ac e6d8 +6a 16c6 +49 1849 +7a 9d46 +ae 467e +66 1c96 +86 ca94 +f2 f704 +73 bda7 +e1 5c89 +b7 ed1f +41 1a89 +3c add8 +dd 717b +72 9f86 +ec f6d2 +f5 5d99 +c 8af8 +25 eb3 +3f ff5 +32 858c +12 892e +b 2267 +d0 5108 +9a 6bec +80 6aaa +91 41a9 +f 8855 +cd 7a71 +a a046 +6e bc56 +5f 11fd +45 10bb +d1 51a1 +8 2260 +2 8886 +1b a3c7 +28 a468 +c1 5a81 +53 93ad +97 63bf +b1 6501 +8c 4a52 +18 89c8 +11 2301 +ff 75df +48 38ea +8e c056 +5d 3b59 +51 13a1 +45 9811 +aa c646 +7e b554 +64 b412 +57 1b95 +c1 7823 +db 7965 +8 a268 +cf f2f5 +39 8543 +3b 8547 +c3 50a7 +4d 90f3 +e3 fea5 +21 2ca3 +3b 2de5 +a0 6600 +fe 5fdc +e4 5e9a +8c ea78 +4a 1a66 +9b 63cf +57 93bd +b5 6511 +1c 89d8 +15 2311 +1 221 +4c 38fa +a0 4ca0 +3f af57 +2 2206 +46 9814 +75 9711 +3a 2dc6 +56 93be +70 9500 +b4 6512 +cb f8cd +2e 2454 +14 2312 +81 60a1 +53 1baf +6d 1cf1 +27 cb7 +a6 4614 +1b a36f +17 995 +f5 551b +7 23d +59 1b61 +4 2030 +b 8e7 +8a 4244 +68 bec2 +82 c004 +93 c9af +7c 955a +18 8b60 +64 3e18 +5e b3de +d9 5361 +a 8a46 +8f e25f +10 a902 +21 623 +3b 765 +c1 5823 +db 5965 +8 8268 +8d ead3 +a7 ec15 +58 1b62 +19 81c1 +22 ac04 +8 aac2 +b9 cd4b +33 2d27 +36 afbe +50 b100 +0 2880 +86 4a94 +5 8219 +a7 649d +f 8077 +7d 1f59 +43 b88f +5d b9d1 +cd f8d1 +16 2316 +f6 7d34 +dc 7bf2 +3d a779 +d5 d199 +51 19a1 +de fbdc +c4 fa9a +ae 6e74 +21 8c23 +3b 8d65 +b8 47c0 +8 8062 +31 a703 +20 c88 +65 3491 +fa 5d66 +9c eb78 +8f ead7 +5a 1b66 +9a c144 +80 c002 +89 c8eb +82 6224 +6e 1e74 +5b 996f +1 a201 +20 420 +6a 1444 +50 1302 +f0 5502 +cc 725a +2 224 +15 2199 +18 3c2 +32 504 +3b ded +21 cab +a0 4608 +32 a726 +74 151a +10 b20 +fe 5dfe +aa eeec +14 813a +a1 e421 +88 6868 +b 265 +56 393e +61 1ca3 +7b 1de5 +e0 5600 +fc 7df0 +e2 7cae +36 a794 +c0 5002 +da 5144 +d9 fb63 +2 8a8c +87 621d +8 28c0 +8e 4ad4 +3e affe +58 b140 +c6 5814 +d 8259 +77 1d3f +25 2cb3 +3f 2df5 +a4 6610 +70 1502 +fa 5de6 +92 6124 +3d 77b +e7 dc95 +74 9712 +cd 5af9 +27 2cb7 +ad ccdb +a6 6614 +4d 125b +d7 5b3f +98 4142 +e7 7c17 +2e a65c +65 349b +7f 35dd +f8 d560 +97 63b7 +53 93a5 +6a 1c44 +50 1b02 +75 35b1 +ae ccdc +94 cb9a +a7 6615 +5d 3bf1 +43 3aaf +2c 65a +93 6905 +31 a781 +48 10c2 +e5 541b +ff 555d +48 1868 +92 6904 +30 a780 +b4 e518 +72 1506 +4d 1079 +26 e96 +54 131a +6e 145c +85 48b1 +ea 76e6 +f6 fd96 +25 2699 +f9 7561 +2a ac46 +0 20 +95 6911 +f a275 +ae eefe +c8 f040 +42 10a4 +39 a56b +9c c1d8 +16 21b4 +f8 57c0 +a5 4491 +ba ef4c +a0 ee0a +e aa76 +72 15a4 +2 284 +34 2792 +d4 d112 +9 aac1 +d6 f936 +5 2239 +bc 6572 +78 9560 +6e 3cfc +54 3bba +ed 7659 +e a85c +13 a105 +e0 de28 +16 81be +58 1b42 +63 34af +7d 35f1 +80 6802 +9a 6944 +c2 f0a4 +e2 562c +38 a7c0 +ac 4e70 +f5 5539 +bc e558 +7a 1546 +71 17a9 +e4 5492 +fe 55d4 +82 40a4 +f2 df0c +18 a148 +4e 92f6 +1f 83d5 +cb 58ef +5 8293 +ef f6fd +70 bda0 +b2 cd2c +98 cbea +ab 6665 +d9 516b +34 fb0 +a2 44a4 +38 25e2 +c 52 +a2 6e04 +7 2bf +21 401 +58 19ea +7 15 +b2 45a4 +5e 9b56 +e5 dc93 +ff ddd5 +eb 766d +aa 6666 +45 3831 +12 8906 +c2 50a4 +38 7c0 +d2 51a4 +9d 6959 +d3 5b07 +5c 9b5a +af ce55 +3b 74d +21 60b +e8 dee8 +1b a1c5 +1 a083 +c5 5031 +ab 4eef +49 18c1 +ae 46f6 +34 2590 +55 1b19 +3e f7c +24 e3a +c 8a78 +25 e33 +3f f75 +40 100a +5a 114c +23 485 +23 a6a7 +e0 7488 +7f bddf +e0 5c20 +b5 e711 +36 adb4 +e3 5ea5 +9 20e1 +58 9948 +9c 695a +f7 75b7 +31 2523 +23 8e2f +3d 8f71 +1d 295b +c a8fa +18 8b42 +4d 3a59 +6f 14fd +55 13bb +98 6940 +e0 5628 +c0 f0a0 +e 205c +0 a000 +19 21cb +f3 5f8f +aa 4e6c +78 1542 +a 8866 +93 cba7 +7c 9752 +d5 5b39 +8e ea76 +f2 55a4 +8 2262 +86 4816 +a8 6462 +a3 c40d +89 c2cb +3 22a7 +31 8f23 +8f 6077 +4b 9065 +18 8362 +30 f88 +19 23c3 +33 2505 +e a56 +0 a008 +e3 7c2f +fd 7d71 +37 a715 +91 4b81 +c 258 +e 8afe +28 8c40 +86 6a14 +58 3160 +c1 f081 +fd 7df9 +37 a79d +29 accb +66 1616 +15 2b91 +ec de7a +1f a157 +6c 3cfa +21 621 +e9 fcc1 +32 2706 +d0 51a0 +f 8aff +29 8c41 +a6 469c +87 6a15 +93 c905 +99 c1c9 +13 21a5 +8c c2d8 +6 22b4 +f4 7592 +ab 646f +1e 81d4 +4 8092 +af 6e77 +6b 9e65 +69 3ec3 +83 4005 +c6 7abc +30 d0a +4d 18fb +5e b376 +cc 5258 +5e 91d4 +44 9092 +83 6a25 +39 87eb +f2 5da6 +68 14c2 +94 ebb8 +eb 5645 +52 1ba6 +1c 29f8 +3 8f +da 7166 +1d 1d1 +1e 297c +4 283a +8a 4a4e +37 597 +c0 72a8 +d9 f163 +2 808c +f3 7d85 +20 a688 +82 680c +36 8fbe +50 9100 +94 6112 +d5 5139 +6 881e +de 7b7c +c4 7a3a +d a8d1 +2d e59 +8b 6a65 +20 ac00 +6 aabe +17 81bd +7 9f +de 7176 +8e 4874 +2c 86f0 +89 6a69 +18 36a +c2 d884 +8e e8d4 +59 1963 +41 3229 +55 9991 +2 aa8c +51 1ba9 +62 b624 +6d 1cd9 +64 b612 +7e b754 +8d 6a51 +b 886f +e6 7ebc +50 110a +2 222c +16 8994 +85 c8b1 +6e 945c +54 931a +a7 c615 +30 58a +67 94b7 +b8 656a +34 879a +4b 1245 +96 491e +67 949d +7e 1ddc +64 1c9a +84 ca98 +b7 4f95 +89 6a61 +4 8a10 +82 68a6 +12 8186 +2c ac5a +5f 3157 +19 8b41 +78 9d40 +5e 9bfe +bc 6d52 +d3 d1a5 +c2 faa6 +1c 2b52 +5e b3f4 +44 b2b2 +70 158a +f8 756a +74 979a +d6 591e +b0 458a +97 c31d +18 89c0 +4c 32f8 +7 97 +14 138 +63 1685 +8 8a60 +83 c8af +9d c9f1 +6c 945a +95 69bb +51 99a9 +8e 407e +21 6a1 +7 8815 +2f c7f +c5 7a31 +7c 1752 +93 4ba7 +ae 447e +a1 4481 +d8 5148 +1b 8367 +f0 7f22 +33 f8d +e 2876 +89 6869 +ba ed66 +1d bd3 +37 d15 +4 8818 +f0 558a +2 2ac +20 a4a0 +51 1b29 +8e ea5e +f2 558c +82 4286 +a9 4469 +e7 5497 +f4 5538 +2d a6d9 +e6 7c94 +83 6a2f +9d 6b71 +6c 3650 +83 6aa5 +45 9819 +5d 935b +2e 2cdc +14 2b9a +5e 135c +44 121a +6e 167e +3e add4 +24 ac92 +e8 5c40 +ce 5afe +f3 75ad +c9 d861 +d0 f9a8 +33 252f +69 b649 +d3 512f +26 a6b4 +ad 6e51 +20 600 +aa 4ee4 +28 460 +71 1583 +9e 4154 +84 4012 +48 1840 +5a 91c6 +4c 9a50 +5a 3144 +40 3002 +11 a32b +2b a46d +ca 78e6 +ef 5eff +15 213b +29 8ee3 +87 6037 +43 9025 +b2 65a6 +40 3a08 +ee 54d4 +d4 5392 +8b 426f +57 9937 +b2 c50c +98 c3ca +12 23a6 +4d 1871 +1e 23fe +d5 f9b9 +38 2540 +2a 8464 +10 8322 +1b 9e5 +1 8a3 +80 4200 +eb f667 +da 5bec +c0 5aaa +51 33a3 +6b 34e5 +f1 d509 +fa ff4c +e0 fe0a +60 3e08 +f4 5792 +ab 466f +d0 5300 +51 19a3 +6a 9e64 +ae 6e76 +b8 c7ca +32 27a6 +30 8722 +82 48a6 +b 2a65 +21 409 +5c 9958 +e6 de1c +c a058 +42 9206 +91 61a9 +74 b732 +7d 1df9 +4c 985a +35 8fb3 +4f 90f5 +93 6107 +7f 1d57 +68 9660 +ac 6672 +47 383d +81 c203 +9b c345 +2 88a6 +9f cbff +b9 cd41 +e7 dc9f +ed 7679 +5b 19e5 +41 18a3 +c0 5200 +0 a82a +1a a96c +ae 4ef4 +c9 dac9 +43 3aa5 +2c 650 +b6 4f34 +ef fef5 +59 9143 +b0 45aa +57 9b95 +a8 6c48 +3 2a8d +52 3924 +d8 d948 +be 4df6 +54 b310 +9 8c1 +f4 d71a +6e 36f6 +1b 2167 +1e 176 +24 ae32 +3e af74 +72 3f86 +99 4169 +88 6a6a +22 aea4 +2 a6 +f6 7d36 +3d a77b +59 1949 +a3 4ea7 +e2 de2e +fc df70 +8 a06a +d8 f9e0 +3b 2567 +f3 5705 +70 97a8 +b4 67ba +5d 9b59 +5a 194e +1e 2974 +4 2832 +29 641 +74 3d1a +c a58 +dd dbd1 +c3 da8f +26 616 +f0 5508 +ba 6fec +a0 6eaa +b1 45a9 +2a cec +10 baa +3b a767 +a9 4649 +2d 265b +73 9f2d +b7 6f3f +a0 4408 +32 a526 +1 2029 +6e 9454 +54 9312 +18 a940 +be 677c +a4 663a +60 9628 +7e 1dd4 +64 1c92 +84 ca90 +1b b6f +30 2520 +d8 53ca +f2 550c +47 909d +cc fad8 +2f 265f +94 e118 +52 1106 +ee 7ef4 +58 1142 +5c 9352 +b a8cd +23 a485 +b8 454a +62 3c06 +82 ea04 +32 a5a6 +a0 4488 +88 68e0 +0 288 +84 4a90 +da d3ce +f4 d510 +92 4106 +7 a235 +82 4006 +c 8052 +a2 ee04 +94 4112 +b0 e5a0 +d ad3 +27 c15 +3 8827 +73 158d +f aa5f +f2 75a4 +21 24a9 +3a 85e4 +20 84a2 +18 ab40 +f1 7d09 +76 3d16 +b4 4598 +1 8a81 +69 36e3 +70 9daa +c3 d0a5 +39 87c1 +12 104 +26 86b6 +49 1ac3 +63 1c05 +9b 4b6f +b0 6520 +8d cad9 +7 2ab5 +30 afa0 +d2 5106 +14 2392 +2e 24d4 +6 82b6 +74 3f32 +8e 4074 +d7 5197 +be cddc +b7 6715 +a4 cc9a +47 9a15 +19 81c9 +22 ac0c +8 aaca +35 25b1 +36 5b6 +28 e40 +e2 54a4 +20 aea0 +c2 5006 +4c 9052 +e2 fe04 +e4 de38 +17 a115 +fa 57ec +e0 56aa +d3 d30d +54 99b0 +98 69c2 +1b ab67 +89 4a49 +6a 1664 +7d 35d9 +89 e8cb +c6 5216 +bb ed4f +45 1ab9 +4e 1856 +76 9fb6 +6 bc +77 b715 +d1 d129 +da fb6c +c0 fa2a +56 9136 +25 c39 +92 e324 +81 48a9 +28 84c2 +b2 cda6 +f8 55ca +a 2ec +22 604 +5c b1d2 +88 6262 +2 2a04 +44 9ab0 +88 6ac2 +a2 6c04 +5e 1156 +55 13b9 +d a271 +d9 73e9 +36 596 +56 b394 +88 4042 +a8 ee40 +13 8185 +29 ac69 +2 aa86 +3b 5ef +d1 73a1 +2d ac59 +35 85bb +84 4810 +a8 64ea +b2 458e +89 e269 +63 bca7 +d1 5b89 +e2 f604 +a8 c6c8 +22 26a4 +3a 85c4 +20 8482 +14 8312 +da 596e +2e 8454 +7e 3d54 +64 3c12 +84 ea10 +d0 59a0 +46 10bc +46 b29e +c1 5221 +ac 447a +17 a3b5 +b 8c5 +0 2000 +8a 68e4 +7 8b7 +86 4214 +6e 1c56 +8e ca54 +db f14f +65 1eb9 +53 b30f +6d b451 +6a 9666 +bb 4d47 +19 abe1 +7d b5db +4f 18f7 +91 e909 +ce 5254 +52 b90e +7 8235 +5e 39de +13 305 +c9 524b +4a 18ee +4e 9854 +d3 f90d +1c 2352 +e6 d636 +91 6ba9 +56 9934 +9a 6946 +c2 f0a6 +38 a7c2 +45 1a11 +90 6122 +7c 1d72 +4e 18fc +5f b377 +cd 5259 +11 a92b +4e 1276 +c9 5269 +90 4182 +ec 7cf8 +26 a69c +31 a721 +11 212b +eb 5eef +c6 d016 +27 ac37 +95 4b19 +3c 257a +ee ded6 +14 a112 +51 9323 +af 6477 +6b 9465 +38 8762 +50 1388 +76 1596 +98 e94a +4d b271 +ca d8ec +c3 7225 +2d 473 +3 2025 +89 c049 +51 1389 +39 8763 +ba e5e4 +a0 e4a2 +55 1399 +d0 59a2 +31 8529 +3a af6c +20 ae2a +43 baa5 +2c 8650 +d8 5b6a +ad e65b +2e acfe +4b b26f +47 1895 +a5 4631 +f0 7d0a +2 2a2c +e3 56a5 +1a ab66 +88 4a48 +48 12c0 +be e556 +8d 6059 +86 e8bc +c3 5207 +21 689 +f8 7760 +f2 dd86 +46 1896 +74 3d18 +e2 56a6 +ac 64f8 +e 807c +57 919f +4d 9a79 +d0 5922 +a 82c6 +47 1815 +8 28ca +8 8242 +9 2c3 +23 405 +44 9812 +5e 9954 +20 620 +f4 7d12 +1e 8954 +4 8812 +dc 7b70 +5 a99 +c2 7a2e +60 1620 +f3 df25 +19 a161 +d8 dbc0 +3b 747 +11 ba9 +22 a624 +9 2a6b +10 ab88 +67 1615 +28 26ca +2 880e +1c 8950 +1f a3df +80 4220 +91 490b +4 810 +5f b3df +c0 5220 +16 a3b4 +27 a635 +0 a2a0 +a2 4406 +43 b8a7 +2c 8452 +c2 f204 +c 885a +c8 7a68 +3f d57 +45 ba13 +5f bb55 +83 caad +6c 9658 +ea 74ee +b3 cd87 +b9 6761 +44 1810 +2a ae6e +c3 d80f +dd d951 +89 6a6b +12 abac +b4 4d12 +78 154a +ad ccf1 +93 cbaf +7c 975a +c 227a +2a ac44 +10 ab02 +4c 987a +93 6127 +7f 1d77 +e2 5426 +7a 354c +60 340a +91 69a1 +e1 5e81 +7 20bd +17 8915 +5c 13da +76 151c +56 9916 +8a 424e +9 8841 +86 429c +5d 13db +77 151d +c 8852 +c1 502b +db 516d +5e 1956 +d9 5949 +81 4281 +4a 92c6 +ee dedc +14 a118 +2d 2cf3 +ac 6650 +fe 57dc +e4 569a +8c e278 +4a 1266 +ea 5466 +b3 450f +11 a3a9 +1a a146 +fa 55e4 +e0 54a2 +d8 7b40 +1d 8959 +24 2690 +98 e36a +94 4990 +4b 386d +7 8895 +2f cff +ae 465c +c5 7ab1 +e2 fc0e +fc fd50 +ec 5c5a +57 bb95 +db 73c7 +e8 7468 +26 8e16 +f5 5731 +b1 67a1 +97 e315 +48 1262 +e8 5462 +85 e2b9 +43 12a7 +ac 44d8 +3e a5f6 +ca 72cc +34 51a +19 8961 +ad 66d9 +38 85e0 +d0 5180 +47 1a17 +39 afc9 +53 3925 +d9 d949 +5e 9956 +b3 6d0d +99 6bcb +55 9bb9 +59 994b +fe 7f74 +e4 7e32 +27 e9d +95 4391 +bd 6559 +b6 edbc +f3 5707 +cb d2ed +90 69a2 +fa 5fc4 +e0 5e82 +6 20be +7a 974c +91 cba1 +be 675e +60 960a +84 e2b8 +42 12a6 +e2 54a6 +c0 7a02 +da 7b44 +d 22d1 +14 8998 +dd f9db +13 89a5 +2e ae5e +cf d27f +50 9922 +df 7b5d +c5 7a1b +42 baac +e4 5c12 +fe 5d54 +2d c59 +5e 9156 +d9 d9c9 +53 39a5 +3c 550 +22 40e +ac 4cf2 +9 261 +54 393a +c0 d802 +da d944 +f3 5fa5 +19 21e1 +9 8849 +6e b67e +74 9512 +de f374 +c4 f232 +7 829d +cd 58f9 +93 4387 +a0 4428 +2d 26d1 +34 8d98 +d 2a73 +7d 3d79 +56 3b96 +25 cbb +3f dfd +36 a736 +a4 4618 +84 e090 +f5 5f33 +1b 216f +d1 d301 +96 69b6 +52 99a4 +c7 7a9d +f2 5f2c +b 20c7 +18 2168 +81 e089 +6d 9cd9 +8e c8fe +87 6237 +43 9225 +f2 f526 +35 8591 +3e afd4 +24 ae92 +b2 67a6 +97 631f +53 930d +18 29c2 +83 e28f +9d e3d1 +68 1460 +22 aeac +de 5154 +c4 5012 +4e 905e +e4 fe10 +57 1b15 +32 2d0c +18 2bca +34 a5b2 +d1 7909 +56 3916 +94 4198 +7c b572 +c8 7840 +d0 51a2 +fd fddb +33 8da5 +d1 7b03 +eb 7c45 +f3 55a7 +4d 1271 +3 a885 +98 494a +da d1ec +c0 d0aa +1a 81e4 +0 80a2 +cf d2ff +e9 d441 +50 99a2 +df 7bdd +c5 7a9b +11 a901 +4e 1a54 +9e 6976 +5a 9964 +40 9822 +d4 5b12 +ee 5c54 +85 4239 +60 1e00 +9 28e3 +88 6240 +53 bba7 +3c 8752 +c1 5a89 +6d 1c79 +46 1a96 +a6 eeb4 +10 8102 +4 2a10 +4 8a92 +1e 8bd4 +b0 45a8 +52 3926 +d8 d94a +d f3 +a3 6ea5 +2b a4c7 +38 a568 +0 2080 +86 4294 +a8 ce48 +22 2e24 +3a 8d44 +20 8c02 +29 2ce3 +a8 6640 +4a 92c4 +8e 62d6 +6c b4d8 +73 bfa7 +e1 5e89 +d7 733d +58 39e0 +66 1e96 +c6 f2b4 +30 8502 +24 2e10 +32 2586 +24 8e92 +3e 8fd4 +d8 7942 +11 8989 +80 e880 +ca 526c +8d 4a5b +6 2216 +6d 1e59 +4d b8d1 +57 9915 +49 bac3 +63 bc05 +9 2243 +10 890a +e4 76b0 +5 2091 +5 88b1 +a2 46a4 +d3 59af +27 8495 +e5 76b1 +ea dee4 +10 a120 +4a 326c +5e 99d4 +44 9892 +0 2800 +86 4a14 +58 31c8 +1c a9fa +10 920 +8f 427d +c4 7298 +28 8c42 +a8 e442 +82 4224 +95 6199 +6 814 +e1 54a1 +39 8d69 +12 8b86 +f9 75c9 +b7 e537 +41 12a1 +22 a4ae +3c a5f0 +70 b5a0 +46 1814 +4e 1854 +cc da50 +ed fcd9 +36 271e +ea 5664 +fd 75d9 +67 3c95 +f5 5d93 +3c 87d8 +9f 63ff +5b 93ed +41 92ab +b9 6541 +46 1036 +88 e048 +52 392e +fb f567 +d0 59aa +24 8490 +c9 f849 +3a 256c +20 242a +8 a8e8 +45 1233 +5f 1375 +2c 8e78 +75 9f9b +a0 46a0 +92 4186 +84 4a10 +c2 7826 +9 a26b +5 891 +6a 36c6 +98 e14a +a1 46a1 +77 959d +87 6a37 +43 9a25 +11 8323 +2b 8465 +48 9862 +e8 7460 +11 389 +e6 7416 +c7 5ab7 +91 6909 +b2 6786 +da 51e4 +c0 50a2 +4a 90ee +e0 fea0 +2e 2e5c +94 e910 +68 be6a +52 9386 +bd 657b +79 9569 +9b 4367 +68 1c48 +8d 6a7b +49 9a69 +d6 793e +8b 4265 +97 e33f +18 a9e2 +87 e01d +38 f6a +d9 dbe1 +22 626 +35 259b +bb 65ef +43 9827 +6a 9e66 +80 62a8 +6c 1ef8 +b6 4596 +98 6948 +47 9895 +6 aa9c +af e67f +30 ad22 +98 6962 +e8 5e42 +e 207e +e3 54ad +64 1690 +90 e1a0 +a3 4687 +b0 4728 +e1 d60b +fb d74d +62 9cae +7c 9df0 +e0 fe88 +7 815 +4a 9846 +83 c8a7 +6c 9452 +c5 5839 +da 7346 +5a 9bec +b8 6d40 +9e 6bfe +40 9aaa +3d 2df9 +b5 efb9 +73 1fa7 +68 bc42 +20 680 +20 2480 +a6 4694 +5a 33cc +40 328a +11 8b29 +62 1e86 +a4 ee98 +d7 7395 +33 70d +e1 5ea3 +fb 5fe5 +84 6810 +e8 7ce8 +22 a68c +2a ac64 +10 ab22 +8f 425d +10 900 +e2 7c26 +29 a66b +25 c91 +5c 1958 +a6 4eb6 +94 4992 +2d 2e59 +4d 18db +5e b356 +45 1a19 +76 9f16 +90 4900 +6 1c +85 6811 +e9 7ce9 +23 a68d +5e 1176 +b0 e5a2 +65 1499 +c3 fa8d +2d 8cdb +26 2614 +ab 44c7 +b8 4568 +d 8db +1e a356 +8e 6a56 +4a 9a44 +1c 81f8 +de d15c +c4 d01a +3e 2ff6 +1c 2b5a +5e b3fc +44 b2ba +c1 5281 +47 1a1d +92 4904 +30 8780 +d8 d34a +52 3326 +c2 d80e +dc d950 +a5 4691 +8d 4871 +2b 86ed +fe fd56 +2d 2659 +b7 6f3d +36 fbe +50 1100 +11 1a1 +73 9785 +b7 6797 +1d a959 +49 1a69 +7a 9f66 +a 24e +c1 d809 +71 9da9 +b5 6dbb +d1 71a1 +4c 1272 +2 a886 +3c f50 +22 e0e +46 9816 +47 9a95 +98 6b48 +f3 77a5 +f1 57a1 +13 905 +b0 c508 +10 23a2 +2a 24e4 +42 9084 +86 6096 +17 8b15 +24 2492 +3e 25d4 +53 1905 +ad 6459 +14 29ba +70 3f08 +2 a026 +57 9b15 +32 ad0c +6f 1657 +18 abca +93 4905 +31 8781 +0 2200 +8a 6ae4 +ee 74de +8f e8df +5a 196e +1b 23ed +1 22ab +37 8fbd +95 6111 +1f 23fd +5 22bb +b 80e7 +79 1fc9 +74 959a +d1 53a1 +29 8c69 +2 8a86 +a9 e461 +f2 f584 +a2 4684 +91 6123 +5e 915c +44 901a +77 1517 +da 7be4 +c0 7aa2 +21 a629 +cc 78d0 +e2 5684 +de 597e +7c 97fa +90 49a0 +79 154b +94 6990 +58 91e2 +df 597f +7d 97fb +8c c8d8 +6 28b4 +85 6211 +32 7a6 +2a 2e44 +60 140a +91 49a1 +7a 154c +5f 995f +a 2ce +c1 d889 +24 410 +e 82fe +28 8440 +98 cbe2 +b2 cd24 +8d c8db +7 28b7 +86 6214 +88 c8e8 +81 6221 +6d 1e71 +2 8a84 +d6 5916 +1d 835b +3e a55c +24 a41a +db f9cf +3e 2556 +35 27b9 +b9 6549 +b2 edac +ef 56f7 +19 2349 +80 40a2 +9a 41e4 +3d a7db +f6 7d96 +44 3298 +12 30e +c9 d8c9 +43 38a5 +2c 450 +66 1e14 +38 5c8 +9a eb4c +80 ea0a +c7 5a9d +b9 ef69 +16 8116 +13 30f +2d 451 +9d 4bf3 +b7 4d35 +4e 9a5e +67 1e15 +39 5c9 +42 300c +28 2eca +96 c91c +8f 6255 +3c 2f5a +7e b7fc +64 b6ba +c6 783e +9f 4155 +85 4013 +f 805f +a5 ee11 +cb 72cd +35 51b +4a 9a6e +82 c80e +9c c950 +47 32bf +61 3401 +98 49ea +75 9519 +7e bf5c +64 be1a +97 4317 +fa f76e +3d 87d9 +f6 5d94 +93 4b2f +ad 4c71 +33 8587 +63 1e25 +92 c92c +8b 6265 +38 2f6a +8c 60fa +48 90e8 +e8 5e62 +d 2af9 +9c c3d2 +b6 c514 +66 3c94 +3 2a2f +1d 2b71 +9d 495b +6d 3659 +7 8215 +cd 5871 +2d c79 +6 a96 +13 aba7 +81 4a89 +39 2fe3 +53 3125 +d9 d149 +c8 fa4a +ee 76fe +78 b548 +d6 f334 +c5 58b9 +6c 94d2 +b0 6588 +f2 7d84 +39 a7c9 +36 2596 +11 2389 +ec 56f2 +c3 5285 +70 1f8a +43 3825 +c9 d849 +1 2023 +1b 2165 +b5 6599 +a2 4624 +e6 7494 +41 1081 +2c 658 +43 3aad +64 9e98 +52 3126 +d8 d14a +4d 90d1 +33 8f8f +a3 66a5 +e3 76ad +30 2782 +7e 9554 +64 9412 +3c 2d72 +69 1669 +49 b0e1 +85 6031 +e8 7e40 +f0 57a2 +1e 21d6 +5d 9bd9 +8b 4247 +ec 7452 +e2 f42e +fc f570 +25 8499 +54 3338 +47 3297 +1d 23d3 +37 2515 +4 a018 +34 251a +f9 d749 +60 9caa +73 3725 +7a 9dec +be 6dfe +19 a961 +a6 6416 +48 92c2 +62 9404 +e9 fc49 +5a 93ce +74 9510 +1d a95b +70 9522 +66 3cbe +ff 775d +e5 761b +80 e280 +6c 9ed0 +e6 5696 +12 a38e +d8 79ea +2c a4d0 +7a b544 +60 b402 +83 480f +9d 4951 +f8 f762 +21 868b +3b 87cd +b5 6533 +71 9521 +7a bf64 +60 be22 +c7 f8bd +2a 2444 +10 2302 +9a 6be6 +1b 2b6d +1 2a2b +59 19e3 +d8 5340 +15 291b +51 198b +62 b406 +31 2f09 +85 4813 +9f 4955 +e9 5ceb +fa f766 +23 868f +3d 87d1 +6d 36f3 +74 9dba +39 2d69 +12 2b86 +c 27a +20 6a0 +28 8e60 +17 8195 +30 522 +50 b320 +60 16a0 +32 526 +41 18a9 +52 b324 +c6 529c +f1 d521 +fa ff64 +23 8e8d +e0 fe22 +12 830c +cb 58c7 +d8 5968 +80 42a0 +4d 9851 +5a 336c +54 9992 +40 322a +8b c24f +c 88f2 +48 b26a +44 1890 +8d 6851 +f1 7d29 +2b a6cd +95 41b3 +99 6149 +92 e9ac +cf 52f7 +1e 2156 +f0 5722 +15 23b9 +8e e2d6 +97 499d +2b ae45 +8c ea5a +f0 5588 +b 47 +33 87a7 +c6 fa14 +27 86b7 +12 830e +43 b8a5 +2c 8450 +d8 596a +8d 4879 +e7 f417 +d6 599c +50 1108 +e7 7695 +cb facd +35 8d1b +2e 2654 +b0 4da0 +26 4bc +c9 f8c1 +12 2306 +5a 9966 +cf 7a5f +4f 1a5d +18 a34a +b4 4d98 +97 69b7 +53 99a5 +66 9494 +f3 5f2d +19 2169 +0 8a2 +1a 9e4 +99 4341 +a 82ce +24 8410 +d0 592a +8c e050 +58 31c2 +65 941b +7f 955d +d1 d303 +eb d445 +52 99a6 +c7 7a9f +e0 d488 +f2 5f2e +18 216a +49 32e3 +63 3425 +e9 d449 +50 99aa +72 970e +a3 cca5 +19 83c1 +61 16a1 +8 8a68 +21 e23 +3b f65 +9e 4174 +84 4032 +92 c904 +9e 41d6 +b9 c54b +33 2527 +d0 f9a0 +ba edc4 +a0 ec82 +ea 566e +ee d676 +99 6be9 +6f 14f7 +b1 e509 +6 2a36 +8c ca5a +bf 4f57 +36 a516 +5 2019 +8f 68fd +e a54 +a0 66a8 +1a 8964 +0 8822 +94 4b12 +ae 4c54 +a3 e40f +bd e551 +b6 e51e +40 1288 +28 8662 +0 aaa0 +a2 4c06 +2c 8c52 +c2 fa04 +fa 5f6c +e0 5e2a +13 2107 +93 c30d +14 89b0 +48 32e8 +3 87 +10 128 +72 970c +b6 671e +44 1092 +5e 11d4 +81 6081 +11 2929 +90 412a +10 8108 +32 2f06 +87 4215 +5e 137e +14 a992 +4e 105c +34 f1a +c0 70a8 +82 6826 +8 2a68 +21 a621 +87 e2bf +a1 e401 +da 594c +c0 580a +39 2569 +12 2386 +2a 664 +9e 63de +5a 93cc +40 928a +3d 25d9 +b5 e799 +73 1787 +86 4216 +64 9418 +5b 994f +6 2be +20 400 +90 4ba2 +aa 4ce4 +14 8392 +da 59ee +2e 84d4 +ec 76f0 +51 bb03 +6b bc45 +18 894a +73 95a7 +15 91b +b8 cf4a +32 2f26 +4b 1845 +c 28fa +19 23eb +33 252d +e a7e +14 a912 +1e 95c +4 81a +57 3b15 +62 16a4 +b a845 +13 81a7 +cd 70db +3 a5 +68 3648 +1a 8bce +34 8d10 +b3 652f +2 8204 +c8 5860 +83 e20f +9d e351 +4 a8b2 +1e a9f4 +82 42a4 +c9 7861 +3 a205 +c2 52a4 +33 f0d +1c a95a +e5 5499 +77 b5b7 +4d 12d3 +67 1415 +10 a988 +4a b26e +46 1894 +e2 56a4 +8 a40 +54 bbb8 +f6 5d1e +4e 12d4 +11 a989 +93 c9a5 +7c 9550 +62 940e +34 2d1a +2a 2466 +7f 9fd7 +aa 6466 +ec 5cf2 +26 8696 +57 93b7 +a8 646a +40 b288 +73 3785 +50 3188 +38 a562 +4d 9251 +12 2906 +30 85a8 +a3 4c87 +b0 4d28 +16 ab16 +cb 72ef +e5 7431 +83 ca85 +0 a22 +1a b64 +30 a782 +d5 5311 +56 19b4 +5c b3f8 +fe 555e +d4 5930 +e 82d4 +c0 f2a8 +37 8597 +4a 9266 +62 1e8c +5d b1db +d d1 +ca 7066 +3 80ad +6d 9479 +46 9296 +e 876 +1c ab52 +d1 732b +eb 746d +55 1b99 +66 b614 +d7 7b95 +9a 4366 +74 9598 +54 993a +4d 3273 +95 6939 +c1 7889 +3c a552 +4 a8b8 +5b 1345 +41 1203 +cb 5ae7 +ab 4cc7 +b8 4d68 +13 bad +81 40a1 +1e ab56 +d3 732f +ed 7471 +32 8d26 +1 829 +63 9e0d +a7 6e1f +9d cbf1 +83 caaf +6c 965a +94 e112 +38 a560 +23 aca7 +91 4b89 +a2 e604 +66 9696 +a4 e498 +62 1486 +82 c284 +19 363 +f a5d +ee 765e +c1 daa1 +e8 746a +30 a5a0 +b3 4785 +80 c288 +19 9e1 +62 96a6 +2c a4f8 +3a f46 +31 2f03 +4b 3045 +18 ab48 +73 b7a5 +1a 8166 +32 d8c +4a 3046 +41 32a9 +ca 7a44 +d2 53a6 +9c 61f8 +7c 3dfa +31 721 +7e 3dfe +fd 775b +33 725 +d3 5925 +0 8228 +85 ea93 +9f ebd5 +6a 1c64 +50 1b22 +39 761 +64 3612 +7e 3754 +84 6830 +22 a6ac +be 4dfe +73 1725 +9e 637c +84 623a +40 9228 +8c 6870 +e3 7ca7 +2a a6ec +4 218 +79 1761 +8e 6874 +2c a6f0 +61 1623 +7b 1765 +8c 627a +48 9268 +d4 f310 +55 b9b3 +3e 855e +fc 7dfa +36 a79e +b1 4721 +fe 7dfe +b3 4725 +95 e39b +af e4dd +7a 156c +60 142a +80 c228 +16 a994 +46 123e +44 3218 +3e a7de +b9 4761 +20 8620 +d8 5b62 +1e a9d4 +4 a892 +4e 127e +3e ddc +24 c9a +44 ba98 +74 951a +10 8b20 +f3 5725 +c0 d228 +6e 14de +f8 5dc2 +af e65f +30 ad02 +2e 8654 +da 5b6e +4b 9ae5 +8f 6af7 +70 b588 +b9 6569 +92 6386 +7e 1fd6 +32 ad06 +1 2809 +dc 5b72 +9 88eb +88 c248 +2 2224 +f0 7502 +1a 8144 +0 8002 +e8 dc4a +62 3c26 +82 ea24 +a0 44a8 +3f a75f +67 363d +49 98eb +c8 d248 +42 3224 +81 e003 +9b e145 +34 adba +42 9006 +11 b09 +12 a926 +80 4808 +d 88fb +8c c258 +6 2234 +f4 7512 +1e 8154 +4 8012 +2 82ac +5e 9154 +44 9012 +85 e013 +9f e155 +44 1098 +c2 f224 +2c 8472 +6 816 +b2 cd06 +e6 763e +54 11b0 +16 a99e +91 4921 +7 3d +31 2709 +71 9d2b +6a 3664 +9a c3cc +80 c28a +b3 4787 +16 a936 +84 4818 +17 a937 +85 4819 +b6 cd16 +58 11c0 +35 2719 +81 c009 +de 7374 +7 29d +c4 7232 +e9 fe69 +46 9016 +15 b19 +54 1198 +d2 f324 +c1 58a9 +3c 8572 +16 916 +a4 6698 +21 8c03 +3b 8d45 +1b bed +1 aab +6f 367d +4a 9046 +19 b49 +9f 63fd +85 62bb +41 92a9 +29 2ee3 +43 3025 +c9 d049 +79 95e9 +bd 65fb +f6 7d94 +3d a7d9 +7 8a37 +88 4a40 +2 282c +1f 235d +5 221b +da fb4c +c0 fa0a +e6 76be +6 896 +6b 1cc7 +ad ecd9 +78 1d68 +2a 6ce +e1 dc89 +41 10a1 +f4 7738 +e7 7697 +d4 591a +98 4b42 +da d3e4 +c0 d2a2 +a3 64af +bd 65f1 +c5 d819 +e 25e +4d 9279 +12 292e +0 828 +62 9e0c +a6 6e1e +ee fef4 +58 9142 +11 8ba9 +1a 8946 +5a 9946 +8e 427e +96 e334 +85 48b9 +c2 f284 +2c 84d2 +ea 76ee +5a 9146 +29 c49 +ca dacc +2d 653 +e9 dce1 +32 726 +e9 56e3 +1a 8944 +0 8802 +e3 d487 +f0 d528 +9 acb +23 c0d +b1 e723 +a0 4ca8 +3f af5f +52 b106 +21 2c09 +fc 5f72 +2 2886 +bf e7d5 +a5 e693 +70 1722 +ad e6d3 +78 1762 +bc e778 +af e6d7 +7a 1766 +29 2ce1 +92 4326 +b2 4726 +4d 18f1 +85 68b3 +6e 345e +41 98a1 +9f 69f5 +e2 de2c +8 a068 +39 8fc3 +53 9105 +97 6117 +42 9806 +48 3240 +db 5167 +a8 cc6a +1b 36d +1 22b +63 b40f +7d b551 +d8 5362 +3b 76d +21 62b +1b a1e5 +1 a0a3 +57 b995 +77 1f1d +8b 62cd +f8 5762 +1d 23f9 +d3 db85 +29 66b +72 178e +3b 2fcf +55 3111 +fa 5766 +78 374a +a9 6ce1 +a5 ec93 +bf edd5 +70 1d22 +ef 567f +30 8582 +4b 1ae5 +34 8592 +a4 6e98 +8 8842 +d8 53c0 +76 151e +be 4f56 +9b 6167 +d1 510b +19 8949 +7d 1751 +63 160f +33 a725 +98 6160 +52 9906 +86 423e +1f 237d +5 223b +6e 3cfe +23 625 +ed 765b +19 969 +11 309 +81 4aab +9b 4bed +99 6961 +e9 5e41 +f 207d +d2 fb26 +15 8b91 +c8 52c2 +e2 5404 +8d 687b +49 9869 +1f 9ff +9e 435c +84 421a +aa 466e +45 1839 +76 9d36 +f3 df2d +19 a169 +5f 19ff +de 535c +c4 521a +be edd4 +ee 567e +a4 ec92 +68 1668 +48 b0e0 +43 380f +5d 3951 +3a a544 +20 a402 +9a e9c4 +80 e882 +ca 526e +a8 4668 +f1 578b +b8 6d42 +5a 9bee +74 9d30 +c0 5228 +a0 eca0 +79 1769 +59 b1e1 +e8 5668 +91 4329 +b9 4769 +45 3813 +5f 3955 +2d 26f3 +34 8dba +62 94a4 +a6 64b6 +f9 5769 +2a 8e4e +ed f6db +23 86a5 +1a a144 +0 a002 +aa 4e6e +19 abc3 +33 ad05 +3e a554 +24 a412 +9e e9d4 +84 e892 +ce 527e +2e 8674 +77 9797 +1e a154 +4 a012 +1d abd3 +37 ad15 +5f 13f7 +a1 e409 +af 4e57 +3f ff7 +81 e009 +6d 9c59 +8f 4a57 +ed de7b +6 a016 +90 4908 +28 a442 +81 6829 +b2 ed26 +94 4918 +f3 57a5 +c0 d2a8 +2a ac66 +98 4b48 +13 ab27 +81 4a09 +21 84a1 +4b 90c7 +9c 617a +58 9168 +d0 7900 +17 ab37 +85 4a19 +e1 7e89 +3c a752 +c1 7a89 +ea dec6 +10 a102 +ba 4f6e +32 a506 +1 2009 +8b 68ed +92 e986 +dc 5372 +f9 df6b +12 a106 +bc 4f72 +4f 10f7 +91 e109 +7d 9d59 +9f 4b57 +fd df7b +16 a116 +d4 5918 +6 8a36 +12 292c +f2 df06 +18 a142 +38 a542 +91 6929 +5a 3944 +40 3802 +6a 1e64 +c6 5a16 +98 41ca +d1 7383 +eb 74c5 +f6 df16 +1c a152 +1a a946 +53 11ad +5c 3bf0 +42 3aae +da 5b6c +c0 5a2a +30 72a +10 a1a2 +49 12eb +63 142d +82 408e +9c 41d0 +d9 7941 +43 b2a5 +d8 536a +2e a4fe +f0 572a +d0 f1a2 +10 2928 +3 2887 +1e 215e +63 b6a5 +f8 576a +28 8642 +81 4a29 +b ac5 +fe 7556 +f4 f532 +37 859d +a1 4e21 +26 ae9e +1e a954 +4 a812 +27 8ebd +85 6011 +f6 7596 +97 6397 +a4 6438 +53 9385 +de d3fe +f8 d540 +81 e809 +6 a816 +8 a842 +9 22c1 +10 8988 +68 9c48 +ac 6c5a +9 8a49 +c a852 +a2 4606 +c 8a5a +3f f57 +8 84a +83 c2a5 +a5 e4b9 +63 14a7 +41 3a03 +5b 3b45 +a 2866 +d5 53b1 +2f a65f +90 43a8 +52 9106 +21 c09 +2 886 +3d af5b +47 1837 +89 e849 +71 3da1 +e2 d624 +f5 f599 +e a856 +a7 4c1f +5 aab9 +fa df64 +67 3e9f +e0 de22 +c3 702f +dd 7171 +68 9662 +38 f42 +9 a69 +3a 8f66 +18 a942 +7a 976c +be 677e +60 962a +4b 98c7 +9c 697a +58 9968 +18 a960 +a1 ce09 +af 64fd +51 93a9 +95 63bb +48 184a +c3 d2a5 +ce 70fe +70 9faa +1a 83ec +0 82aa +d3 59a7 +6d 9c79 +46 9a96 +c 225a +52 9b2c +96 6b3e +44 12b0 +ba e546 +89 6049 +84 e898 +42 1886 +5e 9bdc +44 9a9a +2e e74 +77 1f97 +b8 e54a +8f e2fd +10 a9a0 +23 e87 +30 f28 +93 4b85 +1e 835c +d7 5917 +4 821a +d4 db90 +37 717 +94 e110 +64 1c90 +68 b66a +68 1448 +d8 5bea +2c 86d0 +f2 5d2c +31 781 +96 e114 +50 130a +81 48a1 +6a 144c +da 5bee +2e 86d4 +f4 5d30 +3f df7 +a4 4612 +be 4754 +83 48ad +6c 1458 +51 1b81 +dc 5bfa +f6 5d3c +37 795 +db f36f +d7 5995 +4 8298 +a6 4634 +20 2420 +75 9f91 +dc dbd0 +c2 da8e +3f 757 +0 80a +1a 94c +91 4381 +b1 4781 +46 129e +d1 5381 +f 28dd +88 c860 +47 909f +92 412e +f1 5781 +f3 5785 +c0 d288 +a8 e46a +71 3581 +fd f75b +7e bdfe +33 8725 +99 c3c3 +b3 c505 +a2 ee06 +63 3c85 +95 69b1 +64 341a +7e 355c +f1 5d83 +38 87c8 +3e 875c +55 bbb1 +f7 5d17 +24 861a +ee def4 +14 a130 +f2 df24 +18 a160 +4c 925a +c5 5a31 +d3 51a7 +cb 7845 +ba cdec +b3 6725 +a0 ccaa +b4 ed9a +69 b6c1 +6a 366c +7e 9dd4 +64 9c92 +1b 8b6f +30 a520 +95 cbb1 +7e 975c +64 961a +1a a3ee +34 a530 +8d 6873 +49 9861 +c6 52bc +5 a239 +a0 ee08 +88 404a +c1 7203 +c8 d8ca +42 38a6 +db 7345 +a4 6418 +4 2218 +8e 6afc +a5 6419 +3f ddd +25 c9b +fc 7d72 +36 a716 +8f 6afd +93 6387 +a0 6428 +a6 4c16 +4 aab0 +c3 f8af +dd f9f1 +ac c45a +26 2436 +a1 6429 +d0 d102 +a7 4c17 +5 aab1 +b5 6519 +f8 5768 +eb 56c7 +15 2319 +85 6abb +41 9aa9 +9f 6bfd +d3 f9af +bc c55a +36 2536 +11 2329 +70 35a0 +82 4a06 +a7 64b5 +54 31ba +70 1782 +ce 5ad6 +2f 865d +e6 fc1e +2 8aa6 +17 8b95 +a2 cc0e +bc cd50 +39 a7e1 +4e 985c +f1 5789 +76 1796 +ca daec +2d 673 +53 9927 +50 3182 +42 9a8e +5c 9bd0 +fd 5579 +d6 5396 +8d 4273 +d7 f9bd +20 2402 +3a 2544 +12 8326 +70 3582 +f6 5796 +ad 4673 +89 406b +d2 518e +6 8016 +a9 ee69 +50 9182 +78 954a +76 1d36 +b8 ed48 +87 60bf +43 90ad +e 2a5c +70 958a +14 a192 +65 3cb1 +34 71a +a4 6492 +60 9480 +be 65d4 +b 267 +f0 57a8 +f0 7582 +88 c2c8 +2 22a4 +63 9607 +24 a498 +25 a499 +20 a4a8 +2e ef6 +b8 cd42 +20 8c28 +13 8b87 +ec 767a +21 a4a9 +38 a56a +35 a599 +ec f65a +22 8624 +31 a5a9 +26 8c3c +c 8afa +30 a5aa +b3 6507 +55 93b3 +6f 94f5 +20 688 +61 3e09 +fb 776f +24 698 +31 789 +22 e06 +b6 451e +14 a3b8 +60 1688 +f2 772e +35 799 +c1 7a09 +16 a3b6 +84 4298 +7 883d +2c a472 +44 3098 +32 a7a6 +a0 4688 +e1 7e09 +90 c122 +17 219f +36 a7b6 +a4 4698 +52 b3a6 +c0 5288 +2f 26df +a8 c662 +87 683f +43 982d +2a e64 +3d 2dd9 +b5 ef99 +73 1f87 +1a 2944 +0 2802 +86 4a16 +91 6383 +ab 64c5 +64 9c18 +58 31ca +56 b3b6 +c4 5298 +a2 64ac +a2 4e06 +74 35ba +72 b7a6 +e0 5688 +75 1799 +3d 579 +16 396 +36 796 +b1 4789 +1 223 +1b 365 +b5 4799 +85 683b +9f 697d +41 9829 +9d 4979 +2a 2cec +a9 6649 +10 2baa +4f 1af5 +7d 1579 +56 1396 +d 273 +63 b4a7 +d1 5389 +67 b4b7 +d5 5399 +c8 d2e2 +e2 d424 +ca 58e4 +11 8329 +22 8c86 +28 2660 +28 8442 +81 4829 +b2 cd26 +29 649 +fa dd46 +c8 50c0 +3f 2dff +be 675c +60 9608 +a4 661a +dd fbf1 +c3 faaf +ac c65a +26 2636 +30 78a +67 969d +34 79a +70 178a +81 e021 +ea 56cc +53 9b2f +6d 9c71 +74 179a +d1 d921 +1a 366 +b4 479a +31 2d81 +23 8ca7 +a2 c604 +e7 549d +a0 e4a0 +17 b15 +53 992f +38 2548 +2a 846c +10 832a +ae ecfe +63 b625 +b2 65ae +ed 5cd3 +34 8718 +f0 578a +f4 579a +99 e941 +e1 d629 +f a05d +30 872a +48 9242 +a9 444b +c0 78a0 +4a 9246 +e0 7ca0 +15 2bb9 +6a 9646 +c3 5a2d +19 abc1 +2e 2cfe +ad 665b +69 9649 +99 cbe1 +68 964a +19 9c1 +62 9686 +2c a4d8 +b a4d +c5 7019 +f6 f516 +7a 3d64 +60 3c22 +80 ea20 +ca 78ee +1e a3d4 +4 a292 +ad 4479 +86 4296 +64 9498 +8f 6ad5 +e4 5432 +fe 5574 +17 8bbf +31 8d01 +65 3639 +b5 cd9b +ae 66d4 +0 80 +8d 68db +49 98c9 +34 a598 +30 a5a8 +80 e008 +3e ff6 +ff 7d57 +2c a65a +6 36 +26 ae34 +25 a699 +87 681d +a7 469d +4 20b2 +1e 21f4 +21 2489 +fc 57f2 +a6 469e +45 1293 +5f 13d5 +2c 8ed8 +49 b249 +40 38a0 +29 44b +b3 4d2f +5b 13c7 +9d e3d9 +68 1468 +de 515c +c4 501a +e4 fe18 +31 7a1 +9f e3dd +85 e29b +50 132a +6a 146c +7d 1759 +43 b08f +5d b1d1 +a4 4632 +be 4774 +6d 345b +84 68b0 +e 8256 +71 17a1 +5 a039 +8a c0e6 +f8 5fc8 +6f 345f +86 68b4 +73 17a5 +9e 63fc +84 62ba +40 92a8 +18 b48 +57 191d +4e b256 +3f 7fd +25 6bb +b9 e543 +43 12ad +24 a4ba +3e a5fc +b1 47a1 +e2 de06 +8 a042 +92 e926 +52 b926 +c0 5808 +16 a99c +3 8a27 +d0 590a +99 43cb +b3 450d +b0 6522 +2a 2464 +10 2322 +55 333b +6f 347d +84 6818 +85 6819 +40 1a80 +b6 ed16 +80 6828 +8c c85a +6 2836 +95 6919 +c2 d804 +2c 24d2 +f1 d701 +72 9da4 +b6 6db6 +d2 798e +87 42b5 +4e 10fc +34 fba +2b 2cc7 +38 2d68 +a1 ec89 +1 20a1 +49 3241 +50 9908 +94 691a +9c c95a +16 2936 +58 994a +91 cb03 +ab cc45 +a0 4400 +86 42be +9 8863 +3a 25e6 +65 96b3 +7f 97f5 +8a e8ec +c7 5237 +1c a35a +1b abc5 +1 aa83 +49 32c3 +63 3405 +50 998a +20 c22 +3a d64 +a3 cc85 +b0 67a2 +34 a592 +51 190b +30 a5a2 +d a859 +43 9a07 +9 a869 +5 a899 +fa dd44 +e0 dc02 +1 a8a9 +19 a969 +20 6a8 +22 e26 +35 2d9b +80 42a8 +a0 46a8 +84 68b8 +33 af0f +4d b051 +a2 4e26 +71 9d89 +b5 6d9b +d0 5908 +da 5b4c +c0 5a0a +91 4b21 +16 ab9e +e5 74b9 +28 2e68 +81 6a29 +d4 d910 +3e 25de +4a 9a46 +b2 efac +1c 81fa +55 b3b3 +6f b4f5 +8d 6a5b +49 9a49 +48 9a4a +96 c9be +4b 92e5 +8f 62f7 +bd ef59 +7b 1f47 +6d b4f9 +ca 5ac6 +2b 864d +5 8239 +50 b120 +6f 165f +86 4ab4 +64 9438 +57 9397 +a8 644a +8 224a +ac 6c70 +92 6b2e +4b 9a6f +fe 7ddc +e4 7c9a +4a 3866 +9f 4957 +3d 87d3 +31 d29 +28 a662 +40 3288 +1d 95b +20 480 +1c a958 +52 9b06 +77 b5b5 +47 12b5 +92 498e +14 a998 +1b 8b65 +1 8a23 +4d 12f3 +67 1435 +10 a9a8 +4c 3278 +46 989e +13 185 +29 2c69 +2 2a86 +28 e60 +2d 8cfb +c3 faad +26 2634 +ac c658 +17 195 +1 83 +1b 1c5 +a 2ac6 +1c 2970 +2 282e +88 4a42 +e 2ad6 +6 283e +c8 d268 +0 2008 +8a 68ec +17 397 +24 438 +f5 5533 +86 6a94 +5 a219 +20 2408 +90 6baa +aa 6cec +dd 7bd3 +f7 7d15 +24 a618 +a0 6408 +4 aa90 +0 2208 +8a 6aec +5c b958 +d8 73c2 +f2 7504 +21 2409 +d2 d9a4 +a6 6e94 +de 7bd4 +c4 7a92 +25 a619 +c3 f88f +dd f9d1 +26 2416 +a1 6409 +5a 93ee +b8 6542 +74 9530 +5 aa91 +3b dcd +21 c8b +f8 7d62 +32 a706 +8b 6aed +79 3561 +4 2018 +8e 68fc +84 6018 +5e 137c +44 123a +14 a990 +34 f18 +de 537c +c4 523a +94 e990 +11 2989 +66 9416 +35 f19 +27 497 +34 538 +df 537d +c5 523b +95 e991 +91 6989 +e6 d416 +47 b037 +b5 4f19 +7e 177c +64 163a +34 ad90 +54 1318 +9c 61d2 +58 91c0 +ee 7cd4 +d4 7b92 +35 a719 +87 689d +d3 f98f +36 2516 +85 68bb +41 98a9 +9f 69fd +38 5e0 +94 4192 +0 2808 +4 aa18 +37 2f15 +b8 45e0 +80 6808 +5 aa19 +b9 45e1 +c2 7024 +a8 6ee2 +6 2816 +81 6809 +b2 ed06 +11 2909 +89 eac9 +47 1ab7 +26 8616 +ec 5c72 +b2 cd8c +ab 66c5 +64 9e18 +15 ab19 +16 2916 +e8 5ee2 +b8 cfc2 +d2 d104 +1 8009 +35 25bb +43 b885 +d8 594a +91 6121 +63 1c2f +7d 1d71 +ed 7cf9 +27 a69d +48 3060 +91 4183 +90 49a2 +26 aebc +6 be +d4 731a +a 2e4 +ee 745c +3d dd3 +d3 7b85 +49 10e1 +f6 dd9e +fc 7778 +ef 76d7 +47 b895 +dc 595a +28 24c0 +b2 6da4 +ae 46d4 +dc d3d2 +f6 d514 +aa e66e +a6 4c94 +dd 595b +8a 48c4 +9 8049 +99 6161 +c4 d012 +de d154 +be 4ffc +a4 4eba +8e 48d4 +d 8059 +2f 2e57 +15 8319 +ce 58d4 +c2 5aa6 +23 862d +4d 9059 +11 a303 +2b a445 +0 888 +11 8109 +1a ab4c +0 aa0a +30 8508 +52 3306 +14 8118 +36 2f16 +2b cc7 +38 d68 +a1 cc89 +1 a1 +19 ab49 +9a e36e +96 4994 +4d 3871 +15 8119 +1e ab5c +4 aa1a +37 2f17 +1f abfd +5 aabb +6 9c +dd 7173 +3e 25dc +24 249a +59 11e1 +34 8518 +56 3316 +ba e76e +b6 4d94 +80 4882 +9a 49c4 +19 8149 +8 aa4a +63 b6a7 +84 4892 +9e 49d4 +1d 8159 +3f 2f57 +67 b6b7 +12 926 +3a 76e +f1 dd29 +b6 e53e +40 12a8 +2 a26 +15 299b +be 4fdc +a4 4e9a +a a66 +10 21a0 +50 31a0 +9c c15a +16 2136 +91 6129 +56 1b96 +7d 1d79 +9a 4b64 +80 4a22 +c9 d8eb +c2 7224 +2c 472 +4c b270 +d a253 +1 a3 +1b 1e5 +aa cc4c +90 cb0a +a 2ae6 +88 4a62 +5e 995e +d2 d904 +3c 25d2 +11 21a1 +1 8809 +51 31a1 +7a b54c +91 e9a1 +60 b40a +9d 4959 +14 21b0 +bd 6d73 +79 9d61 +91 6921 +e1 5e01 +7 203d +c9 7ae3 +e3 7c25 +90 492a +59 3341 +a6 44b4 +9 2241 +10 8908 +e6 54b4 +5 a8b1 +1 28a9 +56 9336 +25 e39 +11 8909 +95 691b +51 9909 +90 4382 +aa 44c4 +d 2251 +14 8918 +d0 5382 +ea 54c4 +91 4383 +ab 44c5 +58 11ca +15 8919 +21 8421 +29 84e1 +87 62b5 +9e 6b5c +84 6a1a +40 9a08 +73 1f05 +4e 30fc +34 2fba +3 887 +10 928 +72 9f0c +b6 6f1e +80 4828 +a1 c421 +83 4887 +90 4928 +b1 c521 +ba ef64 +a0 ee22 +30 70a +61 3ca1 +e5 f499 +6f 34d7 +7c 3578 +8b 48c7 +98 4968 +1 a009 +f a57 +86 c0b6 +f4 5f98 +86 c094 +23 ae2f +3d af71 +b3 6f25 +1d 173 +8 a262 +11 929 +73 9f0d +b7 6f1f +1 a29 +32 8f26 +a2 448c +cb 52e5 +c7 f09d +78 1fea +8d 4271 +d8 794a +54 11b8 +16 936 +88 e262 +91 4929 +ce f2f4 +38 8542 +79 b561 +ef d657 +80 6a88 +ba e54e +44 12b8 +89 4a69 +2f e57 +12 924 +dc 795a +dd 79d3 +24 a418 +48 ba6a +48 1848 +11 b81 +43 38ad +2c 458 +a6 6c94 +8a 68c4 +e3 de0d +9 a049 +8e c0f6 +fc 5fd8 +25 691 +e2 7626 +4c 1858 +4b 92cf +65 9411 +b0 edaa +d2 7b26 +15 b91 +a0 4c0a +ba 4d4c +8e 68d4 +9d 6b73 +59 9b61 +e7 de1d +d a059 +43 9207 +cf 5a7f +92 6984 +b2 6d84 +ef dedd +15 a119 +b6 6d94 +d4 7992 +35 a519 +c0 5822 +da 5964 +44 10b8 +6 836 +31 2729 +94 c192 +88 4a6a +39 85e1 +42 b024 +28 aee2 +1 a809 +79 bd61 +5 a819 +4e 1254 +11 a909 +98 6b6a +47 9ab7 +15 a919 +cb d8c7 +d8 d968 +59 1be3 +73 1d25 +9e 697c +84 683a +40 9828 +f 85d +61 b689 +95 e393 +af e4d5 +7a 1564 +60 1422 +7 229d +80 c220 +9d e3d3 +b7 e515 +68 1462 +f 22dd +88 c260 +76 15b4 +ec 7cf2 +26 a696 +82 4026 +c 8072 +a2 ee24 +8a 4066 +aa ee64 +53 1b25 +66 b696 +ca 5066 +ea fe64 +14 992 +12 ab0e +2c ac50 +34 85b2 +54 9118 +93 6b07 +4f 9af5 +55 b313 +6f b455 +44 1898 +c2 fa24 +2c 8c72 +1e 2976 +a7 4e1f +d2 fb2e +ec fc70 +15 8b99 +dc 7b52 +69 1ce3 +e8 5640 +ea 7cee +24 a692 +3e a7d4 +47 98b7 +c6 d214 +98 696a +47 989d +6d 1cf3 +ec 5650 +88 42ea +a2 442c +60 3402 +7a 3544 +e6 7c1e +2 aa6 +72 9f8c +b6 6f9e +ab e4c7 +b8 e568 +91 e323 +ab e465 +80 48a8 +1f ab5f +a1 e423 +bb e565 +90 49a8 +92 e386 +b9 e569 +15 b11 +51 1b01 +0 0 +8a 48e4 +91 4b01 +7 21d +93 4b05 +5a 194c +40 180a +95 4b11 +11 2901 +97 4b15 +13 292d +99 4b41 +c1 d2a1 +f 25d +15 2931 +17 293d +83 4a0f +9d 4b51 +4c 185a +d2 710e +74 9fba +12 18c +d5 5b11 +9a 6164 +80 6022 +6c 1c72 +de 5176 +20 8c00 +6 8abe +99 cb49 +13 2b25 +77 351f +51 3901 +14 190 +d9 f149 +d7 5b15 +82 6026 +6e 1c76 +18 1c0 +88 6062 +96 69b4 +7f 355f +2 8e +1c 1d0 +8a 6066 +10 aba0 +b2 4d06 +f8 55c8 +3b 87e7 +b0 67aa +ef 56f5 +c2 5aa4 +93 e98f +dd 537b +33 a50f +9f 637d +85 623b +41 9229 +fc 55d8 +b5 4fb9 +90 c920 +17 299d +63 bc27 +d1 5b09 +27 ac9d +56 1b16 +61 3483 +7b 35c5 +48 b0c8 +54 bbb0 +3d 875b +f6 5d16 +9c eb58 +5a 1b46 +65 34b3 +7f 35f5 +4c b0f8 +58 bbe0 +fa 5d46 +f1 5fa9 +8f e27d +10 a920 +5e 9b5c +44 9a1a +77 1f17 +69 b4c9 +27 8c95 +9 2cb +23 40d +5 aa39 +9a 494c +80 480a +3d d53 +d3 7b05 +90 4922 +6 3e +90 e920 +e0 de00 +6 a03c +f9 5fcb +8e 6ad4 +1f 3d7 +2c 478 +f5 7d19 +11 ba1 +75 159b +fd 5573 +c6 7814 +d a259 +16 8bbe +af c65d +30 8d00 +64 3638 +89 42cb +a3 440d +1b b4d +1 a0b +cf 725f +50 3902 +d6 5b16 +e1 7483 +fb 75c5 +e8 5c60 +22 8604 +9 a4b +da 5b46 +e5 74b3 +ff 75f5 +ec 5c70 +d2 5b2e +26 8614 +d a5b +de 5b56 +d0 f108 +14 a930 +1b bc7 +28 c68 +71 1d8b +9 aa49 +9e 495c +84 481a +d7 7b15 +49 ba49 +5b 1bc7 +9d ebd9 +68 1c68 +de 595c +c4 581a +d aa79 +36 d9e +aa ccec +90 cbaa +a3 6625 +9b c34f +1c 89f2 +4d 1a7b +aa 6646 +66 9634 +98 4962 +2e ae7c +38 274a +ff 57d5 +e5 5693 +98 e960 +e8 de40 +e a07c +c8 584a +99 4961 +1e a9de +1e 8bfe +38 8d40 +b5 479b +c 85a +45 3a13 +5f 3b55 +a9 e4c9 +67 14b7 +a5 ce19 +0 a08 +33 a5ad +32 ad26 +a0 4c08 +4 a18 +6c bcda +21 8601 +72 9d2c +6b 3665 +b6 6d3e +58 9bea +3a ad66 +a8 4c48 +a3 64a7 +3e ad76 +ac 4c58 +de 7b74 +c4 7a32 +7 a9d +9e 63dc +40 9288 +84 629a +cc 5ad2 +2d 8659 +e6 5c14 +40 1a08 +72 bd26 +e0 5c08 +36 ad9c +d0 5b02 +ea 5c44 +a9 466b +f2 578e +44 1a18 +76 bd36 +e4 5c18 +48 1a48 +7a bd66 +e8 5c48 +43 1a8d +2b 8e67 +24 ac9a +3e addc +44 9838 +88 684a +4c 1a58 +7e bd76 +ec 5c58 +47 1a9d +12 ab26 +80 4a08 +92 418e +52 bb26 +c0 5a08 +85 e811 +59 1b49 +56 bb36 +c4 5a18 +3a 874c +f3 5d07 +51 bba1 +20 860a +77 9d1d +5d 9bdb +55 931b +6f 945d +df dbff +f9 dd41 +c1 d203 +db d345 +42 98a6 +fd 5d59 +3e a75c +f7 7d17 +24 a61a +6b bc67 +d9 5b49 +6f bc77 +dd 5b59 +9 a861 +52 b984 +86 62bc +2 a84 +1d 35b +3e 255c +24 241a +1e 235c +4 221a +1e a35c +d7 7917 +4 a21a +6c 1c5a +6 2a34 +8c ca58 +a5 4e13 +bf 4f55 +e0 542a +fa 556c +55 13b1 +1b 23c7 +28 2468 +7d 9fd9 +40 9008 +9e 615c +84 601a +30 a7aa +cd d279 +92 692e +ba cdce +b3 6707 +6f 96f5 +48 3260 +42 9886 +c2 fa8e +dc fbd0 +3f 2757 +85 6a31 +38 254a +aa ecec +90 ebaa +e7 5637 +3c a75a +18 234a +2e a45c +14 a31a +de 537e +94 e992 +2b 24c7 +38 2568 +a1 e489 +50 9108 +94 611a +b3 47a5 +80 c2a8 +4b 10c7 +8d e0d9 +58 1168 +c1 d089 +21 4a1 +69 1641 +b4 4d1a +4f 9a5d +6d 1671 +23 ac85 +b8 4d4a +fa d5ec +e0 d4aa +9a e966 +4f 185d +bb 6fcf +d5 7111 +77 9fbd +29 4e1 +60 b680 +27 ac95 +bc 4d5a +ee 7c76 +17 b9f +20 ac20 +f0 5d0a +2 a2c +61 14a1 +f4 5d1a +6 a3c +9e e954 +84 e812 +63 bc85 +f8 5d4a +a a6c +6d 1cd1 +53 1b8f +69 14e1 +67 bc95 +fc 5d5a +57 1b9f +e a7c +9a 694c +80 680a +1e 295c +4 281a +1b 2bc7 +28 2c68 +40 9808 +9e 695c +84 681a +f4 ddb8 +ed 76f1 +16 91c +c 285a +b7 6f17 +73 9f05 +4e b0fc +34 afba +48 9848 +8c 685a +6c 147a +10 290a +4f 1855 +bb 6fc7 +c8 7068 +77 9fb5 +83 42a5 +f9 55c3 +8a 6846 +46 9834 +b9 6743 +75 9731 +3a 2de6 +1e abde +99 4b61 +29 86c9 +e2 5c84 +fd 555b +f 27d +42 b804 +d2 fb0c +3c 8d5a +75 bf13 +8f c055 +5d 1959 +51 b9a1 +3a 854c +20 840a +be edde +73 b705 +ec 7cfa +a1 4621 +26 a69e +56 bb9e +d1 5b21 +47 123d +28 ac48 +30 85aa +41 3003 +5b 3145 +5e bbde +d9 5b61 +5 a891 +4f 127d +82 c804 +18 1e0 +74 3d92 +c1 5a23 +db 5b65 +6 aab4 +f4 fd92 +9e e9de +53 b305 +99 c9e1 +68 944a +48 904a +bd e559 +7b 1547 +c6 f2bc +30 850a +99 6163 +86 e096 +d7 7bb5 +a6 461e +11 292b +12 b26 +3a dec +b9 4749 +20 caa +23 ac0f +3d ad51 +a9 c6c9 +23 26a5 +63 bc0f +7d bd51 +1b b6d +1 a2b +9 a6b +6c 1cd0 +52 1b8e +da 5b66 +1e 895c +4 881a +c0 7a28 +37 d17 +57 bb15 +64 9618 +b8 cde8 +b1 6721 +5e 995c +44 981a +77 1d17 +48 bac2 +62 bc04 +ad c6d9 +27 26b5 +f2 ff0c +5c 915a +95 c313 +16 89b6 +af c455 +5b 99c5 +9f 69d7 +41 9883 +23 a625 +8 884a +63 94a7 +3b d47 +41 ba03 +5b bb45 +41 ba01 +db f367 +ca 58ec +4 8290 +88 6060 +48 984a +a8 6662 +43 382d +2b a6e5 +a6 44b6 +8d cad3 +a7 cc15 +57 91b5 +9b 61c7 +6c bcfa +21 8621 +8 a68 +26 cbe +a5 461b +bf 475d +6b 1ccd +62 b606 +51 1b8b +58 1340 +11 b29 +42 9026 +40 1a28 +d7 f195 +d0 5b22 +ea 5c64 +19 b69 +4a 9066 +73 b707 +62 1c8c +96 6394 +48 1a68 +66 1cbe +e5 561b +ff 575d +88 4a68 +c0 5a28 +59 1b69 +c8 5a68 +5d 3959 +63 9ea5 +a7 6eb7 +d9 5b69 +25 8e13 +3f 8f55 +1e a15c +4 a01a +e2 de0e +fc df50 +8 a04a +d8 f9c0 +3b 2547 +e6 de1e +c a05a +c2 f88e +dc f9d0 +3f 2557 +ee dede +14 a11a +18 b6a +6b 3e65 +1c 23fa +36 253c +4b 186d +c3 5027 +4d 9073 +e3 fe25 +d6 dbbc +39 743 +f2 df0e +18 a14a +1e a95c +4 a81a +37 2d17 +f6 df1e +1c a15a +3b f4d +21 e0b +8 a84a +ae ce54 +c a85a +3f 2d57 +52 b98e +7 82b5 +14 a91a +d5 f31b +ef f45d +56 b9be +b 82e5 +18 a94a +e1 5489 +73 b5a7 +35 251b +4a 184c +a7 4417 +5 a2b1 +9a 41c4 +80 4082 +a 80ce +a0 ee80 +c7 d2bf +e1 d401 +c9 d2c3 +e3 d405 +ca 584c +71 9509 +b5 651b +cb d2cf +e5 d411 +f 8a77 +95 4b91 +cd d2d3 +e7 d415 +ce 585c +6c 96d8 +97 4b95 +d5 5b91 +11 8b21 +75 951b +9a 61e4 +80 60a2 +eb 564f +6c 1cf2 +bc 45d2 +1a 8b6c +0 8a2a +33 f27 +88 e86a +bb 6d67 +51 3981 +72 97a4 +b6 67b6 +d7 5b95 +dd fb5b +13 8b25 +77 951f +d2 d984 +28 46a +48 b268 +88 406a +4d b25b +1c 29d8 +9 a63 +ea 7cec +24 a690 +d0 7baa +cf 72df +50 3982 +88 62e2 +a2 6424 +8d 4a73 +a1 6421 +db 7b6f +2f a655 +4 a98 +83 4207 +e8 7c60 +22 a604 +11 b89 +42 9086 +c a872 +2f 2edf +a8 ce62 +47 9a9d +e1 d421 +2 820c +c8 5868 +9d e359 +4 a8ba +5b 1347 +1e a9fc +79 3549 +33 872d +d2 5ba6 +62 1404 +48 12c2 +9c 69f8 +c0 50a0 +d9 fbc1 +22 2606 +c8 50e0 +c7 fabf +e1 fc01 +2a 2646 +45 183b +5f 197d +3e 8574 +24 8432 +46 9234 +8a 6246 +68 b448 +a2 6606 +64 9630 +a8 6642 +44 10b0 +25 ae91 +1f 1d5 +5 93 +6 a89e +81 4821 +f2 fd06 +21 2609 +fa fd46 +29 2649 +1d 17b +b3 6f2d +82 eaa6 +74 1d18 +88 60c8 +10 8120 +c4 50b0 +c3 fa8f +dd fbd1 +26 2616 +f4 5d18 +7a 15e4 +60 14a2 +80 c2a0 +58 3b40 +1f 29ff +9e 635c +40 9208 +84 621a +48 9248 +8c 625a +70 9702 +c9 5ae9 +3b 2def +ba 674c +a0 660a +ed fcdb +23 8ca5 +24 843a +3e 857c +9e 697e +40 982a +5a 996c +83 e20d +4 a8b0 +24 e38 +93 e30d +14 a9b0 +27 e97 +34 f38 +eb 5eed +11 2129 +41 ba09 +95 eb99 +53 1b87 +60 1c28 +37 8fb5 +88 6068 +2e 6de +e5 dc99 +6f 1cd7 +7c 1d78 +8b 60c7 +47 90b5 +98 6168 +8c c25a +6 2236 +95 c991 +81 6229 +6d 1e79 +1c 835a +4d b8f1 +65 1c19 +8c c05a +6 2036 +e 2076 +50 b180 +70 1708 +61 1c29 +89 6069 +ba e566 +ba cfce +d4 d110 +9a 636c +94 c992 +80 622a +6c 1e7a +cb f24f +4c b8f2 +7e 15d4 +64 1492 +84 c290 +86 4096 +71 1f23 +a6 ee94 +c2 5086 +50 13a2 +6a 14e4 +c6 5096 +e6 fe94 +70 1f08 +2 8026 +10 2182 +2 8a8e +1c 8bd0 +2a 84e4 +10 83a2 +f4 f51a +6 a23c +da 79ee +2e a4d4 +14 a392 +d1 590b +14 2192 +94 eb98 +52 1b86 +79 1d69 +d5 591b +73 3f0f +8d 4051 +1a 23c4 +0 2282 +ba 65c4 +a0 6482 +68 96c8 +ac 66da +bc ed5a +17 ab9f +29 2469 +2 2286 +a2 6486 +b6 4796 +74 9d3a +6d 3673 +30 2582 +22 8e8e +3c 8fd0 +5f 137d +15 a991 +45 123b +90 4122 +df 7bf7 +26 a63c +34 2592 +93 430f +14 9b2 +ad 4451 +1 a81 +1c 358 +22 2686 +43 1a0f +5d 1b51 +f6 7516 +9e c9de +97 6317 +53 9305 +84 6a10 +92 6186 +7e 1dd6 +3 a827 +6c 1ed2 +26 ac1c +c aada +da f16e +1d 81d9 +a2 6686 +0 2088 +53 3b87 +60 3c28 +35 afb1 +d7 5117 +10 2188 +20 2488 +73 3f87 +80 4028 +f7 7d95 +24 a698 +86 681c +24 2498 +11 2189 +ec 54f2 +71 3d29 +15 a399 +b8 65c2 +74 95b0 +f8 7de2 +32 a786 +26 2496 +a1 6489 +84 6098 +a0 6488 +73 95af +0 2288 +d1 d121 +da fb64 +3 8a8d +c0 fa22 +f2 fd86 +21 2689 +91 6189 +74 b712 +7d 1dd9 +81 6289 +6d 1ed9 +26 2696 +a1 6689 +1 2089 +32 a586 +f6 5534 +dc 53f2 +24 4b8 +5 a299 +25 2499 +1a 23cc +0 228a +c5 5293 +df 53d5 +1e 23dc +4 229a +28 a642 +31 d09 +34 a79a +57 319f +d0 d122 +3 807 +17 39d +d4 7332 +ee 7474 +3f a577 +14 9ba +ad 4459 +3a 27cc +20 268a +10 3a2 +2a 4e4 +f4 751a +90 6b20 +6 223c +d8 7b60 +1 a89 +9d 617b +59 9169 +32 8f86 +31 25a1 +21 8c09 +15 21bb +c2 d084 +8e e0d4 +59 1163 +50 9188 +94 619a +25 8c19 +47 3a17 +9a 63cc +80 628a +6c 1eda +1d a971 +3 a82f +ba 67cc +a0 668a +1a 894c +0 880a +33 d07 +53 bb05 +10 21a2 +2 8aae +1c 8bf0 +b 82cf +25 8411 +d1 592b +ba 65e4 +a0 64a2 +76 bf36 +e4 5e18 +5b 396f +e5 7c19 +1 aa1 +85 c299 +f 2d7 +1c 378 +2a cee +c0 7aa0 +a9 464b +fc 57d0 +e2 568e +0 20a8 +10 21a8 +90 430a +aa 444c +c1 78a1 +37 2fbd +cf 70d7 +71 9f83 +dc 7178 +4b 9247 +20 24a8 +30 25a8 +b0 470a +e1 7ca1 +ef 74d7 +fc 7578 +6b 9647 +11 21a9 +31 25a9 +26 c3c +c afa +7 2a3f +90 61a8 +7c 1df8 +10 21aa +35 fb3 +4f 10f5 +1c 8bf8 +30 25aa +55 13b3 +6f 14f5 +9c c9d8 +16 29b4 +95 6311 +e a2de +89 4261 +d4 793a +3 ad +b8 cdc8 +32 2da4 +b1 6701 +2 820e +1c 8350 +c8 586a +9d e35b +1e a9fe +5 b1 +1d ab59 +2e 8efc +8c 6050 +4a 1ace +64 1c10 +99 69e1 +68 344a +9 a8eb +46 1236 +88 e248 +30 8f00 +8e 6054 +4c 1ad2 +66 1c14 +13 8985 +df 73d7 +ec 7478 +3b def +ba 474c +a0 460a +d1 7ba1 +7c 1f52 +ca f2e4 +34 8532 +6c 96f0 +b0 6702 +c7 5a37 +99 41eb +c6 5016 +24 aeb0 +1c ab5a +4d 38d1 +cf 5a77 +1e 81d6 +c1 f029 +57 391d +9a 49e6 +30 af00 +3a 274c +20 260a +a1 44ab +bb 45ed +9a 4964 +80 4822 +30 2722 +a8 64e8 +61 be89 +84 e838 +42 1826 +d4 f31a +ee f45c +a 82e4 +28 264a +88 4862 +38 2762 +8c e878 +4a 1866 +24 ae90 +1e 1d4 +4 92 +20 2608 +14 13a +aa 6eec +84 4830 +22 86ac +24 2618 +18 14a +ae 6efc +88 4860 +47 109f +28 2648 +1f 29fd +5 28bb +84 6218 +3b 2ded +21 2cab +a0 6608 +25 2cbb +3f 2dfd +a4 6618 +d8 51c0 +1 8081 +3a 2dec +20 2caa +b9 6749 +64 1c18 +3a 2564 +20 2422 +2c 8e70 +28 2462 +40 b020 +96 4bbe +b0 4d00 +c9 5a4b +80 4a20 +1f abdf +cd 5a7b +60 b420 +c4 5892 +25 8419 +de 59d4 +e7 f695 +1d 359 +d4 5312 +ee 5454 +9e 6176 +5a 9164 +40 9022 +85 6291 +68 9462 +f9 7d63 +33 a707 +22 c8c +e6 7c36 +2d a67b +2d 459 +42 b2ac +e4 5412 +fe 5554 +fd ff73 +26 8e9c +50 9122 +df 735d +46 38be +c5 721b +2f a67f +3d af7b +52 9126 +21 c29 +b0 6722 +b2 6726 +1c 35a +4d 38f1 +f3 dd8f +f9 7769 +b8 6762 +53 392d +ba 6766 +55 3931 +8 8862 +a3 e487 +b0 e528 +be 4f76 +4a 9866 +34 8d90 +20 2628 +94 c990 +80 6228 +6c 1e78 +cb f24d +4c b8f0 +91 6329 +7d 1f79 +60 b480 +93 c98f +99 6369 +8c 6a5a +48 9a48 +96 c9bc +8f 62f5 +61 1e03 +7b 1f45 +56 313c +3c 2ffa +b3 cd8f +b9 6769 +3a a564 +20 a422 +5f 1bff +79 1d41 +8d 60f1 +ea dee6 +10 a122 +8d e051 +80 4008 +12 a126 +7f 1757 +28 acca +d 225b +53 9b2d +97 6b3f +50 bb00 +f9 fd49 +88 486a +c1 7a23 +db 7b65 +82 488e +9c 49d0 +47 92b7 +98 636a +b8 e76a +b4 4d90 +67 96b7 +b8 676a +dd 5b51 +c3 5a0f +37 8fb7 +88 606a +1e 83dc +d7 5997 +4 829a +37 797 +10 a1a0 +14 a1b0 +a7 441f +5 a2b9 +e a056 +fa d764 +67 369f +e0 d622 +a0 ee88 +3d 5d3 +d3 7385 +77 9f95 +c8 7048 +b a267 +91 6381 +35 85b1 +3e aff4 +24 aeb2 +83 4827 +58 936a +f6 fd3e +ab c665 +78 9562 +95 6391 +e8 7448 +1a bec +0 aaa +2b a667 +b1 6781 +78 976a +b5 6791 +32 85ae +aa 64c4 +90 6382 +81 4809 +13 a927 +7c 1fd2 +1f ab57 +20 2688 +6a 9e46 +d2 f3ac +3c 85fa +80 6288 +3 a82d +6c 1ed8 +6e 9e56 +84 6298 +7 a83d +1e 8174 +4 8032 +a0 6688 +3d 2579 +16 2396 +91 6389 +f 80f7 +7d 1fd9 +78 956a +95 6399 +36 2796 +b1 6789 +af eef5 +19 8143 +b5 6799 +cf 52d5 +13 a92f +7c 1fda +df 5bfd +c5 5abb +e5 dc13 +ff dd55 +b0 678a +ef 56d5 +c2 5a84 +5e 19fe +dd 535b +35 27bb +20 26a8 +14 b12 +2e c54 +96 4396 +bd 4579 +68 9e68 +ac 6e7a +30 27aa +29 86e9 +e2 5ca4 +fd 557b +58 13c0 +d0 d12a +1d 951 +3 80f +d4 733a +ee 747c +50 9988 +94 699a +49 32c1 +a 2044 +32 a7a4 +1d 2bd9 +4e b0d6 +5e 9bf4 +44 9ab2 +a2 6c06 +91 e929 +ce 5274 +fe dfdc +e4 de9a +1 8a01 +9a 616e +91 61ab +5 8a31 +7 8a35 +51 91a9 +95 61bb +18 8942 +4c 327a +9 8a41 +c0 7288 +b1 4d29 +a8 e662 +46 1ab4 +ee 565e +9d 6bd9 +28 2e60 +2d acfb +6a 1646 +ac e658 +40 9a00 +9e 6b54 +84 6a12 +7b 3fcf +95 4111 +37 af95 +cc 505a +ec fe58 +fe 7f76 +27 e9f +1 2a09 +32 af06 +81 6a09 +7e bd5c +64 bc1a +97 4117 +47 309f +c0 d022 +da d164 +9 8069 +2b 2e67 +8d 607b +22 8e86 +49 9069 +45 12b3 +5f 13f5 +29 8641 +74 bd1a +b 80c7 +18 8168 +3a 2f66 +80 48a2 +9a 49e4 +10 100 +47 389f +c0 d822 +da d964 +1 a029 +f4 5fb8 +e3 de2d +9 a069 +12 a30e +2c a450 +d8 796a +a7 ce95 +d9 79e3 +13 a387 +20 a428 +2e e76 +c0 78a2 +da 79e4 +21 a429 +77 1d97 +ea deec +3 a087 +10 a128 +1e b76 +eb deed +11 a129 +e2 5486 +99 4363 +f2 df2c +b a0c7 +18 a168 +3e f76 +1 a829 +4e 1274 +11 a929 +95 6b11 +a4 e4b8 +62 14a6 +82 c2a4 +40 3a02 +5a 3b44 +82 40a6 +c2 50a6 +38 7c2 +e3 74ad +8d 4851 +cd d2d1 +92 6986 +cf d2dd +50 9980 +94 6992 +3a afce +54 b110 +4 2890 +fb 5d67 +28 866a +54 b118 +4 2898 +14 2998 +1b b65 +1 a23 +ef d457 +80 6888 +1 2a89 +59 b169 +32 af86 +81 6a89 +55 b119 +6 2896 +d6 d316 +81 6889 +fc 5772 +b2 ed86 +69 3ce3 +e8 7640 +10 298a +4f 18d5 +43 b087 +50 b128 +5e 1b76 +7f 9577 +10 29a8 +4b 9a47 +10 8188 +59 3169 +32 2f86 +48 30c0 +51 b129 +5f 1b77 +15 2b11 +89 426b +55 9933 +8d 405b +93 6b05 +5a 394c +40 380a +1f 8bff +39 8d41 +67 9c9f +6d 3679 +bf 45f5 +a5 44b3 +12 2b06 +f0 7588 +a1 4689 +33 a7a7 +b 2047 +2e 2c54 +14 2b12 +11 8b01 +e2 f4a6 +b1 6fa9 +ae 44d4 +94 4392 +17 8937 +0 2a08 +a0 6c08 +4 2a18 +a4 6c18 +21 a601 +da f3e4 +c0 f2a2 +c ad2 +26 c14 +80 6a08 +84 6a18 +31 8583 +47 b095 +dc 515a +37 f9f +cc 7ad2 +2d a659 +e6 7c14 +16 2b16 +91 6b09 +c2 f006 +95 6b19 +c6 f016 +7d 9fdb +19 2b61 +62 3c84 +94 69b0 +7d 355b +91 6b21 +7 223d +2b 4e5 +11 3a3 +f5 751b +72 bd8e +27 86b5 +54 9192 +63 1ca7 +a5 ecb9 +e2 5604 +f2 ff84 +5c 91d2 +be 657e +7a 956c +60 942a +28 846a +9a 69c4 +80 6882 +70 952a +aa 6c64 +90 6b22 +6 223e +98 6b62 +c5 f839 +e 227e +8 886a +3b d67 +41 ba23 +5b bb65 +0 2a28 +80 6a28 +c8 7ae2 +29 a669 +e2 7c24 +58 3340 +47 b0b5 +dc 517a +65 9c19 +87 4a17 +91 6b29 +38 a742 +60 bc80 +99 6b69 +d 80d1 +ca f066 +67 1495 +34 8f98 +f2 df2e +18 a16a +28 a46a +dd 5b71 +c3 5a2f +dc 51d2 +47 b2bf +61 b401 +4b b2cf +65 b411 +cb d2cd +90 6982 +99 c163 +6 209e +58 9b6a +ab ce65 +99 c363 +6 229e +14 2990 +38 876a +51 932b +af 647f +6b 946d +30 52a +55 199b +66 b416 +35 2f19 +78 9d6a +95 6b99 +e6 561e +c6 f096 +48 3868 +a2 e406 +91 498b +61 b421 +d2 f926 +1 2229 +15 8991 +49 32c9 +92 6ba6 +70 bda8 +22 2404 +8 22c2 +6f 9677 +0 2aa8 +b7 679d +c8 5862 +2 8206 +89 ca4b +3 2a27 +3a 2d4c +20 2c0a +e8 5c62 +22 8606 +89 e861 +28 246a +d2 f984 +82 4a84 +be 655c +60 9408 +a4 641a +1 8209 +8a 4ac4 +68 9448 +ac 645a +c2 5804 +9 8249 +73 1d2f +a2 4e84 +3e dfe +d4 7bb0 +bd 475b +ca 5ac4 +e 28fe +8d 625b +49 9249 +48 924a +a 8266 +f7 df9f +1d a1db +f9 7f63 +22 e8c +ef 7c5d +d5 7b1b +b ae5 +2a 8666 +3d a5db +42 128c +7a 374e +ab 6ce5 +91 6ba3 +c2 5824 +9 8269 +8d 627b +49 9269 +bd 65f3 +79 95e1 +41 b809 +51 b909 +14 8198 +5d 3179 +36 2f96 +81 c809 +91 61a1 +b 28e5 +91 c909 +16 8916 +54 9198 +a 2066 +6c 9c78 +5f 9bd7 +be 65dc +60 9488 +a4 649a +b2 cd2e +ab 6667 +4a 3066 +9f 63dd +85 629b +41 9289 +a5 6c19 +60 1e80 +d6 f116 +a4 4492 +be 45d4 +e1 7c03 +28 a648 +fb 7d45 +d0 d1aa +c8 f848 +da 7966 +1d 9d1 +3 88f +30 85a2 +34 2d3a +22 86a6 +10 81a8 +d2 d10c +b8 cfca +32 2fa6 +2 8826 +94 61ba +50 91a8 +11 2b03 +2b 2c45 +21 8489 +f8 f560 +33 5a7 +e8 546a +53 b3a5 +d2 d1a4 +f2 d5a4 +85 6039 +5e 93fe +78 9540 +bc 6552 +24 2438 +17 2397 +7a 9544 +60 9402 +be 6556 +b5 67b9 +4b 3267 +52 992e +7e bdde +f9 5d61 +33 8705 +ab e64f +2c acf2 +6a 9c44 +ae 6c56 +50 9b02 +75 b5b1 +58 9b42 +63 b4af +7d b5f1 +9 ac1 +f7 5d9f +c9 dae3 +e3 dc25 +8c e850 +8e e854 +43 ba85 +d8 5b4a +a9 4c61 +2e acde +fd 75f9 +d8 5962 +12 8306 +e1 5c03 +fb 5d45 +28 8648 +79 354b +90 69a0 +e0 5e80 +6 20bc +1a 8346 +70 9780 +b4 6792 +b0 6da0 +26 24bc +ae 6456 +6a 9444 +50 9302 +29 c69 +2 a86 +ac 665a +68 9648 +49 1069 +22 e86 +f0 7da0 +66 34bc +82 6086 +6e 1cd6 +f4 7598 +fb 5765 +e1 5623 +f 2057 +a5 4699 +37 a7b7 +13 8b05 +1e 21d4 +4 2092 +ea fec4 +54 9112 +1e 9d4 +4 892 +99 e961 +c8 d2c2 +e2 d404 +70 9508 +b4 651a +d0 d302 +ea d444 +93 630d +14 29b0 +78 9548 +bc 655a +d2 5904 +19 8349 +83 488d +e8 76c2 +98 6b60 +e 227c +98 e962 +e8 de42 +e a07e +69 bce1 +38 874a +a9 cce1 +78 974a +1a 81c4 +0 8082 +50 b102 +fa 5f6e +1a 29c4 +0 2882 +1a 234c +0 220a +81 40ab +9b 41ed +82 6886 +53 3b0f +6d 3c51 +75 15b3 +f1 f5a1 +54 b112 +fe 5f7e +1e 29d4 +4 2892 +78 b560 +9e 69d4 +40 9880 +84 6892 +e1 7c23 +28 a668 +fb 7d65 +c8 f868 +7a 9564 +60 9422 +be 6576 +39 8761 +21 8623 +3b 8765 +58 9b62 +ed 7c59 +9 ae1 +f7 5dbf +22 8486 +fa 77e4 +e0 76a2 +76 3dbe +f5 771b +2b 6e5 +62 9486 +19 8363 +31 f89 +b aa6f +f 8a5d +a1 c489 +2b 4c7 +38 568 +c9 f2e9 +26 8496 +fe 77f4 +e4 76b2 +67 3615 +54 9b9a +6e 9cdc +66 9496 +f2 7f2e +35 f99 +73 15ad +f aa7f +25 ac19 +ac 667a +68 9668 +1a 8366 +5e 33fe +78 3540 +9e c3dc +84 c29a +b7 4797 +75 9d3b +6e 3674 +ae 6476 +6a 9464 +50 9322 +58 9362 +2 80a6 +70 1f88 +be c7dc +a4 c69a +70 9722 +52 1104 +38 fc2 +78 9762 +ed defb +6 a096 +dd dbd9 +57 3bb5 +26 61e +ca 5044 +b0 4f02 +68 9640 +ac 6652 +47 381d +4 aa10 +f9 dfeb +12 a186 +e9 dcc9 +63 3ca5 +32 70e +d6 5134 +bc 4ff2 +57 339f +d0 d322 +ea d464 +6b 94c7 +78 9568 +bc 657a +d2 5924 +19 8369 +a6 649e +62 948c +30 8f88 +8e 60dc +3a a5c4 +20 a482 +22 a486 +e6 5434 +cc 52f2 +3e a5d4 +24 a492 +70 972a +28 660 +71 1783 +c9 dae9 +2c 670 +75 1793 +6 a896 +26 e1e +d5 71b1 +82 682c +f3 7da5 +20 a6a8 +ae 4454 +94 4312 +4c 1050 +32 f0e +11 8381 +72 9584 +b6 6596 +e0 7c02 +fa 7d44 +1c 9d0 +2 88e +89 4a6b +af 64d5 +95 6393 +51 9381 +80 6008 +6c 1c58 +28 2c4a +a3 e6a5 +39 8fc1 +97 6115 +f2 5d84 +39 87c9 +e2 d484 +94 e392 +ae e4d4 +79 1563 +70 9588 +b4 659a +e8 f460 +11 8389 +d8 7342 +40 3aa8 +da f9cc +c0 f88a +3d 2553 +3a 2566 +31 8789 +f8 7742 +f4 759a +79 374b +90 6ba0 +6 22bc +7a 3566 +95 6119 +30 878a +47 1235 +92 490e +b5 6d19 +a3 c685 +20 622 +3a 764 +d5 7119 +f5 f519 +11 83a1 +49 38e3 +c8 7240 +ae 647e +50 932a +6a 946c +fd f7db +33 87a5 +b 45 +7d 9759 +fb 75ef +80 6028 +5f 1bd7 +6c 1c78 +2 8a26 +15 a99b +72 b7a4 +e2 d4a4 +a0 4c02 +ba 4d44 +e8 5e68 +1 2003 +1b 2145 +95 6139 +cb 52e7 +2b 4cf +c1 7281 +a7 64bf +63 94ad +21 c0b +3b d4d +5a 3146 +8 8a42 +13 a3af +2d a4f1 +11 23a1 +1 8a09 +8 8a62 +1 a089 +10 a188 +c1 7a29 +1e bd6 +11 a189 +14 a198 +1b 8365 +1 8223 +9 8a69 +20 a488 +2e ed6 +95 e119 +53 1107 +21 a489 +89 68cb +45 98b9 +30 a588 +e1 7e29 +3e fd6 +8 8a6a +3b f67 +fc 55d2 +e 2f4 +16 2b96 +3d 2d79 +b5 ef39 +73 1f27 +61 3489 +e7 569d +1 a889 +9 22e1 +10 89a8 +5e 195e +8f c2ff +a9 c441 +10 89a2 +b5 6fb9 +3 a88d +4d 1279 +98 4160 +3d a75b +f6 7d16 +80 4022 +9a 4164 +97 6b17 +53 9b05 +fe 7d56 +f4 fd32 +37 8d9d +36 ad3c +1c abfa +83 e88f +9d e9d1 +cd 527b +3d a551 +23 a40f +0 8a08 +33 f05 +74 3d3a +29 661 +72 1784 +9f 4355 +6 8b6 +85 4213 +88 e8c8 +46 18b6 +df 5355 +c5 5213 +5a 9b46 +65 b4b3 +7f b5f5 +c 8ad8 +25 e93 +3f fd5 +79 15c1 +c9 72e1 +33 52f +d0 d9a8 +1e a3dc +d7 7997 +4 a29a +37 2797 +31 8d09 +6c 1cda +a5 4e93 +bf 4fd5 +4b 30c7 +58 3168 +c1 f089 +21 24a1 +69 3641 +70 9d08 +b4 6d1a +11 8b09 +1e 21dc +4 209a +35 8d19 +a3 448d +78 9d48 +bc 6d5a +19 8b49 +70 95aa +e 2a7c +3a 25e4 +20 24a2 +58 9b4a +ab ce45 +7d b5f9 +43 9aa7 +6f 3cdf +e8 dc62 +4a 1244 +d a8f9 +33 587 +de f3fe +f8 f540 +e8 544a +53 b385 +91 61a3 +83 6a2d +9a 69cc +80 688a +1e 29dc +4 289a +9e 69dc +40 9888 +84 689a +73 b785 +a8 6e68 +1f 157 +25 ae13 +3f af55 +e2 548e +fc 55d0 +9e 6b7c +84 6a3a +40 9a28 +73 1f25 +8c 6a7a +48 9a68 +61 1e23 +7b 1f65 +1a 8b66 +5a 9b66 +f4 df90 +1a a1cc +0 a08a +33 2587 +25 8e93 +3f 8fd5 +1e a1dc +4 a09a +37 2597 +de fbfe +f8 fd40 +33 d87 +85 e8b1 +54 b31a +6e b45c +e8 5c4a +53 bb85 +14 a19a +19 8b69 +8e 68dc +3a a5cc +20 a48a +45 9293 +5f 93d5 +e3 5e85 +9 20c1 +62 340e +7c 3550 +93 69a5 +8f 687f +4b 986d +5a b3e6 +c8 52c8 +41 3081 +c7 5295 +74 1f9a +a3 ce25 +1a a9cc +57 1317 +0 a88a +1e a9dc +4 a89a +37 2d97 +ef d6dd +70 9d80 +b4 6d92 +72 9d84 +b6 6d96 +8e 685e +4a 984c +13 8b85 +9e cbfe +b8 cd40 +e6 dc9e +ec 7678 +98 41e0 +f4 7d92 +8c cad8 +6 2ab4 +2c 2c52 +74 3dba +29 6e1 +9f 43d5 +85 4293 +1a 2966 +69 36c1 +70 9d88 +b4 6d9a +e8 fc60 +11 8b89 +d8 7b42 +a9 46c1 +f4 7d9a +6 2abc +5a 3966 +8e 425e +a3 ce85 +20 e22 +3a f64 +8c 687a +48 9868 +61 1c23 +96 eb94 +7b 1d65 +8e 687e +4a 986c +38 25e0 +50 9180 +94 6192 +87 68bf +43 98ad +a3 cea5 +81 c821 +c d2 +a2 6e84 +3e 2dfe +79 9749 +bd 675b +da 7bc4 +c0 7a82 +21 a609 +c8 7ac2 +e2 7c04 +29 a649 +93 412f +5b 13ef +75 1531 +d3 7907 +1a a34c +0 a20a +db 7947 +8 a24a +f3 7d07 +3a a74c +20 a60a +f 28df +88 c862 +fb 7d47 +28 a64a +9a c9c4 +80 c882 +d4 f312 +dd 59d9 +ee f454 +6b 364f +82 6aa4 +59 9369 +9d 637b +e6 749e +1 a229 +9e 417c +84 403a +c2 7824 +9 a269 +32 58e +73 bf0f +8d c051 +8 2062 +e2 5e26 +7 2abd +1a a3c4 +0 a282 +93 c30f +14 89b2 +ad c451 +e8 7ce2 +22 a686 +ae 645e +50 930a +6a 944c +81 c8a1 +21 a689 +83 680d +75 15b1 +d3 7987 +1a a3cc +0 a28a +f3 7d87 +3a a7cc +20 a68a +9c 6950 +82 680e +10 a1a8 +1e bf6 +30 520 +11 a301 +48 b8ea +d9 7961 +13 a305 +c9 f24b +4a b8ee +96 4116 +f9 7d61 +33 a705 +b 8a65 +86 c8b4 +6f 945f +7 289d +80 c820 +d9 7bc3 +f3 7d05 +20 a608 +4d 3851 +55 11b3 +4f 3855 +99 e1c9 +57 11b7 +3e 8dfc +bd c759 +37 2735 +24 8cba +18 a342 +a2 ec26 +9 8cb +1a a346 +c1 d823 +db d965 +4f 9a77 +84 e8b8 +db 5345 +c1 5203 +42 18a6 +20 40a +51 39a1 +3a 54c +23 487 +30 528 +91 c921 +7 803d +f 807d +b4 451a +50 3b20 +6 803e +d9 7be3 +f3 7d25 +20 a628 +11 a321 +94 4132 +e3 7c07 +2a a64c +49 124b +86 4836 +1e 3fe +d5 d9b9 +38 540 +38 a762 +60 9ca8 +be 6dfc +a4 6cba +3a a5e4 +20 a4a2 +90 c982 +6 809e +de 73fc +c4 72ba +2f e75 +ca 78e4 +11 a329 +b4 453a +7 2a1d +d8 7be2 +f2 7d24 +39 a769 +10 108 +ab e467 +80 48aa +9a 49ec +0 2228 +14 8990 +48 32c8 +61 1c09 +2 888e +8 2268 +1c 89d0 +67 1695 +14 990 +18 a36a +95 e911 +69 1c49 +38 548 +34 d90 +38 a76a +6 8894 +2e cfe +ad 465b +c4 7ab0 +31 afa1 +d3 5107 +30 5a0 +11 a381 +f9 7de1 +33 a785 +83 c00f +9d c151 +2a a4c4 +10 a382 +85 c013 +9f c155 +f2 5584 +8e ea56 +e9 7ce1 +b8 474a +23 a685 +94 e39a +ae e4dc +79 156b +a3 c40f +bd c551 +ac ee52 +30 5a8 +11 a389 +be 655e +7a 954c +60 940a +91 c9a1 +7 80bd +12 126 +32 af24 +31 a789 +93 690d +1d 23f3 +37 2535 +bd c559 +39 d61 +ac ee5a +b3 4507 +11 a3a1 +5d 997b +14 8b12 +2e 8c54 +10 180 +d3 5b05 +2a a4e4 +10 a3a2 +30 580 +30 588 +7c 95d8 +9e 43d6 +3e 8d56 +d 859 +85 cab3 +9f cbf5 +6e 965e +1d abd9 +13 b87 +20 c28 +1 aa09 +ba cf64 +27 2e9f +a0 ce22 +da d3c4 +c0 d282 +1 aa29 +8d c851 +8f c855 +31 a723 +20 ca8 +7 2835 +8d c859 +13 ab05 +32 8706 +f8 5d62 +6e 147e +1d 29f9 +95 ebb9 +53 1ba7 +23 8c87 +30 8d28 +29 2661 +72 3784 +8c c8da +9f 6355 +41 9201 +6 28b6 +85 6213 +18 ab42 +43 10a5 +10 8ba8 +b8 e56a +f9 5d49 +23 c87 +30 d28 +11 ab09 +94 4932 +32 87ae +a0 6e00 +a 4e +a6 6616 +62 9604 +49 1a4b +34 271a +7a 9fec +60 9eaa +d8 7140 +be 6ffe +d1 5901 +2d edb +47 101d +0 aa28 +b9 cf49 +33 2f25 +21 8c89 +f8 fd60 +54 b33a +6e b47c +45 92b3 +a3 6407 +5f 93f5 +11 ab29 +49 1261 +94 493a +43 320f +5d 3351 +19 ab69 +f0 5da2 +66 14be +83 c80f +9d c951 +85 c813 +9f c955 +30 8da8 +29 26e1 +11 ab89 +17 2935 +9d c959 +2 82c +9b 49e7 +31 af01 +13 107 +33 af05 +a0 6e28 +17 117 +37 af15 +af 6ef5 +19 143 +c3 f825 +39 af41 +1b 147 +21 ae03 +3b af45 +b3 6f05 +1d 153 +23 ae0f +3d af51 +c7 72bd +31 50b +bb 4def +c0 7228 +d4 d990 +37 517 +1e 815c +4 801a +57 b315 +cf 72fd +50 39a0 +39 54b +29 2ec3 +43 3005 +10 ab08 +22 c84 +ef d677 +80 6aa8 +eb 7ec5 +55 1113 +43 b8af +c2 f20c +5d b9f1 +2c 845a +86 e094 +51 1123 +f3 7f05 +5d 1153 +31 fa3 +4b 10e5 +18 8be8 +de 595e +7c 97da +a6 e49c +71 152b +99 4143 +ce 7ad6 +2f a65d +9d 4153 +21 4a3 +3b 5e5 +8 80e8 +ca d04c +b0 cf0a +2a 2ee6 +a8 4e62 +d0 79a0 +b9 454b +eb 7467 +14 390 +15 9b1 +a2 4c84 +59 3b61 +1b 167 +21 ae23 +3b af65 +b5 459b +5f 33d7 +6c 3478 +3a 74c +20 60a +51 3ba1 +dc fbf0 +c2 faae +3f 2777 +c 58 +34 87b8 +96 493c +55 19b1 +ba 47e6 +10 32a +2a 46c +73 158f +f9 556b +54 13b0 +68 1e42 +1b 345 +1 203 +8b 4ae7 +ad 6cfb +69 9ce9 +62 1406 +a4 e418 +20 2c20 +3 207 +7 217 +b 247 +f0 5788 +d 253 +b9 6d4b +75 9d39 +bc 65da +78 95c8 +9a 43c6 +f 257 +f4 5798 +70 3502 +62 9e0e +7c 9f50 +3f 75d +25 61b +ec def8 +1f a1d5 +5 a093 +c8 dac0 +2b 647 +bd edfb +fa 5746 +a9 6cc1 +40 3aa0 +29 64b +cd 5071 +b3 4f2f +cc dad0 +2f 657 +78 3542 +fe 5756 +93 6b8f +ad 6cd1 +24 eb0 +9a e146 +c1 700b +db 714d +7d 9ff9 +71 35ab +10 a908 +4d 1253 +d7 5b37 +7 2a1f +90 6188 +7c 1dd8 +49 1263 +5c 31d8 +c 2ad2 +26 2c14 +a5 e639 +63 1627 +6 2296 +2d 2479 +76 359c +ce 5a5e +b8 4542 +84 4ab0 +6d 165b +1d 217b +f7 5f3f +ad e679 +6b 1667 +39 adc1 +69 166b +ab 4647 +af 4657 +9b e3c7 +a8 e468 +a5 4413 +bf 4555 +3 a2ad +92 e90c +cf 5257 +cb 5267 +b6 4dbe +6b 16e5 +b2 ed0c +98 ebca +ef 5657 +bb ed47 +45 1ab1 +24 8610 +d0 5b2a +ea 5c6c +6e 1cfe +ed 565b +eb 5667 +e4 749a +fe 75dc +b9 edc1 +e9 566b +3f a7ff +f4 5738 +e7 5697 +8d e279 +4b 1267 +bb 4de7 +9c e9da +51 b301 +4a 9a66 +5d b9db +55 b311 +b6 ed3e +6b b665 +40 1aa8 +f7 579d +da d9cc +c0 d88a +d3 7305 +3d 553 +43 b20f +5d b351 +13 907 +10 8382 +2a 84c4 +e8 76e0 +11 90b +17 917 +1b 947 +43 90a7 +f0 7720 +19 94b +1f 957 +b1 65ab +17 197 +a0 6ea8 +75 1513 +1e 295e +d0 f9a2 +f0 5f2a +7d 1553 +a 2ac4 +57 1917 +54 9392 +6e 94d4 +55 191b +5d 195b +9d e979 +5b 1967 +ec 7cda +a1 4601 +22 ca4 +d3 732d +3d 57b +68 3cc0 +e7 761d +8e e8dc +59 196b +b9 4543 +1b a3ed +1 a2ab +bd 4553 +b0 4d20 +91 e303 +ab e445 +80 4888 +12 a9a6 +4c 1070 +32 f2e +9b 4947 +39 87c3 +98 c3c2 +b2 c504 +99 494b +97 4997 +4e 3874 +a6 e416 +95 499b +4c 3878 +ff 7d77 +2c a67a +8a 6ac4 +1b 3c7 +28 468 +71 158b +9e 415c +84 401a +62 1e06 +a4 ee18 +de d9dc +c4 d89a +d7 7315 +c2 7804 +9 a249 +f9 5563 +20 4a8 +1 a289 +a 86c +53 198f +79 97e3 +d8 d3e2 +f2 d524 +a2 4ca4 +bd 457b +18 3c0 +c0 58a2 +da 59e4 +21 8429 +c9 d24b +4a 98ee +43 3227 +13 830f +2d 8451 +d9 596b +c6 dabc +29 643 +9a cb6c +80 ca2a +b3 4f27 +3 a07 +90 4180 +7 a17 +b a2c5 +d1 7921 +94 41b0 +b a47 +82 c0a6 +f0 5f88 +98 41c0 +f5 d591 +fe ffd4 +e4 fe92 +ed f453 +dc 59d8 +1d 2173 +f7 5f37 +69 1663 +7c 35d8 +a9 66e9 +b1 6729 +8d ea79 +4b 1a67 +43 b285 +d8 534a +a9 4461 +2e a4de +47 b295 +dc 535a +bf 47d5 +a5 4693 +8b 4a47 +ad 6c5b +69 9c49 +89 4a4b +31 a509 +d0 7982 +bd 677b +79 9769 +c8 fae0 +32 8d2e +2b 2667 +e9 5663 +ad 46d9 +3f a7f7 +fc 75d8 +cf 5a57 +7f 1ff7 +c1 f009 +cb 5a67 +c4 789a +de 79dc +c9 5a6b +39 ad41 +1f abff +b5 47b9 +db 5bed +c1 5aab +e1 dc03 +fb dd45 +e7 7eb5 +51 1103 +ef 7ef5 +59 1143 +10 9aa +a9 4449 +3b a567 +9d e159 +5b 1147 +28 8c4a +eb 5ee7 +11 2123 +66 9c94 +d3 f307 +c2 588c +aa cc66 +99 c14b +13 2127 +d4 fbb0 +bd c75b +3e 8dfe +37 2737 +4 18 +f1 5723 +ef fc5f +b 8ae7 +3e a77c +f7 7d37 +24 a63a +24 418 +91 c109 +b 20e5 +40 980a +5a 994c +9e 695e +3c a7da +10 b22 +2a c64 +39 256b +d8 fbe0 +3b 2767 +d9 5143 +6f b65d +e 88d4 +36 d3e +cc 7af0 +26 c94 +2a a66e +41 38a1 +10 30a +2a 44c +9a 4bee +b4 4d30 +f9 554b +b 26d +54 1390 +b 204d +e5 5e11 +10 a180 +30 708 +3f 8fff +59 9141 +9d 6153 +e2 deac +8 a0e8 +16 b36 +80 6088 +7d b753 +6c 1cd8 +b9 656b +14 23b0 +68 9c4a +a5 6c3b +61 9c29 +bf 6d7d +39 a761 +80 4aa0 +69 164b +19 216b +f3 5f2f +16 831c +cf 58d7 +dc 5978 +14 891a +d 2253 +53 9b25 +97 6b37 +c aa52 +70 1580 +c5 50b1 +3f 275d +25 261b +44 1812 +5e 1954 +cc fad0 +36 8d1e +2f 2657 +3d 8dd3 +d3 fb85 +29 266b +8e e8fc +cb 5247 +ae ecfc +94 ebba +eb 5647 +b7 ed37 +41 1aa1 +c5 d299 +6a 1cee +e9 564b +a0 4620 +3f a7df +94 413a +d2 7924 +19 a369 +58 31e2 +8c ea52 +f0 5580 +5 2ab1 +90 6b2a +aa 6c6c +d0 5102 +e3 d40f +fd d551 +ec fe52 +bd cdd3 +a9 666b +71 1503 +28 86ea +fb 5de7 +95 e919 +53 1907 +9d e959 +5b 1947 +59 194b +4a 1844 +fb 7d67 +24 c90 +28 a66a +28 448 +98 4bea +b2 4d2c +20 488 +a 84c +a aa6e +8 824a +db 5947 +79 97c3 +d8 d3c2 +f2 d504 +c0 5882 +da 59c4 +21 8409 +d9 594b +37 d97 +c0 7aa8 +d9 f963 +2 888c +5f 93ff +79 9541 +bd 6553 +b9 6563 +6a 3e64 +9a cbcc +80 ca8a +c0 5028 +b3 4f87 +68 9c42 +11 8309 +ca 58c4 +71 9581 +b5 6593 +b1 65a3 +4e 985e +9b 6967 +39 a7e3 +91 69ab +2c acf8 +69 1643 +da db6c +c0 da2a +96 6b96 +52 9b84 +bd 6d79 +19 2163 +f3 5f27 +d4 51b0 +d3 fb8f +ed fcd1 +36 2716 +df 515f +3d aff9 +6b 9ee5 +af 6ef7 +ca facc +34 8d1a +2d 2653 +73 9f25 +b7 6f37 +30 8d2a +29 2663 +72 3786 +98 61c0 +f 2a57 +ac ecf8 +6a 1ce6 +e9 5643 +3f a7d7 +cb 5a47 +c9 f8e1 +98 c34a +12 2326 +f2 7786 +bf 67d5 +61 9681 +a5 6693 +f6 5536 +f4 553a +47 3a1d +f9 7f69 +56 1116 +9b 6bed +81 6aab +a1 ec03 +bb ed45 +91 4103 +ed 7c79 +c6 7a96 +27 a61d +95 4113 +1d 895b +62 bc24 +48 bae2 +b1 450b +b5 451b +51 3b21 +b3 ef05 +1d 8153 +a8 ee68 +1f 8157 +52 b904 +86 623c +15 8193 +cf f2fd +50 b9a0 +39 854b +c5 5091 +fc 5d58 +57 1b9d +c8 f268 +3f 8557 +29 aec3 +43 b005 +ed 5e71 +f3 ff05 +5d 9153 +e8 fe68 +5f 9157 +5b 9167 +10 2988 +7f 9557 +8 2240 +92 6b24 +12 3a6 +f6 751e +0 2280 +9b 4345 +2 8a6 +81 4203 +d 8a5b +a3 4607 +89 e041 +a5 6c13 +47 9abf +bf 6d55 +61 9c01 +55 31b3 +5c b952 +a7 4617 +b6 4d3e +6b 1665 +8a e8cc +c7 5217 +9c c952 +aa eccc +90 eb8a +e7 5617 +1f 29df +98 c962 +a4 ee30 +e 807e +4d 9a7b +38 a74a +6c 3458 +83 68ad +2a a4c6 +ee 5474 +d4 5332 +d 8253 +54 331a +6e 345c +85 68b1 +99 e9eb +d6 5336 +f 8257 +70 1fa0 +40 baa0 +e2 5c06 +29 864b +2f 8657 +70 b582 +87 481f +4d 9253 +4f 9257 +c aad2 +26 ac14 +4b 9267 +63 1e8d +19 b43 +0 2a88 +6f 9657 +b8 c542 +84 cab0 +6d 965b +6b 9667 +64 b49a +7e b5dc +7b bfcf +95 c111 +9e eb54 +84 ea12 +45 3891 +66 96b4 +aa 66c6 +69 966b +8a 6064 +ba edcc +f7 5717 +a0 ec8a +b0 c582 +c7 581f +39 f4b +dd 5971 +17 8315 +c3 582f +b1 4503 +13 a3ad +b5 4513 +f9 7fe9 +56 1196 +2d ae71 +a3 6e25 +d 73 +93 4907 +31 8783 +97 4917 +fb 5def +35 8793 +12 b86 +39 d69 +95 491b +82 6a84 +1e 29fe +59 9349 +9d 635b +13 387 +20 428 +1 a209 +f1 5523 +12 8b0e +2c 8c50 +9a 696e +bb 6747 +77 9735 +38 a7ea +9e 6bfc +84 6aba +40 9aa8 +73 1fa5 +d3 f305 +3d 8553 +58 bbc2 +72 bd04 +a6 663c +35 8593 +1f 8957 +e 22d4 +15 899b +7d 9553 +a aac4 +5f 9957 +5b 9967 +f2 5504 +d8 53c2 +d3 f32d +3d 857b +59 996b +26 8c9e +2c 2678 +d2 d304 +53 99a7 +22 ca6 +bb 4745 +a1 4603 +2b e65 +57 b3b7 +c5 5299 +c4 d812 +de d954 +d 8859 +8a 6a6e +6b 1e65 +c7 5a17 +99 41cb +18 ab4a +2d 8653 +89 c8e1 +58 934a +f6 fd1e +12 8ba6 +ab c645 +83 4807 +8d c8f1 +5c 935a +87 4817 +cc d052 +34 8f38 +27 8e97 +1 aa01 +b 8a47 +f 8a57 +14 8b38 +7 8a97 +ac cc52 +88 e2e2 +91 49a9 +a2 e424 +6d 9653 +69 9663 +7c b5d8 +a 826c +c3 5827 +4f 9a57 +98 c942 +a4 ee10 +e 805e +cc 727a +4d 9a5b +4b 9a67 +44 b89a +5e b9dc +34 518 +49 9a6b +19 23e3 +33 2525 +b9 c549 +a8 ee4a +db f967 +4 8890 +0 8 +9b e367 +8a 48ec +d1 5103 +1d a97b +68 3640 +8a e046 +81 e2a9 +cb 704d +b1 6f0b +6d 9ef9 +62 9c06 +7b 9547 +65 9e11 +fe 757e +fc dff8 +15 a193 +35 71b +11 a1a3 +31 72b +5 aa31 +9a 4944 +80 4802 +81 4089 +13 a1a7 +d0 dba8 +33 72f +14 99a +3f a557 +31 a5ab +88 c8ea +9b 6365 +81 6223 +71 15a9 +d aa7b +aa ccee +a3 6627 +49 9243 +80 caa0 +69 964b +bf 67df +7b 97cd +61 968b +dd 5951 +c3 580f +cb d8ef +2e 476 +4e b274 +b4 6592 +70 9580 +f a257 +b6 cd9c +af 66d5 +1 81 +1e bdc +4 a9a +2f a657 +90 43a0 +e6 feb4 +50 9102 +e6 7c16 +2d a65b +e0 5402 +fa 5544 +f9 ff63 +22 8e8c +29 449 +9b 416d +81 402b +a1 ee29 +3f a7dd +25 a69b +f8 7560 +21 489 +3b a7ed +21 a6ab +9d 6971 +83 682f +fb fd67 +24 8c90 +3a a76c +f3 7d27 +20 a62a +20 408 +bb e767 +90 4baa +aa 4cec +b1 6523 +88 e042 +7a 9d44 +be 6d56 +60 9c02 +d1 71a9 +1c a9d0 +4c 127a +2 a88e +93 6927 +31 a7a3 +e3 5e07 +9 2043 +91 692b +79 9543 +32 786 +35 a593 +af 64d7 +bc 6578 +51 9383 +6b 94c5 +52 190c +31 a5a3 +1f a957 +89 4849 +1b a967 +a4 ce10 +92 e304 +81 4889 +13 a9a7 +4d 1071 +33 f2f +90 e308 +4e 12f6 +11 a9ab +a4 ecb8 +62 1ca6 +fb 5745 +e1 5603 +fd 7df3 +37 a797 +c2 580e +dc 5950 +a8 ccea +bb 6765 +a1 6623 +29 2e69 +49 18eb +c8 5248 +5a b366 +1e 356 +d5 d911 +9b 6b6d +81 6a2b +69 9643 +bf 67d7 +7b 97c5 +61 9683 +34 a71a +49 9a4b +9b 4165 +81 4023 +b 806f +a1 ee21 +eb 7cef +3f a7d5 +25 a693 +43 9805 +87 6817 +48 3268 +42 988e +5c 99d0 +58 1148 +a2 46a6 +21 481 +83 6827 +de 51fc +c4 50ba +f1 d529 +fa ff6c +e0 fe2a +ac ec52 +74 953a +1f abdd +5 aa9b +bd 4d53 +1b abed +1 aaab +2 8c +d9 7163 +26 8c94 +5d 995b +2e a67e +c8 72c0 +32 50e +bc 4df2 +98 43c0 +c6 fabe +e0 fc00 +1d 2979 +95 eb39 +53 1b27 +3d 8d59 +59 13eb +73 152d +a7 e49f +72 152e +a6 449c +95 4311 +16 9b4 +d5 79b3 +1c a3f8 +be 455e +2d 8cd3 +c3 fa85 +40 3a22 +5a 3b64 +b6 459e +e6 549c +9d 4379 +e2 54ac +f5 df3b +e a0d6 +fa d7e4 +e0 d6a2 +b8 4f42 +a7 e49d +58 13ea +72 152c +8f 6a77 +4b 9a65 +74 3f92 +8e 40d4 +b6 459c +46 3296 +6d 3479 +28 244a +ed d679 +b2 6d2e +c5 7ab3 +df 7bf5 +ae 465e +8e ea7e +f2 55ac +a1 ec81 +eb 566d +d5 d311 +56 99b4 +9a 69c6 +7a 95c4 +60 9482 +be 65d6 +2 80c +6 81c +5 803b +1f 817d +dd d35b +57 3337 +5e 99fe +ec dcf8 +e5 7631 +c 825a +df 5957 +7d 97d3 +a a246 +13 90d +78 3742 +12 90e +16 91e +1a 94e +1e 95e +d0 d9a2 +46 90be +46 181c +42 182c +28 a4ca +4e 185c +35 253b +f7 7d1d +dd 7bdb +13 ba5 +b8 4d60 +77 159f +4a 186c +56 191e +38 ad42 +4f 1275 +9a 494e +38 87ca +9e 495e +3c 87da +8a e2c6 +93 498d +c9 d2e3 +e3 d425 +71 9529 +b5 653b +10 2380 +4 8210 +ca 586c +ac 66fa +68 96e8 +d3 f327 +c2 58ac +38 fc8 +26 8494 +d2 59ae +48 10ca +81 4283 +9b 43c5 +f0 dda8 +e9 76e1 +12 90c +3 a0d +52 3904 +7 a1d +2b a66f +27 c95 +44 181a +5e 195c +ed 7cdb +23 ca5 +24 43a +3e 57c +62 be26 +8f e8dd +40 182a +5a 196c +a7 e417 +96 499c +4d 3879 +5 2839 +a4 4c18 +36 ad36 +8b 4a4d +8 284a +83 e2a5 +b3 6f07 +6f 9ef5 +4a b0ec +30 afaa +8e 4a5e +99 63cb +55 93b9 +b3 650d +83 4a8d +d9 d3e3 +f3 d525 +25 8e91 +e2 fe26 +14 8310 +c0 582a +da 596c +bc 67fa +78 97e8 +e3 f427 +d2 59ac +48 10c8 +dc 5bf0 +c2 5aae +60 b6a8 +38 2f48 +c2 782c +2c 8ef0 +8a 6044 +a ace +24 c10 +b acf +25 c11 +19 2969 +10 b02 +2a c44 +b2 6504 +98 63c2 +54 93b0 +72 150e +36 8796 +fc 5df2 +13 b0f +2d c51 +99 63c3 +b3 6505 +55 93b1 +4d 3259 +6d 1cdb +7e b756 +3a 8fec +20 8eaa +98 6140 +16 2934 +9c c958 +65 bc91 +61 3409 +d6 d914 +15 21b1 +5 8819 +79 3feb +93 412d +c6 5a1e +1a bce +34 d10 +1b bcf +35 d11 +ba cf44 +a0 ce02 +30 8fa0 +8e 60f4 +1d 23db +37 251d +cd d2f1 +92 69a6 +e2 5e86 +8 20c2 +14 8b10 +a6 4e14 +78 35c8 +8 2ac2 +22 2c04 +a 2ace +24 2c10 +62 362c +76 9d94 +aa 46cc +13 8b2f +2d 8c71 +58 13ca +72 150c +13 2b0f +2d 2c51 +35 5b3 +1c b58 +77 17b5 +65 3e19 +8 26a +1a 2bce +34 2d10 +56 9bbc +9a 6bce +b4 6d10 +4b 184d +2a 8c64 +10 8b22 +2d cfb +3e a776 +ac 4658 +c3 7aad +73 1d8d +6a b6c6 +27 261d +c4 50b8 +dd fbd9 +26 261e +ca 7044 +6c 9ef0 +b0 6f02 +12 8b26 +33 507 +d0 d980 +46 909c +8e 4876 +2c 86f2 +fa f7e4 +e0 f6a2 +22 2c0e +3c 2d50 +64 b4b0 +8f e27f +10 a922 +46 9abe +60 9c00 +a4 6c12 +be 6d54 +a0 642a +ba 656c +15 23b1 +b2 65ac +1e 89d4 +a 226c +4 8892 +d3 51ad +dc 7bf0 +c2 7aae +a5 cc93 +bf cdd5 +ab 666d +f4 7790 +99 c341 +1a 89e4 +0 88a2 +be cdd6 +aa 666e +45 3839 +a3 66ad +f6 d594 +bd e759 +3e adfc +7b 1747 +24 acba +dd d1db +57 31b7 +60 b600 +ca 50e6 +42 180c +c 8ad2 +26 8c14 +1a 21c6 +76 3d9e +2b 6c5 +4c 9ad2 +66 9c14 +5a 31c6 +20 2620 +83 e00f +9d e151 +7 281d +17 291d +2 2084 +e9 5ee9 +16 291e +1b 21c7 +e8 5eea +a 20c4 +f1 5f29 +c 8252 +82 68ac +ca 52ce +e4 5410 +97 699d +9e 49d6 +ce 5a7c +c0 52a8 +13 ab0f +2d ac51 +15 ab13 +2f ac55 +37 85b7 +af e65d +16 abbe +30 ad00 +17 abbf +31 ad01 +c1 5a2b +db 5b6d +16 291c +1 2083 +1b 21c5 +e8 5ee8 +db 5967 +8 826a +d8 dbe0 +3b 767 +1a abce +34 ad10 +c2 5826 +9 826b +1b abcf +35 ad11 +c5 5a3b +df 5b7d +1c abd2 +36 ad14 +1c 8352 +92 69ac +93 41ad +88 42ca +a2 440c +fd f573 +26 849c +b0 cd80 +e4 76b8 +d0 f300 +51 b9a3 +3a 854e +f8 776a +bf ed77 +49 1ae1 +32 858e +f0 77aa +66 949c +f0 dd80 +d6 5934 +1d 8379 +a6 64be +62 94ac +20 c0a +3a d4c +b7 65bf +73 95ad +72 95ae +d8 7bc2 +39 a749 +f2 7d04 +b5 4593 +52 332e +6c 3470 +98 43ca +b2 450c +dc 53da +f6 551c +2d 8c59 +4f 3a57 +8 80e0 +7e bdd4 +64 bc92 +1b ab6f +8b c8c7 +98 c968 +30 870a +61 bca1 +6f b4d7 +7c b578 +f9 f741 +7a bde4 +60 bca2 +b6 65be +72 95ac +30 2d22 +af 667f +6b 966d +7c bfd2 +96 c114 +46 3894 +6a 966e +66 969e +15 a1b1 +35 739 +f 885d +cd 7a79 +2d 2cfb +ac 6658 +b6 4fbe +d0 5100 +91 41a1 +dd f973 +6 889c +c4 7ab8 +3e 2dfc +24 2cba +bd 6759 +aa 6ce4 +90 6ba2 +20 2400 +6 22be +46 989c +0 a200 +5a 996e +97 69bf +53 99ad +d1 d30b +4b 32e7 +eb d44d +52 99ae +1 8283 +1b 83c5 +92 490c +30 8788 +96 491c +34 8798 +83 4a0d +39 5e1 +c8 d048 +42 3024 +28 2ee2 +19 a349 +d2 7904 +70 b780 +95 4193 +87 4a1d +86 4a1e +91 638b +ab 64cd +d6 591c +74 9798 +55 11b1 +dc 5b70 +c2 5a2e +32 ad04 +18 abc2 +f 22d5 +16 899c +b 8a4d +e 8a5e +19 a3cb +d2 7986 +33 a50d +93 e98d +dd 5379 +8f 6a7f +4b 9a6d +7a 1d44 +60 1c02 +80 ca00 +60 1488 +a4 ec18 +62 1c06 +82 ca04 +54 b1b8 +2e 8efe +48 9040 +8c 6052 +7e 1d54 +64 1c12 +84 ca10 +64 1498 +73 bd27 +e1 5c09 +37 ad9d +66 1c16 +86 ca14 +58 b1c8 +68 1c42 +88 ca40 +94 e190 +35 dbb +b4 4718 +7b bd67 +e9 5c49 +25 ac9b +3f addd +c aa5a +70 1588 +32 d06 +52 bb04 +54 bb10 +3e 8ffe +58 9140 +9c 6152 +74 1d12 +44 90b0 +88 60c2 +74 1598 +d9 7b69 +36 d16 +56 bb14 +5a 9144 +40 9002 +9e 6156 +95 63b9 +76 1d16 +46 90b4 +8a 60c6 +78 1d42 +48 90e0 +8c 60f2 +c7 5817 +e 825c +bc ed58 +7a 1d46 +4a 90e4 +30 8fa2 +8e 60f6 +71 1fa9 +91 c9a3 +7a 954e +b7 659f +73 958d +72 958e +29 846b +9e 4374 +84 4232 +81 e203 +9b e345 +2 a8a6 +9f ebff +b9 ed41 +26 a49c +fa 576c +e0 562a +b0 ed80 +a6 4e16 +78 35ca +76 b7b6 +e4 5698 +3a 2d44 +20 2c02 +60 3488 +22 2c06 +3e 2d54 +24 2c12 +64 3498 +a1 6c09 +d2 f106 +26 2c16 +28 2c42 +2e 2c56 +25 2eb9 +70 3588 +6d 9651 +32 2d06 +6f 965d +34 2d12 +b1 6d09 +d 20f3 +e7 5eb7 +36 2d16 +3c 2d52 +af 665f +30 2d02 +6b 964d +e0 7ca8 +81 caa3 +9b cbe5 +6a 964e +19 abc9 +7c 97d0 +62 968e +de 5954 +c4 5812 +2e a65e +8c e052 +7e 9d54 +64 9c12 +b1 65a1 +8e e056 +66 9c16 +89 c0c9 +3 20a5 +c2 722c +d6 d994 +2c 47a +4c b278 +c6 7816 +d a25b +c6 fab4 +af c65f +30 8d02 +7e 377c +64 363a +26 ebc +9c e152 +a3 4e07 +75 35bb +ce faf4 +38 8d42 +6c 367a +d2 fb04 +3c 8d52 +6d 3cfb +ec 7658 +86 689e +42 988c +20 a620 +97 699f +53 998d +f a85d +e1 de29 +db 7b67 +4 a90 +c9 fa49 +6 a89c +76 9fbc +ba 6fce +d4 7110 +a8 466a +74 9d32 +7 a89d +e2 dc06 +81 e209 +2 a8ac +d0 7120 +c1 d801 +a 246 +3 a8ad +6e 9474 +54 9332 +1e a95e +4a 92ce +64 9410 +98 63c8 +1a a96e +23 a6a5 +b8 476a +4e 9274 +26 ac16 +db 73ef +f5 7531 +28 ac42 +45 1091 +42 9a0c +86 6a1e +7d 3fdb +97 411d +2c ac52 +e1 742b +fb 756d +2e ac56 +e3 742f +fd 7571 +1f 1fd +5 bb +34 ad12 +de 5b7e +8f 6a5f +4b 9a4d +87 6a9f +43 9a8d +62 3c24 +48 3ae2 +e8 dc48 +dc 71fa +4f 12f7 +91 e309 +12 a9ac +f aa5d +72 158c +e aa5e +20 c08 +37 8f95 +88 6048 +60 1c08 +24 c18 +e0 5c02 +fa 5d44 +2c c58 +a7 6695 +30 d08 +47 9095 +98 6148 +91 e9ab +ce 52f6 +b0 4d08 +f0 5d08 +34 d18 +b4 4d18 +2c a652 +35 d19 +9d 6159 +ba cf4c +a0 ce0a +96 e9bc +d3 5307 +83 eaa7 +6c b652 +75 1d19 +89 60c9 +2 a2a4 +f0 f582 +1f a3f5 +5 a2b3 +c2 7026 +5 91 +85 4091 +bc 4d58 +ee 7c74 +d4 7b32 +17 b9d +6 a2b4 +f4 f592 +b4 4592 +20 2c08 +24 2c18 +30 2d08 +a8 eec8 +66 1eb6 +b0 6d08 +31 2d09 +a9 eec9 +67 1eb7 +34 2d18 +ac eed8 +6a 1ec6 +b4 6d18 +10 2102 +ea 5ec6 +30 25a0 +47 1037 +89 e049 +71 35a1 +49 18e1 +34 25b0 +39 25e1 +2e c74 +14 b32 +29 8c49 +4 2098 +29 2641 +30 8d08 +52 3b06 +57 1137 +99 e149 +1e a156 +b7 451f +15 a3b9 +23 e07 +93 4b07 +59 19e1 +2d 2651 +34 8d18 +56 3b16 +38 8d48 +82 c2a6 +5a 3b46 +39 8d49 +9b 4b47 +c2 f026 +5 8091 +3c 8d58 +5e 3b56 +45 9091 +b4 c592 +51 1301 +9c 49da +21 ac09 +6 209c +99 c161 +88 ea62 +6d 1653 +30 ad08 +66 9eb6 +6e 1654 +31 ad09 +34 ad18 +6a 9ec6 +35 ad19 +39 ad49 +6f 9ef7 +3d ad59 +af e67d +30 ad20 +80 ca08 +b3 4f05 +48 9048 +8c 605a +84 ca18 +b7 4f15 +94 e912 +38 ad60 +86 68be +42 98ac +c1 d209 +71 97a9 +b5 67bb +68 1c4a +2 2a24 +88 ca48 +e3 d6a5 +a1 4e03 +bb 4f45 +b0 cf02 +ca d044 +7a 95e4 +60 94a2 +be 65f6 +58 9148 +9c 615a +9b 69c5 +81 6883 +42 3804 +ed 76db +23 6a5 +72 3d8e +27 6b5 +dc d1da +56 31b6 +30 25a2 +22 8eae +80 6002 +9a 6144 +3c 8ff0 +9 aa41 +f5 f51b +2b 84e5 +11 83a3 +d2 592c +c 82d0 +49 ba41 +af 64f7 +6b 94e5 +51 93a3 +dc d152 +44 9038 +88 604a +37 8f97 +11 ab01 +94 4912 +19 ab41 +3b 85e5 +21 84a3 +4 818 +23 84a7 +95 eb93 +af ecd5 +60 1c22 +7a 1d64 +6b b44f +82 e8a4 +a2 4e2c +52 b30e +6c b450 +83 e8a5 +a3 4e2d +79 b54b +90 e9a0 +e8 fc48 +90 e1aa +75 3733 +7c 9dfa +ba e5ec +a0 e4aa +b0 e5aa +9a c9e4 +80 c8a2 +82 c8a6 +c2 d8a6 +96 6116 +52 9104 +38 8fc2 +90 c9a2 +6 80be +92 c9a6 +8 80c2 +1 2a9 +d2 d9a6 +48 90c2 +82 e8a6 +bc 4f70 +a2 4e2e +90 e9a2 +11 183 +31 af81 +13 187 +be effe +d8 f140 +c8 504a +33 af85 +15 193 +35 af91 +31 58b +35 59b +18 1c2 +ab c44f +c2 f8a4 +8 2068 +e2 5e2c +38 afc0 +38 5c2 +e2 fca4 +58 11c2 +78 15c2 +88 40c2 +a8 44c2 +c8 50c2 +e8 54c2 +a c6 +2a aec4 +1a 1c6 +20 ae82 +3a afc4 +2a 4c6 +4a b2c4 +3a 5c6 +8c e0d8 +4a 10c6 +9c e1d8 +5a 11c6 +ac e4d8 +6a 14c6 +bc e5d8 +7a 15c6 +8a 40c6 +aa 44c6 +ca 50c6 +ea 54c6 +c 2078 +e6 5e3c +22 ae8e +3c afd0 +c2 7284 +2c 4d2 +b6 4db6 +4c b2d0 +d2 7384 +3c 5d2 +f2 7f84 +5c 11d2 +6c 14d2 +3d 87fb +f6 5db6 +7c 15d2 +8c 40d2 +ac 44d2 +cc 50d2 +ec 54d2 +2e aed4 +d1 7329 +2e 4d6 +4e b2d4 +e1 7429 +3e 5d6 +f1 7f29 +4e 10d6 +5e 11d6 +6e 14d6 +7e 15d6 +8e 40d6 +ae 44d6 +ce 50d6 +ee 54d6 +99 41c3 +f5 7d39 +2f a6dd +9b 41c7 +9d 41d3 +9f 41d7 +a8 44ca +ac 44da +48 3ae0 +bb 45c7 +b9 45cb +d 2053 +e7 5e17 +bf 45d7 +87 423f +8 8e2 +bd 45db +28 6c2 +b2 4fa6 +68 16c2 +bc 6df8 +18 21e2 +f2 5fa6 +c8 50e2 +c1 d881 +a 2c6 +e1 dc81 +2a 6c6 +dd 51d3 +df 51d7 +d9 51e3 +6f b6fd +db 51e7 +bc edda +71 b701 +c3 d88d +26 414 +c 2d2 +96 4bb6 +e3 dc8d +2c 6d2 +b6 4fb6 +66 1414 +4c 12d2 +37 873d +d6 5bb6 +6c 16d2 +71 3f09 +3 a027 +1c 21f2 +f6 5fb6 +ea 54e6 +e8 54ea +c5 d891 +e 2d6 +e5 dc91 +2e 6d6 +ff 55d7 +c7 523f +97 e995 +48 18e2 +31 ad8b +6e 16d6 +fd 55db +68 164a +99 4be1 +fb 55e7 +f9 55eb +a6 443e +30 728 +23 687 +69 bce3 +e8 f640 +3b 7cd +f8 7762 +21 68b +34 738 +27 697 +6d bcf3 +ec f650 +3f 7dd +fc 7772 +25 69b +97 431f +18 9c2 +d7 531f +58 19c2 +88 48c2 +98 49c2 +2e aedc +29 8449 +c8 58c2 +39 8549 +d8 59c2 +a 8c6 +1a 9c6 +8c e8d8 +4a 18c6 +9c e9d8 +5a 19c6 +8a 48c6 +11 830b +2b 844d +ca 58c6 +3c a5f2 +c 8d2 +1c 9d2 +4c 18d2 +5c 19d2 +8c 48d2 +9c 49d2 +2d 8459 +cc 58d2 +3d 8559 +dc 59d2 +e 8d6 +c1 7829 +1e 9d6 +4e 18d6 +5e 19d6 +8e 48d6 +15 831b +2f 845d +ce 58d6 +8a c246 +a3 4405 +89 42c3 +3a 7ee +f1 dda9 +a7 4415 +8d 42d3 +a9 46cb +12 8b2e +2c 8c70 +bc 4778 +af 46d7 +ad 46db +22 c04 +8 ac2 +62 1c04 +48 1ac2 +a2 4c04 +88 4ac2 +29 8649 +e2 5c04 +c8 5ac2 +a ac6 +90 e988 +e7 5415 +cd 52d3 +92 e98c +dc 5378 +cf 52d7 +e3 5425 +c9 52e3 +b2 ed8c +fc 5778 +ef 56d7 +ed 56db +e9 56eb +17 211f +31 583 +51 b381 +35 593 +55 b391 +13 987 +d8 f940 +22 a406 +11 98b +65 341b +7f 355d +17 997 +c2 f80e +dc f950 +26 a416 +15 99b +5b 99e5 +41 98a3 +9f 69f7 +c0 d200 +70 97a0 +b4 67b2 +cc f8da +2 88a4 +81 c201 +7b 1545 +61 1403 +86 68b6 +42 98a4 +c1 d201 +71 97a1 +b5 67b3 +54 91b0 +98 61c2 +8a cace +a4 cc10 +64 94b0 +a8 64c2 +ce f8de +83 c205 +a5 e419 +63 1407 +c3 d205 +73 97a5 +b7 67b7 +ce 705e +70 9f0a +74 97b0 +b8 67c2 +a9 4c49 +3b ad67 +6 88b4 +85 c211 +7f 1555 +65 1413 +f1 5f2b +a 20c6 +16 8b14 +2a 24c6 +36 8f14 +56 91b4 +9a 61c6 +8c cad2 +a6 cc14 +66 94b4 +aa 64c6 +87 c215 +10 a98a +67 1417 +4e 905c +34 8f1a +99 696b +c7 d215 +74 9f1a +49 98e3 +c8 d240 +78 97e0 +bc 67f2 +57 39bd +4a 98e4 +8e 68f6 +c9 d241 +79 97e1 +bd 67f3 +4b 98e7 +ca d244 +59 39c1 +48 90c0 +8c 60d2 +68 94c0 +ac 64d2 +d6 f91e +8b c245 +ad e459 +14 a9ba +6b 1447 +38 8f4a +cb d245 +7b 97e5 +61 96a3 +bf 67f7 +78 9f4a +4d 98f3 +cc d250 +6d 1453 +3e 877c +24 863a +f7 5d37 +4f 98f7 +ce d254 +43 388f +5d 39d1 +2e 24d6 +3e 25d6 +59 9363 +6a 94c4 +50 9382 +ae 64d6 +79 9763 +8f c255 +18 a9ca +6f 1457 +3c 8f5a +cf d255 +7c 9f5a +62 b426 +d0 5308 +51 19ab +a5 443b +bf 457d +b9 45c3 +bd 45d3 +88 48ca +41 1201 +8c 48da +9b 49c7 +aa e446 +99 49cb +9f 49d7 +ae e456 +9d 49db +ec fcda +22 8ca4 +a1 c601 +44 92b0 +a2 6404 +88 62c2 +f0 fda8 +b a867 +74 1f12 +64 96b0 +a8 66c2 +e3 d605 +e8 54e2 +65 9cb3 +7f 9df5 +e4 d610 +c9 5049 +5b b167 +fd 55d3 +5a 39ce +f 2f5 +c1 f881 +a 22c6 +e1 fc81 +2a 26c6 +46 92b4 +8a 62c6 +76 1f16 +68 b4c8 +f9 55e3 +29 8ce3 +a8 c640 +37 2dbd +69 9ce3 +e8 d640 +fa dd6c +e0 dc2a +2b 8ce7 +aa c644 +39 2dc1 +6b 9ce7 +ea d644 +c3 f88d +26 2414 +c 22d2 +e3 fc8d +2c 26d2 +c2 da04 +72 9fa4 +b6 6fb6 +cc 58da +52 9ba6 +eb d645 +6d 9cf3 +ec d650 +6f 9cf7 +ee d654 +34 8730 +c5 f891 +e 22d6 +1b bcd +d8 7b62 +1 a8b +6f 365d +1f bdd +dc 7b72 +5 a9b +c3 d20d +44 98b0 +88 68c2 +6 a8b4 +85 e211 +b9 6dc9 +45 9211 +a 28c6 +55 9311 +1a 29c6 +46 98b4 +c5 d211 +8a 68c6 +87 e215 +4e b05c +34 af1a +a a8e4 +89 e241 +da f96c +c0 f82a +c7 d21d +48 98c0 +8c 68d2 +8b e245 +38 af4a +1e 29d6 +66 16be +f0 5fa2 +4a 98c4 +8e 68d6 +59 9b63 +8f e255 +3c af5a +a9 46c3 +ad 46d3 +ad 6cdb +69 9cc9 +1 88b +d8 7962 +12 a306 +1b 9cd +22 aca4 +a1 e601 +a3 e605 +25 acb3 +3f adf5 +a4 e610 +dc 7972 +5 89b +16 a316 +1f 9dd +27 acb7 +95 4b99 +a6 e614 +a7 e615 +29 ace3 +a8 e640 +fa fd6c +e0 fc2a +80 4a88 +12 aba6 +ab e645 +2d acf3 +ac e650 +84 4a98 +16 abb6 +af e655 +e3 5c2d +c9 5aeb +fc 5ff8 +15 2193 +a 80c6 +e2 7424 +c8 72e2 +4a 90c6 +2a a644 +19 bc9 +11 21a3 +3 8aaf +1d 8bf1 +99 c1cb +13 21a7 +5 8ab3 +1f 8bf5 +a2 ee84 +c 80d2 +e2 fe84 +4c 90d2 +b1 ef29 +e 80d6 +d1 f329 +2e 84d6 +87 48bd +ec 76f2 +f1 ff29 +4e 90d6 +da 7b6e +2e a654 +1d bd9 +31 25ab +d9 51c3 +6f b6dd +db 51c7 +e8 54ca +fb 55c7 +f9 55cb +b 2ed +88 60e2 +c3 5807 +a 824c +74 1d32 +99 61e3 +68 94c8 +ac 64da +b9 65eb +70 1728 +a5 e699 +63 1687 +2d 24d9 +b7 6dbd +7b 17cd +61 168b +87 c21f +8 88c2 +97 c31f +18 89c2 +66 343c +4c 32fa +c7 d21f +48 98c2 +d7 d31f +58 99c2 +a6 443c +8c 42fa +4a 98c6 +e 88d6 +c3 50af +dd 51f1 +e6 7c34 +2d a679 +cc 7af2 +4e 98d6 +a9 c6cb +23 26a7 +e3 5405 +c9 52c3 +22 8c04 +8 8ac2 +a6 6c16 +62 9c04 +48 9ac2 +94 c99a +49 92c1 +a7 6415 +8d 62d3 +96 c99e +4b 92c5 +9c 6378 +8f 62d7 +6d b4d9 +69 96c9 +ad 66db +38 85e2 +b2 cdae +ab 66e7 +7e 9f7c +64 9e3a +a9 66eb +35 2593 +31 25a3 +17 2997 +81 c8a9 +7a 9f6c +60 9e2a +be 6f7e +a 2ee +24 430 +c1 d8a9 +c8 58ca +30 8720 +22 a404 +11 989 +8 a2c2 +a8 64e2 +e3 5c07 +41 baa1 +2a 864c +79 95c1 +bd 65d3 +b9 65e3 +91 c9a9 +1a 3ee +34 530 +d1 d9a9 +48 98c8 +41 3201 +8c 68da +b5 6db9 +a a8c6 +2a e4e +d9 71e1 +e a8d6 +fa dfe4 +e0 dea2 +2e e5e +c3 70af +dd 71f1 +e9 56c3 +81 e8a9 +1f b55 +5 a13 +69 96c1 +b4 cd9a +ad 66d3 +b0 cdaa +a9 66e3 +2f c55 +15 b13 +97 c33f +18 89e2 +8b 6ae7 +a3 6c2d +89 6aeb +19 81c3 +1b 81c7 +b3 ef85 +1d 81d3 +a8 eee8 +1f 81d7 +f a07d +e9 de41 +28 84ca +c2 f28c +2c 84da +3b 85c7 +39 85cb +c8 f2e8 +3f 85d7 +d3 f38d +3d 85db +48 90e2 +4a 90e6 +2a a664 +19 be9 +f3 ff85 +5d 91d3 +e8 fee8 +5f 91d7 +59 91e3 +5b 91e7 +6a 94e6 +39 fe9 +68 94ea +7f 95d7 +7d 95db +7b 95e7 +79 95eb +23 8405 +9 82c3 +d3 592f +27 8415 +d 82d3 +e2 5c86 +29 86cb +8b 484f +f5 5d33 +3c 8778 +2f 86d7 +e6 5c96 +2d 86db +8f 485f +67 9415 +4d 92d3 +5c 9378 +4f 92d7 +a7 6437 +63 9425 +49 92e3 +7c 9778 +6f 96d7 +6d 96db +cf 585f +69 96eb +82 c206 +11 2983 +9e c354 +1f 89f7 +84 c212 +13 298f +86 c216 +15 2993 +88 c242 +96 631c +17 29bf +c8 d242 +d6 731c +57 39bf +ca d246 +8c c252 +cc d252 +8e c256 +ce d256 +39 85c3 +d3 f385 +3d 85d3 +1b 2345 +1 2203 +8 88ca +1f 2355 +5 2213 +c 88da +1b 89c7 +a2 c606 +68 94e2 +be c754 +3f 8df7 +a4 c612 +10 2b2a +2a 2c6c +7d 95d3 +a6 c616 +91 6b89 +e6 d616 +79 95e3 +a8 c642 +87 681f +43 980d +b6 671c +37 2dbf +e8 d642 +c6 7894 +d a2d9 +93 6187 +95 6bb9 +ea d646 +c9 d243 +4a 98e6 +c3 faa7 +ac c652 +47 981d +ec d652 +4b 326f +5f 99d7 +ae c656 +99 6bc9 +ee d656 +da d344 +c0 d202 +5b 99e7 +91 4909 +88 e242 +93 490d +8a e246 +7b 37ed +61 36ab +95 4919 +8c e252 +97 491d +8e e256 +7f 37fd +65 36bb +29 86c3 +8b 4847 +8f 4857 +18 8b68 +b 8ac7 +23 8c0d +9 8acb +1c 8b78 +f 8ad7 +27 8c1d +d 8adb +6d 96d3 +cf 5857 +69 96e3 +cb 5867 +b1 4d09 +a8 e642 +b5 4d19 +ac e652 +5c 9b78 +4f 9ad7 +67 9c1d +4d 9adb +a7 6c3f +63 9c2d +49 9aeb +59 91c3 +5b 91c7 +68 94ca +7b 95c7 +79 95cb +ec defa +1f a1d7 +e9 fe41 +c9 5043 +dc dbd8 +56 3bb4 +3f 75f +f3 dfa7 +19 a1e3 +e3 dc85 +39 76b +a4 c690 +d8 dbe8 +3b 76f +2c a4da +98 43c8 +2a a4e6 +3f a5d7 +e9 5443 +a9 44c9 +3b a5e7 +39 a5eb +69 96cb +cb 584f +d5 7933 +1c a378 +f a2d7 +23 a425 +9 a2e3 +b a2e7 +c8 70c8 +f5 7d33 +3c a778 +2f a6d7 +e6 7c96 +2d a6db +4b 984d +8f 685f +2b a6e7 +e8 74c8 +79 95c3 +9a c9ec +93 6325 +80 c8aa +b3 4da7 +94 e99a +49 b2c1 +da d9ec +3d 573 +d3 7325 +c0 d8aa +3a 87ec +20 86aa +f3 5da7 +41 3203 +5b 3345 +48 98ca +5b 99c7 +28 a4e2 +39 a5e3 +a3 6425 +89 62e3 +90 c9aa +b2 c70e +e3 fca5 +c a8da +89 e243 +a a8e6 +2a e6e +9a e344 +80 e202 +89 48c9 +1b a9e7 +a4 ce90 +3b f6f +69 96c3 +cb 5847 +b3 6da7 +35 8f11 +a7 6c1f +63 9c0d +49 9acb +f3 7d2f +2d a6d3 +4b 9845 +8f 6857 +29 a6e3 +8b 6867 +27 ac1d +d aadb +b aae7 +c8 78c8 +23 ac2d +9 aaeb +2 88ac +81 c209 +7b 154d +61 140b +80 48a0 +69 144b +2d 86d3 +f3 5d2f +79 97e9 +bd 67fb +29 8ceb +22 2624 +a8 c648 +69 9ceb +62 3624 +e8 d648 +63 3625 +6a 9cec +ae 6cfe +50 9baa +e9 d649 +29 aceb +66 1636 +a8 e648 +8 e2 +ca 5046 +28 aee0 +18 1e2 +da 5146 +38 afe0 +28 4e2 +ea 5446 +48 b2e0 +38 5e2 +97 e195 +48 10e2 +58 11e2 +b7 e595 +68 14e2 +78 15e2 +88 40e2 +98 41e2 +e7 7cb7 +2e a6fc +a8 44e2 +a e6 +2a aee4 +1a 1e6 +20 aea2 +3a afe4 +2a 4e6 +4a b2e4 +3a 5e6 +8c e0f8 +4a 10e6 +9c e1f8 +5a 11e6 +ac e4f8 +6a 14e6 +bc e5f8 +7a 15e6 +9a 41e6 +30 a700 +aa 44e6 +ba 45e6 +ec 745a +22 424 +8 2e2 +97 e395 +62 1424 +48 12e2 +b7 e795 +68 16e2 +a2 4424 +88 42e2 +a8 46e2 +43 18ad +ee 745e +c1 d8a1 +a 2e6 +97 433f +18 9e2 +d7 533f +58 19e2 +88 48e2 +98 49e2 +2e aefc +29 8469 +2 8286 +c8 58e2 +39 8569 +12 8386 +d8 59e2 +89 4243 +a 8e6 +8c e8f8 +c9 5243 +4a 18e6 +ec 7c5a +22 c24 +8 ae2 +bf 47d7 +97 eb95 +62 1c24 +48 1ae2 +ff 57d7 +a2 4c24 +88 4ae2 +29 8669 +e2 5c24 +c8 5ae2 +ee 7c5e +a ae6 +e2 5ea6 +8 20e2 +14 8b30 +28 24e2 +34 8f30 +90 c10a +a 20e6 +16 8b34 +b0 c50a +2a 24e6 +36 8f34 +c0 da00 +81 ca01 +c1 da01 +83 ca05 +55 b1b9 +c3 da05 +73 9fa5 +b7 6fb7 +c4 da10 +74 9fb0 +d2 7104 +b8 6fc2 +85 ca11 +c5 da11 +c6 da14 +76 9fb4 +ba 6fc6 +87 ca15 +59 b1c9 +c7 da15 +c8 da40 +89 ca41 +c9 da41 +a8 c448 +22 2424 +8 22e2 +28 26e2 +1a 23ce +34 2510 +d1 f989 +8b ca45 +5d b1f9 +cb da45 +61 9ea3 +7b 9fe5 +bf 6ff7 +8d ca51 +23 8eaf +9b 6145 +3d 8ff1 +81 6003 +cd da51 +d4 fb98 +37 271f +90 c30a +aa c44c +c1 f8a1 +a 22e6 +8f ca55 +25 8eb3 +3f 8ff5 +83 6007 +cf da55 +97 633f +53 932d +18 29e2 +c3 d22d +88 68e2 +a 8a4c +d3 d32d +98 69e2 +81 ea01 +83 ea05 +85 ea11 +87 ea15 +89 ea41 +8b ea45 +8d ea51 +8f ea55 +8 80e2 +18 81e2 +28 84e2 +1a 81e6 +2a 84e6 +3a 85e6 +9 e9 +30 2d82 +6b 96cd +af 66df +ec f45a +22 8424 +8 82e2 +ee f45e +a 82e6 +2a 86e6 +87 c23f +8 88e2 +c7 d23f +48 98e2 +d7 d33f +58 99e2 +e7 7c1d +3 aa5 +cd 7adb +89 c243 +a 88e6 +99 c343 +1a 89e6 +62 9c24 +a6 6c36 +48 9ae2 +ee fc5e +a 8ae6 +b3 4707 +2 2226 +88 c24a +bb 4747 +42 3226 +c8 d24a +be edfc +a4 ecba +fb 5747 +d9 fbe1 +22 2626 +a8 c64a +62 3626 +e8 d64a +88 e24a +ba e74c +3b adef +a0 e60a +a8 e64a +9a cb44 +80 ca02 +82 ca06 +54 b1ba +9e cb54 +84 ca12 +de db54 +c4 da12 +86 ca16 +58 b1ca +c6 da16 +88 ca42 +c8 da42 +8a ca46 +7b 1fed +61 1eab +5c b1fa +ca da46 +cc da52 +d7 f3bf +f1 f501 +ce da56 +d9 f3c3 +f3 f505 +47 329d +c0 d220 +cc f8fa +81 c221 +96 e394 +7b 1565 +61 1423 +c1 d221 +ce f8fe +83 c225 +a5 e439 +63 1427 +c3 d225 +ce 707e +70 9f2a +4f 32dd +c8 d260 +d4 f93a +89 c261 +c9 d261 +ca d264 +28 44a +59 39e1 +d6 f93e +8b c265 +38 8f6a +cb d265 +78 9f6a +ec fcfa +a1 c621 +e3 d625 +6f 36dd +e8 d660 +f4 fd3a +a9 c661 +e9 d661 +aa c664 +39 2de1 +ea d664 +eb d665 +81 e221 +6d 9e71 +83 e225 +6f 9e75 +4a b06c +30 af2a +8b e265 +38 af6a +a0 e620 +80 4208 +12 a326 +1 8ab +1b 9ed +a1 e621 +91 4ba9 +a2 e624 +82 420c +3 8af +1d 9f1 +a3 e625 +a8 e660 +a9 e661 +80 4aa8 +ab e665 +c0 da08 +19 2141 +f3 5f05 +ce 70fc +70 9fa8 +b4 6fba +81 ca09 +c1 da09 +cf 70fd +71 9fa9 +b5 6fbb +c4 da18 +1d 2151 +3 200f +f7 5f15 +d2 710c +74 9fb8 +b8 6fca +85 ca19 +c5 da19 +42 3a24 +c8 da48 +e1 5e03 +7 203f +fb 5f45 +3 2a25 +89 ca49 +43 3a25 +c9 da49 +7 2a35 +8d ca59 +81 ea09 +43 1a07 +85 ea19 +47 1a37 +89 ea49 +4b 1a47 +8d ea59 +9a c364 +7 229f +80 c222 +82 c226 +8a c266 +a2 4e8c +ca d266 +8 20c8 +e2 5e8c +a2 c626 +e2 d626 +7 a2bd +6f 36df +e8 d662 +ea d666 +9a e364 +89 48e9 +80 e222 +6c 9e72 +8b 48ed +82 e226 +6e 9e76 +93 492d +8a e266 +45 b831 +9a cb4c +80 ca0a +b3 4f07 +9e cb5c +84 ca1a +b7 4f17 +de db5c +c4 da1a +1d 2153 +f7 5f17 +2 2a26 +88 ca4a +bb 4f47 +42 3a26 +c8 da4a +fb 5f47 +46 3a36 +cc da5a +f1 f509 +ff 5f57 +81 c229 +96 e39c +7b 156d +61 142b +c1 d229 +89 c269 +9e e3dc +84 e29a +69 146b +c9 d269 +a0 c628 +a1 c629 +a8 c668 +e8 d668 +a9 c669 +e9 d669 +81 e229 +6d 9e79 +a0 e628 +a1 e629 +a8 e668 +a9 e669 +47 3a9d +c0 da20 +81 ca21 +c1 da21 +c2 da24 +1e 23de +d5 f999 +e3 fcad +2c 26f2 +83 ca25 +c3 da25 +4f 3add +c8 da60 +e9 fce9 +32 272e +89 ca61 +c9 da61 +d0 fba8 +33 272f +ca da64 +eb fced +d1 fbab +34 2732 +8b ca65 +cb da65 +81 ea21 +83 ea25 +8b ea65 +9a c36c +80 c22a +b3 4727 +88 c26a +bb 4767 +c8 d26a +fb 5767 +ba c76c +a0 c62a +a8 c66a +e8 d66a +9a e36c +80 e22a +ba cdee +b3 6727 +6c 9e7a +84 4890 +88 e26a +bb 6767 +ba e76c +a0 e62a +a4 4c90 +a8 e66a +9a cb64 +7 2a9f +80 ca22 +da db64 +47 3a9f +c0 da22 +a3 6c2f +bd 6d71 +82 ca26 +c2 da26 +a5 6c33 +61 9c21 +bf 6d75 +f 2adf +88 ca62 +4f 3adf +c8 da62 +8a ca66 +ca da66 +c0 d280 +81 c281 +7b 15c5 +61 1483 +18 360 +c1 d281 +8d e2d1 +58 1360 +c2 d284 +8e e2d4 +59 1363 +83 c285 +a5 e499 +70 1528 +63 1487 +0 222 +1a 364 +c3 d285 +8f e2d5 +40 1222 +5a 1364 +ce 70de +70 9f8a +c4 d290 +85 c291 +7f 15d5 +65 1493 +c5 d291 +42 122e +12 a984 +5c 1370 +98 69ea +c6 d294 +87 c295 +74 1538 +67 1497 +4 232 +1e 374 +99 69eb +c7 d295 +44 1232 +5e 1374 +74 9f9a +a0 c680 +e0 d680 +a1 c681 +38 760 +e1 d681 +ad e6d1 +78 1760 +a2 c684 +39 763 +e2 d684 +ae e6d4 +79 1763 +e3 d685 +af e6d5 +60 1622 +7a 1764 +e4 d690 +31 ad83 +7b 176f +a5 c691 +d9 dbe9 +22 62e +3c 770 +e5 d691 +62 162e +32 ad84 +7c 1770 +a6 c694 +b8 6dea +e6 d694 +33 ad87 +7d 1773 +64 1632 +7e 1774 +81 e281 +12 8986 +18 2360 +6d 9ed1 +83 e285 +0 2222 +1a 2364 +6f 9ed5 +4a b0cc +30 af8a +4 2232 +1e 2374 +4e b0dc +34 af9a +a0 e680 +a1 e681 +32 8d86 +38 2760 +a2 e684 +39 2763 +a3 e685 +20 2622 +3a 2764 +a4 e690 +d8 fbe8 +3b 276f +d9 fbe9 +36 8d96 +22 262e +3c 2770 +da fbec +c0 faaa +3d 2773 +db fbed +c1 faab +24 2632 +3e 2774 +81 ca29 +c1 da29 +c8 da68 +e1 5e23 +fb 5f65 +89 ca69 +c9 da69 +81 ea29 +89 ea69 +67 16b5 +b2 4d8e +9a c3c4 +80 c282 +a9 c469 +82 c286 +9e c3d4 +84 c292 +de d3d4 +c4 d292 +89 6249 +a 28ec +ad c479 +86 c296 +ed d479 +c6 d296 +8b 624d +c 28f0 +ba c7c4 +a0 c682 +fa d7c4 +e0 d682 +a2 c686 +e2 d686 +a7 663d +28 2ce0 +be c7d4 +a4 c692 +fe d7d4 +e4 d692 +a6 c696 +e6 d696 +9a e3c4 +80 e282 +6c 9ed2 +a9 e469 +82 e286 +6e 9ed6 +9e e3d4 +69 1463 +84 e292 +ad e479 +6b 1467 +86 e296 +ba e7c4 +a0 e682 +a2 e686 +be e7d4 +a4 e692 +88 ca6a +bb 4f67 +c8 da6a +fb 5f67 +81 c289 +b 2c7 +18 368 +c1 d289 +4b 12c7 +8d e2d9 +58 1368 +b7 e73d +38 ade0 +a0 c688 +e0 d688 +e1 d689 +6b 16c7 +ad e6d9 +78 1768 +58 b1e0 +81 e289 +b 22c7 +12 898e +18 2368 +6d 9ed9 +a0 e688 +a1 e689 +23 607 +c0 da80 +c1 da81 +8d ead1 +58 1b60 +c2 da84 +8e ead4 +59 1b63 +c3 da85 +8f ead5 +40 1a22 +5a 1b64 +27 617 +c4 da90 +5b 1b6f +85 ca91 +2 a2e +1c b70 +c5 da91 +42 1a2e +5c 1b70 +c6 da94 +5d 1b73 +87 ca95 +4 a32 +1e b74 +c7 da95 +44 1a32 +5e 1b74 +83 ea85 +0 2a22 +1a 2b64 +2 2a2e +1c 2b70 +4 2a32 +1e 2b74 +ba c7cc +a0 c68a +fa d7cc +e0 d68a +9a e3cc +80 e28a +6c 9eda +ba e7cc +a0 e68a +9a cbc4 +80 ca82 +da dbc4 +c0 da82 +a3 6c8f +bd 6dd1 +a5 6c93 +61 9c81 +bf 6dd5 +9e cbd4 +84 ca92 +de dbd4 +c4 da92 +ad cc79 +86 ca96 +ed dc79 +c6 da96 +c0 d2a0 +e 25c +98 4b40 +81 c2a1 +7b 15e5 +61 14a3 +59 3b41 +c2 d2a4 +80 4a02 +9a 4b44 +a0 c6a0 +a1 c6a1 +a2 c6a4 +e2 d6a4 +a0 4e02 +ba 4f44 +81 e2a1 +b1 6f03 +cb 7045 +6d 9ef1 +a0 e6a0 +a1 e6a1 +a2 e6a4 +23 60f +3d 751 +c0 da88 +19 21c1 +f3 5f85 +81 ca89 +b ac7 +18 b68 +24 610 +c1 da89 +4b 1ac7 +8d ead9 +58 1b68 +27 61f +c4 da98 +85 ca99 +f ad7 +1c b78 +c5 da99 +4f 1ad7 +5c 1b78 +81 ea89 +b 2ac7 +18 2b68 +f 2ad7 +1c 2b78 +9a c3e4 +80 c2a2 +58 3b42 +ba c7e4 +a0 c6a2 +e2 d6a6 +ba 4f46 +9a e3e4 +80 e2a2 +ca 7046 +6c 9ef2 +ba e7e4 +a0 e6a2 +9e cbdc +84 ca9a +c4 5038 +b7 4f97 +de dbdc +c4 da9a +ae 4e74 +1d 21d3 +f7 5f97 +a0 c6a8 +e0 d6a8 +4a b066 +b8 4f48 +a0 e6a8 +23 627 +c0 daa0 +e a5c +c2 daa4 +c3 daa5 +6a b64c +81 eaa1 +7c 357a +6c b650 +83 eaa5 +7e 357e +fa d7ec +e0 d6aa +23 ae85 +b8 4f4a +9a e3ec +80 e2aa +b3 67a7 +ca 704e +6c 9efa +ba e7ec +a0 e6aa +28 4c8 +b2 4dac +58 11c8 +68 14c8 +f2 5dac +78 15c8 +1a a1e6 +88 40c8 +ef f45f +b 82e7 +d8 51c8 +ff f55f +1b 83e7 +29 4c9 +aa e6e6 +b3 4dad +59 11c9 +62 3c0c +48 3aca +69 14c9 +ea f6e6 +f3 5dad +79 15c9 +1b a1e7 +89 40c9 +5b b1e7 +c9 50c9 +d9 51c9 +e2 7c0c +c8 7aca +9a cbe4 +80 caa2 +62 14ac +82 caa6 +c d8 +a7 e437 +96 49bc +1c 1d8 +2c 4d8 +3c 5d8 +5c 11d8 +6c 14d8 +f6 5dbc +7c 15d8 +1e a1f6 +8c 40d8 +9c 41d8 +dc 51d8 +da 716e +1d 1d9 +fa 756e +3d 5d9 +46 301c +2c 2eda +5d 11d9 +66 3c1c +4c 3ada +6d 14d9 +ee f6f6 +f7 5dbd +7d 15d9 +1f a1f7 +8d 40d9 +dd 51d9 +e6 7c1c +2 aa4 +cc 7ada +9c 41da +d5 7393 +ef 74d5 +90 438a +aa 44cc +91 438b +ab 44cd +94 439a +ae 44dc +95 439b +af 44dd +ba 45ce +a5 449b +bf 45dd +19 a363 +8 8e8 +be 45de +c8 50e8 +c9 50e9 +8 2c8 +28 6c8 +be e55e +48 12c8 +68 16c8 +7a b7e6 +e8 56c8 +af 6c7f +51 9b2b +6b 9c6d +da d9c6 +9 2c9 +7c 1758 +93 4bad +3a 87c6 +fa ddc6 +29 6c9 +b3 4fad +bf e55f +49 12c9 +d3 5bad +7a 97c6 +69 16c9 +dc 51da +5b b3e7 +c9 52c9 +d9 51e9 +e2 7c2c +c8 7aea +d8 51ea +d4 539a +ee 54dc +d5 539b +ef 54dd +d0 53aa +ea 54ec +d1 53ab +eb 54ed +36 8d14 +1c 8bd2 +c 2d8 +2c 6d8 +4c 12d8 +6c 16d8 +3 a02d +1e a3f6 +8c 42d8 +f 887d +3e a7f6 +ac 46d8 +7e b7f6 +ec 56d8 +55 9b3b +6f 9c7d +ca 726e +de d9d6 +d 2d9 +fe ddd6 +ea 766e +2d 6d9 +4d 12d9 +d7 5bbd +7e 97d6 +6d 16d9 +1d 21f9 +f7 5fbd +e5 549b +ff 55dd +59 b363 +48 18e8 +1f a3f7 +8d 42d9 +fe 55de +10 300 +80 4aa2 +9a 4be4 +5f b3f7 +cd 52d9 +fa 55ee +4d 3ad1 +3c 7d0 +22 68e +fe 7774 +e4 7632 +27 69d +b1 4f81 +26 69e +ca 50c4 +b0 4f82 +19 a343 +8 8c8 +59 b343 +48 18c8 +69 b443 +58 19c8 +3a 8dc6 +2a a444 +10 a302 +19 9c9 +40 b202 +5a b344 +49 18c9 +ae 46fe +7a 9dc6 +50 b302 +6a b444 +59 19c9 +e aa7e +72 15ac +5d b353 +4c 18d8 +6d b453 +5c 19d8 +54 b312 +6e b454 +5d 19d9 +67 169d +17 21bd +f1 5f81 +66 169e +16 21be +f0 5f82 +a6 441c +8c 42da +a0 448a +ba 45cc +a4 449a +be 45dc +30 d80 +af 46dd +ae 46de +d8 51e8 +8 ac8 +be ed5e +48 1ac8 +5a bbe6 +c8 5ac8 +9 ac9 +3a 8fc6 +bf ed5f +49 1ac9 +7a 9fc6 +e6 541c +cc 52da +c9 52e9 +e2 542c +c8 52ea +e4 549a +fe 55dc +e0 54aa +fa 55ec +c ad8 +4c 1ad8 +1e abf6 +8c 4ad8 +5e bbf6 +cc 5ad8 +ca 7a6e +d ad9 +e1 fe29 +3e 8fd6 +4d 1ad9 +7e 9fd6 +70 1d80 +ef 56dd +ee 56de +eb 56ed +ea 56ee +13 a307 +d9 7963 +2 88c +17 a317 +dd 7973 +6 89c +6c 36d2 +d0 7922 +a a2c6 +13 98d +12 98e +16 99e +28 24c8 +b2 6dac +a8 64c8 +57 b317 +46 189c +2e 8c76 +53 b327 +c1 5209 +42 18ac +bc 47d2 +56 199e +b8 47e2 +1 283 +1b 3c5 +9b e347 +8a 48cc +82 e206 +8b 48cd +f0 7702 +9f e357 +8e 48dc +86 e216 +8f 48dd +f4 7712 +4f 12f5 +9a 49ce +53 1305 +9e 49de +8 22c8 +28 26c8 +fa fdc6 +29 26c9 +1d 1fb +b3 6fad +df f357 +ce 58dc +c6 f216 +cf 58dd +c 22d8 +2c 26d8 +ac 66d8 +15 ab3b +2f ac7d +fe fdd6 +2d 26d9 +b7 6fbd +de 59de +23 a407 +12 98c +6 a9e +8 28c8 +18 29c8 +88 68c8 +5e 9356 +9 28c9 +3a adc6 +de d356 +89 68c9 +63 b427 +d1 5309 +52 19ac +46 1a9e +ab e447 +80 488a +9a 49cc +af e457 +84 489a +9e 49dc +8b 4acd +a4 4c10 +8a 4ace +8f 4add +8e 4ade +b3 658d +8 2ac8 +88 6ac8 +9 2ac9 +3a afc6 +89 6ac9 +ef f457 +c4 589a +de 59dc +c 2ad8 +8c 6ad8 +cf 5add +ce 5ade +9 80c9 +8d 60db +49 90c9 +9d 61db +59 91c9 +62 bc0c +48 baca +22 24ac +23 24ad +1c 81d8 +3e 2fd6 +5c 91d8 +b0 c520 +37 259d +ca f06e +d 80d9 +36 259e +4d 90d9 +5d 91d9 +66 bc1c +4c bada +33 25ad +28 c40 +e afe +32 25ae +d8 51ca +d0 538a +ea 54cc +d1 538b +eb 54cd +fa 55ce +c 2f0 +88 60e8 +67 1c97 +74 1d38 +89 60e9 +6c b672 +75 1d39 +db 59c5 +c1 5883 +8 82c8 +21 683 +3b 7c5 +f1 570b +72 1dae +61 1683 +7b 17c5 +c2 5884 +9 82c9 +f2 570c +73 1daf +58 91c8 +9c 61da +a7 641d +8d 62db +49 92c9 +99 61e9 +c7 7a35 +95 639b +51 9389 +af 64dd +90 63aa +aa 64ec +86 4a3e +25 693 +3f 7d5 +4c 92d8 +65 1693 +7f 17d5 +8 28e8 +ca f26e +c6 5894 +d 82d9 +f6 571c +77 1dbf +63 168d +7c 17d0 +62 168e +32 25ac +a0 c620 +27 269d +1b 1cf +b1 6f81 +b9 c763 +26 269e +23 26ad +e2 540c +c8 52ca +4b 986f +e0 548a +fa 55cc +eb 56cd +ea 56ce +98 61e8 +c6 7a34 +8 8ac8 +21 e83 +3b fc5 +61 1e83 +7b 1fc5 +a6 641c +48 92c8 +8c 62da +89 62e9 +75 1f39 +3e 855c +24 841a +55 b9b1 +a0 64aa +ba 65ec +6a 96cc +ae 66de +8 e8 +ab 66ed +aa 66ee +45 38b9 +b8 47c2 +3b 8d67 +52 198e +9 86b +28 a4c8 +36 f16 +e3 de8d +9 a0c9 +17 b17 +99 c961 +6 289c +81 6209 +2 28ac +3 28ad +6e 1474 +54 1332 +16 299e +db f347 +ca 58cc +da 59ce +c2 7884 +9 a2c9 +8f 68dd +25 2693 +3f 27d5 +5f 1355 +45 1213 +8 a8c8 +21 2c83 +3b 2dc5 +46 1214 +9 a8c9 +91 6309 +12 29ac +eb f447 +c0 588a +da 59cc +cb 5acd +e4 5c10 +ca 5ace +8 aac8 +21 2e83 +3b 2fc5 +9 aac9 +c aad8 +25 2e93 +3f 2fd5 +85 409b +9f 41dd +b2 ef8c +1c 81da +55 b393 +6f b4d5 +10 838a +2a 84cc +e8 76e8 +e9 76e9 +14 839a +2e 84dc +ec 76f8 +ed 76f9 +d0 f380 +3a 85ce +a9 4e61 +2e aede +d4 f390 +3e 85de +8d 60fb +49 90e9 +f2 ff8c +5c 91da +62 bc2c +48 baea +58 91ea +54 939a +6e 94dc +55 939b +6f 94dd +af 64ff +51 93ab +6b 94ed +65 949b +7f 95dd +7e 95de +7a 95ee +22 840c +db 59c7 +8 82ca +20 848a +3a 85cc +24 849a +3e 85dc +f4 5d3a +2e 86de +9c 61fa +58 91e8 +64 949a +7e 95dc +be 65fe +60 94aa +7a 95ec +34 2d92 +6f 96dd +6e 96de +3 2205 +a 88cc +3f dd7 +c8 7ae8 +26 c96 +c9 7ae9 +7 2215 +e 88dc +cc 7af8 +cc f872 +f 88dd +cd 7af9 +47 3215 +4e 98dc +4f 98dd +8e 68fe +43 3225 +c9 d249 +4a 98ec +8f 68ff +4b 98ed +13 2305 +0 888a +1a 89cc +17 2315 +4 889a +1e 89dc +c8 fa62 +b 8acd +24 8c10 +a 8ace +d9 53e9 +cc fa72 +f 8add +1d 21d1 +f7 5f95 +3 208f +e 8ade +33 a58d +dd 53f9 +57 3315 +44 989a +5e 99dc +d9 d349 +40 98aa +9e 69fe +53 3325 +5a 99ec +4f 9add +4e 9ade +a9 6c41 +8f 6aff +4b 9aed +a8 6c42 +64 9c30 +4a 9aee +58 91ca +af 64df +51 938b +6b 94cd +7a 95ce +e3 dead +9 a0e9 +f6 df9e +1c a1da +3b fcd +f8 7f62 +21 e8b +f3 dfad +19 a1e9 +47 ba35 +14 a39a +2e a4dc +59 19eb +6a b466 +d8 5348 +10 a3aa +2a a4ec +6 8a3e +3a a5ee +be 65de +60 948a +7a 95cc +f2 dfac +18 a1e8 +26 c36 +46 ba34 +c2 78a4 +ab 444f +9 a2e9 +20 a4aa +3a a5ec +f4 7d3a +a9 4661 +2e a6de +8f 68df +4b 98cd +e a8dc +13 a185 +e0 dea8 +f a8dd +ea dc46 +e1 dea9 +47 1237 +89 e249 +a a8ec +b a8ed +5c 9372 +6 80b6 +74 1f98 +53 3305 +40 988a +9e 69de +5a 99cc +8f 6adf +4b 9acd +64 9c10 +4a 9ace +f aadd +ea de46 +89 4a61 +e aade +ad 4c53 +b aaed +9a cbec +80 caaa +b3 4fa7 +da dbec +3d 773 +c0 daaa +bd 6df9 +19 21e3 +f3 5fa7 +28 4e8 +38 5e8 +66 1e34 +48 10e8 +ee 76de +58 11e8 +68 14e8 +78 15e8 +88 40e8 +a8 44e8 +b8 45e8 +c 2070 +e6 5e34 +29 4e9 +39 5e9 +67 1e35 +42 302c +28 2eea +59 11e9 +62 3c2c +48 3aea +69 14e9 +79 15e9 +89 40e9 +a9 44e9 +b9 45e9 +d 2071 +e7 5e35 +c2 702c +a8 6eea +8 2e8 +28 6e8 +be e57e +48 12e8 +68 16e8 +a8 46e8 +f3 d70d +b8 6dc2 +74 9db0 +c8 52e8 +e8 56e8 +16 211c +da d9e6 +9 2e9 +3a 87e6 +fa dde6 +29 6e9 +bf e57f +49 12e9 +7a 97e6 +69 16e9 +a9 46e9 +b9 6dc3 +75 9db1 +29 a463 +18 9e8 +7a 9fcc +60 9e8a +be 6fde +69 b463 +58 19e8 +99 e363 +88 48e8 +a9 e463 +98 49e8 +b9 c743 +3a 8de6 +2a a464 +10 a322 +19 9e9 +40 b222 +5a b364 +49 18e9 +f9 d743 +7a 9de6 +50 b322 +6a b464 +59 19e9 +90 e322 +aa e464 +99 49e9 +8 ae8 +a5 469b +bf 47dd +be ed7e +48 1ae8 +e5 569b +ff 57dd +88 4ae8 +c8 5ae8 +9 ae9 +3a 8fe6 +bf ed7f +49 1ae9 +7a 9fe6 +f2 5fac +18 21e8 +cc da58 +46 3a34 +28 24e8 +38 25e8 +f3 5fad +19 21e9 +cd da59 +47 3a35 +39 25e9 +2e c7c +14 b3a +c6 dabe +e0 dc00 +d4 71b2 +c7 dabf +2a 646 +e1 dc01 +d5 71b3 +c8 dac2 +e2 dc04 +d6 71b6 +c9 dac3 +e3 dc05 +d7 71b7 +ca dace +e4 dc10 +d8 71c2 +8b cacf +a5 cc11 +55 91b1 +99 61c3 +cb dacf +2e 656 +e5 dc11 +cc dad2 +e6 dc14 +da 71c6 +cd dad3 +e7 dc15 +8f caff +a9 cc41 +59 91e1 +9d 61f3 +cf daff +e9 dc41 +dd 71f3 +90 cb02 +aa cc44 +40 90a2 +5a 91e4 +9e 61f6 +d0 db02 +ea dc44 +de 71f6 +d1 db03 +eb dc45 +df 71f7 +93 cb0f +ad cc51 +d3 db0f +ed dc51 +94 cb12 +ae cc54 +d4 db12 +ee dc54 +d5 db13 +ef dc55 +96 cbbe +b0 cd00 +df 59d7 +c 82da +26 841c +e4 7638 +d6 dbbe +f0 dd00 +4c 92da +66 941c +97 cbbf +b1 cd01 +e5 7639 +d7 dbbf +3a 746 +f1 dd01 +98 cbc2 +b2 cd04 +d8 dbc2 +f2 dd04 +99 cbc3 +b3 cd05 +68 3ce0 +e7 763d +d9 dbc3 +f3 dd05 +da dbce +f4 dd10 +db dbcf +3e 756 +f5 dd11 +9c cbd2 +b6 cd14 +dc dbd2 +f6 dd14 +9d cbd3 +b7 cd15 +dd dbd3 +f7 dd15 +8 22e8 +28 26e8 +88 62e8 +d3 f30d +3d 855b +54 b9b0 +a8 66e8 +a3 cc0f +bd cd51 +da f9e6 +9 22e9 +a8 46c8 +3a a7e6 +fa fde6 +29 26e9 +a5 cc13 +bf cd55 +9f 4bfd +85 4abb +18 29e8 +8f e2df +10 a982 +5a 136e +86 eabe +a0 ec00 +87 eabf +a1 ec01 +88 eac2 +a2 ec04 +8a eace +a4 ec10 +8b eacf +a5 ec11 +8c ead2 +a6 ec14 +8f eaff +a9 ec41 +93 eb0f +ad ec51 +96 ebbe +b0 ed00 +97 ebbf +b1 ed01 +98 ebc2 +b2 ed04 +99 ebc3 +b3 ed05 +9a ebce +b4 ed10 +9b ebcf +b5 ed11 +9c ebd2 +b6 ed14 +9d ebd3 +68 1c62 +b7 ed15 +8 2ae8 +61 9689 +a5 669b +bf 67dd +88 6ae8 +a3 ec0f +bd ed51 +9 2ae9 +28 84e8 +d0 d30a +ea d44c +4a 32e6 +38 85e8 +aa 6e46 +66 9e34 +9 80e9 +29 84e9 +39 85e9 +ab 6e47 +67 9e35 +42 b02c +28 aeea +db 59e5 +c1 58a3 +8 82e8 +a6 643c +8c 62fa +48 92e8 +61 16a3 +7b 17e5 +c2 58a4 +9 82e9 +1 2221 +8 88e8 +21 ca3 +a0 4600 +3b de5 +11 2321 +18 89e8 +9 88e9 +19 89e9 +8 8ae8 +21 ea3 +3b fe5 +8c 6afa +a6 6c3c +48 9ae8 +61 1ea3 +7b 1fe5 +9 8ae9 +c1 78a3 +db 79e5 +aa 444e +8 a2e8 +21 26a3 +3b 27e5 +21 2ea3 +c1 d009 +3b 2fe5 +ba cd44 +a0 cc02 +a2 cc06 +be cd54 +a4 cc12 +fe dd54 +e4 dc12 +a6 cc16 +e6 dc16 +aa cc46 +a1 cea9 +ae cc56 +ee dc56 +b0 cd02 +fe 777c +e4 763a +bc cd52 +24 8c38 +17 8b97 +ba ed44 +a0 ec02 +a2 ec06 +be ed54 +a4 ec12 +a6 ec16 +a8 ec42 +10 ab28 +3 aa87 +ae ec56 +9f 41fd +85 40bb +b0 ed02 +26 a41e +b4 ed12 +db 5b45 +c1 5a03 +5a 3be4 +40 3aa2 +e0 dc08 +d4 71ba +41 3aa3 +5b 3be5 +2a 64e +e1 dc09 +d5 71bb +5e 3bf4 +44 3ab2 +e4 dc18 +d8 71ca +c7 7a17 +55 91b9 +99 61cb +45 3ab3 +5f 3bf5 +2e 65e +e5 dc19 +9 2ae3 +23 2c25 +a9 cc49 +59 91e9 +9d 61fb +49 3ae3 +63 3c25 +e9 dc49 +dd 71fb +cf 7a57 +4d 3af3 +67 3c35 +ed dc59 +d2 7b06 +6a 3ce4 +50 3ba2 +e9 7641 +f0 dd08 +51 3ba3 +6b 3ce5 +3a 74e +f1 dd09 +d6 7b16 +6e 3cf4 +54 3bb2 +ed 7651 +f4 dd18 +55 3bb3 +6f 3cf5 +3e 75e +f5 dd19 +da 7b46 +58 3be2 +72 3d24 +f8 dd48 +19 2be3 +33 2d25 +b9 cd49 +59 3be3 +73 3d25 +f9 dd49 +ee fc74 +d4 fb32 +17 8b9d +de 7b56 +76 3d34 +5c 3bf2 +fc dd58 +2 202c +57 9b9d +1d 2bf3 +37 2d35 +bd cd59 +5d 3bf3 +77 3d35 +fd dd59 +3 202d +5f 1bf7 +a1 ec09 +63 1c07 +a5 ec19 +67 1c37 +a9 ec49 +6b 1c47 +ad ec59 +6e 1cf6 +ed 5653 +b0 ed08 +6f 1cf7 +ee 5654 +b1 ed09 +72 1d06 +b4 ed18 +73 1d07 +b5 ed19 +77 1d37 +b9 ed49 +7f 9755 +65 9613 +7b 1d47 +bd ed59 +fa dd4c +5a 3be6 +e0 dc0a +fe dd5c +5e 3bf6 +e4 dc1a +e9 7643 +6a 3ce6 +f0 dd0a +2 8a2c +6 8a3c +6d 9671 +32 2d26 +b8 cd4a +72 3d26 +f8 dd4a +a 8a6c +6d 9cd1 +53 9b8f +ee fc76 +17 8b9f +76 3d36 +fc dd5a +57 9b9f +1c 2170 +f6 5f34 +2 202e +e 8a7c +ba ed4c +a0 ec0a +be ed5c +a4 ec1a +79 9fc3 +d7 7117 +a8 ec4a +1d abd1 +3 aa8f +69 b641 +b4 ed1a +67 3c9d +e0 dc20 +a1 cc21 +2a 666 +e1 dc21 +88 cae2 +a2 cc24 +18 8340 +c8 dae2 +e2 dc24 +9c 6352 +58 9340 +f2 dd84 +56 3b9c +e9 dc61 +90 cb22 +17 2b9f +aa cc64 +d0 db22 +57 3b9f +ea dc64 +d1 db23 +eb dc65 +37 2d9d +b0 cd20 +77 3d9d +f0 dd20 +b1 cd21 +d 82fb +c6 58b6 +27 843d +3a 766 +f1 dd21 +26 2c9c +b9 cd61 +15 833b +ce 58f6 +2f 847d +67 3c9f +e0 dc22 +fa dd64 +a1 cc23 +bb cd65 +e1 dc23 +fb dd65 +a0 ec20 +a1 ec21 +88 eae2 +a2 ec24 +18 a340 +a9 ec61 +f2 fd84 +90 eb22 +aa ec64 +b0 ed20 +b1 ed21 +a1 ec23 +bb ed65 +27 2c9f +ba cd64 +a0 cc22 +ea dc66 +f a8fd +26 843e +ba ed64 +a0 ec22 +aa ec66 +b0 ed22 +26 a43e +95 4133 +ca 7ac6 +2b a64d +9d 4173 +9f 4177 +d3 db87 +e0 dc28 +a1 cc29 +c3 7a27 +2a 66e +e1 dc29 +82 ca86 +a9 cc69 +cb 7a67 +c2 da86 +e9 dc69 +b1 cd29 +ab ccc7 +b8 cd68 +13 8bad +eb dcc7 +f8 dd68 +92 cb86 +b9 cd69 +d2 db86 +f9 dd69 +a1 ec29 +82 ea86 +a9 ec69 +a3 ec87 +ed 5673 +b0 ed28 +ee 5674 +b1 ed29 +9f 4375 +85 4233 +92 eb86 +b9 ed69 +a9 6663 +b0 cd2a +79 b741 +b8 cd6a +2d 8cf1 +13 8baf +f8 dd6a +b1 6d03 +6d 9cf1 +53 9baf +ba ed6c +a0 ec2a +d3 7127 +b5 4533 +b0 ed2a +b8 ed6a +2d acf1 +13 abaf +b7 4537 +bd 4573 +bf 4577 +a0 cc80 +a2 cc84 +59 bb61 +38 76a +e2 dc84 +f 827d +a4 cc90 +d1 7b21 +3b d6f +e4 dc90 +96 eb9e +7b 1d6f +a5 cc91 +a6 cc94 +b3 cd85 +e7 76bd +f3 dd85 +a0 6628 +b4 cd90 +e8 76c8 +e0 7628 +f4 dd90 +a1 6629 +b5 cd91 +e9 76c9 +e2 762c +f6 dd94 +a3 662d +b7 cd95 +e3 762d +f7 dd95 +ec 5670 +a2 ec84 +bf 4775 +a5 4633 +71 1d23 +a6 ec94 +47 18bf +c6 521c +fd 5771 +e3 562f +b3 ed85 +fe 577c +e4 563a +b4 ed90 +e6 563e +b6 ed94 +a7 4637 +af 4677 +ba cdc4 +a0 cc82 +fa ddc4 +e0 dc82 +a8 6660 +a2 cc86 +e8 7660 +e2 dc86 +fe ddd4 +ea 766c +e4 dc92 +ac 6670 +a6 cc96 +ec 7670 +e6 dc96 +ba 676c +a0 662a +b4 cd92 +e8 76ca +ec 5672 +a2 ec86 +a6 ec96 +c6 521e +e0 dc88 +e4 dc98 +a5 cc99 +2f cd7 +3c d78 +d4 7b38 +c7 7a97 +a9 66c1 +b0 cd88 +89 6a63 +33 a70d +f9 7d69 +d2 7b86 +e9 76c1 +f0 dd88 +b 8847 +ad 66d1 +b4 cd98 +49 9a61 +8d 6a73 +37 a71d +fd 7d79 +d6 7b96 +f 8857 +3e 7de +f5 dd99 +ce 7a74 +63 1c87 +70 1d28 +a5 ec99 +2f 2cd7 +3c 2d78 +b4 ef38 +a7 ee97 +72 1f26 +ed 56d3 +b0 ed88 +ee 56d4 +b1 ed89 +72 1d86 +b4 ed98 +73 1d87 +b5 ed99 +a2 ce24 +a0 cca0 +e0 dca0 +14 b1a +2e c5c +70 970a +a1 cca1 +2a 6e6 +e1 dca1 +a2 cca4 +18 83c0 +e2 dca4 +9c 63d2 +b6 6514 +58 93c0 +e3 dca5 +b7 6515 +9d 63d3 +59 93c1 +b0 cda0 +26 84bc +b1 cda1 +27 84bd +17 39f +ee 7476 +a2 eca4 +c2 522c +18 a3c0 +11 18b +31 af89 +15 19b +35 af99 +3c 5d0 +22 48e +33 58f +f8 f548 +28 4ca +b2 4dae +48 b2c8 +68 14ca +a1 4683 +bb 47c5 +f2 5dae +78 15ca +88 40ca +c8 50ca +a2 6e8c +c da +4b 12e5 +96 49be +2c aed8 +b2 6f8c +1c 1da +3c afd8 +55 3393 +6f 34d5 +c2 728c +2c 4da +4c b2d8 +d2 738c +3c 5da +7c 15da +fc 7d58 +18 be0 +8c 40da +cc 50da +73 15af +9d 41db +aa 44ce +ae 44de +d0 db08 +4a 3ae4 +bb 45cf +bf 45df +8 8ea +c8 50ea +22 40c +8 2ca +ac 4cf0 +92 4bae +28 6ca +62 140c +48 12ca +26 8694 +ec 5cf0 +d2 5bae +68 16ca +18 21ea +f2 5fae +dd 51db +d9 51eb +ea 54ee +26 41c +c 2da +2c 6da +66 141c +4c 12da +f0 5d00 +d6 5bbe +6c 16da +1c 21fa +f6 5fbe +f7 df35 +1d a171 +3 a02f +fb 55ef +1b 3cd +d8 7362 +1 28b +7a 3de4 +60 3ca2 +f9 7741 +1f 3dd +dc 7372 +5 29b +e3 760f +7e 3df4 +64 3cb2 +fd 7751 +fa 7766 +3d 7d1 +23 68f +69 bceb +e8 f648 +8 8ca +18 9ca +48 18ca +81 4a83 +9b 4bc5 +58 19ca +91 4b83 +ab 4cc5 +5f 13dd +45 129b +c 8da +1c 9da +4c 18da +5c 19da +a7 441d +8d 42db +ab 46cf +14 8b32 +2e 8c74 +30 d82 +af 46df +e7 541d +cd 52db +e3 542d +c9 52eb +70 1d82 +ef 56df +eb 56ef +ba cde4 +a0 cca2 +e aa5c +fa dde4 +e0 dca2 +c4 7a10 +2e c5e +a2 cca6 +32 8504 +18 83c2 +e2 dca6 +b6 6516 +72 9504 +58 93c2 +13 98f +d8 f948 +17 99f +dc f958 +b0 cda2 +26 84be +46 189e +2c 24da +3c 25da +57 199f +e 87c +48 90c8 +8c 60da +d2 530c +53 19af +8a 48ce +43 1205 +8e 48de +9b 49cf +9f 49df +ce 58de +26 241c +c 22da +52 9bac +b0 6d00 +96 6bbe +2c 26da +72 9fac +d0 7100 +b6 6fbe +df 59df +ed 5cf3 +27 8697 +34 8738 +a2 eca6 +92 e984 +dc 5370 +c2 522e +32 a504 +18 a3c2 +c 28da +1c 29da +a5 4c11 +8b 4acf +8f 4adf +cf 5adf +7c 15d0 +62 148e +c3 d885 +19 36b +8 80ca +48 90ca +11 21ab +1d 8bf9 +a2 ee8c +c 80da +b0 c522 +37 259f +e2 fe8c +4c 90da +33 25af +d9 51cb +ea 54ce +fb 55cf +99 61eb +c7 7a37 +50 938a +6a 94cc +ae 64de +79 976b +1f 23dd +98 c360 +5 229b +ba c764 +a0 c622 +27 269f +a3 642d +89 62eb +75 1f3b +ab 66ef +5c 19d0 +42 188e +99 c963 +6 289e +90 c922 +17 299f +e6 de9e +c a0da +92 630c +13 29af +ca 58ce +db 59cf +e9 5ce3 +23 8687 +30 8728 +43 3205 +4a 98cc +8e 68de +59 9b6b +41 988b +5b 99cd +9f 69df +e5 5c11 +cb 5acf +1b 234d +1 220b +19 81cb +47 9a17 +b3 ef8d +1d 81db +c0 f280 +2a 84ce +e8 76ea +c4 f290 +2e 84de +ec 76fa +d1 f381 +3b 85cf +d5 f391 +3f 85df +48 90ea +f3 ff8d +5d 91db +59 91eb +6a 94ee +7f 95df +7b 95ef +23 840d +c2 5886 +9 82cb +c6 5896 +27 841d +d 82db +8d 4853 +67 941d +4d 92db +6f 96df +6b 96ef +e0 dca8 +a1 cca9 +ac 4652 +c3 7aa7 +a9 66e1 +b0 cda8 +62 3404 +48 32c2 +33 a72d +d2 7ba6 +b1 cda9 +ee 747e +7 2217 +e 88de +dd 51f9 +e6 7c3c +cc 7afa +1b 89cf +1f 89df +47 3217 +4e 98de +5f 99df +da d34c +c0 d20a +5b 99ef +a1 eca9 +4e 1054 +34 f12 +25 8c11 +b 8acf +f 8adf +4f 9adf +59 91cb +6a 94ce +7b 95cf +f3 dfaf +19 a1eb +2a a4ee +6b 96cf +cd 5853 +c6 7896 +27 a41d +d a2db +f5 7d3b +2f a6df +43 3207 +4a 98ce +5b 99cf +89 4861 +e a8de +dd 71f9 +80 4820 +1f a9df +c9 584b +65 9c11 +4b 9acf +f aadf +fa ddec +f3 7725 +e0 dcaa +8 ea +ca 504e +28 aee8 +28 4ea +ea 544e +48 b2e8 +38 5ea +a8 ee48 +66 1e36 +97 e19d +48 10ea +81 42a3 +9b 43e5 +b7 e59d +68 14ea +a1 46a3 +bb 47e5 +78 15ea +88 40ea +a8 44ea +b8 45ea +c 2072 +e6 5e36 +b 2acd +22 42c +8 2ea +28 6ea +97 e39d +62 142c +48 12ea +b7 e79d +68 16ea +a8 46ea +f3 d70f +74 9db2 +18 9ea +88 48ea +58 99c8 +9c 69da +51 3301 +bf 47df +ff 57df +a0 ce00 +a1 ce01 +af 64f5 +51 93a1 +95 63b3 +a2 ce04 +52 93a4 +96 63b6 +74 b5b8 +8 a040 +e2 de04 +9 a041 +e3 de05 +c9 5849 +5b b967 +a5 ce11 +a6 ce14 +56 93b4 +9a 63c6 +78 b5c8 +c a050 +e6 de14 +d a051 +e7 de15 +a8 ce40 +a9 ce41 +aa ce44 +7c b5f8 +ea de44 +eb de45 +ad ce51 +ed de51 +ef de55 +9b 4145 +81 4003 +83 4007 +a9 ee41 +89 4043 +ab ee45 +8b 4047 +8 80ea +28 84ea +38 85ea +66 9e36 +1b 2365 +1 2223 +8 88ea +2b 2465 +11 2323 +18 89ea +22 8c2c +8 8aea +be cf54 +a4 ce12 +a6 ce16 +78 b5ca +c a052 +e6 de16 +a8 ce42 +aa ce46 +7c b5fa +ac ce52 +ec de52 +ae ce56 +ee de56 +e0 de08 +16 819e +e4 de18 +5b b96f +e5 de19 +42 b82e +5c b970 +62 3e24 +e8 de48 +23 2e25 +a9 ce49 +63 3e25 +e9 de49 +67 3e35 +ed de59 +a1 ee09 +9b 414d +81 400b +89 404b +be cf5c +a4 ce1a +9a e9cc +80 e88a +d7 5317 +fe df5c +e4 de1a +22 2e26 +a8 ce4a +9e e9fc +84 e8ba +db 5347 +62 3e26 +e8 de4a +26 2e36 +ac ce5a +88 e8ca +df 5357 +66 3e36 +ec de5a +27 2e9d +a0 ce20 +a1 ce21 +8 a060 +e2 de24 +3e 27de +f5 fd99 +9 a061 +e3 de25 +6f 3edd +e8 de60 +1e 81f6 +a9 ce61 +e9 de61 +aa ce64 +ea de64 +eb de65 +6f 3edf +e8 de62 +af 4c5f +d aaf9 +aa ce66 +c2 5a8c +ea de66 +f aafd +95 413b +a1 ce29 +a9 ce69 +2 a004 +e9 de69 +9f 437d +85 423b +51 9903 +8d 427b +59 9943 +d6 539e +a8 ce6a +db 5367 +b5 453b +10 380 +a0 ce80 +6 a0bc +e0 de80 +a1 ce81 +38 f60 +7 a0bd +e1 de81 +a2 ce84 +8 a0c0 +e2 de84 +9 a0c1 +e3 de85 +af eed5 +60 1e22 +7a 1f64 +a a0cc +e4 de90 +7b 1f6f +a5 ce91 +22 e2e +3c f70 +b a0cd +e5 de91 +90 6320 +62 1e2e +7c 1f70 +a6 ce94 +c a0d0 +e6 de94 +98 c9ea +ab 6465 +91 6323 +d a0d1 +e7 de95 +99 c9eb +92 6324 +bf 477d +a5 463b +b 80cf +a1 ee81 +9b 41c5 +81 4083 +38 2f60 +d 80d3 +a3 ee85 +90 4128 +83 4087 +20 2e22 +3a 2f64 +9f 41d5 +85 4093 +22 2e2e +3c 2f70 +ad 467b +f6 579e +fa dfc4 +6 a0be +e0 de82 +c3 708f +dd 71d1 +8 a0c2 +e2 de86 +c5 7093 +df 71d5 +be cfd4 +a4 ce92 +fe dfd4 +a a0ce +e4 de92 +cd d079 +a6 ce96 +c a0d2 +e6 de96 +e0 de88 +e1 de89 +6b 1ec7 +ad eed9 +78 1f68 +d7 f33d +58 b9e0 +e4 de98 +a5 ce99 +9b e34d +81 e20b +2 a8ae +1c a9f0 +2f ed7 +3c f78 +e5 de99 +83 6287 +90 6328 +6f 1ed7 +7c 1f78 +db f34d +c1 f20b +42 b8ae +5c b9f0 +a1 ee89 +2b 2ec7 +38 2f68 +a0 cea0 +a1 cea1 +a2 cea4 +8 a0e0 +e2 dea4 +9 a0e1 +e3 dea5 +b 80ef +a1 eea1 +9b 41e5 +81 40a3 +b2 4fae +cc 50f0 +dc 51f2 +de 51f6 +74 b710 +d4 53b2 +ee 54f4 +ec 54f8 +ed 54f9 +1e 8bde +fe 55f6 +fd 55f9 +c4 fa90 +27 2617 +2e 8cde +fc 55fa +c2 50ae +dc 51f0 +c4 50b2 +de 51f4 +e4 54b2 +fe 55f4 +fc 55f8 +ed 56f9 +ec 56fa +e3 54af +fd 55f1 +d1 fb29 +2e 8cd6 +fc 55f2 +4f 3ad5 +dd f373 +6 829c +cc 58f8 +d4 f332 +ee f474 +17 839d +dd 59f9 +a6 eebe +c0 f000 +a7 eebf +c1 f001 +a8 eec2 +c2 f004 +aa eece +c4 f010 +ab eecf +c5 f011 +ac eed2 +c6 f014 +af eeff +c9 f041 +b3 ef0f +cd f051 +b6 efbe +d0 f100 +b7 efbf +d1 f101 +b8 efc2 +d2 f104 +b9 efc3 +d3 f105 +ba efce +d4 f110 +bb efcf +d5 f111 +bc efd2 +d6 f114 +bd efd3 +d7 f115 +bf efff +d9 f141 +c1 f003 +db f145 +bb 6fed +a1 6eab +c3 f00f +dd f151 +c5 f013 +df f155 +61 9ea9 +bf 6ffd +a5 6ebb +c6 f2be +e0 f400 +c7 f2bf +e1 f401 +c8 f2c2 +d1 5989 +e2 f404 +ba cfe4 +a0 cea2 +83 60af +9d 61f1 +ca f2ce +e4 f410 +cb f2cf +e5 f411 +8 a0e2 +e2 dea6 +c5 70b3 +df 71f5 +cc f2d2 +d5 5999 +e6 f414 +cf f2ff +50 b9a2 +e9 f441 +d0 f302 +d9 59c9 +ea f444 +d3 f30f +54 b9b2 +ed f451 +d6 f3be +f0 f500 +d8 f3c2 +f2 f504 +e2 54ae +fc 55f0 +da f3ce +f4 f510 +db f3cf +f5 f511 +dc f3d2 +f6 f514 +ed 56f1 +df f3ff +f9 f541 +e1 f403 +d0 5988 +fb f545 +1d 81d1 +da f166 +3 808f +db 73ed +c1 72ab +ed f473 +16 839c +dc 59f8 +e3 f40f +fd f551 +dc f172 +1f 81dd +5 809b +50 93a0 +94 63b2 +ae 64f4 +82 60ae +9c 61f0 +84 60b2 +40 90a0 +9e 61f4 +94 c9b8 +8d 62f1 +79 1f41 +a6 6434 +48 92e0 +8c 62f2 +78 1f42 +4a 92e4 +8e 62f6 +bc ef58 +7a 1f46 +6c b4f8 +60 94a0 +a4 64b2 +be 65f4 +6a 96e4 +ae 66f6 +49 38c1 +ad 66f9 +a2 64ae +bc 65f0 +b4 cdb8 +ad 66f1 +68 96e0 +ac 66f2 +47 38bd +32 8fae +90 6102 +4c 90f0 +92 6106 +34 8fb2 +4e 90f4 +43 90af +5d 91f1 +66 bc34 +4c baf2 +f2 ffa4 +5c 91f2 +45 90b3 +5f 91f5 +4e baf6 +5e 91f6 +54 93b2 +b2 6506 +6e 94f4 +b0 650a +6c 94f8 +8e 42f6 +65 94b3 +7f 95f5 +7e 95f6 +4d 10f9 +7d 95f9 +7c 95fa +42 90ae +5c 91f0 +44 90b2 +5e 91f4 +98 c9ca +91 6303 +ab 6445 +12 29a6 +4d 92f1 +aa 6446 +66 9434 +4c 92f2 +64 94b2 +7e 95f4 +7c 95f8 +9e 43f6 +52 93ae +b0 6502 +6c 94f0 +7c 95f2 +92 6906 +cd d251 +4e 98f4 +82 422c +45 98b3 +c4 d210 +5f 99f5 +dd d353 +5e 99f6 +5d 99f9 +da f144 +c0 f002 +de f154 +c4 f012 +c8 f042 +3e a75e +cc f052 +34 af38 +27 ae97 +d0 f102 +d4 f112 +fa f544 +e0 f402 +d1 598b +e2 f406 +b1 6f09 +fe f554 +e4 f412 +d5 599b +e6 f416 +b5 6f19 +e8 f442 +d9 59cb +ea f446 +e1 f6a9 +b9 6f49 +ec f452 +54 b338 +47 b297 +dd 59db +ee f456 +bd 6f59 +62 94ae +7c 95f0 +c3 d20f +dd d351 +44 98b2 +5e 99f4 +55 3331 +5c 99f8 +ab 6c4d +91 6b0b +4d 9af9 +e a0f4 +77 179f +57 b11f +b8 4f60 +2e 67c +f a0f5 +2f 67d +3e afde +b9 4f61 +3d 779 +f6 dfb6 +1c a1f2 +e6 dc94 +3c 77a +77 b51f +d8 5360 +af 4457 +d a2f1 +15 9b9 +26 a434 +c a2f2 +f a2f5 +2f a6f5 +37 dbd +2e a6f6 +12 a3ae +2c a4f0 +8d e251 +e a8f4 +77 1f9f +f a8f5 +f aaf5 +e aaf6 +5 80b3 +1f 81f5 +d1 f109 +df 5b57 +d8 f148 +13 18f +e1 f409 +ef 5e57 +e8 f448 +3d 5d1 +fa 7566 +23 48f +f0 f508 +fe 5f56 +c f0 +2 ae +1c 1f0 +12 3ae +2c 4f0 +22 4ae +3c 5f0 +42 10ae +5c 11f0 +62 14ae +7c 15f0 +72 3fae +8c 40f0 +82 40ae +9c 41f0 +92 43ae +ac 44f0 +a2 44ae +bc 45f0 +d f1 +3 af +1d 1f1 +13 3af +2d 4f1 +33 faf +4d 10f1 +66 3c34 +ec dc58 +4c 3af2 +53 13af +6d 14f1 +73 3faf +8d 40f1 +83 40af +9d 41f1 +93 43af +ad 44f1 +a3 44af +bd 45f1 +e f4 +4 b2 +1e 1f4 +14 3b2 +2e 4f4 +24 4b2 +3e 5f4 +44 10b2 +5e 11f4 +54 13b2 +6e 14f4 +64 14b2 +7e 15f4 +74 3fb2 +8e 40f4 +84 40b2 +9e 41f4 +94 43b2 +ae 44f4 +a4 44b2 +be 45f4 +f f5 +ae cc5c +94 cb1a +e 2af6 +15 3b3 +2f 4f5 +25 4b3 +3f 5f5 +c 80f8 +ce d05c +b4 cf1a +2e 2ef6 +25 2631 +2c 8cf8 +ee dc5c +d4 db1a +4e 3af6 +90 610a +4c 90f8 +75 3fb3 +8f 40f5 +d2 7bac +3c dfa +95 43b3 +af 44f5 +f2 7fac +5c 11fa +2c 6f0 +4c 12f0 +6c 16f0 +8c 42f0 +ac 46f0 +ec 56f0 +78 3dca +2d 6f1 +98 49ca +4d 12f1 +f8 7dca +ad 46f1 +2e 6f4 +11 a9a9 +4e 12f4 +31 ada9 +6e 16f4 +8e 42f4 +ae 46f4 +91 e9a9 +ce 52f4 +b1 eda9 +ee 56f4 +7a 3dce +2f 6f5 +da 79ce +8f 42f5 +fa 7dce +af 46f5 +76 153c +5c 13fa +8b 424d +c 8f0 +9b 434d +81 420b +2 8ae +1c 9f0 +db 534d +c1 520b +42 18ae +5c 19f0 +8c 48f0 +2 c +82 48ae +9c 49f0 +12 10c +6 8294 +cc 58f0 +38 fca +52 110c +d 8f1 +f8 d74a +72 3726 +d1 5ba9 +e2 f624 +5c 135a +8d 48f1 +3 d +35 791 +f2 7726 +13 10d +1c 2b50 +2 2a0e +7 8295 +cd 58f1 +29 ecb +43 100d +39 fcb +53 110d +8d 4251 +e 8f4 +cd 5251 +4e 18f4 +44 18b2 +c3 520f +dd 5351 +5e 19f4 +f 8f5 +5 8b3 +84 4210 +1f 9f5 +45 18b3 +c4 5210 +5f 19f5 +c af0 +fa 5dce +4c 1af0 +8c 4af0 +2 20c +d af1 +fb 5dcf +4d 1af1 +8d 4af1 +3 20d +e af4 +fc 5dd2 +4e 1af4 +f af5 +22 24ae +3c 25f0 +94 c118 +e 20f4 +b4 c518 +14 23b2 +2e 24f4 +24 24b2 +3e 25f4 +da f14c +c0 f00a +95 c119 +f 20f5 +c8 f04a +d0 f10a +d8 f14a +c 22f0 +2c 26f0 +8c 62f0 +78 1f40 +ac 66f0 +14 89b8 +d 22f1 +fc 75d2 +fa f54c +e0 f40a +b4 c718 +35 8dbb +2e 26f4 +95 c9bb +8e 62f4 +60 1e02 +7a 1f44 +b5 cdbb +ae 66f4 +fd 75d3 +36 8dbc +b5 c719 +2f 26f5 +e8 f44a +f0 f50a +2 a22c +f8 f54a +c3 7827 +a a26c +8c 68f0 +2 200c +82 68ae +9c 69f0 +ec 5ed0 +12 210c +d 28f1 +5e 1376 +5c 335a +8d 68f1 +3 200d +de 5376 +94 c918 +8d 6251 +e 28f4 +a7 4e9d +95 c919 +f 28f5 +5 28b3 +84 6210 +1f 29f5 +c0 f800 +a 2246 +c1 f801 +c2 f804 +c3 f805 +c4 f810 +e 2256 +c5 f811 +c6 f814 +c7 f815 +c9 f841 +25 ae1b +3f af5d +cb f845 +cc f850 +14 938 +7 897 +cd f851 +ce f854 +b6 e734 +a5 4cb9 +cf f855 +d0 f900 +1a 2346 +d1 f901 +d2 f904 +d3 f905 +d4 f910 +1e 2356 +d5 f911 +d6 f914 +d7 f915 +c 2af0 +8c 6af0 +2 220c +d9 f941 +d 2af1 +8d 6af1 +3 220d +c1 f803 +db f945 +94 cb18 +e 2af4 +c3 f80f +dd f951 +ac e6f2 +b5 4db9 +95 cb19 +f 2af5 +c5 f813 +df f955 +c 80f0 +12 83ae +2c 84f0 +22 84ae +3c 85f0 +d 80f1 +13 83af +2d 84f1 +e 80f4 +4 80b2 +1e 81f4 +14 83b2 +2e 84f4 +24 84b2 +3e 85f4 +f 80f5 +15 83b3 +2f 84f5 +25 84b3 +3f 85f5 +2e aef6 +c 82f0 +aa 6444 +90 6302 +4c 92f0 +e 82f4 +2e 86f4 +92 6306 +4e 92f4 +b2 6706 +6e 96f4 +5a b9ce +f 82f5 +7a bdce +2f 86f5 +9b c34d +81 c20b +2 88ae +1c 89f0 +db d34d +c1 d20b +42 98ae +5c 99f0 +83 4287 +90 4328 +8d c251 +e 88f4 +83 c20f +9d c351 +4 88b2 +1e 89f4 +f 88f5 +5 88b3 +84 c210 +1f 89f5 +d 8af1 +1b 21e5 +1 20a3 +91 6b03 +ab 6c45 +4d 9af1 +f 8af5 +89 c0cb +3 20a7 +ae 4456 +c a2f0 +e a2f4 +2e a6f4 +8b e24d +c a8f0 +2c e78 +75 1f9b +d a8f1 +9 28e9 +5e 9376 +2d e79 +ae 4c56 +c aaf0 +af 4c57 +d aaf1 +e aaf4 +c0 f020 +c1 f021 +a8 eee2 +c2 f024 +38 a740 +c9 f061 +de 7bf6 +25 a63b +3f a77d +b0 ef22 +ca f064 +d1 f121 +b8 efe2 +d2 f124 +b9 efe3 +d3 f125 +d9 f161 +c1 f023 +db f165 +e1 f421 +c8 f2e2 +d1 59a9 +e2 f424 +e9 f461 +d0 f322 +13 838d +d9 59e9 +ea f464 +f1 f521 +d8 f3e2 +f2 f524 +d9 f3e3 +f3 f525 +f9 f561 +1b 81ed +1 80ab +1c 81f0 +2 80ae +e1 f423 +d0 59a8 +fb f565 +1d 81f1 +3 80af +da f944 +c0 f802 +c2 f806 +de f954 +c4 f812 +c6 f816 +c8 f842 +3e af5e +ca f846 +24 2630 +c1 faa9 +cc f852 +ce f856 +d0 f902 +1 2209 +d2 f906 +d4 f912 +5 2219 +d6 f916 +d8 f942 +9 2249 +da f946 +b1 4dab +dc f952 +d 2259 +de f956 +3 808d +da f164 +c0 f022 +13 818d +d0 f122 +3 2807 +23 848d +fa f564 +e0 f422 +25 8491 +d1 59ab +e2 f426 +e d6 +b1 6f29 +43 b2a7 +13 838f +2d 84d1 +d9 59eb +ea f466 +16 116 +b9 6f69 +33 858d +f0 f522 +e 225e +c5 f819 +cd f859 +d0 f908 +d4 f918 +dd f959 +c3 f087 +d0 f128 +3 280d +de 5b76 +d1 f129 +df 5b77 +cb f0c7 +d8 f168 +b 284d +13 1af +d5 5113 +33 afad +d3 f387 +e0 f428 +ee 5e76 +3e 85d6 +e1 f429 +ef 5e77 +db f3c7 +e8 f468 +3d 5f1 +23 4af +e3 f487 +f0 f528 +fe 5f76 +f1 f529 +ff 5f77 +eb f4c7 +f8 f568 +33 5af +da f94c +c0 f80a +de f95c +c4 f81a +c8 f84a +2 8824 +cc f85a +85 c231 +d0 f90a +a 88e4 +89 c241 +d4 f91a +8d c271 +d8 f94a +12 8924 +dc f95a +da f16c +c0 f02a +f3 7527 +c8 f06a +24 490 +fb 7567 +d0 f12a +1d 2951 +3 280f +d8 f16a +b 284f +4d b0f1 +33 afaf +fa f56c +e0 f42a +e8 f46a +f0 f52a +f8 f56a +c0 f820 +a 2266 +c1 f821 +c8 f860 +82 4204 +3 8a7 +c9 f861 +25 ae3b +3f af7d +ca f864 +cb f865 +d0 f920 +1a 2366 +d1 f921 +1 8889 +d8 f960 +d9 f961 +c1 f823 +db f965 +c2 f084 +63 1caf +7d 1df1 +e2 560c +59 3163 +c6 f094 +67 1cbf +e6 561c +5d 3173 +d0 f180 +71 1dab +f0 5708 +d3 f185 +d4 f190 +75 1dbb +f4 5718 +d6 f194 +e0 f480 +e2 f484 +79 3563 +e4 f490 +7b 356f +e5 f491 +e6 f494 +7d 3573 +f0 f580 +f1 f581 +f3 f585 +f4 f590 +f5 f591 +f6 f594 +3 888d +da f964 +c0 f822 +5 8891 +c2 f826 +38 af42 +b 88cd +c8 f862 +3e af7e +d 88d1 +ca f866 +13 898d +d0 f922 +fa f5c4 +e0 f482 +e2 f486 +b1 6f89 +fe f5d4 +e4 f492 +e6 f496 +b5 6f99 +cb f8c7 +d8 f968 +92 430c +13 9af +d0 f188 +d1 f189 +e0 f488 +14 2112 +ee 5ed6 +f0 f588 +b a047 +fe 5fd6 +f1 f589 +ff 5fd7 +c8 f86a +d0 f92a +d8 f96a +fa f5cc +e0 f48a +f0 f58a +a4 4412 +be 4554 +2 a2ac +18 236a +c2 f884 +8 2048 +e2 5e0c +c 2058 +e6 5e1c +1d 237b +c7 f895 +d 2059 +e7 5e1d +33 2507 +d0 f980 +82 c026 +f0 5f08 +1a 23c6 +d1 f981 +83 c027 +f1 5f09 +19 2149 +f3 5f0d +37 2517 +d4 f990 +86 c036 +f4 5f18 +87 c037 +f5 5f19 +2c 247a +d6 f994 +2d 247b +d7 f995 +1d 2159 +f7 5f1d +d2 f1a4 +f2 f5a4 +c2 f886 +c6 f896 +c 205a +e6 5e1e +d0 f982 +f0 5f0a +1 2289 +d2 f986 +18 214a +f2 5f0e +d4 f992 +f4 5f1a +5 2299 +d6 f996 +1c 215a +f6 5f1e +e 22de +c5 f899 +4f 38d7 +5c 3978 +33 250f +d0 f988 +37 251f +d4 f998 +d1 f1a9 +6f 1455 +18 a9c8 +55 1313 +f1 f5a9 +38 adc8 +75 1713 +ff 5ff7 +de f9dc +c4 f89a +89 c2c1 +d4 f99a +d0 f1aa +1d 29d1 +3 288f +f0 f5aa +92 c30e +ac c450 +c3 f8a5 +9 2069 +e3 5e2d +39 afc1 +99 41e3 +2f a6fd +bb 45e7 +b9 45eb +d 2073 +e7 5e37 +cc 50f2 +dd 51f3 +df 51f7 +75 b711 +ee 54f6 +13 218d +ec 54fa +ff 55f7 +fd 55fb +23 6a7 +e8 f660 +a3 4425 +89 42e3 +ab 46e7 +a9 46eb +90 e9a8 +e7 5435 +cd 52f3 +30 a528 +23 a487 +ed 56fb +22 a426 +90 4308 +11 9ab +65 343b +7f 357d +99 e9c9 +d6 5314 +57 19b7 +b9 45e3 +aa e466 +99 49eb +fd 55f3 +17 839f +ee f476 +dd 59fb +0 2a +1a 16c +3b 7e5 +21 6a3 +bb 4545 +a1 4403 +1 a2a1 +a3 4407 +b9 6de9 +a9 4443 +cb f245 +9 a2e1 +ab 4447 +a9 46e3 +a3 4c2d +89 4aeb +61 bca3 +7b bde5 +e0 f600 +52 b306 +41 188b +5b 19cd +62 bca4 +e1 f601 +e3 f605 +65 bcb3 +7f bdf5 +e4 f610 +66 bcb4 +e5 f611 +67 bcb7 +d5 5b99 +e6 f614 +b0 eda8 +ed 56f3 +6a bce4 +50 bba2 +e9 f641 +6e bcf4 +54 bbb2 +ed f651 +e7 5c3d +cd 5afb +50 93a2 +6a 94e4 +ae 64f6 +ad c6db +27 26b7 +94 c9ba +a7 6435 +49 92e1 +8d 62f3 +69 96e9 +ad 66fb +68 94e0 +ac 64f2 +59 99e9 +9d 69fb +12 1ac +a7 cc1d +8d cadb +7 2ab7 +68 9c60 +ac 6c72 +c2 f8a6 +fc 5f70 +8 206a +e2 5e2e +52 b104 +38 afc2 +b4 cdba +69 96e1 +ad 66f3 +49 9ae9 +a7 6c3d +8d 6afb +19 81e3 +8e 62dc +1b 81e7 +3b 85e7 +39 85eb +e2 fea4 +4c 90f2 +4e 90f6 +2e a674 +1d bf9 +f3 ffa5 +5d 91f3 +5f 91f7 +6e 94f6 +3d ff9 +6c 94fa +7f 95f7 +7d 95fb +ed f45b +23 8425 +9 82e3 +6d 96fb +39 85e3 +ae 66dc +9a c344 +80 c202 +1b 89e7 +98 c348 +12 2324 +19 89eb +6c 94f2 +7d 95f3 +cd d253 +4e 98f6 +de d354 +c4 d212 +5f 99f7 +dc d358 +56 3334 +5d 99fb +da f344 +c9 58c9 +5b b9e7 +c0 f202 +cb 58cd +c2 f206 +d1 5909 +c8 f242 +d5 5919 +cc f252 +29 86e3 +8b 4867 +23 8c2d +9 8aeb +6d 96f3 +f1 5d09 +e8 f642 +f5 5d19 +ec f652 +ab 6c4f +67 9c3d +4d 9afb +7c 3fd8 +e a0f6 +e5 dc39 +2e 67e +b8 4f62 +3d a5fb +b8 e542 +42 12ac +dd 7373 +6 29c +90 4b80 +27 a435 +d a2f3 +f a2f7 +cc 70d8 +2f a6f7 +ec 74d8 +e6 7cb6 +2d a6fb +2c a4f2 +8d e253 +e a8f6 +9e e354 +8d 48d9 +84 e212 +1f a9f7 +9c e358 +5a 1346 +1d a9fb +a 22ee +24 2430 +c1 f8a9 +5f 1b55 +45 1a13 +2d a6f3 +f aaf7 +cc 78d8 +27 ac3d +d aafb +1a 23ee +34 2530 +d1 f9a9 +6f 1c55 +55 1b13 +bb 454d +a1 440b +61 bcab +7b bded +e0 f608 +62 bcac +e1 f609 +32 5ac +da f9ec +3d 2573 +c0 f8aa +75 9f11 +d0 f9aa +da f34c +5b b9ef +c0 f20a +c8 f24a +fa f74c +7b bdef +e0 f60a +e8 f64a +fd 7773 +26 69c +b0 4f80 +a2 6ea4 +c f2 +ce 5056 +2c aef0 +b2 6fa4 +1c 1f2 +22 aeae +de 5156 +3c aff0 +c2 72a4 +2c 4f2 +ee 5456 +4c b2f0 +e2 7ea4 +4c 10f2 +f2 7fa4 +5c 11f2 +6c 14f2 +7c 15f2 +8c 40f2 +db 7bc7 +e8 7c68 +22 a60c +9c 41f2 +ac 44f2 +e f6 +2e aef4 +2e 4f6 +4e b2f4 +3e 5f6 +90 e108 +4e 10f6 +5e 11f6 +b0 e508 +6e 14f6 +7e 15f6 +9e 41f6 +fa 7d6c +e0 7c2a +34 a710 +ae 44f6 +be 45f6 +c3 d8ad +26 434 +c 2f2 +66 1434 +4c 12f2 +6c 16f2 +a6 4434 +8c 42f2 +ac 46f2 +47 18bd +c5 d8b1 +e 2f6 +e5 dcb1 +2e 6f6 +9b 434f +1c 9f2 +db 534f +5c 19f2 +8c 48f2 +1c 150 +2 e +9c 49f2 +2d 8479 +6 8296 +cc 58f2 +5c 1150 +42 100e +3d 8579 +16 8396 +dc 59f2 +e8 7ec0 +52 110e +8d 4253 +e 8f6 +90 e908 +cd 5253 +4e 18f6 +22 6ac +e6 5eb6 +c 20f2 +18 8b40 +94 c11a +e 20f6 +0 8a02 +1a 8b44 +b4 c51a +2e 24f6 +20 8e02 +3a 8f44 +23 6ad +c3 f8ad +ac c458 +26 2434 +c 22f2 +c5 f8b1 +94 c31a +ae c45c +e 22f6 +e5 fcb1 +b4 c71a +2e 26f6 +9b 634f +57 933d +1c 29f2 +c7 d23d +48 98e0 +8c 68f2 +e 8a5c +d7 d33d +58 99e0 +9c 69f2 +8d 6253 +94 c91a +49 9241 +e 28f6 +a7 4e9f +2b 8c4f +c1 fa01 +2d 8c53 +c3 fa05 +2f 8c5f +c5 fa11 +c7 fa15 +c9 fa41 +cb fa45 +cd fa51 +cf fa55 +a2 eea4 +c 80f2 +b2 efa4 +1c 81f2 +c2 f2a4 +2c 84f2 +d2 f3a4 +3c 85f2 +2e 84f6 +3e 85f6 +d f9 +26 8434 +c 82f2 +e 82f6 +2e 86f6 +8d c253 +e 88f6 +9d c353 +1e 89f6 +8f e0dd +40 102a +5a 116c +e0 f620 +e1 f621 +e3 f625 +e9 f661 +c0 5aa8 +eb f665 +8b e24f +c a8f2 +2c e7a +4e 125c +d8 5b40 +14 ab92 +2e acd4 +0 a880 +4a 126c +10 aba2 +a9 e641 +2a ace4 +da f364 +3 828d +c9 58e9 +c0 f222 +cb 58ed +5 8291 +c2 f226 +d 82d1 +d3 592d +ca f266 +1 a881 +4b 126d +11 aba3 +2b ace5 +f1 5d29 +2b 86cd +e8 f662 +c1 fa09 +c5 fa19 +cd fa59 +e0 f628 +3e 87d6 +e1 f629 +e8 f668 +85 4ab1 +6e 165c +4 203a +1e 217c +f8 5f40 +20 ac80 +6a 166c +30 afa2 +4a b0e4 +da f36c +c0 f22a +c4 5890 +c8 f26a +fa f76c +e0 f62a +e4 5c90 +e8 f66a +66 169c +2b 8c6f +c1 fa21 +2d 8c73 +c3 fa25 +19 343 +cb fa65 +2b 84cf +c1 f281 +bb 45c5 +a1 4483 +52 9986 +58 3360 +2d 84d3 +c3 f285 +b0 4528 +a3 4487 +40 3222 +5a 3364 +2f 84df +c5 f291 +bf 45d5 +a5 4493 +e0 f680 +e1 f681 +72 9d86 +78 3760 +e2 f684 +e3 f685 +60 3622 +7a 3764 +e4 f690 +e5 f691 +e6 f694 +da f3c4 +c0 f282 +e9 f469 +c2 f286 +de f3d4 +c4 f292 +fe f7d4 +e4 f692 +16 93c +87 429d +5e 1bfc +44 1aba +6f b677 +86 429e +9 8843 +83 42ad +1e 8bd6 +c1 fa29 +c1 f289 +bb 45cd +a1 448b +e0 f688 +e1 f689 +fa f7cc +e0 f68a +2f 8cdf +c5 fa91 +c7 fa95 +e0 f6a0 +2e 265c +b8 6f40 +e2 f6a4 +a0 6e02 +ba 6f44 +50 1900 +cf 525d +11 8301 +5c b9da +ce 525e +c7 529d +c6 529e +49 9843 +c3 52ad +dc 53f0 +c2 52ae +24 2610 +c1 fa89 +c5 fa99 +e0 f6a8 +67 9e95 +b8 6f48 +fa f7ec +e0 f6aa +22 4ac +77 15bd +76 15be +90 43aa +aa 44ec +91 43ab +ab 44ed +cc 50f8 +cd 50f9 +dc 51fa +d4 53ba +ee 54fc +d5 53bb +ef 54fd +fe 55fe +10 320 +76 15bc +a0 44aa +ba 45ec +ab 46ed +bb 6dc7 +77 9db5 +dc 51f8 +cd 52f9 +23 a48d +e6 543c +cc 52fa +3c a5d0 +22 a48e +e4 54ba +fe 55fc +70 1da0 +ef 56fd +ee 56fe +13 a327 +81 4209 +2 8ac +3 8ad +68 36e2 +a a2e6 +13 9ad +57 b337 +c5 5219 +46 18bc +bc 47f2 +5 293 +1f 3d5 +67 b437 +d5 5319 +56 19bc +bd ed53 +47 1abd +26 861c +df 5bd7 +ec 5c78 +60 1c00 +46 1abe +2d 8cf3 +ac c650 +c3 faa5 +be 457e +37 25bd +2c c50 +12 b0e +36 25be +50 93a8 +94 63ba +ae 64fc +a5 64bb +61 94a9 +bf 65fd +9a 4b4e +36 25bc +27 26bd +1b 1ef +b1 6fa1 +26 26be +8d 62f9 +b 8067 +79 1f49 +59 b9c1 +60 94a8 +a4 64ba +be 65fc +30 2da0 +af 66fd +6a 96ec +ae 66fe +49 38c9 +85 6219 +6 28bc +95 6319 +16 29bc +20 2c00 +6 2abe +17 1bd +10 83aa +2a 84ec +11 83ab +2b 84ed +21 84ab +3b 85ed +d0 f3a0 +3a 85ee +5d 91f9 +66 bc3c +4c bafa +f2 ffac +5c 91fa +65 94bb +7f 95fd +7e 95fe +20 84aa +3a 85ec +5c 91f8 +aa 644e +66 943c +4c 92fa +64 94ba +7e 95fc +b3 670f +34 2db2 +6f 96fd +6e 96fe +3 2225 +89 c249 +a 88ec +b 88ed +45 98bb +c4 d218 +5f 99fd +99 c349 +13 2325 +0 88aa +1a 89ec +b 8aed +24 8c30 +a 8aee +dd d359 +57 3335 +44 98ba +5e 99fc +93 6b0f +ad 6c51 +4f 9afd +68 9c40 +ac 6c52 +4e 9afe +73 b5ad +14 a3ba +2e a4fc +cb 52c7 +d8 5368 +2e a6fe +4b 1247 +8d e259 +e a8fc +28 ac40 +e aafe +c f8 +2c 4f8 +4c 10f8 +5c 11f8 +6c 14f8 +7c 15f8 +8c 40f8 +ac 44f8 +2d 4f9 +5d 11f9 +66 3c3c +4c 3afa +6d 14f9 +7d 15f9 +8d 40f9 +ad 44f9 +c 2f8 +2c 6f8 +4c 12f8 +6c 16f8 +ac 46f8 +cc 52f8 +22 a48c +ec 56f8 +de d9f6 +d 2f9 +3e 87f6 +fe ddf6 +2d 6f9 +4d 12f9 +7e 97f6 +6d 16f9 +ad 46f9 +bd 6dd3 +79 9dc1 +1d a373 +c 8f8 +2d a473 +1c 9f8 +5d b373 +4c 18f8 +6d b473 +5c 19f8 +9d e373 +8c 48f8 +ad e473 +9c 49f8 +bd c753 +3e 8df6 +54 b332 +6e b474 +5d 19f9 +94 e332 +ae e474 +9d 49f9 +c af8 +4c 1af8 +8c 4af8 +cc 5af8 +d af9 +3e 8ff6 +4d 1af9 +7e 9ff6 +c 22f8 +2c 26f8 +8c 62f8 +a 8066 +78 1f48 +d7 f31d +58 b9c0 +ac 66f8 +fe fdf6 +2d 26f9 +c8 fac2 +e2 fc04 +ca face +e4 fc10 +cb facf +2e 2656 +e5 fc11 +cc fad2 +e6 fc14 +d3 fb0f +ed fc51 +d6 fbbe +f0 fd00 +d7 fbbf +3a 2746 +f1 fd01 +d8 fbc2 +f2 fd04 +d9 fbc3 +f3 fd05 +da fbce +f4 fd10 +db fbcf +3e 2756 +f5 fd11 +dc fbd2 +f6 fd14 +dd fbd3 +f7 fd15 +df fbff +f9 fd41 +1b 89cd +d8 f962 +1 888b +55 b31b +6f b45d +e1 fc03 +fb fd45 +1d 89d1 +9 2269 +da f966 +3 888f +db 7bed +c1 7aab +c 2af8 +8c 6af8 +e3 fc0f +fd fd51 +dc f972 +1f 89dd +5 889b +e5 fc13 +ff fd55 +de f976 +d 2279 +7 889f +df 7bfd +c5 7abb +2c 84f8 +d4 d31a +ee d45c +4e 32f6 +d 80f9 +2d 84f9 +c5 58b3 +df 59f5 +c 82f8 +90 630a +aa 644c +4c 92f8 +65 16b3 +7f 17f5 +c6 58b4 +d 82f9 +8f 487d +5 2231 +c 88f8 +25 cb3 +a4 4610 +3f df5 +d 88f9 +1d 89f9 +d 8af9 +c5 78b3 +ae 445e +df 79f5 +c a2f8 +25 26b3 +3f 27f5 +25 2eb3 +c5 d019 +3f 2ff5 +fa fd44 +e0 fc02 +e2 fc06 +fe fd54 +e4 fc12 +e6 fc16 +e8 fc42 +ec fc52 +54 bb38 +47 ba97 +ee fc56 +df 51fd +c5 50bb +e0 fc08 +0 8aa0 +e4 fc18 +1 8aa1 +2e 265e +e5 fc19 +8 8ae0 +ec fc58 +47 ba9d +9 8ae1 +ed fc59 +f0 fd08 +3a 274e +f1 fd09 +10 8ba0 +f4 fd18 +3e 275e +11 8ba1 +f5 fd19 +f8 fd48 +33 d8f +18 8be0 +fc fd58 +37 d9f +19 8be1 +fd fd59 +fa fd4c +e0 fc0a +fe fd5c +0 8aa2 +1a 8be4 +e4 fc1a +e8 fc4a +22 8c24 +8 8ae2 +ec fc5a +47 ba9f +a5 c631 +f0 fd0a +2 aa2c +6 aa3c +ad c671 +f8 fd4a +a aa6c +e aa7c +e0 fc20 +cc 72f0 +36 53e +2a 2666 +e1 fc21 +e9 fc61 +d0 fb22 +13 8b8d +ea fc64 +f0 fd20 +3a 2766 +f1 fd21 +f9 fd61 +80 c208 +1b 89ed +1 88ab +55 b33b +6f b47d +e1 fc23 +fb fd65 +82 c20c +1d 89f1 +3 88af +23 8c8d +fa fd64 +e0 fc22 +25 8c91 +e2 fc26 +2c 8652 +43 baa7 +2d 8cd1 +13 8b8f +ea fc66 +33 8d8d +f0 fd22 +6e b47e +d3 fb87 +e0 fc28 +2a 266e +3e 8dd6 +e1 fc29 +db fbc7 +e8 fc68 +e5 5c13 +ff 5d55 +2c 8658 +43 baad +c2 fa86 +e9 fc69 +a3 460d +24 cb0 +90 c90a +45 9231 +a 28e6 +89 6243 +eb fcc7 +f8 fd68 +b2 470c +33 daf +d2 fb86 +f9 fd69 +b3 470d +34 db0 +e8 fc6a +5d bbf1 +ff 5d57 +2c 865a +43 baaf +f0 fd2a +f8 fd6a +38 276a +e2 fc84 +f a27d +e4 fc90 +2e 26d6 +e5 fc91 +f0 fd80 +d6 7934 +1d a379 +66 b49c +3a 27c6 +f1 fd81 +67 b49d +f3 fd85 +f4 fd90 +f6 fd94 +f7 fd95 +e2 fc86 +fe fdd4 +e4 fc92 +e6 fc96 +e0 fc88 +e4 fc98 +2e 26de +e5 fc99 +f0 fd88 +b a847 +3a 27ce +f1 fd89 +f4 fd98 +f a857 +fe fddc +e4 fc9a +a9 c6c1 +f4 fd9a +6 aabc +77 15bf +aa 44ee +bb 45ef +cc 50fa +dd 51fb +67 16bf +a3 442d +89 42eb +d4 d310 +55 99b3 +ab 46ef +f6 d714 +77 9db7 +e7 543d +cd 52fb +3d a5d1 +23 a48f +70 1da2 +ef 56ff +d6 531c +57 19bf +61 1c01 +47 1abf +37 25bf +50 93aa +6a 94ec +ae 64fe +27 26bf +a7 643d +49 92e9 +8d 62fb +79 1f4b +30 2da2 +6b 96ed +af 66ff +21 2c01 +7 2abf +e0 fca0 +36 5be +14 2b1a +2e 2c5c +b0 c70a +2a 26e6 +e1 fca1 +f0 fda0 +24 2c1a +3e 2d5c +66 b4bc +3a 27e6 +f1 fda1 +5c 9b50 +42 9a0e +25 2c1b +3f 2d5d +67 b4bd +f2 fda4 +68 b4c0 +19 81eb +47 9a37 +c0 f2a0 +2a 84ee +d1 f3a1 +3b 85ef +e2 feac +4c 90fa +f3 ffad +5d 91fb +6e 94fe +7f 95ff +c1 d829 +a 26e +ab 644f +67 943d +4d 92fb +6f 96ff +89 c24b +3 2227 +a 88ee +9a c34c +80 c20a +1b 89ef +de d35c +c4 d21a +5f 99ff +ad 6c53 +69 9c41 +4f 9aff +f7 dfbf +1d a1fb +98 e142 +22 eac +e0 7622 +23 68d +fa 7764 +2f a6ff +8d e25b +e a8fe +9e e35c +84 e21a +1f a9ff +29 ac41 +f aaff +fa fde4 +e0 fca2 +2e 2c5e +e2 fca6 +f0 fda2 +3e 2d5e +66 b4be +2e 2e7e +37 5bd +40 3000 +26 2ebe +2a 26ee +e1 fca9 +90 c98a +a3 6405 +45 92b1 +89 62c3 +27 6bd +b1 4fa1 +26 6be +ca 50e4 +b0 4fa2 +fa fdec +e0 fcaa +f0 fdaa +4e 3a7e +f4 7f30 +5e 117e +85 e8b9 +c2 5204 +43 18a7 +ce 505e +2c aef8 +c2 72ac +2c 4fa +ee 545e +4c b2f8 +e2 7eac +4c 10fa +6c 14fa +7c 15fa +8c 40fa +ac 44fa +57 11bd +60 3c00 +46 3abe +26 43c +c 2fa +2c 6fa +66 143c +4c 12fa +6c 16fa +ac 46fa +f7 d71f +78 9dc2 +56 11be +4e 385c +c 8fa +1c 9fa +41 1221 +8c 48fa +5c 99d8 +55 3311 +1a a9c4 +0 a882 +4a 126e +21 aea1 +c3 5007 +eb fe45 +a2 eeac +c 80fa +c2 f2ac +2c 84fa +df 59f7 +26 843c +c 82fa +ff 5df7 +2c 86fa +8e 487e +e1 fe09 +db 514d +c1 500b +78 3fe0 +17 a19f +9e 417e +6c 1452 +83 48a7 +9f 417f +bf 457f +0 282 +1a 3c4 +b6 45be +b7 45bf +30 d22 +af 467f +7b 9d47 +de 517e +df 517f +20 e82 +3a fc4 +d6 51be +ce 785c +6c b6d8 +d7 51bf +cf 785d +6d b6d9 +81 e883 +9b e9c5 +cb 526f +3b a545 +10 988 +21 a403 +85 e893 +9f e9d5 +50 1922 +cf 527f +14 998 +3f a555 +25 a413 +4b 90cf +e1 fe81 +db 51c5 +c1 5083 +37 79f +4d 90d3 +e3 fe85 +d0 5128 +c3 5087 +4f 90df +e5 fe91 +df 51d5 +c5 5093 +dd 53f1 +c3 52af +e0 5400 +c6 52be +49 9863 +e1 fe89 +30 700 +a0 4ea2 +ba 4fe4 +50 1300 +21 8629 +c0 5aa2 +da 5be4 +70 1700 +e0 5ea2 +fa 5fe4 +78 1740 +19 9e3 +98 4340 +31 da3 +b0 4700 +39 de3 +b8 4740 +71 1da3 +f0 5700 +79 1de3 +f8 5740 +12 304 +0 202 +1a 344 +32 704 +d7 dbbd +20 602 +3a 744 +15 a9b9 +52 1304 +1d a9f9 +40 1202 +5a 1344 +13 9a7 +92 4304 +1b 9e7 +80 4202 +9a 4344 +33 da7 +b2 4704 +3b de7 +a0 4602 +ba 4744 +95 e9b9 +53 19a7 +d2 5304 +5b 19e7 +9d e9f9 +c0 5202 +da 5344 +b5 edb9 +73 1da7 +f2 5704 +7b 1de7 +bd edf9 +e0 5602 +fa 5744 +14 310 +84 4ab2 +9e 4bf4 +2 20e +1c 350 +8c 4af2 +a6 4c34 +d1 db89 +34 710 +54 1310 +c4 5ab2 +25 8639 +de 5bf4 +42 120e +5c 1350 +cc 5af2 +2d 8679 +e6 5c34 +62 160e +93 4ba5 +7c 1750 +15 9b3 +94 4310 +1d 9f3 +82 420e +9c 4350 +35 db3 +b4 4710 +3d df3 +a2 460e +d3 7ba5 +bc 4750 +5d 19f3 +c2 520e +dc 5350 +7d 1df3 +e2 560e +fc 5750 +16 314 +4 212 +1e 354 +d3 db8d +36 714 +db dbcd +c1 da8b +24 612 +3e 754 +19 a9c9 +56 1314 +44 1212 +5e 1354 +64 1612 +7e 1754 +17 9b7 +96 4314 +1f 9f7 +84 4212 +9e 4354 +37 db7 +b6 4714 +5f 19f7 +c4 5212 +de 5354 +b9 edc9 +77 1db7 +f6 5714 +3a ad6e +7f 1df7 +e4 5612 +fe 5754 +30 780 +70 1780 +12 384 +32 784 +34 790 +b4 4790 +1d 8bf3 +37 8d35 +65 9c93 +7f 9dd5 +6b 366d +36 794 +96 4394 +b6 4794 +67 9c97 +74 9d38 +b8 6d4a +6d 3671 +10 b00 +fe 5dde +50 1b00 +58 1b40 +90 4b00 +6 21c +12 b04 +52 1b04 +40 1a02 +5a 1b44 +92 4b04 +d2 5b04 +c0 5a02 +da 5b44 +14 b10 +54 1b10 +42 1a0e +5c 1b50 +94 4b10 +82 4a0e +9c 4b50 +d4 5b10 +c2 5a0e +dc 5b50 +16 b14 +56 1b14 +44 1a12 +5e 1b54 +96 4b14 +84 4a12 +9e 4b54 +d6 5b14 +c4 5a12 +de 5b54 +10 b80 +50 1b80 +2f 86df +f5 5d3b +eb 7c67 +14 b90 +b9 4d4b +94 4b90 +4b 3a6d +16 b94 +19 abe9 +bb 4d4f +96 4b94 +4d 3a71 +10 2300 +80 6aa2 +9a 6be4 +fe 75de +30 2700 +a0 6ea2 +ba 6fe4 +11 29a3 +90 6300 +62 1e0e +7c 1f50 +19 29e3 +98 6340 +31 2da3 +b0 6700 +39 2de3 +b8 6740 +19 89cb +12 2304 +0 2202 +1a 2344 +d7 fbbd +20 2602 +3a 2744 +99 c9cb +13 29a7 +92 6304 +64 1e12 +7e 1f54 +1b 29e7 +80 6202 +9a 6344 +b9 cdcb +33 2da7 +b2 6704 +3b 2de7 +a0 6602 +ba 6744 +14 2310 +84 6ab2 +40 9aa0 +9e 6bf4 +2 220e +1c 2350 +48 9ae0 +8c 6af2 +a6 6c34 +d1 fb89 +34 2710 +a4 6eb2 +60 9ea0 +be 6ff4 +d9 fbc9 +22 260e +3c 2750 +35 2db3 +b4 6710 +1d 89db +16 2314 +4 2212 +1e 2354 +db fbcd +c1 fa8b +24 2612 +3e 2754 +9d c9db +17 29b7 +96 6314 +1f 29f7 +40 9200 +84 6212 +9e 6354 +bd cddb +37 2db7 +b6 6714 +3f 2df7 +60 9600 +a4 6612 +be 6754 +6a 3cc4 +50 3b82 +30 2780 +32 2784 +92 6384 +64 1e92 +7e 1fd4 +b2 6784 +14 2390 +75 9539 +b9 654b +34 2790 +b4 6790 +16 2394 +36 2794 +b6 6794 +10 2b00 +90 6b00 +6 221c +12 2b04 +92 6b04 +14 2b10 +94 6b10 +16 2b14 +96 6b14 +10 2b80 +71 9d29 +b5 6d3b +90 6b80 +99 c361 +6 229c +12 2b84 +2d 245b +92 6b84 +69 9449 +ad 645b +14 2b90 +94 6b90 +16 2b94 +41 b2a1 +e3 5407 +10 8300 +30 8700 +ae 6454 +94 6312 +50 9300 +b4 6712 +70 9700 +bc 6752 +78 9740 +d8 5960 +12 8304 +0 8202 +1a 8344 +f8 5d60 +32 8704 +20 8602 +3a 8744 +96 6316 +52 9304 +9e 6356 +40 9202 +5a 9344 +b6 6716 +72 9704 +be 6756 +60 9602 +7a 9744 +42 920e +5c 9350 +da 71e6 +93 cba5 +62 960e +7c 9750 +fa 75e6 +dc 5970 +c2 582e +16 8314 +4 8212 +ca 586e +1e 8354 +56 9314 +44 9212 +5e 9354 +76 9714 +64 9612 +7e 9754 +10 8380 +94 6392 +ae 64d4 +50 9380 +d8 59e0 +12 8384 +eb f467 +c0 58aa +da 59ec +14 8390 +54 9390 +b 826d +c2 58ae +dc 59f0 +16 8394 +e2 5cae +fc 5df0 +36 8794 +ae 6c54 +94 6b12 +50 9b00 +5e 31f4 +44 30b2 +9c 6b52 +58 9b40 +4c 30f2 +96 6b16 +52 9b04 +cc d0da +46 30b6 +9e 6b56 +40 9a02 +5a 9b44 +d4 d11a +4e 30f6 +56 9b14 +4a 30c6 +10 8b80 +12 8b84 +eb fc67 +14 8b90 +16 8b94 +10 a300 +d8 7960 +1 889 +12 a304 +0 a202 +9 8c9 +1a a344 +f8 7d60 +21 c89 +32 a704 +29 cc9 +20 a602 +3a a744 +fc 7d70 +e2 7c2e +25 c99 +36 a714 +ea 7c6e +2d cd9 +24 a612 +3e a754 +10 a380 +f8 7de0 +32 a784 +da 79ec +c0 78aa +14 a390 +10 ab00 +12 ab04 +14 ab10 +16 ab14 +10 ab80 +12 ab84 +2d a45b +14 ab90 +4b 90ef +e1 fea1 +db 51e5 +c1 50a3 +2f 2e5d +fb 55c5 +e1 5483 +37 a51f +98 4360 +f0 5528 +e3 5487 +80 4222 +9a 4364 +ff 55d5 +e5 5493 +82 422e +9c 4370 +30 720 +85 e291 +50 1320 +a5 e691 +70 1720 +2f a4df +90 4320 +b0 4720 +b8 4760 +f8 5760 +dc 735a +12 324 +87 e295 +52 1324 +a7 e695 +72 1724 +92 4324 +b2 4724 +a0 4622 +ba 4764 +d2 5324 +c0 5222 +da 5364 +f2 5724 +e0 5622 +fa 5764 +f4 7518 +10 3a0 +30 7a0 +50 13a0 +70 17a0 +b0 47a0 +d0 53a0 +fc 77da +32 7a4 +52 13a4 +72 17a4 +92 43a4 +b2 47a4 +d2 53a4 +f2 57a4 +16 11c +3c fda +56 111c +68 3eca +82 400c +6c 3eda +86 401c +70 3f0a +8a 404c +74 3f1a +8e 405c +78 3fca +92 410c +7c 3fda +96 411c +80 400a +9a 414c +b4 4f1a +ce 505c +c0 500a +da 514c +f2 7f26 +35 f91 +fb 55cd +e1 548b +8b 42c7 +98 4368 +3d fdb +57 111d +46 3a1e +69 3ecb +83 400d +6d 3edb +87 401d +79 3fcb +93 410d +9c 6b50 +82 6a0e +c6 7a1e +85 ea91 +50 1b20 +6f bcdf +d0 5b20 +a5 e611 +26 acb4 +46 123c +77 bd1f +d8 5b60 +4 a890 +4e 127c +87 ea95 +52 1b24 +c0 5a22 +da 5b64 +50 1ba0 +d0 5ba0 +bc e552 +46 12bc +52 1ba4 +6d 147b +30 2720 +92 c986 +98 6360 +b0 6720 +b2 cd86 +b8 6760 +80 6222 +9a 6364 +b9 cdeb +b2 6724 +a0 6622 +ba 6764 +10 23a0 +30 27a0 +90 63a0 +b0 67a0 +98 c3c8 +12 23a4 +b8 c7c8 +32 27a4 +92 63a4 +b2 67a4 +2 2c +a 6c +53 118f +12 12c +30 f2a +4a 106c +87 e09d +38 fea +52 112c +68 3eea +82 402c +70 3f2a +8a 406c +d3 518f +78 3fea +92 412c +80 402a +9a 416c +b0 4f2a +ca 506c +9b e147 +25 eb1 +c0 502a +da 516c +35 fb1 +3 2d +29 eeb +43 102d +39 feb +53 112d +5c 3b70 +42 3a2e +69 3eeb +83 402d +98 cb48 +12 2b24 +76 351e +80 6a22 +9a 6b64 +98 cbc8 +12 2ba4 +76 359e +10 8320 +18 8360 +7b 95c5 +bf 65d7 +61 9483 +38 8760 +9f 61d5 +41 9081 +85 6093 +bf 65d5 +61 9481 +a5 6493 +dc f35a +12 8324 +0 8222 +1a 8364 +70 9528 +b4 653a +63 9487 +fc f75a +32 8724 +20 8622 +3a 8764 +94 6138 +43 9085 +87 6097 +40 9222 +9e 6376 +5a 9364 +b6 6736 +72 9724 +b4 6538 +63 9485 +a7 6497 +be 6776 +60 9622 +7a 9764 +f4 f518 +10 83a0 +f6 f51c +dc f3da +12 83a4 +86 409c +3d 2f79 +5d 19fb +dc 5358 +6e b476 +96 419c +ec 5458 +7e b576 +26 2e96 +4d 3079 +c6 509c +9f 69d5 +41 9881 +85 6893 +dc fb5a +12 8b24 +76 951e +96 6b36 +52 9b24 +94 6938 +43 9885 +87 6897 +96 6bb6 +52 9ba4 +10 a320 +18 a360 +30 a720 +38 a760 +1 8a9 +12 a324 +0 a222 +9 8e9 +1a a364 +21 ca9 +32 a724 +29 ce9 +20 a622 +3a a764 +b2 4506 +10 a3a0 +12 a3a4 +2 ac +42 10ac +82 40ac +92 41ac +c2 50ac +38 7c8 +d2 51ac +83 40ad +c3 50ad +39 7c9 +12 ab24 +0 aa22 +1a ab64 +95 e9b3 +7e b55e +12 aba4 +f1 5521 +76 b59e +fb 55e5 +e1 54a3 +d9 7b41 +a 24c +94 4b30 +4a 124c +d4 5b30 +10 ab82 +2a acc4 +81 4aa1 +6a 164c +7 8bf +86 421c +f 8ff +8e 425c +4b 18ef +ca 524c +4f 18ff +ce 525c +6b 1cef +ea 564c +6f 1cff +ee 565c +b 24d +95 4b31 +4b 124d +d5 5b31 +11 ab83 +2b acc5 +4c 18f0 +cb 524d +d 82f1 +58 b9ca +aa 466c +f3 578f +a0 ec80 +ea 566c +d9 7363 +2 28c +f9 7763 +22 68c +62 168c +a2 468c +b 8aef +25 8c31 +e2 568c +a9 6c43 +4b 9aef +65 9c31 +83 428d +c3 528d +b9 6741 +20 2ca2 +3a 2de4 +82 42ac +a2 46ac +c2 52ac +2 a0c +6 a1c +a a4c +42 1a0c +3d ad5b +46 1a1c +4a 1a4c +4e 1a5c +82 4a0c +86 4a1c +8a 4a4c +8e 4a5c +c2 5a0c +c6 5a1c +ca 5a4c +ce 5a5c +42 1a2c +3d ad7b +4a 1a6c +82 4a2c +8a 4a6c +c2 5a2c +18 abc0 +d9 7b63 +2 a8c +dd 7b73 +6 a9c +46 1a9c +2e 8e76 +c6 5a9c +b8 ed42 +42 1aac +bc e758 +7a 1746 +3d adfb +82 4aac +c2 5aac +f4 5f10 +0 200a +1a 214c +8 22ca +22 240c +92 6bae +ac 6cf0 +10 230a +2a 244c +9a 6bee +b4 6d30 +18 23ca +32 250c +a2 6cae +bc 6df0 +20 240a +3a 254c +24 8eb8 +82 600c +54 1b1a +6e 1c5c +2c 8ef8 +8a 604c +30 8f08 +8e 605c +34 8fb8 +92 610c +64 1c1a +7e 1d5c +38 8fc8 +96 611c +80 600a +3c 8ff8 +9a 614c +88 62ca +44 92b8 +a2 640c +98 63ca +54 93b8 +b2 650c +9 22cb +23 240d +fe 5776 +11 230b +2b 244d +21 240b +3b 254d +25 8eb9 +83 600d +35 8fb9 +93 610d +65 1c1b +7f 1d5d +3d 8ff9 +81 600b +9b 614d +89 62cb +45 92b9 +a3 640d +91 630b +12 29ae +4d 92f9 +ab 644d +e4 5e30 +a 206c +45 9a9b +5f 9bdd +ec 5ef0 +12 212c +67 9c9d +0 202a +f4 5f30 +1a 216c +55 9b9b +6f 9cdd +82 602c +8a 606c +92 612c +64 1c3a +7e 1d7c +80 602a +9a 616c +ed 5ef1 +13 212d +f5 5f31 +1 202b +1b 216d +83 602d +93 612d +65 1c3b +7f 1d7d +2 208c +12 218c +22 248c +32 258c +82 608c +92 618c +a2 648c +b2 658c +3 208d +de 53f6 +23 248d +fe 57f6 +33 258d +83 608d +87 609d +93 618d +97 619d +a3 648d +2 20ac +12 21ac +82 60ac +92 61ac +3 20ad +13 21ad +83 60ad +93 61ad +a 224c +94 6b30 +22 260c +16 13e +ac 6ef0 +26 261c +2a 264c +1e 17e +b4 6f30 +6e 1e5c +4e b8d4 +7 28bf +86 621c +11 8123 +23 2caf +3d 2df1 +a2 660c +27 2cbf +a6 661c +31 8523 +b 224d +95 6b31 +23 260d +17 13f +ad 6ef1 +2b 264d +1f 17f +b5 6f31 +4 28b0 +83 620d +6f 1e5d +4f b8d5 +36 8d94 +22 262c +96 c994 +82 622c +6e 1e7c +cd f251 +4e b8f4 +9e c9d4 +84 c892 +8a 626c +56 b934 +b6 cd94 +a2 662c +be cdd4 +a4 cc92 +aa 666c +17 8995 +3 222d +54 3390 +97 c995 +83 622d +6f 1e7d +1e 835e +4f b8f5 +85 c893 +9f c9d5 +8b 626d +57 b935 +2 228c +22 268c +b9 c761 +26 269c +1a 1ce +b0 6f80 +a2 668c +b aaef +25 ac31 +23 268d +83 628d +a3 668d +22 26ac +82 62ac +a2 66ac +83 62ad +2 280c +12 290c +0 280a +1a 294c +92 690c +30 a788 +13 290d +ee 5c76 +2 288c +12 298c +82 688c +92 698c +2 2a0c +6 2a1c +a 2a4c +82 6a0c +86 6a1c +11 8923 +8a 6a4c +f0 d58a +8e 6a5c +19 8963 +83 6a0d +8b 6a4d +82 6a2c +8a 6a6c +2 2a8c +99 cb61 +6 2a9c +86 6a9c +90 c300 +11 89a3 +65 b433 +54 19b8 +7f b575 +83 6a8d +82 6aac +12 810c +16 811c +0 800a +1a 814c +8e 605e +30 8f0a +4a 904c +a6 641e +48 92ca +62 940c +13 810d +1c ab50 +2 aa0e +17 811d +6 aa1e +a aa4e +f1 7729 +4e ba5e +2 802c +a 806c +53 918f +12 812c +0 802a +1a 816c +db 59e7 +8 82ea +22 842c +8e 607e +30 8f2a +4a 906c +40 902a +9e 617e +5a 916c +a6 643e +48 92ea +62 942c +3 802d +13 812d +c2 58a6 +9 82eb +23 842d +31 8f2b +8f 607f +4b 906d +4a ba6e +a7 643f +49 92eb +63 942d +12 818c +16 819c +97 619f +53 918d +57 919d +c1 5a21 +46 ba9e +2 80ac +12 81ac +97 61bf +53 91ad +5c bbf0 +fe 5d56 +42 baae +46 921c +f5 fdb9 +3e 27fe +c8 70e2 +4e 925c +cc 70f2 +66 961c +81 caa1 +ae 665e +6a 964c +e8 74e2 +85 cab1 +6e 965c +ec 74f2 +c9 5869 +3 820d +9b 61e5 +81 60a3 +cd 5879 +7 821d +b 824d +f 825d +c2 5a86 +e9 5c69 +23 860d +bb 65e5 +a1 64a3 +87 621f +8 28c2 +43 920d +37 27bf +c 28d2 +47 921d +8f 625f +10 2902 +4b 924d +14 2912 +4f 925d +e3 5c27 +2a 866c +1c 8358 +d5 5913 +73 978f +86 623e +42 922c +8e 627e +4a 926c +ae 667e +6a 966c +3 822d +87 623f +8 28e2 +43 922d +d9 f363 +c8 58e8 +2 828c +46 929c +66 969c +a0 6400 +86 62be +42 92ac +6 881c +c4 7a38 +b 2245 +12 890c +f 2255 +16 891c +86 681e +42 980c +46 981c +4b 3245 +96 691e +52 990c +4f 3255 +56 991c +13 890d +17 891d +93 eba7 +7c b752 +97 691f +53 990d +57 991d +2b 445 +11 303 +9b 4be7 +bd 6dfb +79 9de9 +13 307 +61 9cab +bf 6dff +e0 d608 +7b 9ded +17 317 +65 9cbb +e4 d618 +7f 9dfd +1b 347 +1d 353 +1f 357 +31 70b +f8 dfe8 +11 a183 +d5 5131 +bb 4fef +50 3ba0 +39 74b +dd 5171 +c3 502f +f3 df87 +19 a1c3 +86 e294 +6b 1465 +51 1323 +5d 1353 +a6 e69c +71 172b +51 b1a3 +ae e6dc +79 176b +59 b1e3 +2 882c +b 2265 +12 892c +1a 9e6 +99 4343 +86 683e +42 982c +4b 3265 +96 693e +52 992c +1e 9f6 +9d 4353 +af 44d5 +95 4393 +46 9896 +4c 3270 +3a dee +d0 7ba0 +b9 474b +5e 19f6 +dd 5353 +d9 5363 +f9 576b +13 892d +22 a4a6 +90 4388 +78 b762 +97 693f +53 992d +62 b4a6 +d0 5388 +31 703 +bb 4fe7 +da dbcc +c0 da8a +3d 753 +c7 5037 +2b c4d +11 b0b +2f c5d +15 b1b +a6 e694 +71 1723 +7d 1753 +57 1b17 +49 b0c9 +6f 1c5d +55 1b1b +5d 1b5b +9d eb79 +5b 1b67 +8e eadc +59 1b6b +b5 4793 +66 9c96 +6c 3670 +99 4b4b +9d 4b5b +4c 3a78 +7e 1df6 +fd 5753 +f9 5763 +dd 5b5b +db 5b67 +2d 8651 +d9 5b6b +eb 5ced +25 8691 +e2 f626 +d1 5bab +1b 945 +1 803 +1f 955 +5 813 +7 817 +9 843 +d 853 +11 903 +15 913 +19 943 +5b 91e5 +9f 61f7 +41 90a3 +1d 953 +5b 1945 +41 1803 +85 e819 +43 1807 +72 1704 +35 adb9 +5f 1955 +45 1813 +47 1817 +76 1714 +39 adc9 +49 1843 +8d e859 +4b 1847 +60 1602 +7a 1744 +3d adf9 +73 9fa7 +4d 1853 +4f 1857 +c8 706a +77 9fb7 +51 1903 +55 1913 +59 1943 +5d 1953 +9b 4945 +81 4803 +89 4843 +27 86bf +91 4903 +7 1f +95 4913 +99 4943 +2f ae5d +a5 6e11 +f 5f +9d 4953 +3b 87cf +47 101f +d9 5943 +77 97bf +e5 7e11 +4f 105f +dd 5953 +7b 97cf +14 a9b8 +6b 1445 +51 1303 +22 862c +db 5be7 +1c a9f8 +59 1343 +90 4ba0 +79 174b +1c 89da +2f 2455 +15 2313 +1e 89de +17 2317 +99 c34b +1a 89ee +13 2327 +1d 2353 +1f 2357 +1b 2367 +35 271b +31 272b +e3 fc85 +39 276b +9c e9f8 +5a 19e6 +d9 5343 +7a 1dee +f9 574b +59 9341 +1e 29f6 +9d 6353 +ab 64e5 +91 63a3 +b9 676b +b1 67ab +34 adb8 +71 1703 +fb 5fe7 +3c adf8 +79 1743 +9d eb59 +5b 1b47 +4d b0f9 +59 1b4b +da fbcc +c0 fa8a +3d 2753 +17 2b17 +2f 2c5d +15 2b1b +99 cb4b +13 2b27 +2b 2c6d +11 2b2b +1f 2b57 +47 b2b7 +1d 2b5b +5f b3fd +45 b2bb +1b 2b67 +19 2b6b +bc edf8 +7a 1de6 +f9 5743 +db 5b47 +d9 5b4b +46 9a1c +25 a439 +de 79f4 +c4 78b2 +8e 6a5e +4a 9a4c +29 a469 +2 a286 +c8 78e2 +4e 9a5c +2d a479 +6 a296 +cc 78f2 +79 9741 +3e 2df6 +bd 6753 +b9 6763 +71 9781 +b5 6793 +b1 67a3 +59 9b49 +9d 6b5b +99 6b6b +ab 6ced +91 6bab +7 8a1d +87 6a1f +43 9a0d +51 3101 +37 2fbf +dc 717a +47 9a1d +ab 4445 +12 9a6 +91 4303 +af 4455 +16 9b6 +95 4313 +36 dbe +b5 471b +98 e9c8 +56 19b6 +ef 5455 +d5 5313 +eb 5465 +d1 5323 +76 1dbe +f5 571b +f1 572b +1f 215f +19 8343 +1b 8347 +1d 8353 +1f 8357 +2b 84c5 +11 8383 +db 59ef +2f 84d5 +15 8393 +86 6a3e +42 9a2c +8e 6a7e +4a 9a6c +3b 8747 +50 bba0 +f2 5d06 +39 874b +3f 8757 +f9 5de3 +33 8787 +fd 5df3 +37 8797 +5d 9353 +5f 9357 +5b 9367 +73 1f8d +7b 9767 +3 8a2d +87 6a3f +43 9a2d +32 da6 +b1 4703 +b8 edc8 +76 1db6 +f5 5713 +d7 5b17 +39 8743 +3d 8753 +1b 8b47 +86 6a9e +42 9a8c +46 9a9c +19 8b4b +1f 8b57 +1d 8b5b +7d 9753 +5f 9b57 +5d 9b5b +5b 9b67 +af 6cff +6b 9ced +51 9bab +1b 94d +1 80b +1f 95d +5 81b +9 84b +d 85b +5b 194d +41 180b +5f 195d +45 181b +49 184b +cf 70ff +71 9fab +4d 185b +d3 710f +75 9fbb +9b 494d +81 480b +9f 495d +85 481b +89 484b +8d 485b +df 595d +c5 581b +cd 585b +94 e9b8 +eb 5445 +52 19a6 +d1 5303 +34 a538 +27 a497 +9c c9da +af 6455 +16 29b6 +51 9301 +95 6313 +71 9709 +36 2dbe +b5 671b +b1 672b +59 9343 +5b 9347 +7b 9747 +90 cba0 +79 974b +86 6abe +a0 6c00 +42 9aac +c 8d8 +1d a353 +2f a4d5 +db 79ef +15 a393 +2b a4e5 +11 a3a3 +f2 7d26 +35 d91 +39 a76b +b4 edb8 +72 1da6 +f1 5703 +bc cdda +71 9701 +36 2db6 +b5 6713 +b8 cdea +b1 6723 +51 9b09 +af 6c5d +95 6b1b +ab 6c6d +91 6b2b +79 9743 +5b 9b47 +59 9b4b +2 a00c +ac 4e78 +f5 5f9b +e4 de10 +a a04c +fd 5fdb +ec ded0 +12 a10c +af 4ed7 +bc 4f78 +16 a11c +f4 df10 +0 a00a +1a a14c +10 a30a +2a a44c +1d ab5b +19 ab6b +2f acdd +15 ab9b +3 a00d +ad 4e79 +e5 de11 +b a04d +ed ded1 +13 a10d +bd 4f79 +17 a11d +2 a02c +f5 5fbb +e4 de30 +a a06c +16 2196 +fd 5ffb +ec def0 +12 a12c +f4 df30 +0 a02a +1a a16c +8 a2ea +db 79e7 +22 a42c +ed def1 +13 a12d +7c 17d8 +c2 78a6 +9 a2eb +23 a42d +e 8a7e +2 a08c +ac 4ef8 +12 a18c +bc 4ff8 +32 a58c +dc 53f8 +1b 965 +1 823 +3 827 +11 923 +3 a08d +ad 4ef9 +13 a18d +bd 4ff9 +5b 1965 +41 1823 +85 e839 +43 1827 +49 1863 +8d e879 +4b 1867 +86 e894 +51 1923 +9b 4965 +81 4823 +91 4923 +99 4963 +2f ae7d +13 8307 +d9 5963 +2 a0ac +12 a1ac +3 a0ad +13 a1ad +c8 7868 +2 a20c +c3 7807 +a a24c +c9 7869 +3 a20d +b a24d +f a25d +c2 7a86 +e9 7c69 +23 a60d +db 7be7 +22 a62c +e3 7c27 +2a a66c +3 a22d +c2 7aa6 +23 a62d +15 a319 +ce 78d4 +1b 96d +1 82b +5b 196d +41 182b +49 186b +9b 496d +81 482b +2 a80c +6 a81c +a a84c +12 81ae +4f 1257 +12 a90c +16 a91c +0 a80a +1a a94c +3 a80d +7 a81d +b a84d +13 81af +13 a90d +17 a91d +2 a82c +4f 1277 +12 a92c +13 a92d +90 6388 +e 80f6 +7c 1fd8 +4c 1278 +2 a88c +4f 12d7 +5c 1378 +12 a98c +1f 9d5 +5 893 +15 993 +5f 19d5 +45 1893 +54 1938 +47 1897 +9f 49d5 +85 4893 +94 4938 +87 4897 +95 4993 +4c 3870 +df 59d5 +c 82d8 +c5 5893 +2 aa0c +6 aa1c +a aa4c +7 aa1d +3 aa2d +56 b316 +5f 19dd +45 189b +92 e306 +9b 49cd +81 488b +96 e316 +9f 49dd +85 489b +d2 f306 +db 59cd +c1 588b +d6 f316 +df 59dd +c5 589b +90 4300 +11 9a3 +65 3433 +7f 3575 +7a 154e +91 49a3 +27 aebd +e5 7433 +ff 7575 +52 b326 +c0 5208 +5b 19ed +41 18ab +95 433b +af 447d +92 e326 +9b 49ed +81 48ab +d5 733b +ef 747d +d2 f326 +db 59ed +15 8391 +c1 58ab +1b b45 +1 a03 +9 a43 +d a53 +49 1a43 +4d 1a53 +9b 4b45 +81 4a03 +9f 4b55 +85 4a13 +89 4a43 +8d 4a53 +df 5b55 +c5 5a13 +c9 5a43 +2c ac78 +1f abd7 +cd 5a53 +49 1a63 +5c 39d8 +c9 5a63 +8d 4ad9 +1f abf7 +dc 79d8 +8 aae8 +aa 4c4e +5f 1bd5 +45 1a93 +9f 4bd5 +85 4a93 +df 5bd5 +c5 5a93 +c6 d894 +1c 37a +76 9d3c +ba 6d4e +5c 9bfa +6f 3675 +9b 4be5 +6a 164e +81 4aa3 +7 a2b5 +9c 437a +f6 dd3c +dc dbfa +ef 7675 +db 5be5 +c1 5aa3 +92 e98e +47 b2b5 +dc 537a +32 a50e +10 308 +18 348 +88 4aea +a2 4c2c +58 1348 +c8 5aea +e2 5c2c +b7 e71d +68 166a +38 adc0 +78 1748 +58 b1c0 +19 9eb +2a a466 +98 4348 +39 deb +b8 4748 +79 1deb +f8 5748 +14 318 +15 9bb +26 a436 +94 4318 +10 388 +30 788 +70 1788 +b0 4788 +eb 746f +14 398 +34 798 +54 1398 +c1 5aa9 +3c 8772 +74 1798 +26 a4b6 +94 4398 +17 893d +b4 4798 +66 b4b6 +d4 5398 +65 161b +7f 175d +45 b093 +5f b1d5 +95 4bb3 +af 4cf5 +7e 175e +a5 ee99 +70 1f28 +63 1e87 +2d 2cd9 +5e b1d6 +31 ad81 +61 162b +7b 176d +41 b0a3 +5b b1e5 +af e6df +30 ad82 +7a 176e +7e 9776 +29 2ce9 +c8 50c8 +5a b1e6 +10 b08 +50 1b08 +58 1b48 +22 ac26 +90 4b08 +62 bc26 +d0 5b08 +26 ac9c +6a bc66 +d8 5b48 +14 ab9a +2e acdc +14 b18 +54 1b18 +5c 1b58 +f7 57b5 +66 bc36 +d4 5b18 +6e bc76 +dc 5b58 +d5 7bb3 +ef 7cf5 +be 475e +b0 4f28 +a3 4e87 +b3 478d +b2 478e +7d 9dd3 +69 366b +b6 479e +6d 367b +61 b603 +7b b745 +50 1b88 +38 8f62 +a1 e603 +bb e745 +22 aca6 +90 4b88 +e1 f603 +fb f745 +62 bca6 +d0 5b88 +2e 867e +65 b613 +7f b755 +54 1b98 +3c 8f72 +a5 e613 +bf e755 +26 acb6 +94 4b98 +fe 575e +9 20c3 +f0 5f28 +e3 5e87 +ad 6cd9 +e5 f613 +ff f755 +66 bcb6 +d4 5b98 +b0 ed82 +fa 576e +fe d776 +a9 6ce9 +f2 57ae +10 2308 +18 2348 +88 6aea +a2 6c2c +30 2708 +38 2748 +11 29ab +90 6308 +e 8076 +7c 1f58 +42 b88e +5c b9d0 +19 29eb +47 9295 +98 6348 +39 2deb +67 9695 +b8 6748 +14 2318 +34 2718 +15 29bb +94 6318 +35 2dbb +b4 6718 +10 2388 +30 2788 +b0 6788 +14 2398 +34 2798 +94 6398 +17 a93d +b4 6798 +17 b1d +16 b1e +1a b4e +5 a1b +1f b5d +8b 62cf +a5 6411 +47 92bd +1e b5e +a4 6412 +60 9400 +be 6554 +46 92be +57 1b1d +56 1b1e +53 1b2d +87 ea9f +6c 1c70 +52 1b2e +45 1a1b +5f 1b5d +5e 1b5e +41 1a2b +5b 1b6d +8f eadf +5a 1b6e +10 2b08 +90 6b08 +14 2b18 +81 4a0b +9b 4b4d +94 6b18 +85 4a1b +9f 4b5d +9e 4b5e +93 4b8d +49 3a6b +97 4b9d +96 4b9e +4d 3a7b +7f 9757 +10 2b88 +ff d757 +90 6b88 +14 2b98 +c5 5a1b +df 5b5d +de 5b5e +94 6b98 +d7 5b9d +d6 5b9e +8d 4a7b +ee 5e7c +7 2017 +f3 5f07 +19 2143 +e8 5e6a +1b 2147 +c2 f88c +3f 2555 +25 2413 +c4 f890 +27 2417 +c8 f8c0 +2b 2447 +ca f8cc +2d 2453 +cc f8d0 +2f 2457 +31 2503 +bb 6de7 +23 8e0f +3d 8f51 +27 8ebf +41 9001 +9f 6155 +85 6013 +29 8ec3 +43 9005 +87 6017 +2d 8ef3 +47 9035 +8b 6047 +2f 8eff +49 9041 +8d 6053 +31 8f03 +4b 9045 +8f 6057 +33 8faf +4d 90f1 +91 6103 +3d 8ff3 +57 9135 +9b 6147 +47 92bf +bf 6555 +61 9401 +a5 6413 +63 9405 +49 92c3 +a7 6417 +4b 92ef +65 9431 +a9 6443 +67 9435 +4d 92f3 +ab 6447 +4f 92ff +69 9441 +ad 6453 +51 9303 +6b 9445 +af 6457 +53 93af +6d 94f1 +b1 6503 +5b 93ef +75 9531 +b9 6543 +c9 58c3 +10 8308 +d1 5903 +18 8348 +e9 5cc3 +30 8708 +f1 5d03 +38 8748 +ae 645c +94 631a +50 9308 +9c 635a +58 9348 +b4 671a +70 9708 +bc 675a +78 9748 +cd 58d3 +14 8318 +92 61ae +54 9318 +74 9718 +f2 75ae +10 8388 +94 639a +ae 64dc +50 9388 +eb f46f +14 8398 +54 9398 +61 160b +7b 174d +41 b083 +5b b1c5 +91 4ba3 +ab 4ce5 +7a 174e +7e 9756 +29 2cc9 +5a b1c6 +33 272d +35 8d93 +21 262b +3b 276d +f1 fd29 +3a 276e +94 6b1a +ae 6c5c +50 9b08 +97 63b5 +5e 31fc +44 30ba +9c 6b5a +58 9b48 +85 62b3 +41 92a1 +9f 63f5 +4c 30fa +e1 560b +7c 1df0 +62 1cae +fb 574d +fa 574e +fe d756 +a9 6cc9 +54 9b18 +26 a494 +d2 79ae +48 30ca +f3 578d +54 9b98 +b5 cd93 +a1 662b +bb 676d +ba 676e +55 3939 +b3 67ad +b2 67ae +c9 78c3 +10 a308 +d1 7903 +18 a348 +e9 7cc3 +30 a708 +f1 7d03 +38 a748 +cd 78d3 +14 a318 +ed 7cd3 +34 a718 +10 a388 +14 a398 +53 1b0d +6c 1c50 +52 1b0e +77 35bd +41 1a0b +5b 1b4d +5a 1b4e +17 2b1d +16 2b1e +13 2b2d +2c 2c70 +12 2b2e +5 2a1b +1f 2b5d +1e 2b5e +1a 2b6e +c1 5a0b +db 5b4d +da 5b4e +e5 74bb +ff 75fd +d3 5b8d +41 9a09 +85 6a1b +9f 6b5d +5a 9b4c +40 9a0a +9e 6b5e +9a 6b6e +52 9b8c +96 6b9e +51 bba3 +6b bce5 +3a 874e +fd 75f3 +95 cbb3 +af ccf5 +7e 975e +2d acd9 +7a 976e +66 1634 +29 ace9 +77 979d +76 979e +e6 5c36 +2d 867b +93 4b0d +97 4b1d +96 4b1e +d7 5b1d +d6 5b1e +99 69e3 +59 99e1 +9d 69f3 +ed 5ed3 +13 210f +5e 9b5e +5a 9b6e +56 9b9e +d 8a7b +eb d64d +6c 9cf0 +b0 6d02 +52 9bae +1f 215d +5 201b +f3 5f0f +19 214b +3f 255d +25 241b +29 244b +b3 6d2f +31 250b +bb 6def +3d 8f59 +39 254b +41 9009 +9f 615d +85 601b +45 9039 +89 604b +49 9049 +8d 605b +4d 90f9 +91 610b +7d 1d5b +55 9139 +99 614b +bf 655d +61 9409 +a5 641b +6d 94f9 +b1 650b +b3 672d +b2 672e +4d 38f9 +f9 75e3 +91 cba3 +ab cce5 +7a 974e +66 1614 +29 acc9 +36 d94 +3a a76e +d3 5b0d +ec 5c50 +d2 5b0e +f7 75bd +97 6b1d +52 9b0c +96 6b1e +5a 9b4e +65 b4bb +7f b5fd +1e ab5e +c6 7296 +ed 7479 +1a ab6e +ab e64d +2c acf0 +12 abae +3b 2565 +21 2423 +2d 8e71 +76 9f94 +a9 c44b +c0 f8a0 +23 2427 +2f 8e75 +c8 f8e0 +2b 2467 +9b 6165 +81 6023 +83 6027 +8b 6067 +bb 6565 +a1 6423 +a3 6427 +5c 9b7a +ab 6467 +3b 256d +21 242b +2d 8e79 +d3 f985 +29 246b +9b 616d +81 602b +89 606b +bb 656d +a1 642b +14 2138 +ee 5efc +7 2097 +da fb66 +3 8a8f +1d 8bd1 +fa 5fec +e0 5eaa +13 2187 +3b 25c5 +21 2483 +ea fe66 +2d 8ed1 +30 2528 +23 2487 +2f 8ed5 +3f 25d5 +25 2493 +34 2538 +27 2497 +31 2583 +fa ff66 +23 8e8f +3d 8fd1 +9b 61c5 +81 6083 +90 6128 +83 6087 +91 6183 +bb 65c5 +a1 6483 +b0 6528 +a3 6487 +76 9d1c +6f 3655 +5c 9bda +b1 6583 +98 c160 +1f 21dd +5 209b +11 218b +da fb6e +1d 8bd9 +3b 25cd +21 248b +ea fe6e +2d 8ed9 +31 258b +fa ff6e +3d 8fd9 +91 618b +bb 65cd +a1 648b +61 9489 +bf 65dd +a5 649b +b1 658b +3 287 +10 328 +ab 46c7 +b8 4768 +10 3a8 +30 7a8 +50 13a8 +70 17a8 +b0 47a8 +d0 53a8 +86 eab4 +6f b65f +3 a87 +10 b28 +43 1a87 +85 ea99 +50 1b28 +83 4a87 +90 4b28 +8b 4ac7 +98 4b68 +c3 5a87 +d0 5b28 +a5 e619 +63 1607 +26 acbc +cb 5ac7 +d8 5b68 +ad e659 +14 abba +6b 1647 +2e acfc +61 b623 +7b b765 +50 1ba8 +a1 e623 +bb e765 +90 4ba8 +2f ae5f +e1 f623 +fb f765 +d0 5ba8 +92 c98e +47 92b5 +8b 62c7 +98 6368 +b2 cd8e +ab 66c7 +67 96b5 +b8 6768 +10 23a8 +30 27a8 +90 63a8 +7c 1ff8 +b0 67a8 +3 2a87 +10 2b28 +83 6a87 +90 6b28 +3b 25e5 +21 24a3 +8b 6045 +2d 8ef1 +a9 c4cb +23 24a7 +2f 8ef5 +ff d777 +90 6ba8 +c9 58e3 +3 8287 +10 8328 +b 82c7 +d1 5923 +18 8368 +bf 65df +7b 95cd +61 948b +f1 5d23 +2b 86c7 +38 8768 +10 83a8 +41 9889 +9f 69dd +85 689b +ae 6cfc +94 6bba +50 9ba8 +c9 78e3 +3 a287 +10 a328 +b2 450e +10 a3a8 +8b 604d +2d 8ef9 +bb 65ed +a1 64ab +a 88ce +3 2207 +ab 6ee7 +2a 8cce +c0 fa80 +23 2607 +c8 fac0 +32 8d0e +2b 2647 +88 c8ca +9b 6345 +2 28a6 +81 6203 +a8 ccca +22 2ca6 +bb 6745 +a1 6603 +aa ccce +a3 6607 +b2 cd0e +67 9635 +ab 6647 +9 224b +ad 6c71 +93 6b2f +3b 274d +21 260b +29 264b +9b 634d +1c 29f0 +2 28ae +81 620b +6d 1e5b +a 28ee +45 9239 +89 624b +2a 2cee +65 9639 +a9 664b +3b 27c5 +21 2683 +30 2728 +23 2687 +9b 63c5 +81 6283 +b0 6728 +a3 6687 +1b 23cd +1 228b +3b 27cd +21 268b +bb 67cd +a1 668b +a aaee +24 ac30 +2b 44d +11 30b +b5 4d31 +9b 4bef +2f 45d +15 31b +19 34b +d0 db88 +33 70f +fa dfec +e0 deaa +13 a187 +d8 dbc8 +52 3ba4 +3b 74f +e8 deea +1b a1c7 +df 5175 +c5 5033 +6f 145d +55 131b +f9 5d41 +df 5bff +86 e29c +6b 146d +51 132b +8e e2dc +59 136b +73 172f +c1 5089 +53 b1a7 +96 4bb4 +7f 175f +5f b1d7 +b3 478f +d9 536b +b1 ed83 +fb 576f +f3 57af +17 b1f +1f b5f +57 1b1f +6d 1c71 +53 1b2f +5f 1b5f +9b 4b4f +9f 4b5f +97 4b9f +df 5b5f +2f 8655 +db 5b6f +d7 5b9f +8e 4a7c +ed 5cf1 +27 8695 +d3 5baf +1b 2945 +1 2803 +1f 2955 +5 2813 +7 2817 +9 2843 +d 2853 +11 2903 +13 2907 +15 2913 +9b 6945 +81 6803 +83 6807 +3b a7c5 +21 a683 +9f 6955 +41 9801 +85 6813 +e9 7ceb +3d a7d1 +23 a68f +47 9835 +8b 6847 +29 a6c3 +49 9841 +8d 6853 +f1 7d2b +2b a6cf +4d 98f1 +91 6903 +7 201f +4f 98f5 +93 6907 +51 9901 +95 6913 +6b 144d +51 130b +2f 86d5 +f5 5d31 +db 5bef +59 134b +92 4ba4 +7b 174f +2f 245d +15 231b +2b 246d +11 232b +66 9e9c +c3 f885 +19 236b +6e 9edc +dc fbd8 +3f 275f +5a 19ee +d9 534b +7c 1df2 +fb 574f +99 636b +ab 64ed +91 63ab +bb 676f +b3 67af +6d 1c51 +53 1b0f +5b 1b4f +17 2b1f +2d 2c71 +13 2b2f +1f 2b5f +db 5b4f +53 9b8d +97 6b9f +ab 444d +12 9ae +91 430b +6b 366f +7f 9dd7 +ef 545d +56 19be +d5 531b +eb 546d +d1 532b +f3 572f +d2 5906 +19 834b +2b 84cd +e8 f462 +11 838b +ec f472 +2f 84dd +15 839b +52 bba4 +3b 874f +56 bbb4 +3f 875f +59 936b +96 cbb4 +7f 975f +7b 976f +77 979f +e7 5c37 +2e 867c +73 97af +1c 8378 +f 82d7 +d5 5933 +e1 7e01 +4b 104f +ad 4c51 +93 4b0f +97 4b1f +d7 5b1f +1b 8b4f +1f 8b5f +5f 9b5f +5b 9b6f +1b 294d +1 280b +1f 295d +5 281b +9 284b +4b b0ed +31 afab +d 285b +4f b0fd +35 afbb +11 290b +19 294b +9b 694d +81 680b +9f 695d +41 9809 +85 681b +45 9839 +89 684b +49 9849 +8d 685b +4d 98f9 +91 690b +55 9939 +99 694b +eb 544d +52 19ae +d1 530b +88 42e0 +27 a49f +af 645d +51 9309 +16 29be +95 631b +ab 646d +91 632b +7d 1f7b +b3 672f +59 934b +92 cba4 +7b 974f +d2 7926 +15 991 +19 a36b +2f a4dd +15 a39b +2b a4ed +11 a3ab +37 d95 +3b a76f +33 a7af +51 9921 +95 6933 +e5 5e13 +ff 5f55 +b 204f +ed 5c51 +d3 5b0f +53 9b0d +97 6b1f +5b 9b4f +1b 2965 +1 2823 +89 c84b +3 2827 +11 2923 +9b 6965 +81 6823 +91 6923 +99 6963 +1b 296d +1 282b +9 286b +9b 696d +81 682b +89 686b +1f 29d5 +5 2893 +14 2938 +7 2897 +f4 573a +13 2987 +90 6928 +83 6887 +40 900a +5a 914c +9e 615e +91 6983 +80 c022 +9a c164 +7 209f +93 6987 +1b 29cd +1 288b +98 c960 +1f 29dd +5 289b +11 298b +9b 69cd +81 688b +91 698b +89 c8cb +82 6204 +3 28a7 +f8 5f42 +1e 217e +7a 354e +91 69a3 +1b 29ed +80 6208 +1 28ab +9b 69ed +81 68ab +1b 2b45 +1 2a03 +3 2a07 +1f 2b55 +5 2a13 +9 2a43 +b 2a47 +d 2a53 +9b 6b45 +81 6a03 +9f 6b55 +41 9a01 +85 6a13 +45 9a31 +89 6a43 +49 9a41 +8d 6a53 +1b 2b4d +1 2a0b +9 2a4b +9b 6b4d +81 6a0b +45 9a39 +89 6a4b +1b 2b65 +1 2a23 +9b 6b65 +81 6a23 +1b 2bcd +1 2a8b +c6 f894 +1c 237a +6a 364e +9b 6be5 +81 6aa3 +4b 92c7 +58 9368 +9c 637a +55 1331 +0 a8a8 +57 1335 +32 252c +18 23ea +24 8e38 +43 122f +13 a985 +5d 1371 +12 a986 +5c 1372 +b 28ed +b8 e748 +39 adeb +76 1736 +25 2cb1 +75 1739 +55 b1b1 +74 173a +54 b1b2 +7e 1776 +13 2baf +2d 2cf1 +33 ad8d +7d 1779 +43 b0af +5d b1f1 +32 ad8e +7c 177a +5c b1f2 +d7 53b5 +a8 ece8 +e5 5633 +ff 5775 +b3 ed8d +fd 5779 +2e 8e5e +b2 ed8e +67 b6b5 +fc 577a +75 1731 +74 1732 +23 2cad +33 ad85 +63 162f +7d 1771 +32 ad86 +7c 1772 +57 1b35 +98 eb48 +56 1b36 +61 34a3 +7b 35e5 +48 b0e8 +55 1b39 +6e 1c7c +54 1b3a +a7 4e35 +79 35e9 +5c 1b7a +af 4e75 +dd 5b79 +ff f777 +ee 5cfc +d4 5bba +1b 8145 +1 8003 +3 8007 +c8 d8ea +db 7365 +c1 7223 +1f 8155 +5 8013 +7 8017 +cc d8fa +df 7375 +c5 7233 +9 8043 +a3 ee05 +d 8053 +a7 eeb5 +11 8103 +13 8107 +ab eec5 +15 8113 +a0 ee28 +17 8117 +3b 8545 +21 8403 +23 8407 +e8 dcea +fb 7765 +e1 7623 +3f 8555 +25 8413 +f7 dd97 +fd 7771 +e3 762f +27 8417 +ec dcfa +ff 7775 +e5 7633 +29 8443 +2b 8447 +f0 dd2a +e9 7663 +c3 f205 +2d 8453 +ff ddd7 +eb 766f +2f 8457 +f4 dd3a +ed 7673 +c7 f2b5 +31 8503 +33 8507 +cb f2c5 +35 8513 +c0 f228 +37 8517 +5b 9145 +9f 6157 +41 9003 +43 9007 +5f 9155 +45 9013 +47 9017 +49 9043 +4b 9047 +e3 fe05 +4d 9053 +4f 9057 +e7 feb5 +51 9103 +53 9107 +eb fec5 +55 9113 +e0 fe28 +57 9117 +7b 9545 +bf 6557 +61 9403 +63 9407 +7f 9555 +65 9413 +67 9417 +69 9443 +6b 9447 +6d 9453 +0 2888 +6f 9457 +71 9503 +73 9507 +75 9513 +77 9517 +1c 89f8 +15 2331 +cb f8ed +2e 2474 +14 2332 +9d c359 +1e 89fc +4 88ba +17 2335 +cd f8f1 +9c c35a +16 2336 +d3 f92d +1c 2372 +5 2233 +c 88fa +1f 2375 +4f b0dd +35 af9b +d5 f931 +1e 2376 +ed fcf1 +d3 fbaf +bc c75a +36 2736 +35 2739 +34 273a +e6 fc94 +3c 277a +58 9360 +9c 6372 +8c c8fa +85 6233 +41 9221 +9f 6375 +4c 307a +95 63b1 +ac ccfa +a5 6633 +61 9621 +bf 6775 +6c 347a +b7 cd9f +bd 6779 +6b 96c7 +78 9768 +bc 677a +b7 67b5 +7e 35fc +64 34ba +f3 fd2d +d9 fbeb +3c 2772 +9d cb59 +17 2b35 +9c cb5a +16 2b36 +15 2b39 +b4 4f18 +46 b036 +1e 2b76 +1d 2b79 +1c 2b7a +b7 cd97 +a3 662f +bd 6771 +78 9760 +bc 6772 +57 393d +b5 67b1 +4c 387a +40 9a22 +5a 9b64 +9e 6b76 +9d 6b79 +4b 9ac7 +58 9b68 +9c 6b7a +80 e8a8 +d7 5335 +b2 652c +98 63ea +a0 eca8 +f7 5735 +b8 67ea +b9 edeb +f6 5736 +f5 5739 +65 9633 +7f 9775 +7d 9779 +7c 977a +f4 5732 +19 23c9 +a3 6cad +ee 5c7c +d4 5b3a +f9 75e9 +7c 9772 +11 abab +2b aced +5e 9b76 +5d 9b79 +51 312b +9b 6bc7 +a8 6c68 +57 9bb5 +ef d655 +56 9bb6 +1b 814d +1 800b +1f 815d +5 801b +9 804b +a3 ee0d +d 805b +a7 eebd +11 810b +ab eecd +15 811b +da 59c6 +3b 854d +21 840b +3f 855d +de 59d6 +25 841b +40 b8a0 +29 844b +c7 f2bd +31 850b +cb f2cd +35 851b +5f 915d +45 901b +49 904b +e3 fe0d +4d 905b +e7 febd +51 910b +eb fecd +55 911b +bf 655f +7b 954d +61 940b +80 c8a0 +69 944b +71 950b +9c c9f8 +95 6331 +ae 6474 +50 9320 +94 6332 +9e c9fc +84 c8ba +97 6335 +5e 317c +44 303a +52 9324 +96 6336 +67 b497 +74 b538 +be cdfc +a4 ccba +b7 6735 +7e 357c +64 343a +b5 6739 +63 9687 +70 9728 +b4 673a +1c a372 +34 2f98 +5 a233 +1f a375 +b7 4517 +15 a3b1 +2e a4f4 +14 a3b2 +3c a77a +bc cdf8 +b5 6731 +70 9720 +b4 6732 +43 9a87 +ae 6c7c +50 9b28 +94 6b3a +c1 7aa9 +3c a772 +5 aa33 +1f ab75 +8c 4a58 +1e ab76 +1d ab79 +1c ab7a +1b 8165 +1 8023 +71 1f09 +3 8027 +13 8127 +3b 8565 +21 8423 +23 8427 +33 8527 +9f 6177 +5b 9165 +41 9023 +43 9027 +4b 9067 +63 1c8d +51 9123 +53 9127 +63 9427 +6b 9467 +71 9523 +73 9527 +1b 816d +1 802b +9 806b +52 918e +11 812b +da 59e6 +3b 856d +21 842b +31 852b +9f 617f +5b 916d +41 902b +49 906b +51 912b +bf 657f +7b 956d +61 942b +52 918c +96 619e +69 946b +71 952b +1b 81c5 +1 8083 +10 8128 +3 8087 +db 73e5 +c1 72a3 +1f 81d5 +5 8093 +14 8138 +7 8097 +df 73f5 +c5 72b3 +3b 85c5 +21 8483 +30 8528 +23 8487 +fb 77e5 +e1 76a3 +3f 85d5 +25 8493 +fd 77f1 +e3 76af +34 8538 +27 8497 +ff 77f5 +e5 76b3 +5b 91c5 +9f 61d7 +41 9083 +94 613a +50 9128 +43 9087 +5f 91d5 +45 9093 +54 9138 +98 614a +47 9097 +7f 95d5 +65 9493 +74 9538 +b8 654a +67 9497 +4 8232 +1e 8374 +3b 85cd +f8 f562 +21 848b +fc f572 +3f 85dd +25 849b +1b 81e5 +1 80a3 +71 1f89 +3 80a7 +7b 95e5 +bf 65f7 +61 94a3 +59 bb41 +14 330 +d1 dba9 +34 730 +54 1330 +74 1730 +94 4330 +b4 4730 +a2 462e +bc 4770 +f4 5730 +e2 562e +b2 ed84 +fc 5770 +15 331 +35 731 +c0 daa8 +23 62f +3d 771 +95 4331 +b5 4731 +a3 462f +bd 4771 +16 334 +d3 dbad +36 734 +19 a9e9 +56 1334 +96 4334 +b6 4734 +99 e9e9 +d6 5334 +c4 5232 +de 5374 +b9 ede9 +f6 5734 +e4 5632 +fe 5774 +17 335 +34 7b0 +74 17b0 +94 43b0 +b4 47b0 +d4 53b0 +f4 57b0 +35 7b1 +75 17b1 +95 43b1 +b5 47b1 +36 7b4 +76 17b4 +96 43b4 +b6 47b4 +d6 53b4 +f6 57b4 +14 b30 +54 1b30 +82 4a2e +9c 4b70 +15 b31 +55 1b31 +43 1a2f +5d 1b71 +16 b34 +56 1b34 +84 4a32 +9e 4b74 +c4 5a32 +de 5b74 +17 b35 +5 a33 +1f b75 +14 bb0 +b9 4d6b +54 1bb0 +33 870f +f9 5d6b +7d 175b +94 4bb0 +a 2cc +d4 5bb0 +4a 12cc +15 bb1 +a0 4c2a +ba 4d6c +55 1bb1 +e0 5c2a +34 8710 +fa 5d6c +64 161a +7e 175c +95 4bb1 +c8 7262 +b 2cd +d5 5bb1 +4b 12cd +16 bb4 +bb 4d6f +56 1bb4 +35 8713 +fb 5d6f +17 bb5 +a2 4c2e +bc 4d70 +57 1bb5 +e2 5c2e +36 8714 +fc 5d70 +14 2330 +d1 fba9 +34 2730 +94 6330 +b4 6730 +b6 cd96 +a2 662e +bc 6770 +9c c358 +1d 89fb +16 2334 +d3 fbad +bc c758 +3d 8dfb +36 2734 +9d c9fb +96 6334 +84 6232 +40 9220 +9e 6374 +bd cdfb +b6 6734 +a4 6632 +60 9620 +be 6774 +34 27b0 +94 63b0 +b4 67b0 +35 27b1 +bc c7d8 +36 27b4 +96 63b4 +b6 67b4 +14 2b30 +82 6a2e +9c 6b70 +15 2b31 +9c cb58 +16 2b34 +84 6a32 +40 9a20 +9e 6b74 +14 2bb0 +e7 d615 +b9 6d6b +7d 375b +94 6bb0 +a 22cc +9c cbd8 +16 2bb4 +bb 6d6f +9d cbd9 +17 2bb5 +a2 6c2e +bc 6d70 +14 8330 +98 6342 +54 9330 +42 922e +5c 9370 +b8 6742 +74 9730 +62 962e +7c 9770 +15 8331 +35 8731 +23 862f +3d 8771 +99 6343 +1a 29e6 +55 9331 +16 8334 +36 8734 +24 8632 +3e 8774 +9a 6346 +56 9334 +44 9232 +5e 9374 +ba 6746 +76 9734 +64 9632 +7e 9774 +17 8335 +5 8233 +1f 8375 +37 8735 +9b 6347 +57 9335 +32 a52c +18 a3ea +14 83b0 +15 83b1 +16 83b4 +17 83b5 +98 6b42 +54 9b30 +48 30e2 +15 8b31 +99 6b43 +55 9b31 +51 3123 +9a 6b46 +56 9b34 +d0 d10a +4a 30e6 +17 8b35 +9b 6b47 +57 9b35 +32 ad2c +6f 1677 +18 abea +14 8bb0 +15 8bb1 +99 6bc3 +b3 6d05 +55 9bb1 +16 8bb4 +17 8bb5 +14 a330 +34 a730 +c a2da +26 a41c +df 79d7 +15 a331 +35 a731 +5 8b9 +16 a334 +4 a232 +d 8f9 +1e a374 +25 cb9 +36 a734 +24 a632 +2d cf9 +3e a774 +17 a335 +37 a735 +b6 4516 +14 a3b0 +7b 95ed +bf 65ff +61 94ab +cf 7afd +39 d4b +14 ab30 +2 aa2e +1c ab70 +15 ab31 +16 ab34 +4 aa32 +1e ab74 +17 ab35 +b6 4d16 +14 abb0 +1b 8345 +1 8203 +c9 5863 +3 8207 +1f 8355 +cb 586f +5 8213 +cd 5873 +7 8217 +3b 8745 +21 8603 +e9 5c63 +23 8607 +eb 5c6f +3f 8755 +25 8613 +ed 5c73 +27 8617 +9f 6357 +5b 9345 +41 9203 +47 9217 +7b 9745 +bf 6757 +61 9603 +67 9617 +1b 834d +1 820b +1f 835d +5 821b +3f 875d +de 5bd6 +25 861b +9f 635f +5b 934d +41 920b +5f 935d +45 921b +bf 675f +7b 974d +61 960b +7f 975d +65 961b +1b 836d +1 822b +19 a943 +52 938c +96 639e +2b 465 +11 323 +13 327 +1b 367 +18 a9e8 +6f 1475 +55 1333 +99 e349 +0 a8aa +1a a9ec +57 1337 +3e 8f7c +24 8e3a +8 a8ea +5f 1377 +2c 8e7a +75 173b +55 b1b3 +b9 476b +93 e987 +dd 5373 +33 a507 +88 e8ea +df 5377 +a8 ecea +ff 5777 +31 723 +13 b27 +2b c6d +11 b2b +1b b67 +19 b6b +7c 1dd0 +62 1c8e +38 ade8 +75 1733 +99 eb49 +57 1b37 +49 b0e9 +6f 1c7d +55 1b3b +5d 1b7b +b9 4763 +99 4b6b +ab 4ced +a2 e626 +91 4bab +b3 ed87 +fd 5773 +dd 5b7b +d7 5bb7 +e6 f636 +ef 5cfd +d5 5bbb +1b 8945 +1 8803 +3 8807 +1f 8955 +5 8813 +dd 7b71 +c3 7a2f +7 8817 +d 8853 +cb 7a6f +11 8903 +5f 337d +45 323b +13 8907 +15 8913 +17 8917 +19 8943 +4d 327b +1d 8953 +9f 6957 +5b 9945 +41 9803 +43 9807 +5f 9955 +45 9813 +47 9817 +4b 9847 +4d 9853 +4f 9857 +53 9907 +57 9917 +c 8f2 +8b 424f +5d 9953 +10 38a +2a 4cc +b4 4db0 +2f 2475 +1c 89fa +15 2333 +9d c35b +1e 89fe +17 2337 +1f 2377 +35 273b +b5 4db1 +41 9223 +5b 9365 +9f 6377 +56 b396 +7d b579 +61 9623 +7b 9765 +bf 6777 +14 39a +2e 4dc +b8 4dc0 +9d cb5b +17 2b37 +1f 2b77 +1d 2b7b +b9 4dc1 +79 9761 +bd 6773 +41 9a23 +5b 9b65 +9f 6b77 +59 9b69 +9d 6b7b +53 9ba5 +97 6bb7 +af 6cfd +51 9ba9 +95 6bbb +ab 4465 +91 4323 +b1 472b +98 e9e8 +ef 5475 +d5 5333 +9a e9ec +80 e8aa +d7 5337 +ba edec +a0 ecaa +f7 5737 +f5 573b +f2 5d26 +39 876b +5d 9373 +7 80b7 +75 1f99 +5f 9377 +77 1f9d +10 2ba8 +7f 9777 +b1 4723 +ab 4c6d +91 4b2b +b8 ede8 +f5 5733 +19 8b6b +68 3668 +7c 9dd0 +62 9c8e +7d 9773 +5f 9b77 +5d 9b7b +a8 6c6a +57 9bb7 +b3 6d0f +6f 9cfd +55 9bbb +1b 894d +1 880b +1f 895d +5 881b +9 884b +d 885b +a 2244 +11 890b +e 2254 +15 891b +9f 695f +5b 994d +41 980b +5f 995d +45 981b +49 984b +4d 985b +4a 3244 +51 990b +4e 3254 +55 991b +20 48a +3a 5cc +9c c9fa +af 6475 +51 9321 +95 6333 +9e c9fe +53 9325 +97 6337 +75 b539 +be cdfe +73 9725 +b7 6737 +71 9729 +b5 673b +21 48b +f8 7562 +3b 5cd +2f a4f5 +15 a3b3 +24 49a +3e 5dc +bc cdfa +71 9721 +b5 6733 +fc 7572 +25 49b +3f 5dd +1d ab7b +2f acfd +15 abbb +1b 8965 +1 8823 +9f 6977 +5b 9965 +41 9823 +51 9923 +59 9963 +f0 5500 +d6 53be +1b a347 +a 8cc +c8 7862 +2 a206 +b 8cd +70 3702 +1f a357 +e 8dc +cc 7872 +6 a216 +f 8dd +74 3712 +1b 896d +1 882b +9f 697f +5b 996d +41 982b +49 986b +4a 3264 +51 992b +4 89a +2f a457 +1e 9dc +9 2261 +10 8928 +3 8887 +b4 ef10 +1e 815e +1f 89d5 +b 226d +5 8893 +dd 7bf1 +ac 465a +c3 7aaf +d 2271 +14 8938 +7 8897 +1b 236d +1 222b +15 8993 +49 3261 +94 693a +50 9928 +43 9887 +f4 ff10 +5e 915e +4b 326d +5f 99d5 +45 9893 +54 9938 +98 694a +4d 3271 +47 9897 +50 138a +6a 14cc +f4 5db0 +51 138b +6b 14cd +f5 5db1 +54 139a +6e 14dc +f8 5dc0 +55 139b +6f 14dd +f9 5dc1 +5f 99dd +45 989b +15 b33 +2f c75 +60 148a +7a 15cc +61 148b +7b 15cd +64 149a +7e 15dc +65 149b +7f 15dd +82 c204 +3 88a7 +b4 ef30 +1e 817e +c2 d204 +43 98a7 +f4 ff30 +5e 917e +d0 d300 +51 99a3 +5b b347 +4a 18cc +5f b357 +4e 18dc +cb d8ed +2e 474 +14 332 +d3 d92d +1c 372 +eb dced +d1 dbab +34 732 +f3 dd2d +d9 dbeb +3c 772 +ae 4474 +94 4332 +9c 4372 +b4 4732 +cd d8f1 +16 336 +d5 d931 +1e 376 +ed dcf1 +d3 dbaf +36 736 +f5 dd31 +db dbef +3e 776 +1c b72 +6e 1c74 +54 1b32 +79 35e1 +5c 1b72 +9c 4b72 +4a 124e +2a acc6 +f9 75e1 +6e 1cf4 +ed 5651 +54 1bb2 +ae 4cf4 +94 4bb2 +af 4655 +16 bb6 +98 ebc8 +ef 5655 +56 1bb6 +9f 69ff +c0 d208 +5b 99ed +41 98ab +2e 2c74 +14 2b32 +1c 2b72 +ae 6c74 +50 9b20 +94 6b32 +c1 f809 +a 224e +58 9b60 +9c 6b72 +6b b447 +40 188a +5a 19cc +ae 6cf4 +50 9ba0 +94 6bb2 +b6 cd1c +9c cbda +af 6655 +16 2bb6 +2e 8474 +14 8332 +1c 8372 +34 f98 +34 8732 +74 9732 +23 acad +16 8336 +1c 8b72 +6e 9c74 +54 9b32 +79 b5e1 +5c 9b72 +6f b457 +44 189a +5e 19dc +1e 8b76 +56 9b36 +61 b4a3 +7b b5e5 +6e 9cf4 +b2 6d06 +ed d651 +54 9bb2 +af c655 +16 8bb6 +2e a474 +1d 9f9 +14 a332 +1c ab72 +84 4a18 +16 ab36 +ad e651 +2e acf4 +14 abb2 +1b 8b45 +1 8a03 +3 8a07 +1f 8b55 +5 8a13 +7 8a17 +9 8a43 +d 8a53 +9f 6b57 +5b 9b45 +41 9a03 +5f 9b55 +45 9a13 +49 9a43 +4d 9a53 +1b 8b4d +1 8a0b +1f 8b5d +5 8a1b +5b 9b4d +9f 6b5f +41 9a0b +5f 9b5d +45 9a1b +49 9a63 +5c b9d8 +1b 8b6d +1 8a2b +1f 8bd5 +5 8a93 +5f 9bd5 +45 9a93 +e5 fc1b +ff fd5d +1b 8be5 +1 8aa3 +1c 837a +ba ed4e +44 1ab8 +6f b675 +5c 937a +55 1339 +35 adb1 +13 a98d +5d 1379 +a2 e60c +23 acaf +3d adf1 +24 ac90 +6e 167c +97 c11f +f8 5f60 +34 afb2 +4e b0f4 +77 173d +57 b1b5 +76 173e +7a 9746 +25 2cb9 +c4 5098 +56 b1b6 +35 ad91 +65 163b +7f 177d +45 b0b3 +5f b1f5 +34 ad92 +7e 177e +a5 eeb9 +63 1ea7 +2d 2cf9 +cc 50d8 +5e b1f6 +a1 462b +bb 476d +b3 47ad +b2 47ae +a4 ec90 +ee 567c +e5 563b +b5 ed91 +ff 577d +b4 ed92 +fe 577e +9 20e3 +e3 5ea7 +ad 6cf9 +f7 57bd +f6 57be +13 b2d +1a b6e +46 1a3c +4e 1a7c +57 1b3d +56 1b3e +45 1a3b +5f 1b7d +5e 1b7e +9a 4b6e +c6 5abc +1b a145 +e8 de68 +1 a003 +ab 4e6f +1a 21ce +f4 5f92 +ea de6c +3 a007 +1c 21d2 +f6 5f96 +ec de78 +1f a155 +5 a013 +ee de7c +7 a017 +e3 de07 +9 a043 +fc 5fd2 +e7 de17 +d a053 +f8 df68 +eb dec7 +11 a103 +bb 4f6f +fa df6c +e0 de2a +13 a107 +fc df78 +ef ded7 +15 a113 +fe df7c +e4 de3a +17 a117 +f3 df07 +19 a143 +e8 de6a +1b a147 +16 99c +27 a417 +18 9c8 +29 a443 +0 88a +1a 9cc +2b a447 +1c 9d8 +2d a453 +31 a503 +91 e983 +db 536f +35 a513 +95 e993 +df 537f +37 a517 +39 a543 +10 98a +3b a547 +15 2339 +2e 247c +14 233a +26 263c +1a 16e +b0 6f20 +2e 267c +b8 6f60 +97 c99f +9d 6379 +e6 749c +a5 663b +61 9629 +bf 677d +b7 67bd +17 2b3d +16 2b3e +5 2a3b +1f 2b7d +1e 2b7e +f0 d5aa +8e 6a7c +86 6abc +85 6a3b +41 9a29 +9f 6b7d +5a 9b6c +40 9a2a +9e 6b7e +d5 5339 +6 8a1e +11 a38b +2b a4cd +ee 547c +d4 533a +2a a4ce +f6 573e +fa d746 +a5 6cb9 +da 5be6 +21 862b +3b 876d +3a 876e +5d 9379 +6e 967c +7e 977e +63 9ea7 +6a 1644 +2d acf9 +c6 5a3c +2 aa8e +1c abd0 +1a 8b6e +ab c64d +2c 8cf0 +12 8bae +4e 9a7c +5e 9b7e +9b 6bcf +b5 6d11 +57 9bbd +ef d65d +70 9d00 +b4 6d12 +56 9bbe +f5 df11 +1b a14d +1 a00b +f4 5f9a +1f a15d +5 a01b +e3 de0f +fd df51 +9 a04b +fc 5fda +e7 de1f +d a05b +eb decf +11 a10b +ef dedf +15 a11b +f3 df0f +19 a14b +29 a44b +95 6339 +ae 647c +43 9287 +50 9328 +94 633a +38 2de0 +b7 673d +1c a37a +86 6a3c +5 aa3b +1f ab7d +1e ab7e +1b a165 +1 a023 +1a 21ee +f4 5fb2 +eb dee7 +11 a123 +81 4009 +13 a127 +7c 17d2 +3b a565 +10 9a8 +21 a423 +91 4309 +12 9ac +23 a427 +31 a523 +a1 4409 +33 a527 +f5 df31 +1b a16d +1 a02b +f4 5fba +e3 de2f +fd df71 +9 a06b +fc 5ffa +eb deef +11 a12b +da 79e6 +3b a56d +21 a42b +29 a46b +31 a52b +3b a5c5 +21 a483 +3f a5d5 +25 a493 +31 a583 +33 a587 +f7 5535 +dd 53f3 +f5 df91 +1b a1cd +1 a08b +1f a1dd +5 a09b +11 a18b +3b a5cd +21 a48b +3f a5dd +25 a49b +31 a58b +3b a5e5 +21 a4a3 +91 4389 +23 a4a7 +f5 dfb1 +1b a1ed +1 a0ab +3b a5ed +21 a4ab +7 297 +14 338 +47 1297 +54 1338 +b3 e70d +34 adb0 +67 1697 +74 1738 +54 b1b0 +87 4297 +94 4338 +a7 4697 +b4 4738 +c7 5297 +d4 5338 +10 a38a +2a a4cc +15 339 +95 4339 +b5 4739 +bd 4779 +14 3b8 +34 7b8 +54 13b8 +74 17b8 +94 43b8 +b4 47b8 +d4 53b8 +f4 57b8 +f 277 +7 a97 +14 b38 +47 1a97 +54 1b38 +87 4a97 +94 4b38 +8f 4ad7 +9c 4b78 +c7 5a97 +d4 5b38 +cf 5ad7 +dc 5b78 +9d 4b79 +f7 f717 +e6 5c9c +25 a633 +3f a775 +14 bb8 +65 b633 +7f b775 +54 1bb8 +a5 e633 +bf e775 +94 4bb8 +e5 f633 +ff f775 +d4 5bb8 +7 2297 +14 2338 +27 2697 +34 2738 +43 9285 +87 6297 +94 6338 +63 9685 +a7 6697 +b4 6738 +14 23b8 +34 27b8 +94 63b8 +b4 67b8 +7 2a97 +14 2b38 +87 6a97 +43 9a85 +94 6b38 +4b 9ac5 +8f 6ad7 +9c 6b78 +14 2bb8 +94 6bb8 +cd 58f3 +7 8297 +14 8338 +47 9297 +98 634a +54 9338 +b8 674a +67 9697 +74 9738 +ce 58f4 +15 8339 +ee 5cf4 +d4 5bb2 +35 8739 +dc 5bf2 +f6 5d34 +3d 8779 +3a 2dee +b9 674b +75 9739 +14 83b8 +98 6b4a +47 9a97 +54 9b38 +81 62a3 +9b 63e5 +48 30ea +15 8b39 +99 6b4b +55 9b39 +b2 6d0c +98 6bca +54 9bb8 +cd 78f3 +7 a297 +14 a338 +ed 7cf3 +27 a697 +34 a738 +df 79df +ce 78f4 +15 a339 +ee 7cf4 +d4 7bb2 +35 a739 +1b a345 +1 a203 +c9 7863 +3 a207 +cb 786f +1f a355 +5 a213 +cd 7873 +7 a217 +9 a243 +b a247 +3b a745 +10 b88 +21 a603 +e9 7c63 +12 b8c +23 a607 +eb 7c6f +3f a755 +14 b98 +25 a613 +ed 7c73 +16 b9c +27 a617 +1a bcc +0 a8a +2b a647 +bc 4578 +af 44d7 +7 aa97 +14 ab38 +2d 2ef3 +cd d059 +47 3035 +f aad7 +1c ab78 +35 2f33 +4f 3075 +15 ab39 +cd d0d9 +47 30b5 +ad 44db +49 3ae1 +ab 44e7 +a9 44eb +1b a34d +1 a20b +1f a35d +5 a21b +c2 7806 +9 a24b +da 7bc6 +3b a74d +21 a60b +de 7bd6 +3f a75d +25 a61b +e2 7c06 +29 a64b +1b a365 +1 a223 +3b a765 +10 ba8 +21 a623 +9c 4978 +8f 48d7 +9e e356 +8d 48db +9a e366 +89 48eb +1b a36d +1 a22b +da 7be6 +3b a76d +21 a62b +2d a459 +cc 78d2 +1b a3c5 +1 a283 +e9 7ce3 +30 a728 +23 a687 +fc 5578 +ef 54d7 +eb 54e7 +e9 54eb +1b a3cd +1 a28b +3b a7cd +21 a68b +9d 6951 +83 680f +de f356 +cd 58db +da f366 +1d 83d1 +3 828f +c9 58eb +2b 46d +11 32b +6f 147d +55 133b +77 173f +c5 5099 +57 b1b7 +35 ad93 +7f 177f +cd 50d9 +5f b1f7 +bb 476f +b3 47af +b5 ed93 +ff 577f +2d c71 +13 b2f +57 1b3f +5f 1b7f +7c 175a +ad 4cf1 +93 4baf +df 5b7f +f1 5d01 +d7 5bbf +1b a945 +1 a803 +3 a807 +1f a955 +5 a813 +7 a817 +9 a843 +42 928c +86 629e +d a853 +11 a903 +13 a907 +15 a913 +17 a917 +1b a947 +7d 9771 +63 962f +1d a953 +7f 977d +65 963b +2f 247d +15 233b +d4 fbb8 +37 273f +dc fbf8 +3f 277f +c4 7290 +2e 4de +b8 4dc2 +7b 976d +61 962b +bf 677f +11 3ab +2b 4ed +17 2b3f +1f 2b7f +c0 72a0 +2a 4ee +5b 9b6d +41 9a2b +9f 6b7f +b1 6d01 +53 9bad +97 6bbf +ab 446d +91 432b +c2 d20e +dc d350 +5d 99f3 +b3 472f +fe d754 +e4 d612 +7f 9df7 +ef 547d +d5 533b +3b 876f +33 87af +a1 6e01 +b 4f +7f 977f +5f 9b7f +b5 6d13 +71 9d01 +57 9bbf +1b a94d +1 a80b +1f a95d +5 a81b +9 a84b +d a85b +4e 1256 +11 a90b +15 a91b +19 a94b +af 647d +51 9329 +95 633b +73 972d +38 2de2 +b7 673f +d4 7390 +3e 5de +2f a4fd +15 a3bb +21 4ab +3b 5ed +d0 73a0 +3a 5ee +1f ab7f +1b a965 +1 a823 +11 a923 +19 a963 +52 93ac +b0 6500 +96 63be +e 8de +2 a226 +b 8ed +70 3722 +89 424b +a 8ee +1b a96d +1 a82b +1e 9de +99 434b +1a 9ee +4d 1273 +10 a928 +3 a887 +1e a15e +3d f51 +23 e0f +4f 127f +1f a9d5 +5 a893 +3f f5d +25 e1b +14 a938 +7 a897 +27 e1f +5b 136f +11 a983 +4b 104d +31 f0b +5d 1373 +13 a987 +4d 1051 +33 f0f +5f 137f +15 a993 +4f 105d +35 f1b +51 13ab +6b 14ed +6a 14ee +1b a9cd +1 a88b +1f a9dd +5 a89b +4e 12d6 +11 a98b +7e 15de +fe 7d5c +e4 7c1a +0 aa2 +1a be4 +61 14ab +7b 15ed +7a 15ee +82 e204 +3 a8a7 +f8 df42 +1e a17e +90 e300 +11 a9a3 +4b 106d +31 f2b +4e 18de +1b a9ed +80 e208 +1 a8ab +5e 19de +2e 47c +14 33a +b2 6d0e +ed d659 +6e 9cfc +54 9bba +67 3635 +ae 447c +94 433a +ee dcfc +d4 dbba +e7 7635 +b4 473a +27 a6b5 +bc 477a +1c b7a +7 aab5 +9c 4b7a +2e cfc +3f a777 +ad 4659 +14 bba +6e 1cfc +7f b777 +ed 5659 +54 1bba +a7 4eb5 +ae 4cfc +bf e777 +94 4bba +2e 847c +14 833a +b2 ed0e +67 b635 +34 873a +3c 877a +6e 947c +54 933a +74 973a +1b ab45 +1 aa03 +3 aa07 +1f ab55 +5 aa13 +7 aa17 +9 aa43 +71 1581 +d aa53 +1c 8b7a +4f 1077 +9b 63e7 +79 b5e9 +2e 8cfc +ad c659 +27 2635 +14 8bba +89 e0c9 +47 10b7 +af 44df +d1 db09 +4b 3ae5 +2e a47c +14 a33a +34 a73a +ab 44ef +1b ab4d +1 aa0b +1f ab5d +5 aa1b +9 aa4b +87 489f +6c 145a +9d 49f1 +83 48af +1b ab65 +1 aa23 +9 aa63 +8f 48df +1b ab6d +1 aa2b +1f abd5 +5 aa93 +eb 54ef +1b abcd +1 aa8b +c7 589f +17 8395 +dd 59f1 +c3 58af +78 17c0 +79 17c1 +d9 53c1 +f9 57c1 +20 682 +3a 7c4 +60 1682 +7a 17c4 +80 4282 +9a 43c4 +a0 4682 +ba 47c4 +c0 5282 +da 53c4 +e0 5682 +fa 57c4 +41 1283 +5b 13c5 +86 601c +28 8ec8 +c1 5283 +db 53c5 +e1 5683 +fb 57c5 +a6 4cb4 +2d 86f9 +e6 5cb4 +a2 468e +bc 47d0 +25 8c33 +3f 8d75 +43 128f +5d 13d1 +e7 5cb5 +63 168f +7d 17d1 +a3 468f +bd 47d1 +f4 5dba +c3 528f +dd 53d1 +e3 568f +fd 57d1 +4 292 +1e 3d4 +24 692 +3e 7d4 +44 1292 +5e 13d4 +64 1692 +7e 17d4 +84 4292 +9e 43d4 +a4 4692 +be 47d4 +c4 5292 +de 53d4 +e4 5692 +fe 57d4 +b6 4514 +9c 43d2 +1f 8977 +b9 47c9 +b8 47ca +d1 fb21 +3b 8d6f +be 47d6 +bd 47d9 +bc 47da +18 bc0 +58 1bc0 +37 871f +fd 5d7b +98 4bc0 +e 2dc +d8 5bc0 +4e 12dc +19 bc1 +59 1bc1 +e4 5c3a +fe 5d7c +99 4bc1 +cc 7272 +f 2dd +d9 5bc1 +4f 12dd +0 a82 +1a bc4 +40 1a82 +5a 1bc4 +ff 5d7f +80 4a82 +9a 4bc4 +c0 5a82 +21 8609 +da 5bc4 +1 a83 +1b bc5 +41 1a83 +5b 1bc5 +de 53d6 +c1 5a83 +db 5bc5 +f2 5524 +d8 53e2 +c1 52a3 +db 53e5 +da 53e6 +2 a8e +1c bd0 +42 1a8e +5c 1bd0 +82 4a8e +9c 4bd0 +c2 5a8e +dc 5bd0 +3 a8f +da 7b66 +1d bd1 +43 1a8f +5d 1bd1 +83 4a8f +9d 4bd1 +c3 5a8f +dd 5bd1 +4 a92 +1e bd4 +44 1a92 +5e 1bd4 +84 4a92 +9e 4bd4 +25 8619 +c4 5a92 +de 5bd4 +5 a93 +1f bd5 +fe 57d6 +fd 57d9 +fc 57da +7f 9d7f +e1 56a3 +fb 57e5 +fa 57e6 +f9 57e9 +44 9010 +2a 8ece +f8 57ea +20 2682 +3a 27c4 +80 6282 +9a 63c4 +a0 6682 +ba 67c4 +1 2283 +1b 23c5 +d1 730b +eb 744d +52 39ae +a1 6683 +bb 67c5 +74 9f18 +68 34ca +a6 6cb4 +c6 70b4 +25 ac33 +3f ad75 +3 228f +1d 23d1 +76 371e +a7 6cb5 +ed 7459 +9 2e1 +54 39ba +c7 70b5 +83 628f +9d 63d1 +89 42e1 +d4 79ba +4 2292 +1e 23d4 +24 2692 +3e 27d4 +a4 6692 +60 9680 +be 67d4 +5 2293 +1f 23d5 +d5 731b +b 2e5 +ef 745d +56 39be +41 9281 +85 6293 +9f 63d5 +8b 42e5 +d6 79be +4c 30da +9a 4bc6 +2b ace7 +aa e644 +99 4bc9 +2f acf7 +ae e654 +9d 4bd9 +18 2bc0 +79 9d69 +52 9b86 +bd 6d7b +98 6bc0 +e 22dc +0 2a82 +1a 2bc4 +80 6a82 +9a 6bc4 +fc 57d2 +7f 9d77 +1 2a83 +1b 2bc5 +81 6a83 +9b 6bc5 +48 38ca +f9 57e1 +2a 8ec6 +f8 57e2 +2 2a8e +1c 2bd0 +82 6a8e +9c 6bd0 +4 2a92 +1e 2bd4 +5 2a93 +1f 2bd5 +6f bcf7 +ee f654 +dd 5bd9 +41 9a81 +85 6a93 +9f 6bd5 +1 201 +4c 38da +ea f664 +d9 5be9 +0 8282 +1a 83c4 +20 8682 +3a 87c4 +40 9282 +9e 63d6 +5a 93c4 +60 9682 +be 67d6 +7a 97c4 +21 8683 +3b 87c5 +f1 d70b +6b 36e7 +72 9dae +ac 6478 +41 9283 +9f 63d7 +5b 93c5 +43 928f +5d 93d1 +ca 58ee +4 8292 +1e 83d4 +ea 5cee +24 8692 +3e 87d4 +44 9292 +5e 93d4 +64 9692 +7e 97d4 +eb 5cef +25 8693 +3f 87d5 +f5 d71b +6f 36f7 +76 9dbe +65 9693 +7f 97d5 +da 53c6 +fa 57c6 +f9 57c9 +f8 57ca +7b 9d6f +18 8bc0 +19 8bc1 +9d 6bd3 +b7 6d15 +59 9bc1 +0 8a82 +1a 8bc4 +1 8a83 +1b 8bc5 +9f 6bd7 +ac 6c78 +41 9a83 +5b 9bc5 +99 63e1 +b2 6524 +98 63e2 +9a 63e6 +78 b5e8 +bd 67d9 +a1 66a3 +bb 67e5 +67 9e97 +b8 6f4a +74 9f38 +68 34ea +ba 67e6 +24 41a +3e 55c +55 39b1 +b9 67e9 +c6 523c +c9 78eb +3 a28f +1d a3d1 +48 18e0 +c7 523d +9 82e1 +ed f459 +54 b9ba +f8 57c2 +7b 9d67 +3b 874d +21 860b +da 5bc6 +6b bce7 +ea f644 +d9 5bc9 +78 97c0 +bc 67d2 +ad 4c59 +3f ad77 +d0 d920 +57 399d +b9 67e1 +b8 67e2 +3c 558 +53 39ad +40 9a82 +5a 9bc4 +9e 6bd6 +1a 83c6 +36 8514 +1c 83d2 +c1 f229 +1e 83d6 +76 9514 +5c 93d2 +5e 93d6 +ea 7e6e +2d ed9 +b7 6535 +9d 63f3 +59 93e1 +b6 6536 +72 9524 +58 93e2 +9f 63f7 +41 92a3 +5b 93e5 +5a 93e6 +9f e17f +29 ee9 +19 8bc9 +32 8d0c +2b 2645 +18 8bca +36 8d1c +2f 2655 +1c 8bda +5e 9bd6 +9f 6bf7 +41 9aa3 +5b 9be5 +5a 9be6 +5a 93c6 +9f e15f +29 ec9 +36 a514 +1c a3d2 +96 e994 +c6 523e +bb 4547 +19 a3e1 +32 a524 +18 a3e2 +1 a2a3 +1b a3e5 +88 42c8 +1a a3e6 +21 a6a3 +3b a7e5 +5a 9bc6 +36 ad1c +1c abda +1 aaa3 +1b abe5 +e0 5420 +7f b5df +88 4ac8 +1a abe6 +fc 7558 +18 3e0 +38 7e0 +58 13e0 +78 17e0 +37 a59f +98 43e0 +b8 47e0 +77 b59f +d8 53e0 +f8 57e0 +fd 7559 +19 3e1 +39 7e1 +59 13e1 +79 17e1 +99 43e1 +b9 47e1 +fe 755c +0 2a2 +e4 741a +1a 3e4 +20 6a2 +3a 7e4 +40 12a2 +5a 13e4 +60 16a2 +7a 17e4 +80 42a2 +9a 43e4 +a0 46a2 +ba 47e4 +c0 52a2 +da 53e4 +e0 56a2 +fa 57e4 +e5 741b +ff 755d +1 2a3 +1b 3e5 +41 12a3 +5b 13e5 +b0 6f00 +1a 14e +ec 7ed0 +56 111e +f0 7f00 +5a 114e +9c 4150 +82 400e +86 401e +92 410e +96 411e +20 aea8 +dc 5150 +c2 500e +24 aeb8 +c6 501e +30 afa8 +d2 510e +34 afb8 +d6 511e +58 1be0 +77 bd9f +d8 5be0 +fd 7d59 +19 be1 +59 1be1 +d9 5be1 +40 1aa2 +5a 1be4 +e5 7c1b +ff 7d5d +1 aa3 +1b be5 +41 1aa3 +5b 1be5 +18 23e0 +38 27e0 +98 63e0 +b8 67e0 +19 23e1 +39 27e1 +a0 c408 +0 22a2 +1a 23e4 +20 26a2 +3a 27e4 +80 62a2 +9a 63e4 +a0 66a2 +ba 67e4 +a1 c409 +1 22a3 +1b 23e5 +1c 170 +2 2e +5c 1170 +42 102e +87 e09f +e8 7ee0 +52 112e +9c 4170 +82 402e +18 2be0 +7c 35da +98 6be0 +32 5a4 +fc 75da +a0 cc08 +0 2aa2 +1a 2be4 +a1 cc09 +1 2aa3 +1b 2be5 +fc f558 +18 83e0 +b6 6534 +9c 63f2 +58 93e0 +fd f559 +19 83e1 +fe f55c +e4 f41a +0 82a2 +1a 83e4 +20 86a2 +3a 87e4 +40 92a2 +9e 63f6 +5a 93e4 +60 96a2 +be 67f6 +7a 97e4 +e5 f41b +ff f55d +1 82a3 +1b 83e5 +21 86a3 +3b 87e5 +12 18e +5c 11d0 +42 108e +40 9aa2 +9e 6bf6 +5a 9be4 +ba 4546 +18 a3e0 +0 a2a2 +1a a3e4 +20 a6a2 +3a a7e4 +ba 4d46 +18 abe0 +7c b5da +0 aaa2 +1a abe4 +f9 5561 +7e b5de +86 421e +ca 524e +1c 370 +2 22e +1c 3d0 +2 28e +7b 3de7 +e0 7602 +fa 7744 +5c 13d0 +42 128e +9c 43d0 +82 428e +dc 53d0 +c2 528e +d9 f9c9 +3c 2550 +22 240e +2e 8e5c +2a 244e +ef d67d +70 9d20 +b4 6d32 +3a 254e +3e 8ffc +24 8eba +9c 6150 +82 600e +6e 1c5e +4e 90fc +34 8fba +92 610e +7e 1d5e +5e 93fc +44 92ba +bc 6550 +a2 640e +54 93ba +6e 94fc +b2 650e +2f 24d5 +15 2393 +5f 9bdf +ec 5ef2 +12 212e +f4 5f32 +1a 216e +9c 6170 +82 602e +6e 1c7e +8a 606e +92 612e +7e 1d7e +1c 21d0 +f6 5f94 +2 208e +e 8adc +12 218e +3c 25d0 +22 248e +2e 8edc +32 258e +9c 61d0 +82 608e +42 908c +86 609e +92 618e +bc 65d0 +a2 648e +b2 658e +f6 5fb4 +1c 21f0 +2 20ae +12 21ae +e1 fc09 +2a 264e +1d 29f3 +9c 6350 +82 620e +6e 1e5e +4e b8d6 +42 920c +86 621e +4a 924c +8e 625e +31 d8b +3d 2df3 +bc 6750 +a2 660e +16 8996 +1c 2370 +2 222e +1e 89d6 +c1 f829 +a 226e +96 c996 +9c 6370 +82 622e +6e 1e7e +cd f253 +4e b8f6 +9e c9d6 +8a 626e +c4 5818 +56 b936 +1c 23d0 +2 228e +3c 27d0 +22 268e +9c 63d0 +82 628e +bc 67d0 +a2 668e +1c 2950 +2 280e +a 284e +4c b0f0 +32 afae +4d 9259 +12 290e +1a 294e +1c 29d0 +2 288e +4d 92d9 +12 298e +9c 69d0 +82 688e +78 17c2 +d1 d981 +1a 3c6 +f1 dd81 +3a 7c6 +9c e3d8 +5a 13c6 +bc e7d8 +7a 17c6 +d3 d98d +36 514 +1c 3d2 +a6 4cb6 +76 1514 +5c 13d2 +2d 86fb +e6 5cb6 +c1 7229 +d5 d991 +1e 3d6 +e1 7629 +f5 dd91 +3e 7d6 +5e 13d6 +7e 17d6 +2b 4c5 +11 383 +b6 453e +31 78b +35 79b +a8 eec0 +12 810e +ac eed0 +16 811e +b0 ef00 +1a 814e +c8 f2c0 +32 850e +f0 772a +cc f2d0 +36 851e +5c 9150 +42 900e +e0 fe00 +4a 904e +e8 fec0 +52 910e +ec fed0 +56 911e +f0 ff00 +5a 914e +72 950e +b3 4505 +99 43c3 +b7 4515 +9d 43d3 +bb 47c7 +b9 47cb +22 8c2e +3c 8d70 +bd 47db +32 d04 +18 bc2 +72 1d04 +58 1bc2 +b2 4d04 +98 4bc2 +39 8749 +f2 5d04 +d8 5bc2 +1a bc6 +f7 5515 +dd 53d3 +9c ebd8 +5a 1bc6 +ec 5478 +df 53d7 +f3 5525 +d9 53e3 +db 53e7 +36 d14 +1c bd2 +76 1d14 +5c 1bd2 +b6 4d14 +9c 4bd2 +f6 5d14 +3d 8759 +dc 5bd2 +fd 57db +fb 57e7 +f9 57eb +d3 f98d +36 2514 +1c 23d2 +62 9ca4 +e1 d601 +a6 6cb6 +f3 fd8d +3c 27d2 +c6 70b6 +d5 f991 +1e 23d6 +f5 fd91 +3e 27d6 +31 783 +35 793 +2b ccd +e8 7c62 +22 a606 +11 b8b +2f cdd +ec 7c72 +26 a616 +15 b9b +1c 8170 +2 802e +a0 ee20 +a 806e +a8 eee0 +12 812e +b0 ef20 +1a 816e +3c 8570 +22 842e +c8 f2e0 +32 852e +5c 9170 +42 902e +e0 fe20 +4a 906e +e8 fee0 +52 912e +f0 ff20 +5a 916e +72 952e +6b 1ced +62 b626 +51 1bab +b9 47c3 +bd 47d3 +a8 4c68 +9b 4bc7 +bd 6ddb +79 9dc9 +32 2d04 +18 2bc2 +b2 6d04 +54 9bb0 +98 6bc2 +fd 57d3 +1a 2bc6 +56 9bb4 +9a 6bc6 +f9 57e3 +36 2d14 +1c 2bd2 +b6 6d14 +58 9bc0 +9c 6bd2 +1e 2bd6 +2d 86d1 +f3 5d2d +ea f666 +d9 5beb +2b 24e5 +b1 c509 +11 23a3 +12 818e +5c 91d0 +42 908e +56 919e +35 279b +31 27ab +f3 5505 +d9 53c3 +e8 5468 +db 53c7 +fb 57c7 +32 8d04 +18 8bc2 +72 9d04 +b6 6d16 +58 9bc2 +b3 6525 +99 63e3 +76 9d14 +5c 9bd2 +bb 67e7 +74 9f3a +b9 67eb +35 2793 +31 27a3 +52 91ae +2f 2cdd +a8 cc60 +15 2b9b +2b 2ced +11 2bab +f9 57c3 +e8 5c68 +22 860c +db 5bc7 +79 97c1 +bd 67d3 +b9 67e3 +9b 6be7 +2b 2445 +18 89ca +11 2303 +b3 6d2d +99 6beb +cc 587a +6 821e +a 824e +e 825e +3c 8750 +e8 5c6a +53 bba5 +22 860e +57 bbb5 +ec 5c7a +26 861e +46 921e +4a 924e +4e 925e +33 8505 +19 83c3 +28 8468 +1b 83c7 +37 8515 +1d 83d3 +2c 8478 +1f 83d7 +3b 87c7 +3f 87d7 +77 9515 +5d 93d3 +6c 9478 +5f 93d7 +b7 6537 +73 9525 +59 93e3 +5b 93e7 +7f 97d7 +7b 97e7 +1c 8370 +2 822e +a 826e +3c 8770 +22 862e +4a 926e +28 8c68 +1b 8bc7 +33 8d0d +19 8bcb +2c 8c78 +1f 8bd7 +37 8d1d +1d 8bdb +5b 9be7 +73 9d2d +b7 6d3f +59 9beb +c8 58ea +1c 83d0 +2 828e +cc 58fa +6 829e +5c 93d0 +42 928e +b8 c560 +25 249b +3f 25dd +46 929e +73 9505 +b7 6517 +59 93c3 +68 9468 +ac 647a +5b 93c7 +7b 97c7 +37 a515 +1d a3d3 +33 a525 +19 a3e3 +89 42c9 +1b a3e7 +d8 71c8 +a9 46c9 +3b a7e7 +f8 75c8 +68 9c68 +ac 6c7a +5b 9bc7 +37 ad1d +1d abdb +89 4ac9 +1b abe7 +d8 79c8 +33 ad2d +19 abeb +b 2247 +12 890e +ea 7c6c +24 a610 +d0 7b2a +f 2257 +16 891e +5c 9950 +42 980e +46 981e +4b 3247 +52 990e +4f 3257 +56 991e +1c 8970 +2 882e +46 9a1e +1c 8b70 +2 8a2e +5c 9b70 +42 9a2e +25 2c3b +3f 2d7d +f6 df14 +1c a150 +2 a00e +ac 4e7a +fe df54 +e4 de12 +a a04e +ec ded2 +12 a10e +16 a11e +f4 df12 +1a a14e +2a a44e +36 a51e +df 5bf5 +c5 5ab3 +3a a54e +e3 5c25 +c9 5ae3 +f6 df34 +1c a170 +2 a02e +fe df74 +e4 de32 +a a06e +ec def2 +12 a12e +f4 df32 +1a a16e +3c a570 +22 a42e +32 a52e +f6 df94 +1c a1d0 +2 a08e +c6 503c +ac 4efa +12 a18e +32 a58e +f6 553c +dc 53fa +f6 dfb4 +1c a1f0 +2 a0ae +12 a1ae +c8 786a +1c a350 +2 a20e +a a24e +e a25e +e8 7c6a +3c a750 +22 a60e +ec 7c7a +26 a61e +2a a64e +1c a370 +2 a22e +6 894 +a a26e +3c a770 +22 a62e +c8 78ea +1c a3d0 +2 a28e +81 4221 +cc 78fa +6 a29e +6 a81e +d5 7139 +12 a90e +16 a91e +1a a94e +1c a970 +2 a82e +fc 755a +32 524 +18 3e2 +38 7e2 +a7 e495 +72 1524 +58 13e2 +78 17e2 +b2 4524 +98 43e2 +d1 d9a1 +fe 755e +1a 3e6 +f1 dda1 +3a 7e6 +9c e3f8 +5a 13e6 +bc e7f8 +7a 17e6 +fc 7d5a +32 d24 +18 be2 +a7 ec95 +72 1d24 +58 1be2 +b2 4d24 +98 4be2 +f2 5d24 +39 8769 +d8 5be2 +fe 7d5e +1a be6 +9c ebf8 +5a 1be6 +32 2524 +b8 c548 +18 23e2 +24 8e30 +ba c54c +a0 c40a +d1 f9a1 +1a 23e6 +26 8e34 +32 2d24 +b8 cd48 +18 2be2 +b2 6d24 +98 6be2 +ba cd4c +a0 cc0a +1a 2be6 +fc f55a +32 8524 +18 83e2 +fe f55e +1a 83e6 +32 8d24 +fc fd5a +18 8be2 +72 9d24 +b6 6d36 +58 9be2 +fe fd5e +1a 8be6 +32 ad24 +18 abe2 +18 3c8 +b3 e727 +a2 4cac +58 13c8 +f3 f727 +e2 5cac +78 17c8 +b8 47c8 +21 8c2b +3b 8d6d +f8 57c8 +61 9c2b +bf 6d7f +7b 9d6d +19 3c9 +a3 4cad +59 13c9 +e3 5cad +79 17c9 +2b a4e7 +99 43c9 +6b b4e7 +d9 53c9 +1c 3d8 +3c 7d8 +c6 50bc +5c 13d8 +f7 f737 +e6 5cbc +2e a4f6 +9c 43d8 +5 883b +1f 897d +bc 47d8 +fc 57d8 +65 9c3b +7f 9d7d +da 736e +1d 3d9 +fa 776e +3d 7d9 +c7 50bd +5d 13d9 +e7 5cbd +7d 17d9 +2f a4f7 +9d 43d9 +6f b4f7 +dd 53d9 +f0 7722 +33 78d +32 78e +77 179d +76 179e +d7 db95 +2d 67b +73 17ad +72 17ae +b6 451c +9c 43da +f6 dd9c +ef 76d5 +1f 897f +ae 46dc +a1 468b +bb 47cd +be 47de +29 a643 +18 bc8 +69 b643 +58 1bc8 +a9 e643 +2a ace6 +98 4bc8 +e9 f643 +6a bce6 +d8 5bc8 +6a b644 +59 1bc9 +f2 552c +d8 53ea +ee 56dc +ea 56ec +2d a653 +1c bd8 +ad e653 +2e acf6 +9c 4bd8 +ed f653 +6e bcf6 +dc 5bd8 +fe 57de +e1 56ab +fb 57ed +46 9014 +2c 8ed2 +fa 57ee +18 23c8 +a2 6cac +38 27c8 +b8 67c8 +21 ac2b +3b ad6d +39 27c9 +99 63c9 +b9 67c9 +1c 23d8 +a6 6cbc +3c 27d8 +bc 67d8 +25 ac3b +3f ad7d +3d 27d9 +9d 63d9 +16 b9e +56 1b9e +6c 1cf0 +eb 564d +52 1bae +8a 4acc +8e 4adc +81 4a8b +9b 4bcd +b4 4d10 +9a 4bce +85 4a9b +9f 4bdd +9e 4bde +18 2bc8 +98 6bc8 +ce 5adc +1c 2bd8 +9c 6bd8 +c5 5a9b +df 5bdd +de 5bde +d1 5983 +18 83c8 +9c 63da +b6 651c +58 93c8 +d2 5984 +19 83c9 +b7 651d +9d 63db +59 93c9 +d5 5993 +1c 83d8 +5c 93d8 +da f36e +d6 5994 +1d 83d9 +5d 93d9 +73 178d +33 27ad +32 27ae +e1 568b +fb 57cd +fa 57ce +99 63e9 +aa 66ec +a1 66ab +bb 67ed +ba 67ee +55 39b9 +d2 7984 +19 a3c9 +27 e17 +d5 7993 +1c a3d8 +d6 7994 +1d a3d9 +a9 cc63 +16 2b9e +ab 664d +2c 2cf0 +12 2bae +ca 5acc +c1 5a8b +db 5bcd +f4 5d10 +da 5bce +6f 1655 +18 abc8 +31 2f83 +4b 30c5 +8e 6adc +98 c340 +19 89e3 +1c abd8 +35 2f93 +4f 30d5 +41 9a89 +85 6a9b +9f 6bdd +5a 9bcc +40 9a8a +9e 6bde +69 9e6b +32 850c +18 83ca +b6 ed9e +6b b6c5 +36 851c +1c 83da +6f b6d5 +fc f772 +25 869b +3f 87dd +3e 87de +76 951c +5c 93da +b7 653d +9d 63fb +59 93e9 +b6 653e +72 952c +58 93ea +6e 96dc +65 969b +7f 97dd +7e 97de +a 8acc +d8 fb62 +1 8a8b +1b 8bcd +dc fb72 +5 8a9b +1f 8bdd +13 218f +4e 9adc +a8 6c40 +8e 6afe +4a 9aec +5e 9bde +b9 6d41 +41 9aab +9f 6bff +5b 9bed +b6 651e +72 950c +58 93ca +36 a51c +1c a3da +bb 454f +d2 79a4 +19 a3e9 +8e 6ade +4a 9acc +74 9d10 +5a 9bce +e aadc +ac 4c52 +a aaec +18 3e8 +38 7e8 +58 13e8 +78 17e8 +98 43e8 +b8 47e8 +d8 53e8 +f8 57e8 +19 3e9 +39 7e9 +59 13e9 +79 17e9 +99 43e9 +b9 47e9 +29 a663 +18 be8 +69 b663 +58 1be8 +a9 e663 +98 4be8 +e9 f663 +d8 5be8 +6a b664 +59 1be9 +aa e664 +99 4be9 +18 23e8 +38 27e8 +98 63e8 +b8 67e8 +19 23e9 +39 27e9 +18 2be8 +98 6be8 +d1 59a3 +18 83e8 +b6 653c +9c 63fa +58 93e8 +d2 59a4 +19 83e9 +b6 6d3c +9c 6bfa +58 9be8 +19 8be9 +d1 79a3 +ba 454e +18 a3e8 +26 e36 +31 2fa3 +d1 d109 +4b 30e5 +bf 47f5 +a5 46b3 +9d 4973 +3b 87ef +9f 4977 +7e 155e +95 49b3 +97 49b7 +2d aed1 +9f 4bf5 +6e 165e +85 4ab3 +87 4ab7 +a9 6ccb +65 9cb9 +17 8317 +dd 5973 +7b 97ef +cd 5a73 +32 50c +18 3ca +bc 4df0 +a2 4cae +78 17ca +36 51c +1c 3da +3c 7da +c6 50be +7c 17da +ed def3 +13 a12f +e8 7462 +2b 4cd +11 38b +ec 7472 +2f 4dd +15 39b +33 78f +73 17af +b7 451d +9d 43db +bb 47cf +ab 4ec5 +f7 551d +dd 53db +f3 552d +d9 53eb +af 4ed5 +fb 57ef +36 251c +1c 23da +3c 27da +b5 4d11 +9b 4bcf +9f 4bdf +df 5bdf +b0 c722 +37 279f +33 27af +b3 652d +99 63eb +bb 67ef +f5 5d11 +db 5bcf +41 9a8b +5b 9bcd +9f 6bdf +ae 6e7e +6a 9e6c +b5 6d31 +9b 6bef +d2 5986 +33 850d +19 83cb +d6 5996 +37 851d +1d 83db +3f 87df +77 951d +5d 93db +b7 653f +73 952d +59 93eb +7f 97df +35 8d11 +1b 8bcf +1f 8bdf +b9 6d43 +75 9d31 +5b 9bef +b7 651f +73 950d +59 93cb +d6 7996 +37 a51d +1d a3db +d2 79a6 +33 a52d +19 a3eb +3b a7ef +59 9961 +9d 6973 +75 9d11 +5b 9bcf +32 52c +18 3ea +f1 d709 +72 9dac +b6 6dbe +6b 36e5 +38 7ea +78 17ea +b2 452c +98 43ea +f2 ddac +eb 76e5 +b8 47ea +32 852c +18 83ea +b6 edbe +6b b6e5 +32 8d2c +2b 2665 +18 8bea +9d 497b +a6 e436 +95 49bb +e5 56b3 +ff 57f5 +fd 57f9 +2e 8ede +fc 57fa +e3 56af +fd 57f1 +2e 8ed6 +ee f674 +dd 5bf9 +a5 66b3 +61 96a1 +bf 67f5 +6c 34fa +bd 67f9 +a3 66af +bd 67f1 +43 92af +bb 6545 +a1 6403 +5d 93f1 +76 9534 +ba 6546 +5c 93f2 +5e 93f6 +2d ef9 +5e 9bf6 +a1 6c0b +bb 6d4d +5d 9bf9 +51 31ab +bf 4557 +3 a2af +1d a3f1 +36 a534 +1c a3f2 +2 2ae +1c 3f0 +22 6ae +3c 7f0 +42 12ae +5c 13f0 +62 16ae +7c 17f0 +82 42ae +9c 43f0 +a2 46ae +bc 47f0 +e2 56ae +fc 57f0 +3 2af +1d 3f1 +23 6af +3d 7f1 +43 12af +5d 13f1 +63 16af +7d 17f1 +83 42af +9d 43f1 +a3 46af +bd 47f1 +4 2b2 +1e 3f4 +24 6b2 +3e 7f4 +44 12b2 +5e 13f4 +64 16b2 +7e 17f4 +84 42b2 +9e 43f4 +a4 46b2 +be 47f4 +c4 52b2 +de 53f4 +e4 56b2 +fe 57f4 +5 2b3 +1f 3f5 +2 aae +1c bf0 +42 1aae +5c 1bf0 +82 4aae +9c 4bf0 +3 aaf +1d bf1 +43 1aaf +5d 1bf1 +83 4aaf +6c 165a +9d 4bf1 +4 ab2 +1e bf4 +44 1ab2 +5e 1bf4 +5 ab3 +1f bf5 +2 22ae +1c 23f0 +22 26ae +3c 27f0 +82 62ae +9c 63f0 +a2 66ae +bc 67f0 +3 22af +1d 23f1 +23 26af +3d 27f1 +a4 c418 +4 22b2 +1e 23f4 +24 26b2 +3e 27f4 +84 62b2 +40 92a0 +9e 63f4 +60 96a0 +a4 66b2 +be 67f4 +a5 c419 +5 22b3 +1f 23f5 +2 2aae +1c 2bf0 +82 6aae +9c 6bf0 +a4 cc18 +4 2ab2 +1e 2bf4 +2 82ae +1c 83f0 +a0 6402 +ba 6544 +42 92ae +5c 93f0 +3 82af +1d 83f1 +4 82b2 +1e 83f4 +24 86b2 +3e 87f4 +44 92b2 +a2 6406 +5e 93f4 +64 96b2 +7e 97f4 +5 82b3 +1f 83f5 +25 86b3 +3f 87f5 +a1 6c03 +bb 6d45 +43 9aaf +5d 9bf1 +51 31a3 +be 4556 +2 a2ae +1c a3f0 +2 aaae +be 4d56 +1c abf0 +b3 4525 +99 43e3 +bb 47e7 +b9 47eb +df 53f7 +ff 57f7 +fd 57fb +2b ced +22 a626 +11 bab +99 ebc9 +57 1bb7 +36 8716 +fc 5d72 +6f 1cfd +66 b636 +55 1bbb +b9 47e3 +b3 4d2d +aa e666 +99 4beb +fd 57f3 +ee f676 +f7 5d3d +dd 5bfb +20 4aa +3a 5ec +b7 cd1d +9d cbdb +17 2bb7 +78 9d60 +bc 6d72 +2f 2cfd +15 2bbb +b7 6d3d +59 9be9 +9d 6bfb +fd f55b +33 8525 +19 83e3 +77 9535 +bb 6547 +5d 93f3 +5f 93f7 +7f 97f7 +33 8d2d +19 8beb +5f 9bf7 +77 9d3d +bb 6d4f +5d 9bfb +2e 6dc +b8 4fc0 +ec 7672 +2f 6dd +b9 4fc1 +37 a535 +1d a3f3 +2a 6ec +2b 6ed +37 ad3d +1d abfb +0 8aa +99 4349 +2b a467 +1a 9ec +e adc +cc 7a72 +f add +a aec +f8 5dca +b aed +f9 5dcb +60 14aa +7a 15ec +6f 16dd +1f 21fd +5 20bb +f9 5fc1 +6a 16ec +6b 16ed +6b b467 +40 18aa +d9 5349 +5a 19ec +4e 1adc +4f 1add +4a 1aec +4b 1aed +d3 d9ad +36 534 +1c 3f2 +f3 ddad +3c 7f2 +76 1534 +5c 13f2 +7c 17f2 +b6 4534 +9c 43f2 +d5 d9b1 +1e 3f6 +f5 ddb1 +3e 7f6 +a0 e408 +5e 13f6 +7e 17f6 +76 1d34 +5c 1bf2 +b6 4d34 +9c 4bf2 +b6 6d34 +58 9be0 +9c 6bf2 +be cd5c +a4 cc1a +1e 2bf6 +36 8534 +1c 83f2 +1e 83f6 +36 8d34 +1c 8bf2 +ba 6d46 +76 9d34 +5c 9bf2 +1e 8bf6 +33 7ad +32 7ae +77 17bd +76 17be +aa 46ec +f5 d711 +ba 6dc6 +76 9db4 +a1 46ab +bb 47ed +ee 56fc +e5 56bb +ff 57fd +fe 57fe +bc ed52 +46 1abc +57 1bbd +ef 5cd7 +36 871c +fc 5d78 +70 1d00 +ef 565d +56 1bbe +26 26bc +1a 1ee +b0 6fa0 +9d 63f9 +ae 66fc +61 96a9 +a5 66bb +bf 67fd +7a 97ec +60 96aa +be 67fe +59 39c9 +af 665d +30 2d00 +16 2bbe +a1 640b +bb 654d +5d 93f9 +b2 670e +6e 96fc +65 96bb +7f 97fd +7e 97fe +a 8aec +1 8aab +1b 8bed +34 8d30 +1a 8bee +92 6b0e +ac 6c50 +4e 9afc +a3 6c0f +bd 6d51 +45 9abb +5f 9bfd +2b e47 +e aafc +bf 45f7 +bd 45fb +eb 5e47 +1c 3f8 +3c 7f8 +5c 13f8 +7c 17f8 +9c 43f8 +bc 47f8 +fc 57f8 +1d 3f9 +3d 7f9 +5d 13f9 +7d 17f9 +9d 43f9 +bd 47f9 +2d a673 +1c bf8 +6d b673 +5c 1bf8 +ad e673 +9c 4bf8 +ed f673 +dc 5bf8 +ae e674 +9d 4bf9 +1c 23f8 +3c 27f8 +9c 63f8 +bc 67f8 +3d 27f9 +1c 2bf8 +9c 6bf8 +d5 59b3 +1c 83f8 +f5 5db3 +3c 87f8 +84 483a +9e 497c +a0 640a +ba 654c +5c 93f8 +d6 59b4 +1d 83f9 +f6 5db4 +3d 87f9 +85 483b +9f 497d +af 46f7 +35 2fb3 +d5 d119 +4f 30f5 +ad 46fb +ae e476 +9d 49fb +a7 4c3d +8d 4afb +33 7af +77 17bf +b3 452d +99 43eb +bb 47ef +f7 553d +dd 53fb +33 a58f +ff 57ff +71 1d01 +57 1bbf +36 871e +67 bcb5 +fc 5d7a +7b 97ed +61 96ab +bf 67ff +25 4bb +3f 5fd +31 2d01 +17 2bbf +6b 9cc7 +78 9d68 +bc 6d7a +d4 73b0 +3e 5fe +d2 59a6 +33 852d +19 83eb +7f 97ff +35 8d31 +1b 8bef +bd 6d53 +79 9d41 +5f 9bff +e1 dca9 +2a 6ee +2f 6fd +b9 4fe1 +e5 dcb9 +2e 6fe +d2 5124 +b8 4fe2 +e a2f6 +17 9bd +af 445d +95 431b +16 9be +5 8bb +84 4218 +16 a336 +1f 9fd +9d 435b +1e 9fe +7 abd +f5 5d9b +9f 4bd7 +ac 4c78 +20 c00 +6 abe +f afd +fd 5ddb +65 14bb +7f 15fd +7e 15fe +6a 16ee +6f 16fd +f9 5fe1 +6e 16fe +f8 5fe2 +56 b336 +45 18bb +c4 5218 +5f 19fd +4f 1afd +68 1c40 +4e 1afe +73 35ad +be 45fe +bf 45ff +f5 d719 +ba 6dce +76 9dbc +6f 36f5 +3c 7fa +7c 17fa +b6 453c +9c 43fa +f6 ddbc +ef 76f5 +bc 47fa +36 d3c +1c bfa +ba edce +6f b6f5 +3c 87fa +9e 497e +36 8d3c +2f 2675 +1c 8bfa +30 da2 +af 46ff +7b 9dc7 +97 49bf +2d aed9 +a0 4c00 +86 4abe +a1 4c01 +87 4abf +cb 7acd +35 d1b +d3 7b0d +3d d5b +5d bb59 +75 1d1b +a6 ec9c +71 1d2b +ae ecdc +94 eb9a +79 1d6b +bd 4d5b +b5 4d9b +fd 5d5b +f a7d +f1 5dab +29 e4b +2d e5b +67 1e17 +a5 ee39 +63 1e27 +6 2a96 +2d 2c79 +cc 5058 +5e b176 +5c b17a +ad ee79 +6b 1e67 +69 1e6b +ab 4e47 +a9 4e4b +ad 4e5b +ed 5e5b +eb 5e67 +2 2006 +e9 5e6b +59 b141 +3f afff +db 734f +5c 39f2 +fb 5fed +e1 5eab +71 1d0b +79 1d4b +35 2d1b +31 2d2b +3d 2d5b +67 9615 +39 2d6b +79 9d49 +bd 6d5b +b1 6dab +ad ee59 +6b 1e47 +69 1e4b +3f 2f5d +25 2e1b +3b 2f6d +21 2e2b +2d 2e5b +29 2e6b +e9 5e4b +a0 4e20 +3f afdf +d7 733f +58 39e2 +a9 6e6b +b1 4d0b +b5 4d1b +f5 5d1b +7 a3d +2b 86cf +f1 5d2b +3b 8d47 +6f 367f +cf fafd +39 8d4b +c8 fa68 +3f 8d57 +d3 fb0d +3d 8d5b +39 2761 +33 8d87 +67 36bf +c0 faa8 +23 262f +3d 2771 +37 8d97 +7f 9d57 +7d 9d5b +79 9d6b +f2 d704 +73 9da7 +a7 4e17 +5c 3972 +9 2063 +e3 5e27 +86 6a96 +42 9a84 +ad 6c79 +fb 5f6d +e1 5e2b +f 285f +51 b101 +37 afbf +2b 8e47 +29 8e4b +2f 8e57 +2d 8e5b +6f 9e57 +6d 9e5b +6b 9e67 +71 9d09 +b5 6d1b +b1 6d2b +79 9d4b +3f ad57 +16 1bc +37 ad97 +b2 e704 +a1 4c89 +33 ada7 +b0 e708 +6e 16f6 +31 adab +6b 9e47 +69 9e4b +2d ae5b +29 ae6b +22 48c +f9 7563 +8 aca +22 c0c +c ada +26 c1c +d adb +27 c1d +10 b0a +2a c4c +19 bcb +2a a646 +33 d0d +1d bdb +2e a656 +37 d1d +4c 1ada +66 1c1c +4d 1adb +67 1c1d +97 eb9d +48 1aea +62 1c2c +49 1aeb +63 1c2d +9f ebdd +85 ea9b +50 1b2a +6a 1c6c +86 ea9c +51 1b2b +6b 1c6d +5d 1bdb +6e b656 +77 1d1d +8b 60cd +76 1d1e +59 1beb +6a b666 +73 1d2d +96 eb9c +61 1c2b +7b 1d6d +af ecdf +7a 1d6e +91 4b0b +ab 4c4d +94 4b1a +ae 4c5c +95 4b1b +af 4c5d +a3 4c8d +b7 e717 +a6 4c9c +a7 4c9d +a1 4c0b +bb 4d4d +6f 1675 +18 abe8 +ba 4d4e +a5 4c1b +bf 4d5d +1c abf8 +be 4d5e +aa e6c6 +b3 4d8d +ae e6d6 +b7 4d9d +6b 16c5 +b6 4d9e +d4 5b1a +ee 5c5c +d5 5b1b +ef 5c5d +25 8611 +d1 5b2b +eb 5c6d +e7 5c9d +e5 5c1b +ff 5d5d +5c bbf8 +fe 5d5e +e1 5c2b +35 8711 +fb 5d6d +34 8712 +fa 5d6e +6f 1cf5 +55 1bb3 +ee f6d6 +f7 5d9d +18 bca +32 d0c +1c bda +36 d1c +5c 1bda +76 1d1c +8a 60cc +a7 ec9d +58 1bea +72 1d2c +af ecdd +95 eb9b +60 1c2a +7a 1d6c +6b 1e6d +1a 834e +4b b8e5 +6a 1e6e +c9 f243 +4a b8e6 +a4 4c1a +be 4d5c +b6 4d9c +ab 4e4d +aa 4e4e +af 4e5d +ae 4e5e +a6 4e9e +e4 5c1a +fe 5d5c +f6 5d9c +86 4a96 +ad 4c79 +ef 5e5d +ee 5e5e +d 20d9 +e7 5e9d +c 20da +e6 5e9e +9 20e9 +e3 5ead +fc 5ff0 +8 20ea +e2 5eae +48 1aca +62 1c0c +49 1acb +63 1c0d +50 1b0a +6a 1c4c +51 1b0b +6b 1c4d +59 1bcb +6a b646 +73 1d0d +87 60bd +72 1d0e +42 90ac +86 60be +61 1c0b +7b 1d4d +7a 1d4e +c 2ada +26 2c1c +d 2adb +27 2c1d +8 2aea +22 2c2c +9 2aeb +23 2c2d +1d 2bdb +37 2d1d +36 2d1e +19 2beb +33 2d2d +6d 9679 +32 2d2e +21 2c2b +3b 2d6d +3a 2d6e +d1 5b0b +eb 5c4d +e3 5c8d +e1 5c0b +fb 5d4d +ea f6c6 +f3 5d8d +a6 6c9c +a7 6c9d +a5 6c1b +61 9c09 +bf 6d5d +ba 6d6e +b7 6d9d +72 9d8c +6b 36c5 +b6 6d9e +58 1bca +72 1d0c +86 60bc +60 1c0a +7a 1d4c +6b 1e4d +4b b8c5 +6a 1e4e +4a b8c6 +1c 2bda +36 2d1c +18 2bea +32 2d2c +20 2c2a +3a 2d6c +27 2e1d +23 2e2d +2b 2e6d +eb 5e4d +ea 5e4e +9 20c9 +e3 5e8d +fc 5fd0 +8 20ca +e2 5e8e +60 9c08 +a4 6c1a +be 6d5c +b6 6d9c +a3 4e27 +ab 6e6d +d fb +a3 6ead +88 4aca +a2 4c0c +89 4acb +a3 4c0d +8c 4ada +a6 4c1c +8d 4adb +a7 4c1d +aa e646 +99 4bcb +b3 4d0d +10 aba8 +67 1635 +b2 4d0e +ae e656 +9d 4bdb +b7 4d1d +6b 1645 +14 abb8 +b6 4d1e +cc 5ada +e6 5c1c +cd 5adb +e7 5c1d +ee f656 +dd 5bdb +f7 5d1d +14 8b1a +2e 8c5c +15 8b1b +2f 8c5d +fd fd73 +26 8c9c +21 8c0b +3b 8d4d +d0 fb00 +3a 8d4e +25 8c1b +3f 8d5d +d4 fb10 +3e 8d5e +2b 26c7 +38 2768 +32 8d8e +2f 26d7 +3c 2778 +36 8d9e +54 9b1a +6e 9c5c +55 9b1b +6f 9c5d +e1 d609 +a6 6cbe +62 9cac +a7 6cbf +63 9cad +65 9c1b +7f 9d5d +7e 9d5e +97 6b9d +7a 9d6e +77 9d9d +6f 36d7 +7c 3778 +76 9d9e +98 4bca +b2 4d0c +9c 4bda +b6 4d1c +dc 5bda +f6 5d1c +20 8c0a +3a 8d4c +24 8c1a +3e 8d5c +2b 8e4d +2f 8e5d +c0 f888 +3d 2551 +23 240f +64 9c1a +7e 9d5c +6f 36d5 +76 9d9c +6 8a96 +2d 8c79 +4f 3a77 +6f 9e5d +6e 9e5e +af 6e7f +6b 9e6d +6a 9e6e +67 9e9d +66 9e9e +7c 9ff0 +da 7144 +c0 7002 +62 9eae +c8 5aca +e2 5c0c +c9 5acb +e3 5c0d +ea f646 +d9 5bcb +f3 5d0d +8c 6ada +48 9ac8 +a6 6c1c +49 9ac9 +8d 6adb +a7 6c1d +9d 6bdb +59 9bc9 +b7 6d1d +72 9d0c +6b 3645 +58 9bca +b6 6d1e +af 6c5f +51 9b0b +6b 9c4d +a7 6c9f +63 9c8d +7a 9d4e +6b 36c7 +78 3768 +72 9d8e +14 ab1a +2e ac5c +36 85be +15 ab1b +2f ac5d +37 85bf +10 ab2a +2a ac6c +a1 e609 +22 acac +25 ac1b +3f ad5d +33 adad +d8 5bca +f2 5d0c +9c 6bda +58 9bc8 +b6 6d1c +d 7b +a3 6e2d +60 9c0a +be 6d5e +7a 9d4c +af 6e5f +6b 9e4d +6a 9e4e +a7 6e9f +63 9e8d +fc 75fa +7c 9fd0 +62 9e8e +20 ac2a +3a ad6c +6f 16f7 +b1 e709 +32 adac +3c d50 +22 c0e +26 c1e +c0 7a00 +2a c4e +7d 95f1 +63 94af +66 1c1e +9f ebdf +6a 1c6e +77 1d1f +7f 1d5f +35 8fbb +4f 90fd +93 610f +c aaf8 +ae 4c5e +a6 4c9e +1d abf9 +bf 4d5f +b3 4d8f +b7 4d9f +24 8612 +3e 8754 +ea 5c6e +5f 1bf5 +45 1ab3 +e6 5c9e +5d bbf9 +ff 5d5f +f3 5daf +2b e4f +2f e5f +67 1e1f +7d 1f71 +63 1e2f +6f 1e5f +4 28b2 +9d 6351 +1e 29f4 +83 620f +6b 1e6f +ab 4e4f +af 4e5f +ef 5e5f +eb 5e6f +d 20db +e7 5e9f +dd 7353 +5e 39f6 +9 20eb +fd 5ff1 +e3 5eaf +7c 1d50 +62 1c0e +6a 1c4e +73 1d0f +7b 1d4f +26 2c1e +3c 2d70 +22 2c2e +2a 2c6e +37 2d1f +33 2d2f +3f 2d5f +50 9b0a +6a 9c4c +ae 6c5e +aa 6c6e +a5 cc19 +1f 2bf5 +5 2ab3 +62 9c8c +a6 6c9e +61 9c0b +7b 9d4d +bf 6d5f +73 9d8d +b7 6d9f +b3 6daf +35 8f19 +7d 1f51 +63 1e0f +6b 1e4f +27 2e1f +3d 2f71 +23 2e2f +2f 2e5f +2b 2e6f +eb 5e4f +d9 7343 +5a 39e6 +ab 6e6f +0 aaa8 +bc 4d50 +a2 4c0e +4 aab8 +a6 4c1e +11 aba9 +b3 4d0f +15 abb9 +b7 4d1f +d1 fb01 +3b 8d4f +d5 fb11 +3f 8d5f +39 2769 +33 8d8f +3d 2779 +37 8d9f +6e 9c5e +87 6a9d +6a 9c6e +6c 3678 +66 9c9e +7f 9d5f +7d 3779 +77 9d9f +f2 d70c +73 9daf +d 205b +e7 5e1f +5e 3976 +fd 5f71 +9 206b +e3 5e2f +2b 8e4f +2f 8e5f +6f 9e5f +6b 9e6f +67 9e9f +fc 5d50 +40 baa8 +e2 5c0e +62 9c0c +48 9aca +a6 6c1e +73 9d0d +59 9bcb +b7 6d1f +6a 9c4e +7b 9d4f +10 8b2a +2a 8c6c +2e ac5e +d6 7396 +fd 7579 +a1 e60b +bb e74d +3c adf0 +22 acae +98 4be0 +37 ad9f +b2 e70c +33 adaf +6b 9e4f +7d 9fd1 +63 9e8f +2b ae6f +4c 1af2 +66 1c34 +4d 1af3 +67 1c35 +28 2cea +57 1b97 +64 1c38 +65 1c39 +d4 5b32 +ee 5c74 +c6 5a96 +27 861d +ed 5c79 +f5 f733 +e4 5cb8 +f6 f734 +e5 5cb9 +e5 5c33 +2c 8678 +ff 5d75 +fe 5d76 +37 871d +d6 5b96 +fd 5d79 +ec f6f2 +f5 5db9 +65 1e39 +14 831a +2e 845c +45 b8b1 +7e 1f7c +64 1e3a +e4 5c32 +fe 5d74 +f4 5db8 +ef 5e75 +ca 706c +b0 6f2a +6 2014 +ed 5e79 +1f 2157 +ec 5e7a +d 20f1 +e7 5eb5 +fe 5ffc +17 2197 +e4 5eba +ac cc58 +c 2af2 +26 2c34 +d 2af3 +ad cc59 +27 2c35 +17 2b97 +24 2c38 +25 2c39 +c4 5018 +56 b136 +bc cd5a +36 2d36 +35 2d39 +3e 2d76 +48 18c2 +c7 521f +5c 397a +a4 6cb8 +60 9c22 +7a 9d64 +be 6d76 +bc cd58 +1c 2bf2 +36 2d34 +27 2c97 +34 2d38 +ac eef8 +6a 1ee6 +ad ce59 +27 2e35 +25 2e39 +2f 2e75 +5f bbdd +45 ba9b +2d 2e79 +af 6e75 +ad 6e79 +11 103 +a7 6eb5 +d7 5b97 +e4 5c38 +e5 5c39 +ec f672 +2f 86dd +f5 5d39 +ab 6cc7 +b8 6d68 +67 9cb5 +b9 6dcb +75 9db9 +e7 5c97 +2e 86dc +f4 5d38 +e5 5e39 +fe 5f7c +17 2117 +e4 5e3a +97 6b97 +53 9b85 +a4 6c38 +a5 6c39 +63 9c87 +69 3661 +70 9d28 +b4 6d3a +14 ab32 +2e ac74 +67 96bd +ab 66cf +6 aa96 +2d ac79 +61 1603 +7b 1745 +24 acb8 +dd d1d9 +57 31b5 +62 1604 +25 acb9 +16 ab96 +3d ad79 +a7 6e35 +a5 6e39 +bc 4f7a +27 aeb5 +7 b7 +a aee +24 c30 +12 b2e +2c c70 +1a bee +34 d30 +4a 1aee +64 1c30 +8a 4aee +a4 4c30 +ca 5aee +e4 5c30 +20 ac82 +6a 166e +3a adc4 +b aef +25 c31 +1b bef +35 d31 +23 c2f +3d d71 +4b 1aef +65 1c31 +8b 4aef +a5 4c31 +a3 4c2f +bd 4d71 +cb 5aef +e5 5c31 +21 ac83 +6b 166f +3b adc5 +27 8615 +d3 5b2f +ed 5c71 +e3 5c2f +37 8715 +fd 5d71 +c af2 +26 c34 +1c bf2 +36 d34 +d af3 +27 c35 +a5 4c33 +bf 4d75 +e3 560d +64 1cb0 +f3 570d +74 1db0 +a4 4cb0 +e4 5cb0 +25 cb1 +35 db1 +65 1cb1 +75 1db1 +74 171a +a5 4cb1 +e5 5cb1 +a5 4611 +26 cb4 +b5 4711 +36 db4 +e5 5611 +66 1cb4 +f5 5711 +76 1db4 +27 cb5 +67 1cb5 +24 e30 +2c e70 +64 1e30 +a4 4e30 +25 e31 +2d e71 +76 1f94 +65 1e31 +a5 4e31 +b 206d +e5 5e31 +21 ae83 +3b afc5 +26 e34 +a6 4e34 +78 35e8 +ee 5e74 +27 e35 +da f146 +64 1eb0 +a4 4eb0 +db f147 +65 1eb1 +a5 4eb1 +66 1eb4 +a6 4eb4 +c 20f0 +e6 5eb4 +67 1eb5 +8a 6aee +a4 6c30 +b 2aef +25 2c31 +1b 2bef +35 2d31 +8b 6aef +a5 6c31 +a3 660d +24 2cb0 +b3 670d +34 2db0 +a4 6cb0 +b4 6db0 +10 238a +2a 24cc +35 2db1 +ac ccd8 +a5 6611 +26 2cb4 +bc cdd8 +b5 6711 +36 2db4 +ad ccd9 +27 2cb5 +24 2e30 +2c 2e70 +e 7e +a4 6e30 +ac 6e70 +25 2e31 +2d 2e71 +f 7f +a5 6e31 +ad 6e71 +ac ce58 +26 2e34 +a6 6e34 +24 2eb0 +e fe +a4 6eb0 +25 2eb1 +f ff +a5 6eb1 +ac ced8 +26 2eb4 +10 102 +a6 6eb4 +ad ced9 +27 2eb5 +b2 6f2e +cc 7070 +23 8c2f +3d 8d71 +a3 c60d +24 8cb0 +b3 c70d +34 8db0 +68 36e8 +25 8cb1 +35 8db1 +69 36e9 +a9 6cc3 +65 9cb1 +a5 c611 +26 8cb4 +b5 c711 +36 8db4 +27 8cb5 +37 8db5 +a8 6e42 +64 9e30 +25 8e31 +a9 6e43 +65 9e31 +27 8e35 +82 6004 +24 8eb0 +a8 6ec2 +c2 7004 +64 9eb0 +83 6005 +25 8eb1 +a9 6ec3 +c3 7005 +65 9eb1 +26 8eb4 +aa 6ec6 +66 9eb4 +27 8eb5 +12 ab2e +2c ac70 +65 96b9 +a9 66cb +1a abee +34 ad30 +1b abef +35 ad31 +23 ac2f +3d ad71 +c aaf2 +26 ac34 +bd 67d1 +a3 668f +1c abf2 +36 ad34 +25 ae31 +1f 175 +5 33 +27 ae35 +7 37 +c7 5017 +25 aeb1 +1f 1f5 +5 b3 +31 d2b +39 d6b +59 bb69 +55 1933 +a8 ec48 +66 1c36 +58 b1e8 +75 1d3b +2c 867a +ff 5d77 +3e 87fc +24 86ba +f7 5db7 +f5 5dbb +2b e67 +29 e6b +72 1f8e +5f 1b75 +45 1a33 +4d 1a73 +a9 ee49 +67 1e37 +ab 4e67 +60 9c88 +a4 6c9a +be 6ddc +a9 4e6b +18 21ca +f2 5f8e +6 2016 +ed 5e7b +ff 5ffd +e5 5ebb +15 2933 +ac cc5a +26 2c36 +2e 2c76 +2c 2c7a +35 2d3b +3d 2d7b +51 99a1 +7e 355e +95 69b3 +6a 9c64 +50 9b22 +ae 6c76 +61 9c23 +7b 9d65 +bf 6d77 +1f 2b75 +5 2a33 +3f 2f7d +25 2e3b +2d 2e7b +b1 4d2b +39 8d6b +b2 c704 +33 8da7 +b0 c708 +2a 26e4 +31 8dab +5d 9973 +e5 d613 +ff d755 +66 9cb6 +7d 9d7b +bb 4f6d +a1 4e2b +df 5b75 +c5 5a33 +ff 5f7d +e5 5e3b +29 8e6b +72 9f8e +4d 9a73 +a3 6c07 +5f 9bf5 +45 9ab3 +6f 9e77 +6d 9e7b +60 9c28 +53 9b87 +be 6d7c +a4 6c3a +1d a973 +94 e310 +15 a9b3 +9c 4b58 +2e ac76 +d9 d961 +46 389c +2c ac7a +5f 3177 +b6 e714 +a5 4c99 +37 adb7 +b4 e718 +72 1706 +35 adbb +41 9a21 +9f 6b75 +85 6a33 +63 9e25 +a7 6e37 +71 15a1 +d aa73 +7d bd79 +56 bb96 +1f abf5 +5 aab3 +2d ae7b +3f affd +25 aebb +26 49c +fd 7573 +3e d74 +24 c32 +44 ba30 +c2 7a24 +2c c72 +4c ba70 +ca 7ae4 +34 d32 +d2 7b24 +3c d72 +7e 1d74 +64 1c32 +be 4d74 +a4 4c32 +ac 4c72 +bc 4d72 +2e c76 +4e ba74 +36 d36 +3e d76 +ae 4c76 +be 4d76 +e6 5c16 +2d 865b +44 bab0 +b3 470f +34 db2 +e3 560f +fd 5751 +7e 1df4 +64 1cb2 +f3 570f +74 1db2 +c3 5887 +d0 5928 +a 82cc +be 4df4 +a4 4cb2 +b4 4db2 +fe 5df4 +e4 5cb2 +f4 5db2 +bf 4755 +a5 4613 +26 cb6 +2f 865f +46 bab4 +b5 4713 +36 db6 +a8 ecc8 +ff 5755 +e5 5613 +66 1cb6 +3e f74 +24 e32 +2c e72 +7e 1f74 +64 1e32 +a6 4e36 +78 35ea +7e 1ff4 +64 1eb2 +be 4ff4 +a4 4eb2 +a 20ee +fe 5ff4 +e4 5eb2 +26 eb6 +3e 2d74 +24 2c32 +2c 2c72 +6f 967d +34 2d32 +60 9c20 +be 6d74 +a4 6c32 +60 9ca0 +be 6df4 +a4 6cb2 +ac ccda +61 9601 +bf 6755 +a5 6613 +26 2cb6 +3e 8d74 +24 8c32 +7e 9d74 +64 9c32 +26 8c36 +66 9c36 +b3 c70f +34 8db2 +68 36ea +e3 d60f +fd d751 +7e 9df4 +64 9cb2 +a5 c613 +bf c755 +26 8cb6 +3e 8f74 +24 8e32 +7e 9f74 +64 9e32 +26 8e36 +3e 8ff4 +82 6006 +24 8eb2 +7e 9ff4 +c2 7006 +64 9eb2 +26 8eb6 +3e ad74 +24 ac32 +34 ad32 +94 4b18 +26 ac36 +8 aea +22 c2c +9 aeb +23 c2d +10 b2a +2a c6c +73 1d8f +19 beb +2a a666 +33 d2d +c8 7ae0 +32 d2e +21 c2b +3b d6d +d0 7b20 +3a d6e +4c 1afa +66 1c3c +4d 1afb +67 1c3d +5d 1bfb +6e b676 +77 1d3d +8b 60ed +a1 4c2b +bb 4d6d +ba 4d6e +d5 5b3b +ef 5c7d +e5 5c3b +ff 5d7d +fe 5d7e +18 bea +32 d2c +20 c2a +3a d6c +5c 1bfa +76 1d3c +8a 60ec +67 1e3d +16 831e +dc 597a +47 b8b5 +66 1e3e +c5 f213 +df f355 +46 b8b6 +a3 4ead +bc 4ff0 +a2 4eae +d 20f9 +e7 5ebd +c 20fa +e6 5ebe +c 2afa +26 2c3c +d 2afb +27 2c3d +14 2b3a +2e 2c7c +15 2b3b +2f 2c7d +1d 2bfb +37 2d3d +36 2d3e +3e 2d7e +51 9b29 +95 6b3b +af 6c7d +60 9c2a +7a 9d6c +be 6d7e +1c 2bfa +36 2d3c +24 2c3a +3e 2d7c +27 2e3d +26 2e3e +1d 3fb +37 53d +2f 2e7d +af 6e7d +11 10b +a7 6ebd +b7 45bd +a1 c609 +22 8cac +d0 fb20 +3a 8d6e +b1 c70b +2b 26e7 +32 8dae +54 9b3a +6e 9c7c +aa 6cce +e5 d619 +66 9cbc +ab 6ccf +67 9cbd +7e 9d7e +bb 6dcf +77 9dbd +d 2079 +e7 5e3d +23 ae8f +3d afd1 +c 207a +e6 5e3e +56 b114 +3c afd2 +20 8c2a +3a 8d6c +b1 c709 +2b 26e5 +32 8dac +2b 8e6d +74 9f90 +2a 8e6e +64 9c3a +7e 9d7c +6f 9e7d +6e 9e7e +ab 6ecf +c5 7011 +67 9ebd +c4 7012 +de 7154 +66 9ebe +14 ab3a +2e ac7c +a7 6e3d +62 9e2c +a6 6e3e +9d 43fb +b7 453d +2e ae7e +25 843b +de 59f6 +3f 857d +40 b000 +26 aebe +37 85bd +27 49f +fe 7576 +17 b97 +24 c38 +1f bd7 +2c c78 +75 1d9b +27 c97 +34 d38 +97 4b97 +a4 4c38 +a7 4c97 +b4 4d38 +af 4cd7 +bc 4d78 +2c a672 +35 d39 +16 b96 +3d d79 +a5 4c39 +ac e672 +b5 4d39 +96 4b96 +bd 4d79 +35 a733 +24 cb8 +34 db8 +75 b733 +64 1cb8 +74 1db8 +b5 e733 +a4 4cb8 +b4 4db8 +64 1e38 +c3 f20d +2d 845b +44 b8b0 +a4 4e38 +17 2115 +e4 5e38 +1f 2155 +5 2013 +ec 5e78 +a5 4e39 +9a e14e +24 eb8 +da f14e +64 1eb8 +a4 4eb8 +17 2195 +e4 5eb8 +24 2cb8 +34 2db8 +35 2db9 +24 2e38 +2c 2e78 +a4 6e38 +ac 6e78 +24 2eb8 +a4 6eb8 +27 8c97 +2d 2671 +34 8d38 +2f 8cd7 +3c 8d78 +25 8c39 +cd da5b +47 3a37 +35 8d39 +16 8b96 +3d 8d79 +a9 6c4b +65 9c39 +2d 26f1 +34 8db8 +25 8cb9 +e7 dc1d +cd dadb +47 3ab7 +35 8db9 +25 8e39 +a9 6e4b +65 9e39 +c2 700c +a8 6eca +64 9eb8 +17 ab97 +24 ac38 +3d 2ff3 +dd d159 +57 3135 +27 ac97 +34 ad38 +6a 9ee6 +25 ac39 +35 ad39 +6b 9ee7 +25 ae39 +1f 17d +5 3b +3c d70 +22 c2e +c0 7a20 +2a c6e +c9 7ae1 +33 d2f +7f 1d7f +93 612f +aa 4c6e +b3 4daf +49 b2c9 +ee 5c7e +2b e6f +74 1f92 +67 1e3f +6f 1e7f +97 c997 +9d 6371 +83 622f +bd 4ff1 +a3 4eaf +ef 5e7f +d 20fb +e7 5ebf +26 2c3e +2e 2c7e +37 2d3f +3f 2d7f +50 9b2a +6a 9c6c +ae 6c7e +73 9dad +b7 6dbf +27 2e3f +2f 2e7f +c1 7001 +63 9ead +a7 6ebf +c0 fa20 +2a 8c6e +b2 c70c +33 8daf +6e 9c7e +f6 d71c +77 9dbf +bd 4f71 +a3 4e2f +d 207b +e7 5e3f +2b 8e6f +74 9f92 +6f 9e7f +c5 7013 +df 7155 +67 9ebf +2e ac7e +b6 e71c +37 adbf +63 9e2d +a7 6e3f +2f ae7f +41 b001 +27 aebf +ff 7577 +50 1902 +cf 525f +3e d7c +24 c3a +44 ba38 +c2 7a2c +2c c7a +4c ba78 +17 abb5 +ac 4c7a +69 1661 +b4 4d3a +63 360f +7d 3751 +27 acb5 +bc 4d7a +3e dfc +bd 4759 +24 cba +e6 5c1e +44 bab8 +be 4dfc +a4 4cba +74 9d98 +6d 36d1 +69 16e1 +b4 4dba +be 4f7c +a4 4e3a +ca faec +2d 2673 +34 8d3a +d2 fb2c +3c 8d7a +3e ad7c +24 ac3a +dd d15b +57 3137 +34 ad3a +5b 914d +41 900b +9f 615f +9b 616f +9f c9d7 +8b 626f +9d 63f1 +83 62af +a7 461d +28 cc0 +b7 471d +38 dc0 +e7 561d +68 1cc0 +f7 571d +78 1dc0 +a8 4cc0 +e8 5cc0 +29 cc1 +39 dc1 +69 1cc1 +79 1dc1 +a9 4cc1 +e9 5cc1 +10 b82 +2a cc4 +20 c82 +3a dc4 +50 1b82 +6a 1cc4 +60 1c82 +7a 1dc4 +90 4b82 +aa 4cc4 +a0 4c82 +ba 4dc4 +d0 5b82 +31 8709 +ea 5cc4 +e0 5c82 +fa 5dc4 +11 b83 +2b cc5 +51 1b83 +6b 1cc5 +a1 4c83 +bb 4dc5 +d1 5b83 +eb 5cc5 +e1 5c83 +28 86c8 +fb 5dc5 +12 b8e +2c cd0 +92 4b8e +ac 4cd0 +a2 4c8e +bc 4dd0 +d2 5b8e +ec 5cd0 +e2 5c8e +fc 5dd0 +ea 7c66 +13 b8f +2d cd1 +23 c8f +fa 7d66 +3d dd1 +93 4b8f +ad 4cd1 +a3 4c8f +bd 4dd1 +d3 5b8f +ed 5cd1 +e3 5c8f +fd 5dd1 +14 b92 +2e cd4 +24 c92 +3e dd4 +94 4b92 +ae 4cd4 +a4 4c92 +be 4dd4 +35 8719 +d4 5b92 +ee 5cd4 +e4 5c92 +fe 5dd4 +15 b93 +2f cd5 +a5 4c93 +bf 4dd5 +e5 5c93 +2c 86d8 +ff 5dd5 +9e e156 +28 ec0 +cd 507b +de f156 +68 1ec0 +a8 4ec0 +e 20fc +e8 5ec0 +9f e157 +29 ec1 +df f157 +69 1ec1 +a9 4ec1 +f 20fd +e9 5ec1 +2a ec4 +6a 1ec4 +aa 4ec4 +10 2100 +ea 5ec4 +2b ec5 +6b 1ec5 +11 2101 +eb 5ec5 +2c ed0 +ac 4ed0 +ea 7e66 +2d ed1 +ad 4ed1 +13 210d +ed 5ed1 +2e ed4 +ae 4ed4 +14 2110 +ee 5ed4 +2f ed5 +15 2111 +ef 5ed5 +ca 70cc +b0 6f8a +b9 e743 +3a ade6 +a8 4cc8 +ba e744 +a0 e602 +3b ade7 +a9 4cc9 +bd e753 +3e adf6 +ac 4cd8 +ba 4dc6 +b0 e702 +b9 4dc9 +6d 16f1 +b8 4dca +be 4dd6 +b4 e712 +bd 4dd9 +71 1701 +bc 4dda +fd f753 +7e bdf6 +ec 5cd8 +fe f754 +7f bdf7 +e4 f612 +ed 5cd9 +f9 f763 +22 868c +e8 5ce8 +fa f764 +23 868d +e0 f622 +e9 5ce9 +fe 5dd6 +f4 f712 +fd 5dd9 +fc 5dda +f0 f722 +33 878d +f9 5de9 +32 878e +f8 5dea +a7 661d +28 2cc0 +b7 671d +38 2dc0 +a8 6cc0 +b8 6dc0 +14 239a +2e 24dc +b9 6dc1 +a8 c460 +15 239b +2f 24dd +10 2b82 +2a 2cc4 +20 2c82 +3a 2dc4 +90 6b82 +aa 6cc4 +a0 6c82 +ba 6dc4 +11 2b83 +2b 2cc5 +91 6b83 +ab 6cc5 +d 2f1 +58 39ca +a1 6c83 +bb 6dc5 +12 2b8e +2c 2cd0 +22 2c8e +3c 2dd0 +92 6b8e +ac 6cd0 +a2 6c8e +bc 6dd0 +14 2b92 +2e 2cd4 +24 2c92 +3e 2dd4 +94 6b92 +50 9b80 +ae 6cd4 +bd 6f73 +79 9f61 +a4 6c92 +60 9c80 +be 6dd4 +95 6b93 +51 9b81 +af 6cd5 +11 301 +5c 39da +b8 4dc8 +bc 4dd8 +aa 4ec6 +c2 500c +a8 4eca +c6 501c +ac 4eda +28 2ec0 +12 10e +a8 6ec0 +29 2ec1 +2a 2ec4 +14 112 +aa 6ec4 +2b 2ec5 +15 113 +ab 6ec5 +2c 2ed0 +16 11e +ac 6ed0 +2d 2ed1 +fc 5dd8 +17 11f +ad 6ed1 +32 878c +f8 5de8 +2e 2ed4 +ae 6ed4 +2f 2ed5 +7f bff7 +ed 5ed9 +ec 5eda +af 6ed5 +a7 c61d +28 8cc0 +b7 c71d +38 8dc0 +6c 36f8 +29 8cc1 +39 8dc1 +6d 36f9 +ad 6cd3 +69 9cc1 +10 8b82 +2a 8cc4 +20 8c82 +3a 8dc4 +11 8b83 +2b 8cc5 +21 8c83 +3b 8dc5 +af 6cd7 +bc 6d78 +51 9b83 +6b 9cc5 +22 8c8e +28 2668 +3c 8dd0 +23 8c8f +29 2669 +fa fd66 +3d 8dd1 +14 8b92 +2e 8cd4 +24 8c92 +2a 266c +3e 8dd4 +25 8c93 +2b 266d +3f 8dd5 +86 6014 +28 8ec0 +c6 7014 +ac 6ed2 +68 9ec0 +87 6015 +29 8ec1 +ad 6ed3 +c7 7015 +69 9ec1 +2a 8ec4 +ae 6ed6 +6a 9ec4 +2b 8ec5 +f9 f743 +7a bde6 +e8 5cc8 +2b 8ee7 +fa f744 +7b bde7 +e0 f602 +e9 5cc9 +fa 5dc6 +f0 f702 +f9 5dc9 +a8 6ce8 +60 9c82 +7a 9dc4 +be 6dd6 +f8 5dc8 +3b 8fe7 +7b bfe7 +e9 5ec9 +e8 5eca +2b aec5 +bc 6dd8 +18 21c2 +f2 5f86 +ad 6ed9 +b7 c73d +38 8de0 +2f aed5 +6 96 +a9 6ee9 +29 8cc9 +ea fc6e +2d 8cd9 +39 8dc9 +31 2703 +38 8dca +fa fd6e +3d 8dd9 +d2 fb8c +35 2713 +3c 8dda +ae 6cf6 +50 9ba2 +e9 d641 +6a 9ce4 +af 6cf7 +51 9ba3 +6b 9ce5 +61 9ca3 +bf 6df7 +e0 d600 +7b 9de5 +71 3723 +78 9dea +31 2701 +38 8dc8 +35 2711 +3c 8dd8 +87 601d +29 8ec9 +42 900c +86 601e +28 8eca +46 901c +2c 8eda +f9 d741 +60 9ca2 +be 6df6 +7a 9de4 +c6 703e +68 9eea +71 3703 +78 9dca +7f 1775 +65 1633 +28 ace8 +41 30a3 +5b 31e5 +21 aca3 +a0 e600 +3b ade5 +41 122b +11 a981 +5b 136d +76 1734 +39 ade9 +c6 701e +68 9eca +b9 e741 +20 aca2 +3a ade4 +8f e2dd +40 122a +10 a980 +5a 136c +2b aee5 +cb 504f +29 aee9 +a7 463d +28 ce0 +ce 72d6 +b7 473d +38 de0 +e7 563d +68 1ce0 +f7 573d +78 1de0 +a8 4ce0 +b8 4de0 +22 8684 +e8 5ce0 +32 8784 +f8 5de0 +29 ce1 +d6 d99e +dc 7378 +cf 72d7 +39 de1 +69 1ce1 +79 1de1 +78 174a +a9 4ce1 +b9 4de1 +23 8685 +e9 5ce1 +33 8785 +f9 5de1 +a9 4641 +f4 7d1a +10 ba2 +2a ce4 +20 ca2 +b9 4741 +3a de4 +e9 5641 +50 1ba2 +6a 1ce4 +60 1ca2 +f9 5741 +7a 1de4 +f5 7d1b +11 ba3 +2b ce5 +51 1ba3 +6b 1ce5 +9e e176 +28 ee0 +de f176 +7 809f +68 1ee0 +47 b09f +a8 4ee0 +9f e177 +29 ee1 +df f177 +69 1ee1 +a9 4ee1 +f4 7f1a +2a ee4 +6a 1ee4 +f5 7f1b +2b ee5 +6b 1ee5 +a8 6ce0 +b8 6de0 +a9 6641 +10 2ba2 +b0 cd08 +2a 2ce4 +11 2ba3 +b1 cd09 +2b 2ce5 +28 2ee0 +12 12e +a8 6ee0 +29 2ee1 +b0 cf08 +2a 2ee4 +b1 cf09 +2b 2ee5 +a7 c63d +28 8ce0 +e7 d63d +ac 6cf2 +68 9ce0 +f7 d73d +bc 6df2 +78 9de0 +29 8ce1 +39 8de1 +ad 6cf3 +69 9ce1 +bd 6df3 +79 9de1 +10 8ba2 +a9 c641 +f4 fd1a +2a 8ce4 +b9 c741 +20 8ca2 +3a 8de4 +af 6edd +f5 fd1b +11 8ba3 +2b 8ce5 +21 8ca3 +a0 c600 +3b 8de5 +86 6034 +28 8ee0 +ac 6ef2 +c6 7034 +68 9ee0 +87 6035 +29 8ee1 +ad 6ef3 +c7 7035 +69 9ee1 +f4 ff1a +2a 8ee4 +ae 6ef6 +6a 9ee4 +f5 ff1b +2b 8ee5 +cb 5047 +29 aee1 +a7 461f +28 cc2 +48 bac0 +b7 471f +38 dc2 +e7 561f +68 1cc2 +f7 571f +78 1dc2 +d4 5938 +c7 5897 +e 82dc +a8 4cc2 +e8 5cc2 +2a cc6 +4a bac4 +3a dc6 +ac ecd8 +6a 1cc6 +bc edd8 +7a 1dc6 +aa 4cc6 +31 870b +ea 5cc6 +c2 7a84 +2c cd2 +4c bad0 +d2 7b84 +3c dd2 +ac 4cd2 +bc 4dd2 +ec 5cd2 +d1 7b29 +2e cd6 +4e bad4 +e1 7c29 +3e dd6 +ae 4cd6 +35 871b +ee 5cd6 +42 1004 +28 ec2 +68 1ec2 +c2 5004 +a8 4ec2 +e 20fe +e8 5ec2 +2a ec6 +46 1014 +2c ed2 +c6 5014 +ac 4ed2 +12 210e +ec 5ed2 +35 d9b +55 bb99 +99 49c3 +2f aedd +9d 49d3 +a8 4cca +61 1601 +ac 4cda +bb 4dc7 +b9 4dcb +bf 4dd7 +bd 4ddb +24 8418 +dd 59d3 +20 8428 +13 8387 +d9 59e3 +ec 5cda +2c 86da +ff 5dd7 +33 878f +f9 5deb +3f fdd +fc 7f72 +25 e9b +a7 661f +63 960d +28 2cc2 +e3 d60d +64 9cb0 +a8 6cc2 +65 9611 +2a 2cc6 +e5 d611 +66 9cb4 +aa 6cc6 +67 961d +2c 2cd2 +77 971d +3c 2dd2 +e7 d61d +68 9cc0 +ac 6cd2 +f7 d71d +78 9dc0 +bc 6dd2 +74 1f38 +67 1e97 +2e 2cd6 +3e 2dd6 +7f 1fdd +65 1e9b +50 9b82 +6a 9cc4 +ae 6cd6 +a3 4c05 +89 4ac3 +a7 4c15 +8d 4ad3 +c3 500d +a9 4ecb +c7 501d +ad 4edb +e7 5c15 +cd 5ad3 +ed 5edb +a7 c61f +28 8cc2 +b7 c71f +38 8dc2 +6c 36fa +e7 d61f +68 9cc2 +6a 9cc6 +d2 fb84 +28 266a +3c 8dd2 +42 9004 +86 6016 +28 8ec2 +c6 7016 +68 9ec2 +b0 6708 +31 2dab +20 8408 +d9 59c3 +e8 5cca +28 86ca +fb 5dc7 +61 3601 +68 9cc8 +ac 6cda +61 9c83 +7b 9dc5 +bf 6dd7 +e7 d695 +b9 6deb +7b 1fcd +61 1e8b +5c b1da +3f 2fdd +b8 cf60 +25 2e9b +e3 5c05 +c9 5ac3 +a7 6c15 +49 9ac1 +8d 6ad3 +a3 6c25 +89 6ae3 +c7 701d +69 9ec9 +ad 6edb +b7 c73f +38 8de2 +c3 702d +a9 6eeb +19 89c3 +9 226b +1d 89d3 +3b 2745 +21 2603 +28 8cca +c2 fa8c +3f 2755 +25 2613 +2c 8cda +3b 8dc7 +32 2704 +39 8dcb +c8 fae8 +2b 266f +3f 8dd7 +d3 fb8d +36 2714 +3d 8ddb +d8 d340 +59 99e3 +e9 d643 +6a 9ce6 +fa d744 +e0 d602 +7b 9de7 +23 8c05 +9 8ac3 +27 8c15 +d 8ad3 +38 8f68 +2b 8ec7 +3c 8f78 +2f 8ed7 +47 901d +2d 8edb +67 9c15 +4d 9ad3 +a7 6c37 +63 9c25 +49 9ae3 +c7 703f +69 9eeb +59 99c3 +61 3603 +7b 3745 +68 9cca +98 e340 +19 a9e3 +39 f6b +2c acda +3f add7 +a7 6c17 +63 9c05 +49 9ac3 +c7 701f +69 9ecb +27 ac15 +d aad3 +23 ac25 +9 aae3 +3c af78 +2f aed7 +47 b01d +2d aedb +2b aee7 +43 b02d +29 aeeb +a7 463f +28 ce2 +ea 5c46 +48 bae0 +b7 473f +38 de2 +e7 563f +b7 ed95 +68 1ce2 +f7 573f +78 1de2 +a8 4ce2 +b8 4de2 +22 8686 +e8 5ce2 +32 8786 +f8 5de2 +a9 4643 +2a ce6 +4a bae4 +b9 4743 +3a de6 +42 1024 +28 ee2 +b7 ef95 +68 1ee2 +c2 5024 +a8 4ee2 +2a ee6 +a7 663f +63 962d +28 2ce2 +e3 d62d +a8 6ce2 +2a 8e4c +f3 d72d +b8 6de2 +65 9631 +b0 cd0a +a9 6643 +2a 2ce6 +a7 c63f +28 8ce2 +e7 d63f +68 9ce2 +f7 d73f +78 9de2 +a9 c643 +2a 8ce6 +86 6036 +42 9024 +28 8ee2 +c6 7036 +68 9ee2 +2a 8ee6 +39 a743 +28 cc8 +38 dc8 +79 b743 +68 1cc8 +78 1dc8 +30 a702 +39 dc9 +70 b702 +79 1dc9 +3d a753 +2c cd8 +3c dd8 +fa 7d6e +34 a712 +3d dd9 +9e e15e +28 ec8 +de f15e +68 1ec8 +3a afe6 +a8 4ec8 +7a bfe6 +e8 5ec8 +2c ed8 +3e aff6 +ac 4ed8 +7e bff6 +ec 5ed8 +e4 7c32 +fe 7d74 +27 c9d +f4 7d32 +2e a6d6 +37 d9d +67 1c9d +73 b727 +e1 5609 +62 1cac +63 1cad +76 1d9e +4d ba79 +6a b6e6 +73 1dad +bb e747 +90 4b8a +aa 4ccc +91 4b8b +a2 e606 +ab 4ccd +bf e757 +94 4b9a +ae 4cdc +a6 e616 +95 4b9b +af 4cdd +a1 4c8b +b2 e706 +bb 4dcd +6f 16f5 +ba 4dce +a5 4c9b +b6 e716 +bf 4ddd +73 1705 +be 4dde +ff f757 +d4 5b9a +ee 5cdc +d5 5b9b +e6 f616 +ef 5cdd +e5 5c9b +f6 f716 +ff 5ddd +3c fd0 +22 e8e +26 e9e +28 2cc8 +38 2dc8 +a8 6cc8 +39 2dc9 +f1 5709 +72 1dac +3c 2dd8 +b4 ef98 +72 1f86 +66 1e9e +d9 f143 +63 1ead +7c 1ff0 +62 1eae +a0 4c8a +ba 4dcc +a4 4c9a +be 4ddc +ab 4ecd +c4 5010 +aa 4ece +af 4edd +ae 4ede +28 2ec8 +a8 6ec8 +29 2ec9 +a9 6ec9 +e4 5c9a +fe 5ddc +2c 2ed8 +ac 6ed8 +2d 2ed9 +15 2119 +ef 5edd +14 211a +ee 5ede +c6 701c +ac 6eda +68 9ec8 +a0 cc20 +27 2c9d +a1 6609 +22 2cac +36 2d9e +33 2dad +b1 670b +6d 96f9 +32 2dae +fb f747 +d0 5b8a +ea 5ccc +d1 5b8b +e2 f606 +eb 5ccd +e1 5c8b +f2 f706 +fb 5dcd +94 6b9a +50 9b88 +ae 6cdc +95 6b9b +51 9b89 +af 6cdd +a5 6c9b +61 9c89 +bf 6ddd +7c 1fd0 +62 1e8e +7f 1755 +65 1613 +28 acc8 +41 3083 +5b 31c5 +b1 6709 +32 2dac +e0 5c8a +fa 5dcc +11 2109 +eb 5ecd +10 210a +ea 5ece +10 8b8a +23 2605 +2a 8ccc +e8 fc62 +11 8b8b +2b 8ccd +27 2615 +14 8b9a +2e 8cdc +ec fc72 +15 8b9b +2f 8cdd +21 8c8b +f8 fd62 +3b 8dcd +d0 fb80 +33 2707 +3a 8dce +fc fd72 +25 8c9b +3f 8ddd +d4 fb90 +37 2717 +3e 8dde +65 9c9b +7f 9ddd +77 3717 +7e 9dde +f9 d74b +73 3727 +7a 9dee +20 8c8a +33 2705 +3a 8dcc +24 8c9a +37 2715 +3e 8ddc +e8 fe62 +2b 8ecd +ec fe72 +2f 8edd +3d 25d1 +23 248f +77 3715 +64 9c9a +7e 9ddc +6f 9edd +14 8992 +0 222a +1a 236c +6e 9ede +c9 7041 +af 6eff +6b 9eed +c8 7042 +6a 9eee +af 6cdf +51 9b8b +6b 9ccd +73 3707 +7a 9dce +67 1637 +10 abaa +a9 e649 +2a acec +21 acab +a0 e608 +3b aded +73 3705 +60 9c8a +be 6dde +7a 9dcc +af 6edf +6b 9ecd +6a 9ece +b9 e749 +20 acaa +77 1737 +3a adec +cd 5053 +2b aeed +39 a763 +28 ce8 +38 de8 +79 b763 +68 1ce8 +78 1de8 +b9 e763 +a8 4ce8 +b8 4de8 +30 a722 +39 de9 +70 b722 +79 1de9 +ba e764 +a0 e622 +a9 4ce9 +b0 e722 +b9 4de9 +9e e17e +28 ee8 +de f17e +68 1ee8 +a8 4ee8 +a9 4ee9 +28 2ce8 +38 2de8 +39 2de9 +28 2ee8 +1f 1d7 +a8 6ee8 +29 2ee9 +31 2721 +38 8de8 +29 8ce9 +39 8de9 +c6 703c +ac 6efa +68 9ee8 +28 cca +48 bac8 +68 1cca +a1 4e83 +bb 4fc5 +c2 7a8c +2c cda +4c bad8 +42 100c +28 eca +46 101c +2c eda +3c dd0 +22 c8e +26 c9e +66 1c9e +77 1d9f +aa 4cce +63 1605 +ae 4cde +bb 4dcf +bf 4ddf +2c 2cda +67 1e9f +7d 1ff1 +63 1eaf +c5 5011 +ab 4ecf +af 4edf +15 211b +ef 5edf +b9 cd63 +26 2c9e +bb 674d +3c 2df0 +a1 660b +22 2cae +b0 cd22 +37 2d9f +b2 670c +33 2daf +ea 5cce +63 3605 +50 9b8a +6a 9ccc +ae 6cde +79 9f6b +61 9c8b +7b 9dcd +bf 6ddf +7d 1fd1 +63 1e8f +d1 fb81 +3b 8dcf +d5 fb91 +3f 8ddf +7f 9ddf +fa d74c +e0 d60a +7b 9def +45 9011 +2b 8ecf +2f 8edf +6f 9edf +d1 f929 +1a 236e +c9 7043 +6b 9eef +63 3607 +6a 9cce +7b 9dcf +6b 9ecf +2f aedf +28 cea +ea 5c4e +48 bae8 +a8 4cea +78 9dc8 +bc 6dda +71 3701 +b8 4dea +42 102c +28 eea +b7 ef9d +68 1eea +c2 502c +a8 4eea +21 2623 +3b 2765 +28 8cea +31 2723 +38 8dea +fd f773 +26 869c +ec 5cf8 +fe f774 +e4 f632 +27 869d +ed 5cf9 +f4 f732 +37 879d +fd 5df9 +36 879e +fc 5dfa +36 879c +fc 5df8 +6 2094 +ed 5ef9 +1f 21d7 +ec 5efa +ad 6ef9 +2f aef5 +cf 505f +2d aef9 +ab 464d +12 bae +2c cf0 +13 baf +2d cf1 +23 caf +a2 460c +3d df1 +ad 4651 +14 bb2 +2e cf4 +a3 460f +bd 4751 +24 cb2 +3e df4 +15 bb3 +2f cf5 +2c ef0 +2d ef1 +2e ef4 +2f ef5 +ad 6651 +b4 cd18 +14 2bb2 +2e 2cf4 +b5 cd19 +15 2bb3 +2f 2cf5 +2c 2ef0 +2d 2ef1 +b4 cf18 +2e 2ef4 +b5 cf19 +2f 2ef5 +a1 c60b +bb c74d +22 8cae +3c 8df0 +a2 c60c +23 8caf +3d 8df1 +71 3729 +ad c651 +14 8bb2 +2e 8cf4 +a3 c60f +bd c751 +24 8cb2 +3e 8df4 +15 8bb3 +2f 8cf5 +77 9d95 +63 362d +25 8cb3 +a4 c610 +3f 8df5 +cf 5057 +2d aef1 +d4 5310 +55 19b3 +99 49e3 +2f aefd +b9 4deb +24 8438 +17 8397 +dd 59f3 +e9 7ec1 +53 110f +37 879f +fd 5dfb +a3 4c25 +89 4ae3 +c3 502d +a9 4eeb +6 2096 +ed 5efb +94 6310 +15 29b3 +49 9ae1 +a7 6c35 +8d 6af3 +1d 2351 +3 220f +c7 703d +69 9ee9 +ad 6efb +ba c744 +a0 c602 +3b 8de7 +b8 c748 +32 2724 +39 8deb +fc d758 +76 3734 +7d 9dfb +ed fc5b +23 8c25 +9 8ae3 +87 603f +43 902d +29 8eeb +ab 6c47 +67 9c35 +4d 9af3 +cb 704f +6d 9efb +82 e20e +9c e350 +1d a9f3 +be e754 +a4 e612 +ad 4cd9 +3f adf7 +52 13ac +27 ac35 +d aaf3 +2f aef7 +47 b03d +2d aefb +af e6dd +60 162a +30 ad80 +7a 176c +40 b0a2 +5a b1e4 +72 17ac +12 23ac +34 8d92 +20 262a +3a 276c +32 27ac +46 18be +c5 521b +df 535d +d7 539d +ec 54f0 +d2 53ae +c2 7aa4 +ab 464f +2c cf2 +ee 5c56 +4c baf0 +ad 4653 +2e cf6 +4e baf4 +bd 4753 +3e df6 +46 1034 +2c ef2 +69 9641 +ad 6653 +b4 cd1a +2e 2cf6 +d2 fba4 +bb c74f +3c 8df2 +70 372a +ad c653 +2e 8cf6 +7c 3770 +76 9d96 +62 362e +8a 6046 +46 9034 +2c 8ef2 +2e 8ef6 +85 621b +41 9209 +6 28be +9f 635d +9a 636e +7a 956e +97 639d +93 63ad +ac 64f0 +92 63ae +33 a727 +a1 4609 +22 cac +23 cad +2a a6e6 +33 dad +b1 470b +32 dae +77 b737 +e5 5619 +66 1cbc +67 1cbd +6e b6f6 +77 1dbd +b1 4709 +32 dac +10 2922 +4b 926d +8f 627f +f5 5719 +76 1dbc +dd f153 +67 1ebd +66 1ebe +a5 6619 +26 2cbc +b5 6719 +36 2dbc +27 2ebd +61 9e83 +7b 9fc5 +bf 6fd7 +cc 7078 +10 8baa +23 2625 +a9 c649 +2a 8cec +11 8bab +2b 8ced +21 8cab +a0 c608 +3b 8ded +d0 fba0 +b9 c74b +33 2727 +3a 8dee +fd d75b +77 3737 +7e 9dfe +b9 c749 +20 8caa +33 2725 +3a 8dec +89 6041 +2b 8eed +44 9030 +88 6042 +2a 8eee +b3 6f0f +cd 7051 +6f 9efd +cc 7052 +6e 9efe +48 b040 +2e aefe +3d a773 +2c cf8 +3c df8 +bd e773 +ac 4cf8 +bc 4df8 +34 a732 +3d df9 +be e774 +a4 e632 +ad 4cf9 +b4 e732 +bd 4df9 +2c ef8 +1f 21d5 +5 2093 +ec 5ef8 +3c 2df8 +b4 efb8 +72 1fa6 +2c 2ef8 +ac 6ef8 +2d 2ef9 +35 2731 +3c 8df8 +2d 8cf9 +3d 8df9 +a1 460b +bb 474d +3c df0 +22 cae +67 1ebf +61 9609 +a5 661b +bf 675d +26 2cbe +41 3001 +27 2ebf +a9 c64b +c0 faa0 +23 2627 +2a 8cee +ba c74c +d1 fba1 +a0 c60a +3b 8def +fe d75c +e4 d61a +7f 9dff +45 9031 +89 6043 +2b 8eef +cd 7053 +6f 9eff +6c 14f0 +52 13ae +49 b041 +2f aeff +2c 24f0 +12 23ae +ed 54f1 +d3 53af +c2 7aac +2c cfa +ee 5c5e +4c baf8 +61 1621 +ac 4cfa +7c 9dd8 +75 3711 +71 1721 +bc 4dfa +46 103c +2c efa +c2 faac +25 2633 +3f 2775 +2c 8cfa +d2 fbac +35 2733 +3c 8dfa +46 903c +8a 604e +2c 8efa +5b 936d +41 922b +9f 637f +ad 64f1 +93 63af +30 f00 +31 f01 +32 f04 +b2 4f04 +18 2140 +f2 5f04 +6 203e +e0 5e02 +fa 5f44 +34 f10 +b4 4f10 +a2 4e0e +bc 4f50 +8 204a +e2 5e0e +fc 5f50 +b5 4f11 +a3 4e0f +bd 4f51 +1 200b +1b 214d +f5 5f11 +9 204b +e3 5e0f +fd 5f51 +36 f14 +b6 4f14 +a4 4e12 +be 4f54 +1c 2150 +2 200e +f6 5f14 +a 204e +e4 5e12 +fe 5f54 +70 1f80 +31 f81 +bc 4ffa +d6 513c +71 1f81 +b4 4f90 +75 1f91 +b5 4f91 +1 208b +1b 21cd +f5 5f91 +36 f94 +39 afe9 +db 514f +b6 4f94 +bc ef78 +af eed7 +7a 1f66 +79 1f69 +28 844a +59 b9e1 +30 2f00 +31 2f01 +32 2f04 +1c 152 +b2 6f04 +34 2f10 +22 2e0e +3c 2f50 +d a279 +c6 7834 +64 b6b0 +1e 15e +b4 6f10 +a2 6e0e +bc 6f50 +35 2f11 +23 2e0f +3d 2f51 +c7 7835 +b0 ed8a +65 b6b1 +1f 15f +b5 6f11 +a3 6e0f +bd 6f51 +36 2f14 +24 2e12 +3e 2f54 +66 b6b4 +b6 6f14 +60 9e00 +a4 6e12 +be 6f54 +27 ae95 +bc 4f5a +d9 5169 +b2 4f86 +dd 5179 +b6 4f96 +30 2f80 +d5 713b +31 2f81 +32 2f84 +1c 1d2 +b2 6f84 +34 2f90 +1e 1de +b4 6f90 +35 2f91 +1f 1df +b5 6f91 +36 2f94 +8f c077 +fd 5f59 +67 be95 +fc 5f5a +b6 6f94 +fa 5f66 +5 2abb +1f 2bfd +12 2104 +f9 5f69 +63 bea5 +f8 5f6a +87 c0b7 +f5 5f99 +f0 5faa +1e 29de +6c 34f2 +8f 6055 +31 8f01 +b5 6f13 +cf 7055 +71 9f01 +bd 6f53 +79 9f41 +b6 6f16 +72 9f04 +ec d4da +66 34b6 +be 6f56 +60 9e02 +7a 9f44 +f4 d51a +6e 34f6 +33 8f05 +21 8e03 +3b 8f45 +61 9e03 +bf 6f57 +7b 9f45 +56 b13c +3c affa +63 9e0f +7d 9f51 +76 9f14 +6a 34c6 +37 8f15 +77 9f15 +52 b10c +38 afca +8e 60d4 +30 8f80 +9d 6179 +32 8f84 +34 8f90 +f2 ff26 +35 8f91 +36 8f94 +fa 5f46 +98 cb60 +5 2a9b +1f 2bdd +8b c067 +f9 5f49 +63 be85 +f8 5f4a +83 c0a7 +f1 5f89 +f0 5f8a +78 9f48 +bc 6f5a +67 9eb7 +b8 6f6a +b2 4f06 +b6 4f16 +1c 2152 +f6 5f16 +39 8f49 +41 9aa1 +9f 6bf5 +85 6ab3 +6e 365e +f2 ff2e +35 8f99 +7d 9f59 +1d 151 +3 f +a9 6ec1 +13 10f +b1 6f01 +1b 14f +3d 551 +c0 d888 +23 40f +c4 d898 +27 41f +c8 d8c8 +c1 7201 +42 38a4 +2b 44f +5d 1151 +43 100f +ed 7ed1 +57 111f +f1 7f01 +5b 114f +f5 7f11 +5f 115f +7d 1551 +63 140f +82 48a4 +6b 144f +9d 4151 +83 400f +87 401f +8b 404f +93 410f +97 411f +9b 414f +bd 4551 +1 a2a9 +a3 440f +dd 5151 +21 aea9 +c3 500f +25 aeb9 +c7 501f +31 afa9 +d3 510f +35 afb9 +d7 511f +fd 5551 +41 b2a9 +e3 540f +ce 705c +70 9f08 +b4 6f1a +1d 171 +3 2f +a9 6ee1 +13 12f +c0 d8a8 +3d 571 +23 42f +5d 1171 +43 102f +e1 7e21 +4b 106f +e9 7ee1 +53 112f +7d 1571 +63 142f +86 e29e +6b 146f +9d 4171 +83 402f +bd 4571 +a3 442f +fd 5571 +e3 542f +39 a5c3 +5d 11d1 +43 108f +7d 15d1 +63 148f +9d 41d1 +83 408f +93 418f +bd 45d1 +a3 448f +dd 51d1 +c3 508f +fd 55d1 +e3 548f +5d 11f1 +43 10af +53 11af +7d 15f1 +63 14af +1d 351 +3 20f +7 21f +ab 4c45 +91 4b03 +b 24f +f 25f +99 4b43 +47 121f +eb 5c45 +d1 5b03 +4b 124f +ef 5c75 +d5 5b33 +4f 125f +d9 5b43 +4 8b2 +9d 4351 +1e 9f4 +83 420f +8 8c2 +87 421f +10 902 +8f 425f +4c 18f2 +cb 524f +30 f20 +f 80df +a5 ee91 +70 1f20 +31 f21 +39 f61 +71 1f21 +79 1f61 +fc 7f5a +32 f24 +a7 ee95 +72 1f24 +a0 4e22 +ba 4f64 +e0 5e22 +fa 5f64 +30 fa0 +b0 4fa0 +26 6bc +4f b257 +31 fa1 +71 1fa1 +fc 7fda +32 fa4 +6d b679 +72 1fa4 +16 13c +1c 3fa +36 53c +3c ffa +56 113c +6c 3efa +86 403c +7c 3ffa +96 413c +3d ffb +57 113d +46 3a3e +6d 3efb +87 403d +75 3f3b +8f 407d +8d 42fb +a7 443d +b5 4f3b +cf 507d +bd 4ffb +d7 513d +c6 7a3e +c5 503b +df 517d +ce 7a7e +30 2f20 +31 2f21 +b8 cf48 +32 2f24 +a0 6e22 +ba 6f64 +30 2fa0 +31 2fa1 +b8 cfc8 +32 2fa4 +8e 6074 +30 8f20 +38 8f60 +ce 7074 +b4 6f32 +70 9f20 +8f 6075 +31 8f21 +39 8f61 +fc ff5a +32 8f24 +20 8e22 +3a 8f64 +b6 6f36 +72 9f24 +60 9e22 +be 6f76 +7a 9f64 +fd ff5b +33 8f25 +21 8e23 +3b 8f65 +36 5bc +5f b157 +56 11bc +86 40bc +96 41bc +a6 44bc +b6 45bc +d6 51bc +87 40bd +a7 44bd +d7 51bd +e0 7c00 +c6 7abe +1d 371 +3 22f +1b a9c5 +1 a883 +4b 126f +9d 4371 +83 422f +93 e985 +dd 5371 +c3 522f +c7 52bd +6 83c +4 83a +1e 97c +7 83d +6c 3672 +f 87d +e a276 +17 93d +5 83b +1f 97d +17 a337 +85 4219 +6 8bc +27 a437 +95 4319 +16 9bc +97 e337 +86 48bc +7 8bd +6c 36f2 +86 4a3c +8f 4a7d +86 4abc +f0 5f00 +16 213c +28 8ee8 +86 603c +23 8e87 +30 8f28 +8e 607c +38 8fe8 +96 613c +f1 5f01 +17 213d +5 203b +f9 5f41 +1f 217d +29 8ee9 +87 603d +85 603b +41 9029 +9f 617d +f0 5f80 +16 21bc +96 61bc +97 61bd +4a 1044 +30 f02 +ce 5054 +b4 4f12 +bc 4f52 +1a 214e +f4 5f12 +fc 5f52 +4a 10c4 +30 f82 +70 1f82 +59 1169 +32 f86 +84 eab0 +6d b65b +4e 10d4 +34 f92 +ce 50d4 +b4 4f92 +5d 1179 +36 f96 +76 1f96 +2b c45 +11 b03 +86 ea94 +6b 1c65 +51 1b23 +5d 1b53 +a6 ee9c +71 1f2b +7d 1f5b +ae eedc +79 1f6b +4a 3044 +30 2f02 +9d 4b53 +af 4cd5 +95 4b93 +4c 3a70 +4e 3054 +34 2f12 +3c 2f52 +7e b7f4 +64 b6b2 +d a27b +c6 7836 +ce 7054 +70 9f00 +b4 6f12 +78 9f40 +bc 6f52 +b9 4f4b +bd 4f5b +dd 5b53 +d9 5b63 +fd 5f5b +12 2106 +f9 5f6b +f1 5fab +8e 6056 +4a 9044 +30 8f02 +ce 7056 +70 9f02 +78 9f42 +4e 9054 +34 8f12 +3c 8f52 +74 9f12 +7c 9f52 +4a 90c4 +8e 60d6 +30 8f82 +4e 90d4 +34 8f92 +6b 1c45 +51 1b03 +59 1b43 +2f 2c55 +15 2b13 +2b 2c65 +11 2b23 +1d 2b53 +5f b3f5 +45 b2b3 +4f 305d +35 2f1b +4b 306d +31 2f2b +3d 2f5b +7f b7fd +65 b6bb +4a b044 +30 af02 +4e b054 +34 af12 +3c af52 +f9 5f4b +4a b0c4 +30 af82 +59 9b41 +9d 6b53 +4e b0d4 +34 af92 +79 9f49 +bd 6f5b +b9 6f6b +cf 70dd +71 9f89 +b5 6f9b +cb 70ed +b1 6fab +af 4c55 +95 4b13 +cb 504d +b1 4f0b +68 3ee0 +7 a09f +cf 505d +b5 4f1b +ef 5c55 +d5 5b13 +eb 5c65 +d1 5b23 +f5 5f1b +19 8b43 +1d 8b53 +2f 8cd5 +15 8b93 +3b 8f47 +39 8f4b +3f 8f57 +3d 8f5b +5d 9b53 +7f 9f57 +7d 9f5b +7b 9f67 +7 81f +f 85f +5d 1951 +43 180f +47 181f +4b 184f +d1 7103 +73 9faf +4f 185f +af 6c55 +51 9b01 +95 6b13 +ab 6c65 +91 6b23 +cf 705d +71 9f09 +b5 6f1b +cb 706d +b1 6f2b +59 9b43 +7b 9f47 +79 9f4b +1d ab53 +2f acd5 +15 ab93 +39 af6b +16 813c +1c 83fa +36 853c +ba 654e +5c 93fa +76 953c +17 813d +6 aa3e +1d 83fb +d6 59b6 +37 853d +45 903b +5f 917d +4e ba7e +bb 654f +5d 93fb +77 953d +16 81bc +36 85bc +9a 61ce +56 91bc +9b 61cf +57 91bd +60 bc00 +46 babe +df 5bf7 +26 863c +8a 624e +46 923c +f1 fda9 +3a 27ee +4e 927c +7 823d +c6 5ab6 +27 863d +ce 5af6 +2f 867d +14 2932 +4f 927d +1d 971 +3 82f +5d 1971 +43 182f +4b 186f +8a 62ce +a4 6410 +46 92bc +7 82bd +ce 7276 +6 883c +f 2275 +16 893c +4 883a +1e 897c +8a 684e +46 983c +85 c219 +6 88bc +95 c319 +f 22f5 +16 89bc +7 88bd +ce 7876 +75 1db9 +6c b6f2 +17 89bd +7 89f +de 7976 +8a 6a4e +46 9a3c +7 8a3d +f 8a7d +1d 2171 +f7 5f35 +3 202f +5d 19d1 +43 188f +47 189f +9d 49d1 +83 488f +dd 59d1 +c3 588f +f0 df00 +16 a13c +1c a3fa +36 a53c +f1 df01 +17 a13d +f9 df41 +5 a03b +1f a17d +7 a23d +c2 520c +5d 19f1 +43 18af +6 a83c +16 a93c +7 aa3d +4a 1064 +30 f22 +87 e015 +38 f62 +a5 ee93 +bf efd5 +70 1f22 +ad eed3 +c7 f015 +78 1f62 +32 f26 +3a f66 +ba 4f66 +4a 10e4 +30 fa2 +70 1fa2 +32 fa6 +6d b67b +4a 3064 +30 2f22 +38 2f62 +8e 6076 +4a 9064 +30 8f22 +ce 7076 +70 9f22 +78 9f62 +4a b064 +30 af22 +38 af62 +8a c066 +f8 5f48 +4e b076 +bc 4f58 +8e c076 +fc 5f58 +42 b0a6 +b0 4f88 +46 b0b6 +b4 4f98 +6a 1e6c +c9 f241 +4a b8e4 +65 1e1b +7f 1f5d +45 b893 +5f b9d5 +7e 1f5e +5e b9d6 +61 1e2b +7b 1f6d +41 b8a3 +2a 844e +c0 f200 +5b b9e5 +af eedf +7a 1f6e +d9 f343 +c8 58c8 +5a b9e6 +30 2f08 +b0 6f08 +aa 4e4c +ae 4e5c +a6 4e9c +34 2f18 +3c 2f58 +c6 783c +64 b6b8 +a1 4e0b +bb 4f4d +ba 4f4e +b4 6f18 +bc 6f58 +a5 4e1b +bf 4f5d +be 4f5e +b3 4f8d +cc 50d0 +b2 4f8e +b7 4f9d +b6 4f9e +b0 6f88 +ee 5e5c +c 20d8 +e6 5e9c +8 20e8 +e2 5eac +e5 5e1b +ff 5f5d +fe 5f5e +b4 6f98 +1d 21d9 +f7 5f9d +1c 21da +f6 5f9e +f7 df15 +1d a151 +3 a00f +8f 605d +31 8f09 +75 9f19 +ce 70dc +b4 6f9a +70 9f88 +74 9f98 +6a 1e4c +4a b8c4 +61 1e0b +7b 1f4d +41 b883 +5b b9c5 +7a 1f4e +5a b9c6 +26 2e1c +22 2e2c +2a 2e6c +33 2f2d +4c 3070 +32 2f2e +3a 2f6e +ea 5e4c +e1 5e0b +fb 5f4d +fa 5f4e +19 21c9 +f3 5f8d +aa 6e6c +a6 6e9c +c fa +a2 6eac +61 9e09 +a5 6e1b +bf 6f5d +a1 6e2b +bb 6f6d +ba 6f6e +b7 6f9d +21 8e0b +3b 8f4d +3a 8f4e +4c 90d0 +32 8f8e +6e 9e5c +c0 7000 +a6 6ebe +62 9eac +65 9e1b +7f 9f5d +7e 9f5e +7a 9f6e +76 9f9e +f5 5f13 +1b 214f +c4 f898 +27 241f +c8 f8c8 +2b 244f +cc f8d8 +2f 245f +d8 f9c8 +3b 254f +3f 8ffd +25 8ebb +9d 6151 +83 600f +43 900d +29 8ecb +87 601f +2d 8efb +47 903d +8b 604f +31 8f0b +4b 904d +8f 605f +3d 8ffb +57 913d +9b 614f +5f 93fd +45 92bb +bd 6551 +a3 640f +49 92cb +63 940d +a7 641f +51 930b +6b 944d +af 645f +55 93bb +6f 94fd +b3 650f +a6 6e1c +c 7a +a2 6e2c +b7 6f1d +ae 6e5e +6a 9e4c +a6 6e9e +62 9e8c +61 9e0b +bf 6f5f +7b 9f4d +7a 9f4e +b7 6f9f +73 9f8d +3a af6e +ed 5ef3 +13 212f +c0 f8a8 +3d 2571 +23 242f +9d 6171 +83 602f +bd 6571 +a3 642f +ba c564 +a0 c422 +27 249f +33 258f +fc ff72 +25 8e9b +3f 8fdd +9d 61d1 +83 608f +43 908d +87 609f +93 618f +bd 65d1 +a3 648f +63 948d +a7 649f +b3 658f +f7 5fb5 +1d 21f1 +3 20af +13 21af +3d 25f1 +23 24af +ab 4ec7 +b8 4f68 +11 2103 +eb 5ec7 +f8 5f68 +30 fa8 +70 1fa8 +f 825f +b0 4fa8 +7 221f +b 224f +51 9b21 +af 6c75 +95 6b33 +f 225f +c0 fa88 +3d 2751 +23 260f +c8 fac8 +2b 264f +23 2e87 +30 2f28 +a3 6e87 +b0 6f28 +ab 6ec7 +67 9eb5 +b8 6f68 +c 28f2 +47 923d +8b 624f +bd 6751 +3e 2df4 +24 2cb2 +a3 660f +67 963d +2c 2cf2 +ab 664f +30 2fa8 +b0 6fa8 +8f 607d +31 8f29 +39 8f69 +1d 2371 +17 8997 +3 222f +1f 89d7 +b 226f +3d 27d1 +23 268f +73 1f2f +d2 f304 +3c 8552 +c1 5889 +53 b9a7 +7f 1f5f +5f b9d7 +bb 4f4f +bf 4f5f +cd 50d1 +b3 4f8f +b7 4f9f +ff 5f5f +fb 5f6f +7b 1f4f +37 2f1f +4d 3071 +33 2f2f +3f 2f5f +67 b6bf +fb 5f4f +bb 6f6f +cd 5051 +b3 4f0f +b7 4f1f +1d 215b +f7 5f1f +3b 8f4f +3f 8f5f +7f 9f5f +7b 9f6f +7 281f +13 290f +1b 294f +47 983d +8b 684f +e2 7c86 +29 a6cb +4f 98fd +93 690f +57 993d +9b 694f +7b 9f4f +73 9f8f +2a 8e6c +1d 2971 +3 282f +9a c964 +80 c822 +7 289f +9d 69d1 +83 688f +43 988d +87 689f +93 698f +1d 29f1 +82 620c +3 28af +23 aea5 +b8 4f6a +1d 2b51 +3 2a0f +b 2a4f +9d 6b51 +83 6a0f +47 9a3d +8b 6a4f +9d 6bd1 +83 6a8f +16 2114 +fd 5f79 +67 beb5 +fc 5f7a +35 2f39 +d4 5318 +55 19bb +66 b436 +a5 6e33 +61 9e21 +bf 6f75 +bd 6f79 +6b 9ec7 +78 9f68 +bc 6f7a +e 20d4 +f5 5f39 +f4 5f3a +7d 9f79 +71 352b +7c 9f7a +1d 8151 +3 800f +db 736d +d5 d993 +c1 722b +68 1e60 +7 801f +df 737d +c5 723b +a1 ee01 +b 804f +dd d9d3 +c9 726b +a9 eec1 +13 810f +ad eed1 +78 1f60 +17 811f +b1 ef01 +1b 814f +3d 8551 +23 840f +f5 dd93 +fb 776d +e1 762b +27 841f +ff 777d +e5 763b +42 b8a4 +c1 f201 +2b 844f +fd ddd3 +e9 766b +ed 767b +c9 f2c1 +33 850f +cd f2d1 +37 851f +5d 9151 +43 900f +47 901f +e1 fe01 +4b 904f +e5 fe11 +4f 905f +e9 fec1 +53 910f +ed fed1 +57 911f +f1 ff01 +5b 914f +f5 ff11 +5f 915f +7d 9551 +63 940f +82 c8a4 +6b 944f +73 950f +b5 6f39 +ce 707c +63 9e87 +70 9f28 +b4 6f3a +25 ae33 +3f af75 +1f 177 +3c af7a +1d 8171 +3 802f +a9 eee1 +13 812f +b1 ef21 +1b 816f +3d 8571 +23 842f +c9 f2e1 +33 852f +5d 9171 +43 902f +e1 fe21 +4b 906f +e9 fee1 +53 912f +f1 ff21 +5b 916f +7d 9571 +63 942f +6b 946f +73 952f +13 818f +3d 85d1 +fa f566 +23 848f +fb 77ed +e1 76ab +5d 91d1 +43 908f +7d 95d1 +63 948f +3d 85f1 +23 84af +53 91af +34 f30 +35 f31 +23 e2f +3d f71 +36 f34 +a4 4e32 +be 4f74 +e4 5e32 +a 206e +fe 5f74 +74 1fb0 +b4 4fb0 +2a 6cc +75 1fb1 +b5 4fb1 +e8 7662 +2b 6cd +6b 16cd +76 1fb4 +34 2f30 +a2 6e2e +bc 6f70 +35 2f31 +a3 6e2f +bd 6f71 +bc cf58 +36 2f34 +a4 6e32 +60 9e20 +be 6f74 +34 2fb0 +d9 716b +1e 1fe +b4 6fb0 +2a 26cc +35 2fb1 +1f 1ff +b5 6fb1 +2b 26cd +bc cfd8 +36 2fb4 +b8 6f42 +74 9f30 +68 34e2 +35 8f31 +b9 6f43 +75 9f31 +63 9e2f +7d 9f71 +71 3523 +ba 6f46 +76 9f34 +f0 d50a +6a 34e6 +37 8f35 +bb 6f47 +77 9f35 +52 b12c +38 afea +35 af31 +ab 6ee5 +15 133 +37 af35 +17 137 +1d 8351 +c9 586b +3 820f +cd 587b +7 821f +b 824f +e9 5c6b +3d 8751 +23 860f +ed 5c7b +27 861f +5d 9351 +43 920f +47 921f +4b 924f +4f 925f +7d 9751 +63 960f +67 961f +1d 8371 +3 822f +5d 9371 +43 922f +4b 926f +2b c65 +11 b23 +6f 1c75 +55 1b33 +b9 4f6b +dd 5b73 +ef 5cf5 +d5 5bb3 +16 2116 +fd 5f7b +1d 21f3 +f7 5fb7 +2f 2c75 +15 2b33 +4f 307d +35 2f3b +7e 375e +51 9ba1 +af 6cf5 +95 6bb3 +61 9e23 +7b 9f65 +bf 6f77 +79 9f69 +bd 6f7b +ab 4c65 +91 4b23 +cb 506d +b1 4f2b +e 20d6 +f5 5f3b +39 8f6b +5d 9b73 +b3 6d07 +6f 9cf5 +55 9bb3 +7f 9f77 +7d 9f7b +1d 8951 +3 880f +db 7b6d +c1 7a2b +7 881f +df 7b7d +c5 7a3b +b 884f +c9 7a6b +f 885f +cd 7a7b +13 890f +17 891f +5d 9951 +43 980f +47 981f +4b 984f +4f 985f +53 990f +57 991f +cf 707d +71 9f29 +b5 6f3b +2f acf5 +15 abb3 +4a 1acc +4b 1acd +20 248a +3a 25cc +49 3269 +5d 99d1 +43 988f +4d 3279 +47 989f +2e 26dc +b8 6fc0 +a8 c660 +2f 26dd +b9 6fc1 +0 288a +1a 29cc +c2 d20c +5d 99f1 +43 98af +a 2acc +e 2adc +4e 1074 +34 f32 +3c f72 +4e 10f4 +34 fb2 +74 1fb2 +ce 50f4 +b4 4fb2 +36 fb6 +b8 efc8 +76 1fb6 +4e 3074 +34 2f32 +3c 2f72 +78 9f60 +bc 6f72 +4e 9074 +34 8f32 +74 9f32 +7c 9f72 +3c af72 +1d 8b51 +3 8a0f +7 8a1f +5d 9b51 +43 9a0f +47 9a1f +1d 8b71 +3 8a2f +5d 9b71 +43 9a2f +8b 6acd +a4 6c10 +46 9abc +8a 6ace +81 408b +9b 41cd +66 1e3c +c5 f211 +2f 845f +46 b8b4 +77 1f3d +26 841e +57 b9b5 +8b 62ed +76 1f3e +d5 f313 +ef f455 +c4 5898 +56 b9b6 +65 1e3b +7f 1f7d +45 b8b3 +c4 f210 +2e 845e +5f b9f5 +7e 1f7e +dd f353 +cc 58d8 +5e b9f6 +a2 4eac +c 20f8 +e6 5ebc +26 2e3c +2e 2e7c +10 10a +a6 6ebc +61 9e29 +a5 6e3b +bf 6f7d +21 8e2b +3b 8f6d +3a 8f6e +6e 9e7c +7c 3570 +62 342e +aa 6ece +c4 7010 +66 9ebc +65 9e3b +7f 9f7d +73 352f +7e 9f7e +68 3e60 +7 a01f +e5 de13 +ff df55 +b a04f +fe 5fde +ed ded3 +13 a10f +78 3f60 +17 a11f +88 4260 +27 a41f +2b a44f +a6 6e3c +3d a571 +23 a42f +33 a52f +f7 df95 +1d a1d1 +3 a08f +13 a18f +f7 dfb5 +1d a1f1 +3 a0af +13 a1af +3d a5f1 +23 a4af +a7 4e97 +b4 4f38 +d 20d3 +e7 5e97 +f4 5f38 +ef 5ed7 +15 2113 +fc 5f78 +b5 4f39 +34 fb8 +74 1fb8 +b4 4fb8 +27 2e97 +34 2f38 +a7 6e97 +63 9e85 +b4 6f38 +af 6ed7 +6b 9ec5 +bc 6f78 +34 2fb8 +b4 6fb8 +35 8f39 +b9 6f4b +75 9f39 +35 af39 +ab 6eed +15 13b +c9 786b +1d a351 +3 a20f +cd 787b +7 a21f +b a24f +f a25f +e9 7c6b +3d a751 +23 a60f +ed 7c7b +27 a61f +2b a64f +1d a371 +3 a22f +7 895 +b a26f +3d a771 +23 a62f +15 a31b +2f a45d +ce 78d6 +77 1f3f +d6 f314 +c5 5899 +57 b9b7 +7f 1f7f +de f354 +cd 58d9 +c4 f212 +5f b9f7 +cd 50f1 +b3 4faf +ff 5f7f +37 2f3f +3f 2f7f +61 9e2b +7b 9f6d +bf 6f7f +d1 7101 +73 9fad +b7 6fbf +3b 8f6f +7f 9f7f +1d a951 +3 a80f +7 a81f +b a84f +f a85f +13 a90f +17 a91f +1b a94f +6e 16de +1e 21fe +f8 5fc2 +3f af7f +4e 1ade +21 24ab +3b 25ed +1d a9d1 +4d 127b +3 a88f +7 a89f +5d 137b +13 a98f +2b 26ed +99 634b +55 9339 +1a 29ee +82 e20c +1d a9f1 +3 a8af +e 2ade +dc 7172 +5 9b +1f 1dd +b 2aed +24 2c30 +a 2aee +1 ab +1b 1ed +ce 507c +b4 4f3a +ce 50fc +b4 4fba +4e 907c +34 8f3a +10 a9aa +a9 e449 +67 1437 +3c 8f7a +18 a9ea +6f 1477 +4e b07c +34 af3a +1d ab51 +3 aa0f +7 aa1f +b aa4f +1d ab71 +3 aa2f +78 1fc0 +79 1fc1 +60 1e82 +7a 1fc4 +a0 4e82 +ba 4fc4 +e1 5e83 +7 20bf +fb 5fc5 +a2 4e8e +bc 4fd0 +a3 4e8f +bd 4fd1 +9 20cb +e3 5e8f +fd 5fd1 +24 e92 +3e fd4 +a4 4e92 +be 4fd4 +ba 4fc6 +4b b0e7 +b9 4fc9 +d2 510c +b8 4fca +4f b0f7 +bd 4fd9 +d6 511c +bc 4fda +20 2e82 +3a 2fc4 +a0 6e82 +ba 6fc4 +a1 6e83 +bb 6fc5 +22 2e8e +3c 2fd0 +af 445f +d a2f9 +c6 78b4 +a2 6e8e +bc 6fd0 +23 2e8f +3d 2fd1 +96 431e +c7 78b5 +a3 6e8f +bd 6fd1 +24 2e92 +3e 2fd4 +a4 6e92 +60 9e80 +be 6fd4 +8f c0f7 +fd 5fd9 +a5 6e93 +61 9e81 +bf 6fd5 +12 2184 +f9 5fe9 +f8 5fea +bd 6fd3 +d7 7115 +79 9fc1 +20 8e82 +3a 8fc4 +be 6fd6 +60 9e82 +7a 9fc4 +21 8e83 +8c 6078 +3b 8fc5 +fa 5fc6 +8b c0e7 +f9 5fc9 +f8 5fca +bd 6fd9 +16 196 +b9 6fe9 +97 611d +39 8fc9 +52 910c +96 611e +38 8fca +56 911c +3c 8fda +7c 9fda +d7 713d +bd 6ffb +79 9fe9 +d6 713e +78 9fea +d6 711e +78 9fca +3d afd9 +56 b11c +3c afda +21 aea3 +3b afe5 +38 fe0 +17 819f +78 1fe0 +57 b19f +b8 4fe0 +97 c19f +f8 5fe0 +39 fe1 +79 1fe1 +20 ea2 +3a fe4 +60 1ea2 +7a 1fe4 +ec 7ef0 +56 113e +86 403e +ce 507e +38 2fe0 +b8 6fe0 +39 2fe1 +b9 6fe1 +2f 26fd +c0 d008 +20 2ea2 +3a 2fe4 +96 6134 +38 8fe0 +97 6135 +39 8fe1 +20 8ea2 +3a 8fe4 +60 9ea2 +be 6ff6 +7a 9fe4 +21 8ea3 +3b 8fe5 +86 40be +a6 44be +db 5147 +39 afe1 +6 83e +e 87e +16 93e +1e 97e +9f 435d +85 421b +6 8be +86 48be +56 999c +4f 32d5 +6 a3e +11 23ab +2b 24ed +f0 5f02 +16 213e +42 902c +28 8eea +86 603e +52 91ac +96 61be +ac eef0 +16 813e +cc f2f0 +36 853e +d4 f330 +3e 857e +ec fef0 +56 913e +76 953e +78 1fc2 +d2 5104 +b8 4fc2 +3a fc6 +bc efd8 +7a 1fc6 +d6 5114 +bc 4fd2 +4b 10cd +31 f8b +4f 10dd +35 f9b +71 1fab +b3 4d05 +99 4bc3 +b7 4d15 +9d 4bd3 +c8 5068 +bb 4fc7 +d3 510d +b9 4fcb +d7 511d +bd 4fdb +f7 5d15 +24 8618 +dd 5bd3 +f3 5d25 +20 8628 +d9 5be3 +56 3114 +3c 2fd2 +27 a43d +d a2fb +c6 78b6 +d6 7114 +78 9fc0 +bc 6fd2 +12 2186 +f9 5feb +56 91be +4e b85c +d6 7116 +78 9fc2 +56 9114 +3c 8fd2 +7c 9fd2 +2f 2cd5 +15 2b93 +4f 30dd +c8 d060 +35 2f9b +4b 30ed +31 2fab +f3 5d05 +20 8608 +d9 5bc3 +fb 5fc7 +b3 6d25 +99 6be3 +d7 711d +79 9fc9 +bd 6fdb +bb 6fe7 +d3 712d +b9 6feb +6 823e +e 827e +26 863e +46 923e +4e 927e +33 8d05 +19 8bc3 +37 8d15 +1d 8bd3 +48 9068 +8c 607a +3b 8fc7 +53 910d +97 611f +39 8fcb +4c 9078 +3f 8fd7 +57 911d +3d 8fdb +73 9d25 +b7 6d37 +59 9be3 +7b 9fe7 +d7 713f +79 9feb +20 8400 +6 82be +b7 6d17 +73 9d05 +59 9bc3 +cc 707a +7b 9fc7 +d7 711f +79 9fcb +33 ad25 +19 abe3 +4c b078 +3f afd7 +57 b11d +3d afdb +a9 4ec9 +3b afe7 +53 b12d +39 afeb +6 883e +e 887e +1e 897e +f5 dd9b +ee 76d4 +9f c35d +85 c21b +6 88be +95 c31b +f 22f7 +af c45d +16 89be +46 9a3e +51 b3ab +6b b4ed +f0 df02 +16 a13e +36 a53e +6 a23e +e a27e +26 a63e +20 a400 +6 a2be +6 a83e +16 a93e +87 e095 +52 1124 +38 fe2 +c7 f095 +78 1fe2 +3a fe6 +bc eff8 +7a 1fe6 +d2 7124 +b8 6fe2 +e5 fcb9 +2e 26fe +96 6136 +52 9124 +38 8fe2 +52 b124 +38 afe2 +a 80e6 +78 1fc8 +4a b0e6 +b8 4fc8 +4e b0f6 +bc 4fd8 +4c 10d0 +32 f8e +d8 f142 +62 1eac +5d b1fb +76 1f9e +73 1fad +72 1fae +aa 4ecc +ae 4edc +a1 4e8b +bb 4fcd +d4 5110 +ba 4fce +a5 4e9b +bf 4fdd +be 4fde +38 2fc8 +c2 78ac +b8 6fc8 +39 2fc9 +3e a576 +ac 4458 +c3 78ad +6a b4c6 +b9 6fc9 +14 2118 +ee 5edc +3c 2fd8 +c6 78bc +bc 6fd8 +3d 2fd9 +c7 78bd +6e b4d6 +e5 5e9b +ff 5fdd +d6 711c +bc 6fda +78 9fc8 +22 2eac +e1 5e8b +fb 5fcd +fa 5fce +ae 6edc +61 9e89 +a5 6e9b +bf 6fdd +2a 8ecc +f8 ff62 +21 8e8b +3b 8fcd +54 9110 +3a 8fce +3e 8fde +c8 7040 +ae 6efe +6a 9eec +7e 9fde +d9 7141 +bf 6fff +61 9eab +7b 9fed +d8 7142 +7a 9fee +ae 6ede +6a 9ecc +61 9e8b +bf 6fdf +7b 9fcd +7a 9fce +cc 5052 +2a aeec +dd 5153 +21 aeab +3b afed +38 fe8 +78 1fe8 +b8 4fe8 +11 2183 +f8 5fe8 +b9 4fe9 +38 2fe8 +b8 6fe8 +39 2fe9 +d8 53c8 +6a b4e6 +d6 713c +bc 6ffa +78 9fe8 +97 613d +39 8fe9 +73 1faf +d5 5111 +bb 4fcf +bf 4fdf +d5 7131 +bb 6fef +55 9111 +3b 8fcf +3f 8fdf +d9 7143 +7b 9fef +7b 9fcf +d2 512c +b8 4fea +52 912c +96 613e +38 8fea +16 2194 +fd 5ff9 +bd 6ff9 +da 714e +7c 9ffa +22 eae +3c ff0 +23 eaf +3d ff1 +24 eb2 +3e ff4 +22 2eae +3c 2ff0 +a2 6eae +bc 6ff0 +23 2eaf +3d 2ff1 +a3 6eaf +bd 6ff1 +c4 d018 +24 2eb2 +3e 2ff4 +c1 7003 +db 7145 +63 9eaf +7d 9ff1 +71 35a3 +df 5157 +23 aeaf +3d aff1 +75 1fbb +b3 4d25 +99 4be3 +d3 512d +b9 4feb +24 8638 +f7 5d35 +dd 5bf3 +b7 6d35 +59 9be1 +9d 6bf3 +fd fd5b +33 8d25 +19 8be3 +97 613f +53 912d +39 8feb +bb 6d47 +77 9d35 +5d 9bf3 +7f 9ff7 +db 714f +7d 9ffb +65 169b +7f 17dd +60 16aa +7a 17ec +61 16ab +7b 17ed +37 ad35 +1d abf3 +ad 4ed9 +3f aff7 +57 b13d +3d affb +63 b627 +52 1bac +44 1a9a +6f b657 +5e 1bdc +45 1a9b +5f 1bdd +40 1aaa +6b b667 +5a 1bec +41 1aab +5b 1bed +2a 26ec +24 269a +3e 27dc +c8 70c0 +b8 c760 +25 269b +3f 27dd +c9 70c1 +20 26aa +3a 27ec +21 26ab +3b 27ed +a 2aec +a9 cc61 +16 2b9c +12 2bac +56 1134 +3c ff2 +4 2a9a +1e 2bdc +0 2aaa +1a 2bec +56 9134 +9a 6146 +3c 8ff2 +da 7146 +7c 9ff2 +4c 10f0 +32 fae +dc f152 +66 1ebc +77 1fbd +76 1fbe +26 2ebc +88 6040 +2a 8eec +99 6141 +21 8eab +3b 8fed +54 9130 +98 6142 +3a 8fee +b2 6f0e +cc 7050 +6e 9efc +c3 700f +dd 7151 +65 9ebb +7f 9ffd +73 35af +dc 7152 +7e 9ffe +3c ff8 +3c 2ff8 +bc 6ff8 +3d 2ff9 +dc 53d8 +6e b4f6 +77 1fbf +55 9131 +99 6143 +3b 8fef +dd 7153 +7f 9fff +7a 17ee +65 16bb +7f 17fd +7e 17fe +45 1abb +5f 1bfd +25 26bb +3f 27fd +c9 70e1 +28 2c40 +e 2afe +9a 614e +56 913c +3c 8ffa +34 2d30 +1a 2bee +38 2d40 +1e 2bfe +55 3113 +c a2fa +26 a43c +df 79f7 +5d 3153 +79 356b +d1 71a3 +f9 756b +f1 75ab +5f 3355 +4c 98da +45 3213 +cf 7af7 +5b 3365 +48 98ea +41 3223 +54 991a +4d 3253 +6e 9cde +67 3617 +7f 375d +65 361b +e9 d64b +6a 9cee +63 3627 +75 9d93 +7b 376d +61 362b +76 9d1e +6f 3657 +84 6ab0 +6d 365b +72 9d2e +6b 3667 +df 73d5 +c5 7293 +f2 dd2e +eb 7667 +75 3513 +7d 3553 +57 3917 +55 391b +d9 d94b +53 3927 +51 392b +5f 3957 +5d 395b +59 396b +f1 75a3 +c a25a +df 7957 +13 925 +dd 795b +4 890 +8 a26a +db 7967 +13 a30f +2d a451 +d9 796b +25 a491 +d1 79ab +6c 9cda +7f 3755 +65 3613 +68 9cea +7b 3765 +61 3623 +74 9d1a +6d 3653 +5f 3b5d +45 3a1b +5b 3b6d +41 3a2b +4d 3a5b +6e 3cf6 +f4 dd1a +ed 7653 +ff 77d5 +e5 7693 +77 9fbf +d5 7113 +d1 7123 +f1 752b +54 199a +7f b557 +79 b56b +71 b5ab +ea dcee +e3 7627 +4b b267 +5f b3d5 +45 b293 +5b b3e5 +41 b2a3 +65 1c91 +69 b66b +74 b738 +67 b697 +7f b7dd +65 b69b +7b b7ed +61 b6ab +f1 7523 +b 8e5 +d5 791b +1a a36c +0 a22a +d3 7927 +b a2cf +25 a411 +d1 792b +7d b553 +71 b5a3 +5f b957 +5d b95b +59 b96b +d0 f308 +51 b9ab +ec dcda +66 3cb6 +ff 7755 +e5 7613 +5c 1bd8 +6d b653 +7f b7d5 +65 b693 +7b b7e5 +61 b6a3 +4f ba57 +4d ba5b +4b ba67 +49 ba6b +fd 5d53 +5b bbed +41 baab +48 32ea +62 342c +49 32eb +63 342d +5d 33db +77 351d +72 352e +61 342b +7b 356d +7a 356e +d0 732a +ea 746c +90 698a +45 32b1 +e7 749d +e2 74ac +5c 33da +76 351c +58 33ea +72 352c +60 342a +7a 356c +67 361d +97 6bb5 +66 361e +e0 742a +fa 756c +f2 75ac +46 381c +42 382c +4f 385d +b 245 +56 391e +45 381b +5f 395d +5e 395e +41 382b +5b 396d +5a 396e +4 a210 +ca 786c +68 b6e8 +5 a211 +cb 786d +69 b6e9 +c6 789c +c7 789d +c5 781b +df 795d +de 795e +c1 782b +15 a311 +db 796d +14 a312 +2e a454 +1d 9d9 +da 796e +d7 799d +8b 42c5 +d6 799e +bc 4558 +d3 79ad +7a b5c6 +49 30c9 +56 391c +52 392c +44 381a +5e 395c +40 382a +5a 396c +43 3a2d +c4 781a +de 795c +c0 782a +14 a310 +da 796c +d2 79ac +48 30c8 +3 2a5 +cd 72db +e7 741d +35 599 +f2 752e +50 b32a +6a b46c +62 b4ac +63 b4ad +61 b42b +7b b56d +7a b56e +77 b59d +72 b5ae +dc 73da +12 3a4 +f6 751c +d8 73ea +f2 752c +60 b42a +7a b56c +72 b5ac +85 eab3 +9f ebf5 +6e b65e +6b b66d +e1 5621 +66 b69e +63 b6ad +c5 7831 +7c b7f0 +62 b6ae +d7 791d +75 b799 +8b 4245 +d6 791e +74 b79a +4f b85d +4a b86c +46 b89c +47 b89d +c1 f209 +42 b8ac +2c 8458 +43 b8ad +45 b81b +5f b95d +5e b95e +41 b82b +5b b96d +5a b96e +57 b99d +b 82c5 +d1 5921 +56 b99e +3c 8558 +53 b9ad +d6 791c +44 b81a +5e b95c +40 b82a +5a b96c +d1 f309 +52 b9ac +4f ba5d +55 311b +df 79ff +59 316b +6a 346e +d1 71ab +2d 4d9 +ea 746e +5f 33f5 +e5 d419 +45 32b3 +24 498 +fb 756f +5f 335d +45 321b +e9 7c41 +cf 7aff +5b 336d +55 9993 +41 322b +4d 325b +5d 99d3 +49 326b +15 8933 +67 361f +7d 3771 +77 9d97 +63 362f +86 6ab4 +6f 365f +46 381e +4e 385e +4a 386e +57 391f +53 392f +5f 395f +ce 785e +6c b6da +4 a212 +d 8d9 +1e a354 +ca 786e +68 b6ea +c6 789e +df 795f +15 a313 +2f a455 +4 898 +db 796f +47 3a1f +5d 3b71 +43 3a2f +4f 3a5f +b e5 +d5 711b +d1 712b +fc 7570 +25 499 +e2 742e +f3 752f +59 b16b +51 b1ab +6a b46e +7b b56f +73 b5af +45 1891 +49 b26b +5b b3ed +fd 5553 +41 b2ab +67 1c95 +6b b66f +7d b7f1 +63 b6af +27 a415 +d a2d3 +d3 792f +4e b85e +4a b86e +c1 5821 +46 b89e +5f b95f +d2 f30c +3c 855a +53 b9af +4f ba5f +4b ba6f +2a 2eee +44 3030 +2b 2eef +45 3031 +cc d058 +2c 2ef2 +46 3034 +3b 2fef +55 3131 +54 3132 +dc d15a +56 3136 +57 3397 +64 3438 +65 3439 +5d 33f3 +fd d559 +77 3535 +37 af97 +44 b038 +fc d55a +76 3536 +75 3539 +74 353a +7e 3576 +56 3396 +7d 3579 +b3 6f2f +cd 7071 +b5 6f33 +71 9f21 +cf 7075 +c4 70b0 +c5 70b1 +d7 71b5 +e6 74b4 +e4 74b8 +fc 757a +f6 75b6 +f4 75ba +3a 2fee +54 3130 +dc d158 +3c 2ff2 +56 3134 +90 690a +4c 98f8 +45 3231 +5e 3374 +44 3232 +92 690e +cd d259 +4e 98fc +47 3235 +4c 3272 +9a 694e +56 993c +4f 3275 +5c 33f2 +fc d558 +76 3534 +67 3497 +74 3538 +d4 71b0 +d6 71b4 +d6 d93c +cf 7275 +31 503 +c7 72b5 +f6 75b4 +f4 75b8 +4a 32ee +64 3430 +4b 32ef +65 3431 +5b 33ef +75 3531 +74 3532 +63 342f +7d 3571 +7c 3572 +cc d858 +46 3834 +cd d859 +47 3835 +44 3838 +4f 3875 +dd d959 +57 3935 +dc d95a +56 3936 +45 3833 +5f 3975 +5d 3979 +d2 732e +15 399 +ec 7470 +cd d2d9 +92 698e +47 32b5 +e4 74b0 +f4 75b2 +ce 7874 +6c b6f0 +cf 7875 +b8 edca +6d b6f1 +6 a21c +cc 7878 +7 a21d +cd 7879 +c4 78b8 +c5 78b9 +6c b4d2 +c5 7833 +c a278 +df 7975 +17 a31d +dd 7979 +16 a31e +dc 797a +a6 441e +4 a2b8 +d7 79b5 +33 2f8f +4d 30d1 +1d a3fb +37 a53d +d6 79b6 +4c 30d2 +d5 79b9 +7c b5d2 +5a 33ee +74 3530 +6c 9cf8 +b0 6d0a +65 3631 +7e 3774 +64 3632 +dc d958 +56 3934 +47 3897 +54 3938 +45 3a39 +4f 3a75 +4d 3a79 +f4 75b0 +7 89d +c4 7832 +de 7974 +cf 78d7 +16 a31c +dc 7978 +bf 455f +1d a3f9 +d6 79b4 +32 2f8e +4c 30d0 +d4 79b8 +cf 7a75 +31 d03 +c7 7ab5 +aa 6eee +c4 7030 +ab 6eef +c5 7031 +17 19d +d4 7132 +bd 6ff3 +79 9fe1 +d7 7135 +78 9fe2 +d6 7136 +cc 72f2 +e6 7434 +d7 7397 +e4 7438 +e5 7439 +dd 73f3 +f7 7535 +f6 7536 +f5 7539 +f4 753a +32 af2e +4c b070 +34 af32 +4e b074 +35 af33 +4f b075 +43 b02f +5d b171 +45 b033 +5f b175 +7c b57a +e4 5498 +76 b5b6 +45 30b9 +ba 6fee +d4 7130 +bc 6ff2 +78 9fe0 +d6 7134 +ce d8fc +c7 7235 +c6 7236 +dc 73f2 +f6 7534 +e7 7497 +f4 7538 +56 b1b4 +9a e94e +4f b275 +cc d8f0 +2f 477 +57 193d +4e b276 +76 b5b4 +6c b67a +ca 72ee +e4 7430 +37 59d +f4 7532 +c4 7838 +52 b32e +6c b470 +74 b5b2 +4e b874 +4f b875 +36 51e +cc 72d0 +4c b878 +4d b879 +44 b8b8 +45 b8b9 +45 b833 +5f b975 +cc 5858 +5e b976 +5d b979 +5c b97a +55 b9b9 +da 73ee +f4 7530 +c7 7897 +e a2dc +d4 7938 +62 b42e +7c b570 +74 b5b0 +b8 ed4a +6d b671 +4f b8d7 +5c b978 +d5 f311 +3f 855f +56 b9b4 +8a 62ec +54 b9b8 +4f ba75 +dc 5b7a +47 bab5 +44 30b0 +54 31b0 +64 34b0 +74 35b0 +45 30b1 +55 31b1 +cc d0d8 +46 30b4 +dc d1d8 +56 31b4 +ec d4d8 +66 34b4 +fc d5d8 +76 35b4 +44 3230 +64 3630 +4d 98fb +cc d258 +46 3234 +6d 9cfb +ec d658 +66 3634 +cd d8fb +c6 7234 +ed dcfb +e6 7634 +44 32b0 +64 36b0 +b0 6d8a +65 36b1 +cc d2d8 +46 32b4 +ec d6d8 +66 36b4 +30 502 +c6 72b4 +e6 76b4 +44 3830 +54 3930 +c4 7830 +6 a214 +cc 7870 +e a2d4 +d4 7930 +c2 782e +5 899 +16 a314 +dc 7970 +43 382f +5d 3971 +7 a215 +cd 7871 +6b b6ed +f a2d5 +d5 7931 +c3 782f +17 a315 +dd 7971 +c3 720d +2d 45b +44 38b0 +d3 730d +3d 55b +54 39b0 +ad 445b +c4 78b0 +bd 455b +d4 79b0 +94 431a +ae 445c +c5 78b1 +a4 441a +be 455c +d5 79b1 +cc d8d8 +c5 7211 +2f 45f +46 38b4 +16 31e +cd d8d9 +47 38b5 +dd d9d9 +26 41e +57 39b5 +44 3a30 +45 3a31 +2d 65b +44 3ab0 +2e 65c +45 3ab1 +cc dad8 +2f 65f +46 3ab4 +af 465f +30 d02 +c6 7ab4 +cd dad9 +47 3ab5 +2a aeee +44 b030 +3a afee +54 b130 +4a b2ee +64 b430 +5a b3ee +74 b530 +2b aeef +45 b031 +3b afef +55 b131 +4b b2ef +65 b431 +5b b3ef +75 b531 +2c aef2 +46 b034 +3c aff2 +56 b134 +5c b3f2 +76 b534 +2d aef3 +47 b035 +3d aff3 +57 b135 +5d b3f3 +77 b535 +64 b630 +6c b670 +90 e90a +45 b231 +c2 d8ac +3f 575 +25 433 +b0 ed0a +65 b631 +55 1bb9 +66 b634 +92 e90e +47 b235 +c4 d8b0 +27 437 +44 b830 +4c b870 +43 b82f +5d b971 +46 b834 +45 ba31 +e7 5c17 +2e 865c +45 bab1 +5e 3174 +44 3032 +cc d05a +46 3036 +4c 3072 +55 3133 +ec d45a +66 3436 +fd d55b +77 3537 +5e b17c +44 b03a +75 353b +7f 3577 +4c b07a +e6 74b6 +f5 75bb +5f 3375 +4c 98fa +45 3233 +ed d65b +6e 9cfe +67 3637 +7f 377d +65 363b +76 9d3e +6f 3677 +f6 dd3e +ef 7677 +7e 3574 +64 3432 +6c 3472 +75 3533 +cc d85a +46 3836 +4e 3876 +dd d95b +57 3937 +55 393b +5f 3977 +5d 397b +f5 75b3 +6 a21e +cc 787a +c a27a +df 7977 +17 a31f +dd 797b +1e a3fc +4 a2ba +d7 79b7 +d5 79bb +7f 3775 +6c 9cfa +65 3633 +5f 3b7d +45 3a3b +cf 7a77 +c7 7ab7 +7 9d +de 7174 +c4 7032 +d5 7133 +79 9fe3 +d7 7137 +e6 7436 +fe 757c +e4 743a +f7 7537 +f5 753b +50 3380 +cd 5059 +5f b177 +6c b47a +75 b5bb +ce d8fe +c7 7237 +ee dcfe +e7 7637 +4f b277 +27 49d +fe 7574 +e4 7432 +f5 7533 +1e a37c +4 a23a +d7 7937 +f a2df +d5 793b +75 b5b3 +4e b876 +4c b87a +cd 5859 +5f b977 +5d b97b +d4 f318 +55 b9bb +7f b7f5 +65 b6b3 +4f ba77 +4d ba7b +47 bab7 +5f bbfd +45 babb +74 35b2 +fc d5da +76 35b6 +5e 33f4 +e4 d418 +44 32b2 +7e 37f4 +64 36b2 +5e 3974 +44 3832 +4c 3872 +54 3932 +e a2d6 +17 99d +d4 7932 +d3 730f +ed 7451 +54 39b2 +35 a539 +d4 79b2 +cc d8da +df 7355 +c5 7213 +46 38b6 +dc d9da +d5 7313 +ef 7455 +56 39b6 +5e b174 +44 b032 +54 b132 +7e b574 +64 b432 +74 b532 +e4 5418 +76 b536 +45 3039 +5e b374 +4d 18f9 +44 b232 +7e b774 +6d 1cf9 +64 b632 +4f 18fd +46 b236 +5e b974 +44 b832 +4c b872 +5c b972 +46 b836 +37 2f97 +44 3038 +55 3139 +5d 33fb +77 353d +76 353e +a6 6e96 +62 9e84 +cd 7079 +c4 70b8 +c5 70b9 +e6 74bc +47 3097 +54 3138 +5c 33fa +76 353c +d4 71b8 +f6 75bc +46 383c +4f 387d +45 383b +5f 397d +5e 397e +ce 787c +6c b6f8 +cf 787d +6d b6f9 +c5 783b +df 797d +de 797e +d7 79bd +7e b5d6 +4d 30d9 +56 393c +44 383a +5e 397c +47 3a3d +4f 3a7d +c4 783a +de 797c +d6 79bc +4c 30d8 +cf 7a7d +31 d0b +c7 7abd +77 1d9d +6e b6d6 +b7 6f97 +73 9f85 +c4 7038 +c5 7039 +d4 713a +dd 73fb +f7 753d +36 af96 +5d b179 +7e b57e +77 b5bd +76 b5be +c7 7097 +d4 7138 +dc 73fa +f6 753c +51 3381 +4c b27a +76 b5bc +4e b87c +4f b87d +cc 72d8 +c5 f219 +46 b8bc +47 b8bd +45 b83b +5f b97d +5e b97e +57 b9bd +44 b83a +5e b97c +d5 f319 +56 b9bc +4f ba7d +47 babd +44 30b8 +64 34b8 +44 38b8 +54 39b8 +44 3a38 +44 3ab8 +47 b097 +54 b138 +45 b039 +55 b139 +64 b638 +6c b678 +45 b239 +3f 57d +25 43b +65 b639 +44 b838 +47 b897 +54 b938 +45 b839 +55 b939 +45 ba39 +55 313b +77 353f +7f 357f +de 71fc +c4 70ba +e6 74be +ff 757f +67 363f +70 3d22 +ef 767f +4e 387e +57 393f +5f 397f +ce 787e +6c b6fa +df 797f +d7 79bf +47 3a3f +4f 3a7f +cf 7a7f +e1 7c01 +c7 7abf +de 717c +73 9f87 +c4 703a +e6 743e +55 b1bb +7f b57f +77 b5bf +68 3ce2 +e7 763f +6f b67f +d7 793f +4e b87e +5f b97f +d6 f31c +57 b9bf +c7 7a3f +4f ba7f +61 bc01 +47 babf +54 b13a +7e b57c +64 b43a +74 b53a +5e b37c +44 b23a +7e b77c +64 b63a +58 31c0 +78 35c0 +d8 71c0 +e8 74c0 +f8 75c0 +59 31c1 +d9 71c1 +30 2f82 +4a 30c4 +40 3082 +5a 31c4 +60 3482 +7a 35c4 +b0 6f82 +ca 70c4 +c0 7082 +da 71c4 +e0 7482 +fa 75c4 +b1 6f83 +cb 70c5 +c1 7083 +db 71c5 +42 308e +5c 31d0 +52 338e +6c 34d0 +62 348e +7c 35d0 +b2 6f8e +cc 70d0 +c2 708e +dc 71d0 +d2 738e +ec 74d0 +e2 748e +fc 75d0 +53 338f +6d 34d1 +63 348f +7d 35d1 +b3 6f8f +cd 70d1 +d3 738f +ed 74d1 +e3 748f +fd 75d1 +34 2f92 +4e 30d4 +44 3092 +5e 31d4 +54 3392 +6e 34d4 +64 3492 +7e 35d4 +70 9f80 +b4 6f92 +ce 70d4 +c4 7092 +de 71d4 +d4 7392 +ee 74d4 +e4 7492 +fe 75d4 +65 3493 +7f 35d5 +4c b0d8 +b5 6f93 +71 9f81 +cf 70d5 +e5 7493 +ff 75d5 +c7 723d +48 38e0 +48 32c0 +bb 474f +3c df2 +d2 7ba4 +68 36c0 +d0 d988 +33 50f +c9 72c1 +51 998b +4a 32c4 +71 9d8b +6a 36c4 +d1 d98b +34 512 +ca 72c4 +d2 d98c +35 513 +cb 72c5 +4c 32d0 +6c 36d0 +d4 d998 +37 51f +cd 72d1 +f4 dd98 +ed 76d1 +1d 897b +55 999b +4e 32d4 +75 9d9b +6e 36d4 +d5 d99b +ce 72d4 +d6 d99c +cf 72d5 +c7 721d +48 38c0 +d7 731d +58 39c0 +c8 78c0 +d8 79c0 +c9 78c1 +d9 79c1 +4a 38c4 +40 3882 +5a 39c4 +11 a309 +ca 78c4 +c0 7882 +21 a409 +da 79c4 +4b 38c5 +41 3883 +5b 39c5 +cb 78c5 +c1 7883 +8 a2c8 +db 79c5 +4c 38d0 +42 388e +5c 39d0 +cd 78d1 +c3 788f +dd 79d1 +4e 38d4 +44 3892 +5e 39d4 +c4 7892 +25 a419 +de 79d4 +4f 38d5 +45 3893 +5f 39d5 +cf 78d5 +c5 7893 +c a2d8 +df 79d5 +48 3ac0 +32 d0e +c8 7ac0 +49 3ac1 +33 d0f +c9 7ac1 +4a 3ac4 +34 d12 +ca 7ac4 +35 d13 +cb 7ac5 +4c 3ad0 +36 d1e +cc 7ad0 +37 d1f +cd 7ad1 +4e 3ad4 +ce 7ad4 +cf 7ad5 +c8 70e0 +b0 6fa2 +ca 70e4 +b1 6fa3 +cb 70e5 +dc 71d2 +de 71d6 +d8 71e2 +ed 74d9 +d0 73a2 +ea 74e4 +e8 74e8 +fe 75d6 +f8 75ea +d8 71e0 +c0 70a2 +da 71e4 +e6 7414 +cc 72d2 +ca 72e6 +e0 74a2 +fa 75e4 +f8 75e8 +22 6a4 +ec 76da +78 b5c0 +40 b082 +5a b1c4 +50 b382 +6a b4c4 +60 b482 +7a b5c4 +52 b38e +6c b4d0 +62 b48e +7c b5d0 +53 b38f +6d b4d1 +63 b48f +7d b5d1 +54 b392 +6e b4d4 +64 b492 +7e b5d4 +65 b493 +7f b5d5 +e8 74e0 +6a b6c4 +96 e99e +4b b2c5 +f8 75e2 +cd 78d9 +4d b2d1 +2 a28c +c8 78e8 +6e b6d4 +3f a55d +25 a41b +de 79d6 +91 4301 +12 9a4 +dc 79da +4f b2d5 +13 a38d +d9 79e9 +40 b882 +5a b9c4 +44 b892 +5e b9d4 +49 bac1 +f8 75e0 +4b bac5 +4d bad1 +12 a38c +d8 79e8 +4f bad5 +50 b3a2 +6a b4e4 +e8 54c8 +7a b5e6 +49 30e9 +78 b5ea +66 b414 +55 1999 +4c b2d2 +57 199d +4e b2d6 +94 e9ba +eb 5447 +49 b2e1 +51 19a9 +62 b424 +48 b2e2 +96 e9be +4b b2e5 +53 19ad +4a b2e6 +60 b4a2 +7a b5e4 +68 b4e0 +78 b5e2 +4c b8d8 +4d b8d9 +48 b8e8 +49 b8e9 +59 b9e9 +78 b5e0 +d9 f341 +40 b8a2 +5a b9e4 +58 b9e8 +4d bad9 +4b bae5 +eb 5c4f +49 bae9 +48 30e0 +68 34e0 +78 35e0 +49 30e1 +59 31e1 +d0 d108 +30 2fa2 +4a 30e4 +50 33a2 +f0 d508 +6a 34e4 +60 34a2 +7a 35e4 +48 32e0 +68 36e0 +94 69ba +50 99a8 +49 32e1 +b4 6dba +70 9da8 +69 36e1 +d0 d308 +51 99ab +4a 32e4 +f0 d708 +71 9dab +6a 36e4 +d1 d9ab +34 532 +ca 72e4 +f1 ddab +ea 76e4 +d1 d309 +96 69be +52 99ac +4b 32e5 +2 a284 +c8 78e0 +12 a384 +d8 79e0 +18 34a +49 38e1 +3 a285 +98 434a +c9 78e1 +a8 444a +13 a385 +d9 79e1 +d0 d908 +c9 7241 +4a 38e4 +1a 34e +d1 d909 +4b 38e5 +41 38a3 +c0 7200 +2a 44e +5b 39e5 +6a b6e4 +eb 5c47 +49 bae1 +78 35c2 +c8 70c2 +e8 74c2 +7a 35c6 +ca 70c6 +ea 74c6 +fa 75c6 +5c 31d2 +6c 34d2 +7c 35d2 +cc 70d2 +ec 74d2 +4e 30d6 +5e 31d6 +6e 34d6 +7e 35d6 +70 9f82 +ce 70d6 +ee 74d6 +e2 7404 +c8 72c2 +ca 72c6 +66 3414 +4c 32d2 +6e 36d6 +c7 721f +48 38c2 +d7 731f +58 39c2 +29 a449 +c8 78c2 +39 a549 +d8 79c2 +4a 38c6 +5a 39c6 +11 a30b +2b a44d +ca 78c6 +3b a54d +21 a40b +da 79c6 +4c 38d2 +5c 39d2 +3d a559 +dc 79d2 +4e 38d6 +5e 39d6 +dd 71d3 +df 71d7 +d9 71e3 +e8 74ea +ff 75d7 +c7 723f +48 38e2 +f9 75eb +5f 33d5 +45 3293 +5b 33e5 +e1 d409 +41 32a3 +74 3738 +67 3697 +7f 37dd +f8 d760 +65 369b +d4 d99a +e7 7415 +cd 72d3 +e3 7425 +d0 d9aa +c9 72e3 +d2 d9ae +cb 72e7 +f2 ddae +eb 76e7 +e9 76eb +55 99b9 +99 69cb +62 b404 +51 1989 +48 b2c2 +53 198d +4a b2c6 +2 8a4 +81 4201 +cc 78da +13 a38f +2d a4d1 +d9 79eb +7f 37d5 +65 3693 +7b 37e5 +61 36a3 +65 96b1 +b0 cd8a +a9 66c3 +47 9ab5 +98 6b68 +8b 6ac7 +a3 6c0d +45 9ab9 +89 6acb +f4 dd9a +ed 76d3 +f0 ddaa +e9 76e3 +dc 7b78 +cf 7ad7 +cb 7ae7 +e3 7c2d +c9 7aeb +3b a5c7 +ff 5575 +e5 5433 +39 a5cb +68 b4ea +7f b5d7 +e9 54c9 +7b b5e7 +79 b5eb +23 a405 +9 a2c3 +d1 7923 +18 a368 +b a2c7 +f1 7d23 +38 a768 +2b a6c7 +67 b415 +4d b2d3 +5c b378 +4f b2d7 +63 b425 +49 b2e3 +4b b2e7 +7c b778 +6f b6d7 +6d b6db +6b b6e7 +69 b6eb +56 1316 +19 a9cb +68 b4e2 +7d b5d3 +79 b5e3 +1 8201 +4c b8da +d8 f348 +59 b9eb +18 ab68 +b aac7 +23 ac0d +9 aacb +6d b6d3 +69 b6e3 +5c bb78 +4f bad7 +67 bc1d +4d badb +4b bae7 +63 bc2d +49 baeb +78 35e2 +7a 35e6 +e8 d448 +62 3424 +48 32e2 +39 a569 +12 a386 +d8 79e2 +c9 7243 +d0 d90a +4a 38e6 +68 34c8 +c9 70c9 +d9 71c9 +6c 34d8 +6d 34d9 +cd 70d9 +4c 32d8 +6c 36d8 +ec 76d8 +4d 32d9 +6d 36d9 +48 38c8 +58 39c8 +c9 78c9 +d9 79c9 +4c 38d8 +4d 38d9 +48 3ac8 +c8 7ac8 +49 3ac9 +c9 7ac9 +4c 3ad8 +cc 7ad8 +4d 3ad9 +62 34ac +63 34ad +f0 d520 +77 359d +72 35ae +90 638a +aa 64cc +c8 70e8 +c9 70e9 +12 1a4 +dc 71da +d9 71e9 +a a8ce +d8 71ea +d4 739a +ee 74dc +d5 739b +ef 74dd +d0 73aa +ea 74ec +e5 749b +ff 75dd +48 38e8 +72 35ac +e0 d620 +67 369d +f9 d763 +66 369e +63 36ad +7c 37f0 +62 36ae +a0 648a +ba 65cc +d8 71e8 +2 2a4 +e6 741c +cc 72da +26 496 +c9 72e9 +c8 f062 +b 80cd +e2 742c +c8 72ea +e0 74aa +fa 75ec +c1 7209 +42 38ac +b 2c5 +56 399e +4d b0d9 +8a 68cc +ce 78dc +cf 78dd +c5 789b +df 79dd +93 4305 +de 79de +4d b2d9 +48 b8c8 +58 b9c8 +49 b8c9 +59 b9c9 +d1 7309 +52 39ac +49 bac9 +ce 7ade +c5 509b +df 51dd +59 b1e9 +58 b1ea +54 b39a +6e b4dc +55 b39b +6f b4dd +50 b3aa +6a b4ec +65 b49b +7f b5dd +61 b4ab +7b b5ed +7a b5ee +66 b41c +4c b2da +eb 544f +49 b2e9 +62 b42c +48 b2ea +60 b4aa +7a b5ec +e9 5661 +6e b6de +47 1217 +a a8cc +4e b8dc +4f b8dd +c9 f249 +4a b8ec +4b b8ed +45 b89b +5f b9dd +d9 5961 +13 8305 +5e b9de +41 b8ab +c0 f208 +5b b9ed +b aacd +24 ac10 +a aace +d8 f162 +1 808b +1b 81cd +d9 f349 +40 b8aa +5a b9ec +4f badd +c9 5a61 +4e bade +45 909b +5f 91dd +ed 5c53 +4b baed +64 bc30 +4a baee +9f 61ff +41 90ab +5b 91ed +48 30e8 +68 34e8 +3f 5d7 +c8 72e8 +a 80cc +58 39e8 +49 38e9 +59 39e9 +48 3ae8 +49 3ae9 +cb f2e5 +35 8533 +37 8537 +3f 8775 +25 8633 +1d 8973 +1f 8b75 +5 8a33 +75 9533 +77 9537 +67 9637 +5f 9b75 +45 9a33 +c8 70ca +e8 74ca +f8 75ca +a 22ec +2 a4 +cc 70da +66 341c +4c 32da +6c 36da +c8 78ca +8d 42f1 +d8 79ca +66 94bc +aa 64ce +d9 71eb +5f 33dd +d8 d360 +45 329b +7d 37f1 +63 36af +e3 742d +c9 72eb +eb 76ef +48 b0ca +68 b4ca +d9 d963 +46 389e +4c b0da +d2 730c +3c 55a +53 39af +57 99bd +9b 69cf +62 b40c +48 b2ca +68 b6ca +83 4205 +ce 78de +48 b8ca +47 9abd +a5 6c11 +8b 6acf +e5 7c31 +cb 7aef +f3 df8f +19 a1cb +48 b0ea +59 b1eb +6a b4ee +7b b5ef +23 a40d +c2 7886 +9 a2cb +67 b41d +4d b2db +63 b42d +49 b2eb +6f b6df +6b b6ef +1b a9cf +c9 5861 +3 8205 +4e b8de +c0 5820 +5f b9df +25 ac11 +b aacf +4f badf +65 bc31 +4b baef +cb f2ed +35 853b +3f 877d +de 5bf6 +25 863b +1f 8b7d +5 8a3b +75 953b +4e 3274 +55 993b +5f 9b7d +45 9a3b +b2 6fae +cc 70f0 +b3 6faf +cd 70f1 +70 9fa0 +b4 6fb2 +ce 70f4 +b5 6fb3 +71 9fa1 +cf 70f5 +d4 73b2 +ee 74f4 +ec 74f8 +fe 75f6 +c2 70ae +dc 71f0 +c4 70b2 +de 71f4 +d6 d9bc +39 543 +cf 72f5 +ce 72f6 +e4 74b2 +fe 75f4 +fc 75f8 +d2 73ae +ec 74f0 +6 a29c +cc 78f8 +17 a39d +dd 79f9 +e2 74ae +fc 75f0 +16 a39c +dc 79f8 +54 b3b2 +6e b4f4 +ec 54d8 +7e b5f6 +4d 30f9 +98 e9ca +ef 5457 +4d b2f1 +55 19b9 +66 b434 +4c b2f2 +9a e9ce +4f b2f5 +57 19bd +4e b2f6 +64 b4b2 +7e b5f4 +52 b3ae +6c b4f0 +7c b5f2 +4c b8f8 +4d b8f9 +5d b9f9 +62 b4ae +7c b5f0 +c3 f20f +dd f351 +44 b8b2 +5e b9f4 +5c b9f8 +4f baf5 +ef 5c5f +4d baf9 +32 2fae +4c 30f0 +52 33ae +6c 34f0 +62 34ae +7c 35f0 +33 2faf +4d 30f1 +d4 d118 +34 2fb2 +4e 30f4 +54 33b2 +f4 d518 +6e 34f4 +64 34b2 +7e 35f4 +4c 32f0 +6c 36f0 +54 99b8 +98 69ca +4d 32f1 +b8 6dca +74 9db8 +6d 36f1 +d4 d318 +55 99bb +4e 32f4 +f4 d718 +75 9dbb +6e 36f4 +d5 d9bb +38 542 +ce 72f4 +f5 ddbb +ee 76f4 +d5 d319 +56 99bc +9a 69ce +4f 32f5 +cb 724d +4c 38f0 +6 a294 +cc 78f0 +c2 78ae +16 a394 +dc 79f0 +43 38af +c2 720c +2c 45a +5d 39f1 +7 a295 +9c 435a +cd 78f1 +c3 78af +ac 445a +17 a395 +dd 79f1 +d4 d918 +cd 7251 +4e 38f4 +1e 35e +d5 d919 +4f 38f5 +45 38b3 +2e 45e +c4 7210 +5f 39f5 +4c 3af0 +4d 3af1 +d4 db18 +4e 3af4 +d5 db19 +4f 3af5 +6e b6f4 +ef 5c57 +4d baf1 +70 9fa2 +ce 70f6 +ee 74f6 +ec 74fa +ff 75f7 +fd 75fb +d4 d9ba +e7 7435 +cd 72f3 +d6 d9be +cf 72f7 +f6 ddbe +ef 76f7 +ed 76fb +75 35b3 +d4 7318 +55 39bb +17 a39f +dd 79fb +7f 37f5 +65 36b3 +f4 ddba +ed 76f3 +e7 7c3d +cd 7afb +6c b4fa +ed 54d9 +7f b5f7 +7d b5fb +67 b435 +4d b2f3 +4f b2f7 +6f b6f7 +6d b6fb +6c b4f2 +7d b5f3 +dc f358 +5d b9fb +6d b6f3 +4f baf7 +67 bc3d +4d bafb +7c 35f2 +7e 35f6 +ec d458 +66 3434 +4c 32f2 +cb 724f +4c 38f2 +3d a579 +16 a396 +dc 79f2 +d4 d91a +cd 7253 +4e 38f6 +76 35be +cc 70f8 +cd 70f9 +d4 73ba +ee 74fc +76 35bc +dc 71f8 +cd 72f9 +cc f072 +f 80dd +e6 743c +cc 72fa +e4 74ba +fe 75fc +70 3da0 +ef 76fd +c5 7219 +46 38bc +d5 7319 +56 39bc +47 3abd +54 b3ba +6e b4fc +7e b5fe +ef 545f +4d b2f9 +66 b43c +4c b2fa +64 b4ba +7e b5fc +cd f259 +4e b8fc +4f b8fd +45 b8bb +c4 f218 +5f b9fd +dd f359 +44 b8ba +5e b9fc +4f bafd +68 bc40 +4e bafe +4c 30f8 +6c 34f8 +cc 72f8 +e 80dc +4c 38f8 +5c 39f8 +5d 39f9 +4c 3af8 +4d 3af9 +3f 87fd +25 86bb +96 c314 +17 89b7 +94 c318 +e 22f4 +15 89bb +77 35bf +cc 70fa +e7 743d +cd 72fb +70 3da2 +ef 76ff +61 3c01 +47 3abf +4c b0fa +6e b4fe +7f b5ff +67 b43d +4d b2fb +6f b6ff +de f35c +c4 f21a +5f b9ff +69 bc41 +4f baff +d5 f331 +3f 857f +96 c31c +17 89bf +f 8a7f +50 3300 +70 3700 +78 3740 +51 39a3 +3a 54e +d0 7300 +59 39e3 +d8 7340 +71 3da3 +f0 7700 +d8 d9c8 +52 39a4 +3b 54f +d1 7301 +40 38a2 +5a 39e4 +d9 7341 +f8 ddc8 +72 3da4 +f1 7701 +59 99cb +52 3304 +40 3202 +5a 3344 +79 9dcb +72 3704 +60 3602 +7a 3744 +d9 d9cb +3c 552 +53 39a7 +d2 7304 +5b 39e7 +c0 7202 +da 7344 +f9 ddcb +73 3da7 +f2 7704 +54 3310 +42 320e +5c 3350 +74 3710 +62 360e +93 6ba5 +7c 3750 +55 39b3 +3e 55e +d4 7310 +5d 39f3 +c2 720e +dc 7350 +75 3db3 +f4 7710 +dc d9d8 +3f 55f +56 39b4 +d5 7311 +c3 720f +5e 39f4 +44 38b2 +dd 7351 +fc ddd8 +76 3db4 +f5 7711 +5d 99db +56 3314 +44 3212 +5e 3354 +7d 9ddb +76 3714 +dd d9db +57 39b7 +d6 7314 +5f 39f7 +c4 7212 +de 7354 +fd dddb +77 3db7 +f6 7714 +70 3780 +71 3781 +3b 5cf +d1 7381 +f1 7781 +55 3391 +75 3791 +3f 5df +d5 7391 +f5 7791 +50 3b00 +3a d4e +d0 7b00 +51 3b01 +3b d4f +d1 7b01 +52 3b04 +3c d52 +d2 7b04 +54 3b10 +3e d5e +d4 7b10 +55 3b11 +43 3a0f +5d 3b51 +7f 15f5 +65 14b3 +3f d5f +d5 7b11 +c3 7a0f +dd 7b51 +ff 55f5 +e5 54b3 +56 3b14 +d6 7b14 +50 3b80 +55 3b91 +3f ddf +d5 7b91 +6e 3454 +54 3312 +58 99e8 +9c 69fa +51 3321 +6a 3464 +50 3322 +5c 3352 +58 3362 +76 3716 +75 3719 +a5 6cb1 +74 371a +7d 3759 +ad 6cf1 +93 6baf +7c 375a +73 9d8f +79 3769 +78 376a +7e 3dfc +64 3cba +fd 7759 +32 724 +fc 775a +f5 7799 +f4 779a +78 b740 +61 1c89 +72 b704 +60 b602 +69 1cc9 +7a b744 +62 b60e +93 eba5 +7c b750 +63 b60f +7d b751 +65 1c99 +76 b714 +bc 6dfa +78 9de8 +71 3721 +73 9d87 +79 3761 +51 bb01 +55 bb11 +43 ba0f +5d bb51 +55 bb91 +dd 7b59 +ff 55fd +e5 54bb +12 b24 +dc 7b5a +d5 7b99 +ee 7cdc +d4 7b9a +ee 7454 +d4 7312 +76 3dbc +f5 7719 +2a 6e4 +f4 771a +5c b352 +58 b362 +ad ecf1 +93 ebaf +7c b75a +79 b769 +74 1d90 +78 b76a +d5 7b19 +ee 7c5c +a ae4 +d4 7b1a +79 b761 +5e bb56 +c8 5a48 +5a bb66 +c0 5a88 +eb f645 +52 bba6 +d2 d9ac +cb 72e5 +35 533 +5f 1175 +45 1033 +e3 7e25 +4d 1073 +7f 1575 +65 1433 +6d 1473 +9f 4175 +85 4033 +87 4037 +8f 4077 +bf 4575 +a5 4433 +a7 4437 +af 4477 +cd 5073 +cf 5077 +90 e9aa +e7 5437 +ed 5473 +98 e9ea +ef 5477 +cb 72ed +35 53b +5f 117d +45 103b +e3 7e2d +4d 107b +7f 157d +65 143b +9f 417d +85 403b +5f 11f5 +45 10b3 +9f 41f5 +85 40b3 +87 40b7 +50 3320 +70 3720 +df 51f5 +c5 50b3 +c7 50b7 +d8 d348 +59 99eb +52 3324 +f8 d748 +79 9deb +72 3724 +d9 d9eb +3c 572 +d2 7324 +c0 7222 +3 28d +da 7364 +f9 ddeb +f2 7724 +50 33a0 +70 37a0 +51 33a1 +71 37a1 +d8 d3c8 +52 33a4 +f8 d7c8 +72 37a4 +3c 5f2 +d2 73a4 +f2 77a4 +c0 7a22 +3 a8d +da 7b64 +0 8a +1a 1cc +4 9a +1e 1dc +40 108a +5a 11cc +44 109a +5e 11dc +70 3f8a +8a 40cc +74 3f9a +8e 40dc +80 408a +9a 41cc +84 409a +9e 41dc +b0 4f8a +ca 50cc +b4 4f9a +ce 50dc +c0 508a +da 51cc +c4 509a +de 51dc +d8 7162 +1 8b +1b 1cd +41 108b +5b 11cd +45 109b +5f 11dd +71 3f8b +8b 40cd +75 3f9b +8f 40dd +b1 4f8b +cb 50cd +b5 4f9b +cf 50dd +c1 508b +db 51cd +70 b720 +78 b760 +bc edfa +71 b721 +61 1ca9 +72 b724 +60 b622 +69 1ce9 +7a b764 +a ec +0 aa +1a 1ec +10 3aa +2a 4ec +30 faa +4a 10ec +40 10aa +5a 11ec +50 13aa +6a 14ec +70 3faa +8a 40ec +80 40aa +9a 41ec +b0 4faa +ca 50ec +c0 50aa +da 51ec +b ed +31 fab +4b 10ed +41 10ab +5b 11ed +71 3fab +8b 40ed +b1 4fab +cb 50ed +51 bb21 +8e 42dc +ce 52dc +10 980 +8f 42dd +cb 52cd +50 1980 +cf 52dd +4a 12ec +4b 12ed +1b a367 +89 4249 +a 8ec +5b b367 +c9 5249 +4a 18ec +a acc +c8 7a62 +b acd +1f 375 +5 233 +7 237 +c2 daac +3f 775 +25 633 +c4 dab0 +27 637 +cc daf0 +2f 677 +f4 5f90 +0 208a +1a 21cc +80 608a +9a 61cc +31 8f89 +8f 60dd +81 608b +9b 61cd +41 9089 +85 609b +9f 61dd +e4 5eb0 +a 20ec +f4 5fb0 +0 20aa +1a 21ec +10 23aa +2a 24ec +20 24aa +3a 25ec +80 60aa +9a 61ec +e5 5eb1 +b 20ed +1 20ab +f5 5fb1 +1b 21ed +81 60ab +9b 61ed +1f 37d +5 23b +3f 77d +25 63b +b 22ed +a 28cc +0 28aa +99 6349 +1a 29ec +8a 6acc +3f 7f5 +25 6b3 +27 6b7 +9f 43f5 +85 42b3 +0 808a +1a 81cc +4 809a +1e 81dc +8e 60de +30 8f8a +4a 90cc +34 8f9a +4e 90dc +9e 61de +40 908a +5a 91cc +44 909a +5e 91dc +8f 60df +31 8f8b +4b 90cd +35 8f9b +4f 90dd +9f 61df +41 908b +5b 91cd +a 80ec +0 80aa +1a 81ec +8e 60fe +30 8faa +4a 90ec +9e 61fe +40 90aa +5a 91ec +b 80ed +8f 60ff +31 8fab +4b 90ed +8e 62de +4a 92cc +4e 92dc +c8 f262 +d1 5929 +b 82cd +cc f272 +d5 5939 +f 82dd +8f 62df +10 2982 +4b 92cd +14 2992 +4f 92dd +c3 58a7 +a 82ec +8e 62fe +a8 6440 +4a 92ec +b 82ed +8f 62ff +10 29a2 +a9 6441 +4b 92ed +6a 3444 +50 3302 +ea 7444 +d0 7302 +6a 34c4 +50 3382 +70 3782 +ea 74c4 +d0 7382 +f0 7782 +6a 3c44 +50 3b02 +ea 7c44 +d0 7b02 +6e 3c54 +54 3b12 +5c 3b52 +ee 7c54 +d4 7b12 +ea 7cc4 +31 a709 +d0 7b82 +1a 89ce +13 2307 +19 2343 +1b 2347 diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_7.txt b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_7.txt new file mode 100644 index 000000000..32db3ccad --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/8_split_7.txt @@ -0,0 +1,43905 @@ +43904 +f9 2bfb28 +7b 1f69d6 +f7 2bfa09 +f7 2bf9fb +f3 2bf9ca +f7 2bf75b +f5 2bf754 +f7 2bf74d +f9 2bf87a +f3 2bf71c +b5 2a2e63 +b1 2a2e32 +b5 2a2bb5 +b5 2a2b53 +7b 1f6728 +79 1f6721 +71 1f65ca +31 1d9cd9 +ff 2be89e +ef 2bdf3d +fd 2be897 +ed 2bdf36 +fb 2be86d +ff 2be83c +eb 2bdf0c +ef 2bdedb +f9 2be866 +fd 2be835 +ff 2be82e +e9 2bdf05 +ed 2bded4 +ef 2bdecd +bd 2a1cf8 +ad 2a1397 +a9 2a1366 +6b 1f6075 +69 1f606e +6b 1f6067 +7b 1f5714 +6b 1f4db3 +69 1f4dac +6b 1f4da5 +29 1d94cf +29 1d820d +e7 2bf0a8 +e5 2bf0a1 +e7 2bedfa +e5 2bedf3 +e3 2bedc9 +e9 2bef19 +eb 2bef12 +e1 2bedc2 +e3 2bedbb +e5 2bed91 +f7 2be747 +e7 2bdde6 +f5 2be740 +f7 2be739 +e5 2bdddf +f3 2be716 +e3 2bddb5 +f1 2be70f +f3 2be708 +e1 2bddae +e3 2bdda7 +e5 2bdd7d +ff 2be5f0 +f7 2be499 +ef 2bdc8f +e7 2bdb38 +fd 2be5e9 +f5 2be492 +f7 2be48b +ed 2bdc88 +e5 2bdb31 +fb 2be5bf +ff 2be58e +f3 2be468 +eb 2bdc5e +ef 2bdc2d +e3 2bdb07 +f9 2be5b8 +fd 2be587 +ff 2be580 +f1 2be461 +f3 2be45a +e9 2bdc57 +ed 2bdc26 +ef 2bdc1f +e1 2bdb00 +e3 2bdaf9 +e5 2bdacf +a5 2a2502 +a5 2a2254 +a9 2a237a +a5 2a21f2 +b5 2a1ba1 +a5 2a1240 +b1 2a1b70 +a1 2a120f +bd 2a1a4a +b5 2a18f3 +ad 2a10e9 +a5 2a0f92 +bd 2a19e8 +b1 2a18c2 +b5 2a1891 +a9 2a10b8 +ad 2a1087 +a1 2a0f61 +a5 2a0f30 +61 1f5f17 +6b 1f5dc7 +69 1f5dc0 +6b 1f5db9 +61 1f5c69 +61 1f4c55 +7b 1f5466 +6b 1f4b05 +79 1f545f +71 1f5308 +69 1f4afe +6b 1f4af7 +61 1f49a7 +21 1d9378 +29 1d9221 +31 1d8769 +29 1d7f5f +21 1d7e08 +db 2bb988 +d9 2bb981 +db 2bb97a +fb 2b77d3 +da 2b3639 +da 2b362b +99 29ede2 +b9 29ac3b +98 296a93 +5b 1f282f +7b 1ee688 +5b 1f2821 +7b 1ee67a +5a 1ea4e0 +5a 1ea4d2 +19 1d5c89 +39 1d1ae2 +18 1cd93a +d7 2bb862 +f7 2b76bb +d5 2bb85b +d7 2bb854 +f5 2b76b4 +f7 2b76ad +f3 2b768a +d3 2bb823 +f3 2b767c +f5 2b7652 +d7 2bb5b4 +f7 2b740d +d5 2bb5ad +f5 2b7406 +f7 2b73ff +f3 2b73dc +db 2bb6cc +d3 2bb575 +d5 2bb54b +f5 2b73a4 +d6 2b3513 +d4 2b350c +d6 2b3505 +d2 2b34e2 +d2 2b34d4 +d6 2b3265 +d4 2b325e +d6 2b3257 +da 2b338b +d2 2b3234 +da 2b337d +d2 2b3226 +95 29ecbc +b5 29ab15 +91 29ec8b +95 29ec5a +b1 29aae4 +95 29ea0e +b5 29a867 +99 29eb34 +95 29e9ac +b5 29a805 +94 29696d +90 29693c +94 29690b +94 2966bf +98 2967e5 +94 29665d +5b 1f2581 +7b 1ee3da +59 1f257a +79 1ee3d3 +71 1ee27c +5a 1ea232 +5a 1ea224 +11 1d5b32 +31 1d198b +eb 2b6e72 +df 2ba6f7 +cf 2b9d96 +df 2ba6e9 +db 2ba6c6 +df 2ba695 +cb 2b9d65 +cf 2b9d34 +db 2ba6b8 +df 2ba687 +cb 2b9d57 +cf 2b9d26 +de 2b23a8 +ce 2b1a47 +de 2b239a +ce 2b1a39 +da 2b2377 +de 2b2346 +ca 2b1a16 +ce 2b19e5 +da 2b2369 +de 2b2338 +ca 2b1a08 +ce 2b19d7 +a9 29a2da +9d 29db51 +8d 29d1f0 +bd 2999aa +ad 299049 +99 29db20 +9d 29daef +89 29d1bf +8d 29d18e +b9 299979 +a9 299018 +9c 295802 +8c 294ea1 +98 2957d1 +9c 2957a0 +88 294e70 +8c 294e3f +4b 1f1ece +6b 1edd27 +49 1f1ec7 +4b 1f1ec0 +69 1edd20 +6b 1edd19 +5b 1f156d +4b 1f0c0c +5b 1f155f +4b 1f0bfe +5a 1e921e +4a 1e88bd +5a 1e9210 +4a 1e88af +9 1d5328 +29 1d1181 +9 1d4066 +39 1d0820 +29 1cfebf +10 1cd7e3 +18 1cc678 +8 1cbd17 +c7 2baf01 +e7 2b6d5a +c5 2baefa +e5 2b6d53 +e7 2b6d4c +c7 2bac53 +e7 2b6aac +c5 2bac4c +e5 2b6aa5 +d7 2ba5a0 +c7 2b9c3f +f7 2b63f9 +e7 2b5a98 +d7 2ba592 +f5 2b63f2 +f7 2b63eb +e5 2b5a91 +e7 2b5a8a +df 2ba449 +d7 2ba2f2 +cf 2b9ae8 +c7 2b9991 +f7 2b614b +e7 2b57ea +f5 2b6144 +f7 2b613d +e5 2b57e3 +c6 2b2904 +c4 2b28fd +c6 2b28f6 +d6 2b2251 +c6 2b18f0 +d6 2b2243 +c6 2b18e2 +de 2b20fa +d6 2b1fa3 +ce 2b1799 +c6 2b1642 +de 2b20ec +d6 2b1f95 +ce 2b178b +c6 2b1634 +85 29e35b +a5 29a1b4 +85 29e0ad +a5 299f06 +95 29d9fa +85 29d099 +b5 299853 +a5 298ef2 +9d 29d8a3 +95 29d74c +8d 29cf42 +85 29cdeb +bd 2996fc +b5 2995a5 +ad 298d9b +a5 298c44 +84 295d5e +94 2956ab +84 294d4a +9c 295554 +94 2953fd +8c 294bf3 +84 294a9c +c3 2bac22 +4b 1f1c20 +d3 2ba56f +c3 2b9c0e +df 2ba3e7 +d3 2ba2c1 +cf 2b9a86 +c3 2b9960 +5b 1f12bf +4b 1f095e +e3 2b6d29 +e3 2b6a7b +c2 2b28d3 +6b 1eda79 +4a 1e98d1 +f3 2b63c8 +e3 2b5a67 +d2 2b2220 +c2 2b18bf +f3 2b611a +e3 2b57b9 +da 2b20c9 +de 2b2098 +d2 2b1f72 +ce 2b1737 +c2 2b1611 +5a 1e8f70 +4a 1e860f +bb 2a2f9e +b9 2a2f97 +3b 1d9e45 +7b 1bd2b4 +39 1d9e3e +79 1bd2ad +39 1a071c +3b 1a0715 +b7 2a2e78 +b7 2a2e6a +b3 2a2e47 +b7 2a2e16 +b3 2a2e39 +b7 2a2e08 +b7 2a2bca +b5 2a2bc3 +b7 2a2bbc +bb 2a2cf0 +b3 2a2b99 +b7 2a2b68 +b9 2a2ce9 +b3 2a2b8b +b5 2a2b61 +b7 2a2b5a +f5 2862d2 +f1 2862a1 +f5 286270 +b7 2696e6 +f5 286024 +f5 285fd0 +b5 26943f +f1 285ff3 +f5 285fc2 +33 1d9cee +33 1d9ce0 +3b 1d9b97 +33 1d9a40 +7b 1bd006 +39 1d9b90 +33 1d9a32 +71 1bd148 +33 1a05be +31 1a05b7 +79 1bcfff +71 1bcea8 +39 1a046e +71 1bce9a +bf 2a1d0d +bd 2a1d06 +af 2a139e +bb 2a1cdc +bf 2a1cab +ff 28511a +ef 2847b9 +b9 2a1cd5 +ab 2a136d +af 2a133c +fd 285167 +fd 285113 +ed 2847b2 +ff 28510c +fd 285105 +e9 2847d5 +ef 2847ab +ed 2847a4 +bf 26857b +af 267c1a +3b 1d8b83 +7b 1bbff2 +39 1d8b7c +2b 1d8214 +69 1bc94c +29 19fdbb +6b 1bc945 +69 1bc93e +29 19fdad +79 1bbfeb +69 1bb68a +39 19f45a +29 19eaf9 +6b 1bb683 +69 1bb67c +3b 19f453 +29 19eaeb +a7 2a2517 +a5 2a2510 +a7 2a2509 +a7 2a24b5 +a7 2a24a7 +a7 2a2269 +a5 2a2262 +a7 2a225b +a3 2a2238 +a7 2a2207 +a3 2a222a +a5 2a2200 +a7 2a21f9 +b7 2a1bb6 +a7 2a1255 +b5 2a1baf +a5 2a124e +a7 2a1247 +b3 2a1b85 +b7 2a1b54 +a7 2a11f3 +b1 2a1b7e +a3 2a1216 +a7 2a11e5 +b7 2a1908 +a7 2a0fa7 +bf 2a1a51 +b7 2a18fa +af 2a10f0 +a5 2a0fa0 +a7 2a0f99 +b3 2a18d7 +b7 2a18a6 +a7 2a0f45 +ef 28450b +bf 2a19ef +b3 2a18c9 +b7 2a1898 +ab 2a10bf +af 2a108e +a3 2a0f68 +a5 2a0f3e +a7 2a0f37 +e5 28591d +e5 28590f +e5 28566f +a5 268ade +e9 2857e9 +e1 285692 +e5 285661 +f5 285010 +f5 284fbc +e5 28465b +f1 284fdf +f5 284fae +e1 28467e +e5 28464d +b7 268424 +fd 284eb9 +f5 284d62 +fd 284e65 +f5 284d0e +ed 284504 +e5 2843ad +bd 2682d4 +ad 267973 +a5 26781c +ff 284e5e +fd 284e57 +f1 284d31 +f5 284d00 +e9 284527 +ef 2844fd +ed 2844f6 +e1 2843d0 +e5 28439f +bd 2682c6 +b7 268176 +b5 26816f +af 26796c +ad 267965 +a5 26780e +21 1d9386 +33 1d8a2c +33 1d877e +7b 1bbd44 +33 1d8770 +2b 1d7f66 +61 1bc7f5 +21 19fc64 +61 1bc7e7 +21 19fc56 +69 1bc69e +61 1bc547 +29 19fb0d +6b 1bc697 +69 1bc690 +61 1bc539 +29 19faff +71 1bbe94 +61 1bb533 +71 1bbe86 +61 1bb525 +33 19f2fc +79 1bbd3d +71 1bbbe6 +69 1bb3dc +61 1bb285 +39 19f1ac +29 19e84b +21 19e6f4 +71 1bbbd8 +6b 1bb3d5 +69 1bb3ce +61 1bb277 +31 19f047 +29 19e83d +21 19e6e6 +9b 29edf7 +bb 29ac50 +db 282266 +99 29edf0 +9b 29ede9 +bb 29ac42 +9a 296aa8 +da 279f17 +9a 296a9a +99 2656ce +db 282258 +9b 2656c7 +99 2656c0 +d8 279f10 +98 25d37f +da 279f09 +d8 279f02 +9a 25d378 +98 25d371 +1b 1d5c9e +3b 1d1af7 +5b 1b910d +19 1d5c97 +1b 1d5c90 +39 1d1af0 +3b 1d1ae9 +1a 1cd94f +5a 1b0dbe +1a 1cd941 +59 1b9106 +79 1b4f5f +19 19c575 +39 1983ce +5b 1b90ff +59 1b90f8 +79 1b4f51 +1b 19c56e +19 19c567 +3b 1983c7 +39 1983c0 +58 1b0db7 +18 194226 +5a 1b0db0 +58 1b0da9 +1a 19421f +18 194218 +97 29ecd1 +b7 29ab2a +d7 282140 +95 29ecca +97 29ecc3 +b5 29ab23 +b7 29ab1c +93 29eca0 +97 29ec6f +b3 29aaf9 +b7 29aac8 +91 29ec99 +93 29ec92 +95 29ec68 +97 29ec61 +b3 29aaeb +b7 29aaba +97 29ea23 +b7 29a87c +d7 281e92 +95 29ea1c +b5 29a875 +b7 29a86e +9b 29eb49 +93 29e9f2 +97 29e9c1 +bb 29a9a2 +b3 29a84b +b7 29a81a +99 29eb42 +9b 29eb3b +93 29e9e4 +95 29e9ba +97 29e9b3 +b5 29a813 +b7 29a80c +96 296982 +d6 279df1 +94 29697b +96 296974 +92 296951 +96 296920 +d2 279dc0 +92 296943 +94 296919 +96 296912 +96 2966d4 +d6 279b43 +94 2966cd +96 2966c6 +9a 2967fa +92 2966a3 +96 296672 +da 279c69 +d2 279b12 +9a 2967ec +92 296695 +94 29666b +96 296664 +95 2655a8 +d7 282132 +97 2655a1 +95 26559a +f5 27df30 +91 265577 +95 265546 +d3 282101 +f5 27df22 +93 265570 +91 265569 +97 26553f +95 265538 +b7 261398 +95 2652fa +d7 281e84 +f5 27dc82 +99 265420 +95 265298 +b5 2610f1 +db 281faa +d9 281fa3 +d3 281e53 +d1 281e4c +9b 265419 +99 265412 +b7 2610ea +b5 2610e3 +94 25d259 +d6 279de3 +96 25d252 +94 25d24b +90 25d228 +94 25d1f7 +d2 279db2 +92 25d221 +90 25d21a +96 25d1f0 +94 25d1e9 +94 25cfab +d6 279b35 +96 25cfa4 +94 25cf9d +d8 279c62 +d0 279b0b +98 25d0d1 +94 25cf49 +da 279c5b +d2 279b04 +d0 279afd +9a 25d0ca +92 25cf73 +96 25cf42 +94 25cf3b +13 1d5b47 +33 1d19a0 +11 1d5b40 +13 1d5b39 +31 1d1999 +33 1d1992 +1b 1d59f0 +13 1d5899 +3b 1d1849 +33 1d16f2 +5b 1b8e5f +7b 1b4cb8 +19 1d59e9 +39 1d1842 +12 1cd7f8 +12 1cd7ea +1a 1cd6a1 +12 1cd54a +5a 1b0b10 +51 1b8faf +71 1b4e08 +11 19c41e +31 198277 +51 1b8fa1 +71 1b4dfa +13 19c417 +11 19c410 +33 198270 +31 198269 +59 1b8e58 +79 1b4cb1 +71 1b4b5a +19 19c2c7 +4a 1d00f +38 1d8bdd +3c 1d8bac +1f 1d4a5f +2c 1d824b +f 1d40fe +b8 10fa84 +9b 10b937 +68 1bc9ad +61 1f5ccb +9f 2957b4 +e8 f3854 +cb ef707 +78 1bc04c +a4 26780d +7c 1bc01b +5f 1b7ece +71 1f536a +68 1bb6eb +6c 1bb6ba +4f 1b756d +61 1f4a09 +3e 19f483 +33 1d87d2 +2e 19eb22 +f8 f2ef3 +db eeda6 +e8 f2592 +cb ee445 +49 57989 +bc 10fab5 +49 1b01a7 +9f 10b968 +38 4698d +1b 42840 +28 4602c +b 41edf +1b 1cd6a0 +ec f3885 +e1 12cbd4 +ae d6ced +d5 281e7d +a3 11003c +6c 2a78e +61 63add +2e dbf6 +9f 1035c6 +94 25bf89 +fc f2f24 +5d 5831b +fd 2bf8ab +f1 12c273 +49 579eb +db 279c68 +be d638c +1f 3b783 +bf 2a2d13 +b3 10f6db +ae d5a2b +f 3ae22 +78 29dfc +5b 25caf +4b 2534e +5b 1b0b0f +3e d295 +2e c934 +9a 1035ea +39 19f19e +4 19222d +3f 3f92c +1a 3a4f3 +9e 1035c7 +35 4b3c +ac 107e6e +a5 29a1c0 +72 1b4e70 +9c 1035c0 +9e 1035b9 +1c 3a4c9 +1e 3a4c2 +18 3a498 +1a 3a491 +70 1b4e69 +a 39b30 +72 1b4e62 +e6 f34e9 +68 1bc99f +2a 1d8275 +2e 1d8244 +e6 f2227 +78 1bc03e +7c 1bc00d +68 1bb6dd +6c 1bb6ac +9d 2957ad +21 1d912c +7e 1bc014 +2d 19e8de +73 1f5363 +6e 1bb6b3 +3c 19f47c +31 1d87cb +90 29567a +6c 2a780 +68 2a74f +ba 10fadf +ae 10f14d +fc f2f16 +e8 f25e6 +db 279c5a +ec f25b5 +3e 469b7 +3a 46986 +2e 46056 +78 29dee +ee f387e +14 1d4662 +e3 12cbcd +ac d6ce6 +a1 110035 +6e 2a787 +63 63ad6 +2c dbef +fe f2f1d +5f 58314 +ff 2bf8a4 +f3 12c26c +ee f25bc +bc d6385 +1d 3b77c +bd 2a2d0c +b1 10f6d4 +ac d5a24 +d 3ae1b +ad 2a23ab +a1 10ed73 +7e 29e26 +6e 294c5 +3c d28e +2c c92d +18 1d4a34 +69 1bc9ac +48 1b8804 +a 19bc6c +c8 ef6ab +8a d2b13 +95 25d1e8 +79 1bc04b +a5 26780c +7d 1bc01a +58 1b7ea3 +eb 2bef20 +84 263664 +3b 19f4b3 +a 19a9aa +e 19a979 +3 1d3cc8 +20 1d7e15 +f9 f2ef2 +c8 ee3e9 +bb d635a +ab d59f9 +8c 10c26d +2d 4731e +88 10afdc +29 472ed +66 1b320e +ee 123742 +a9 10f184 +d7 e78e4 +98 10b93d +3d 47c7f +2d 4605c +a 19b9be +29 4602b +e9 f38b5 +c8 ef70d +8a d2b75 +8e d2b44 +83 10be93 +1b 19c322 +3 42d9c +a 9a1c +15 1940f1 +f9 f2f54 +fd f2f23 +48 26608 +d8 eedac +7d 2b0ee +c8 ee44b +6d 2a78d +bb d63bc +29 3ef9f +9a d2214 +8a d18b3 +95 25bf88 +8e d1882 +1b 19b060 +58 25c53 +48 252f2 +70 1b4e67 +2b c902 +a 875a +15 192e2f +e7 f34e8 +8a 29d1c3 +69 1bc99e +48 1b87f6 +fd 285111 +95 29690a +c8 ef69d +79 1bc03d +7d 1bc00c +58 1b7e95 +a2 25f7a3 +48 1b7534 +9a 10b8d4 +c8 ee3db +6b 1bc9a5 +94 1044df +4a 1b87fd +ff 285118 +29 19fe0d +ca ef6a4 +88 d2b0c +7b 1bc044 +6b 1bb6e3 +94 10321d +4a 1b753b +f7 27dc7b +bd cf0ac +39 19f4ac +63 63d22 +29 19eb4b +8 19a9a3 +da eed43 +ca ee3e2 +88 d184a +2a 1cfc77 +8e 10c266 +1b 1d5a44 +c8 ef6ff +48 265a6 +af 10f14c +9a 10b936 +9e 10b905 +f9 f2f46 +be 107522 +2f 46055 +58 25c45 +48 252e4 +88 d2b6e +8c d2b3d +81 10be8c +19 19c31b +ca ee444 +4a 265ad +2d dbee +88 d18ac +fb f2f4d +eb f25ec +69 5bb30 +da eeda5 +b9 d63b5 +a9 d5a54 +98 d220d +3d e54f +8c d187b +19 19b059 +5a 25c4c +4a 252eb +2d c92c +8 8753 +c9 ef710 +26 1d815d +2e 1d8006 +26 1d7eaf +cc 277ffe +66 1bc5e0 +34 19f395 +24 19ea34 +24 19e786 +be 10f870 +b6 10f719 +ae 10ef0f +a6 10edb8 +3a 46748 +62 2a66f +c8 e5ddf +ee f237e +4b 26612 +b 9a81 +48 1f0c66 +e7 2bed96 +b5 26817d +ef eb531 +eb eb500 +cc 2b9d2c +4b 265b0 +4c 1f0c35 +d 9a49 +db f0076 +b 9a1f +48 1f0c04 +e8 eb2aa +49 26619 +64 222d9 +d1 eff0c +e8 e9fe8 +1e 1d47be +16 1d4667 +e 1d3e5d +6c 22182 +d9 efdb5 +46 1b86e5 +46 1b8437 +25 19fa47 +25 19ea33 +2d 19e8dc +1a 202f +25 19e785 +12 1ed8 +1c 19b095 +46 5f90b +c 19a734 +52 1f11b9 +4 19a5dd +a7 110327 +de 12147c +a7 110079 +8e 10c028 +94 25bf95 +a 42f00 +15 1cd5d5 +a7 10f065 +71 229c0 +de 1201ba +af 10ef0e +9e 10b6c7 +39 1983cc +8e 10ad66 +29 197a6b +86 10ac0f +21 197914 +de 11ff0c +a 41c3e +15 1cc313 +e 1d511f +2 41ae7 +e7 eb3d8 +c6 ef5ee +a5 d6bfe +1b 427e0 +d6 e78e5 +42 264c6 +dc e7d53 +ef f363f +c6 ef340 +6b 2a517 +63 2a3c0 +b5 d629d +ad 2679d5 +c 1d3df4 +a5 d593c +e0 eb153 +dc e6a91 +eb 12bd1e +e7 f2226 +2f 1d8243 +ce ee1d5 +c6 ee07e +94 d1e47 +84 d14e6 +28 19fe1c +d9 eedaf +6c 2117c +9 87c6 +64 1bc5d9 +74 1bbf26 +36 19f38e +7c 1bbdcf +18 1dec +41 1b00b2 +38 46741 +60 2a668 +4f 1f098d +ec f3639 +e4 f34e2 +e 97a1 +34 3f7db +34 3f52d +2c 3ed23 +2d 58a2 +fb ebecf +88 c9560 +3a d018 +9a 2957e6 +2a c6b7 +db eeda8 +d9 eeda1 +6c 2116e +cb ee447 +c9 ee440 +14 193ea4 +eb 2b5910 +a2 2a2237 +4 193543 +99 d2210 +a7 10edb7 +5b 25cb1 +d3 120fe3 +4b 25350 +c3 120682 +49 25349 +c1 12067b +1b 9120 +42 1f1b1a +83 103af1 +e7 2bdad4 +9 87b8 +eb 2bdc50 +81 103aea +e5 2bdacd +ad cd6e5 +ff 2b77a2 +f3 12416a +a9 cd6b4 +be 2a1cfe +2d 45ee +dd eeace +c1 2bac1b +db eed46 +d9 eed3f +c9 ee3de +ef 2b58df +a6 2a2206 +16 193e49 +14 193e42 +6 1934e8 +27 3d902 +25 3d8fb +99 d21ae +bb ce00e +ab cd6ad +f6 12521e +a9 cd6a6 +4b 252ee +49 252e7 +6b 1ec7b7 +22 1d90de +50 1b0cc2 +db eedb4 +3f 4f48 +2f 45e7 +df eeac7 +c3 2bac14 +2d 45e0 +dd eeac0 +29 45af +d9 eea8f +64 1bc879 +8e 2637c4 +ca 127b74 +6f 63eb6 +d5 2b2249 +bd cf05a +4a 1b74e9 +8 147d +bd cdd98 +66 1bc880 +d7 2b2250 +95 2956b8 +d7 2b1fa2 +34 19f387 +3f 469c4 +8e 10afa4 +1b 1d4782 +76 1bbc71 +12 1c8e +72 1f6624 +2 132d +3c 19f230 +34 19f0d9 +2c 19e8cf +24 19e778 +60 2a65a +e4 f34d4 +9e 1035c5 +60 2a3ac +eb 2b6bd0 +32 1d16f1 +4b 26610 +be 10f862 +a6 10edaa +a1 2a121b +3a 4673a +6a 63e86 +6c 1bc722 +60 290ea +65 1edbfa +62 2a661 +69 211a2 +20 dac9 +e6 f34db +ac d6a9a +4f 1f0c3b +47 1f0ae4 +62 2a3b3 +de e6a34 +28 d972 +99 103342 +f6 f2e28 +e6 f24c7 +64 5ba0b +fe f2cd1 +68 2a511 +a3 2a1222 +f6 f2b7a +ee f2370 +6c 5b8b4 +ac d6aa8 +f2 12d52d +e6 f2219 +64 5b75d +a4 d5681 +6d 22433 +c8 e60f1 +7a 29ba9 +25 19fcf5 +6a 29248 +75 1b391d +28 d980 +67 1edc01 +38 d011 +1c 1d47b7 +ca e60f8 +14 1d4660 +c 1d3e56 +44 1b8430 +8 1ccd99 +27 19fa40 +0 1ccc42 +37 19f38d +10 1cc58f +27 19ea2c +0 1cbc2e +19 1deb +5c 1b7c26 +54 1b7acf +4c 1b72c5 +72 2acb2 +44 1b716e +18 1cc438 +10 1cc2e1 +2f 19e8d5 +8d 10c26e +8 1cbad7 +27 19e77e +85 10c117 +0 1cb980 +1e 19b08e +e 19a72d +dc 121475 +15 8cef +dc 1211c7 +87 29e36e +e7 2bdad6 +ad 10ef07 +59 25caa +d1 120fdc +a5 10edb0 +1c 19b097 +dc 11ff05 +9f 29db64 +97 29da0d +ed eb528 +8f 29d203 +e5 eb3d1 +87 29d0ac +c4 ef5e7 +80 103df9 +61 2a667 +40 264bf +de e7d4c +62 5ba35 +88 103ca2 +80 103b4b +18 193fda +c7 2817dd +35 3f7da +25 3ee79 +96 d20ee +86 d178d +3 1d4f38 +de e6a8a +ed f2376 +16 19c4a9 +e5 f221f +dc eeb2f +cc ee1ce +35 3f52c +25 3ebcb +9e d1f97 +96 d1e40 +58 25a07 +df 280fd3 +d7 280e7c +48 250a6 +cf 280672 +4c 1b8587 +40 24f4f +c7 28051b +d6 e68d1 +2a 19fe15 +9b 2957e5 +c6 e5f70 +8b 294e84 +e 19b9ef +2 83b7 +7 1ccec7 +9d caeb1 +44 1b8422 +3b d263 +1e 1d47b0 +9 166 +16 1d4659 +5c 1b7c18 +a6 25f526 +54 1b7ac1 +4c 1b72b7 +9d c9bef +44 1b7160 +95 c9a98 +48 1e992a +67 1bb5bd +40 1e87bf +20 5727 +3 15da +d0 efc07 +6f 1bb466 +28 55d0 +73 1f6623 +67 1bb30f +40 1e8511 +20 5479 +3 132c +5e 1b7c1f +56 1b7ac8 +0 83be +c 19b9f6 +52 1f247b +1c 19b087 +46 5f8fd +c 19a726 +18 1cd9aa +de 12146e +61 2a659 +82 26394c +27 19fc8e +18 1cd6fc +94 25bf87 +a 42ef2 +15 1cd5c7 +b4 2681d0 +ef 2b5b8d +a6 2a24b4 +61 2a3ab +c1 2b2b79 +a7 10f057 +b7 29a818 +3f 19f4e6 +96 10b810 +40 1b004f +86 10aeaf +1e 19b33e +96 296670 +33 1d16f0 +de 1201ac +c7 2bac45 +12 1cd548 +89 d18af +af 10ef00 +27 19e9cc +a7 10eda9 +3f 19f238 +48 265b4 +9e 10b6b9 +16 19b185 +39 1983be +86 10ac01 +21 197906 +1e 19b090 +a 41c30 +15 1cc305 +ae 2a1349 +c1 2b18b7 +ff 2b6542 +b6 2a2e69 +58 259f9 +98 cb12f +df 280fc5 +fc 285112 +4c 1b8579 +40 24f41 +45 1e9a51 +c7 28050d +e4 28465a +f7 f2dd3 +21 dac8 +dc e7d45 +c0 1206dc +58 1b0b6b +c6 ef332 +cb 2b2cd7 +c3 2b2b80 +b9 d6353 +63 1bc540 +29 d971 +89 29613f +87 264c3e +c6 ee31e +27 1d9170 +d6 279adf +0 1921f0 +73 1b4b5f +52 1b09b7 +c7 28026d +eb 12bd10 +6d 5b8b3 +c8 11f571 +e7 285676 +ad d6aa7 +65 5b75c +dd f0040 +de eeb28 +c6 ee070 +a5 d5680 +3 1d51e6 +84 d14d8 +6a 1bc9a6 +6f 1bc728 +63 290f0 +59 26f18 +c3 2b18be +ee 2847b8 +47 1e9a58 +28 19fe0e +99 2957de +bf d638d +63 1bb27e +29 c6af +c4 e5f69 +89 294e7d +15 1cd821 +a 4314c +97 2642dd +b4 26842a +c 19b9e8 +0 83b0 +5 1ccec0 +87 26397c +a4 267ac9 +d6 11fd53 +24 1d8156 +32 1d8a8d +22 1d812c +2e 1d7fa4 +26 1d7e4d +6a 1bc706 +66 1bc57e +7 19b899 +e2 f3456 +bf ce03f +e6 2b6a39 +62 1bb59b +30 19f364 +20 19ea03 +17 19af38 +ea f22eb +40 1b00a5 +4b 576e2 +f5 27c9c0 +43 5758b +b8 d60b4 +9b d1f67 +13 1cd5ab +35 1d171c +36 4686e +ba 10f83f +be 10f80e +14 1cd5c8 +b6 10f6b7 +3a 466e6 +36 465c0 +2e 45db6 +35 1d045a +26 45c5f +3b 46985 +66 2a63e +d7 279b42 +ee f35de +e2 f34b8 +e6 f3487 +c4 2b9987 +53 1b0a1a +6a 2a4b6 +75 1b4b8b +62 2a35f +3 967a +40 1f085f +43 5789b +11 3b664 +1 3ad03 +66 2937c +ed eb538 +fa f2cae +5b 580a5 +11 3b3b6 +1c 2067 +a 39b92 +f9 2b752c +5f 603b4 +75 1b38c9 +e4 2b6aa4 +3c cfee +24 1d7ea8 +38 cfbd +1b 8e70 +34 ce97 +37 19f0df +6 19bb48 +2c c68d +ff eabde +e6 2b6a47 +e8 eb248 +6b 1bc9a7 +33 1d8a8c +37 1d8a5b +27 1d80fa +60 222a8 +42 264b8 +2b 1d7fd4 +66 290c0 +e8 e9f86 +23 1d7e7d +27 1d7e4c +a 1d3e2c +68 22151 +2 1d3cd5 +e7 27d5c6 +60 21ffa +14 1940f0 +1 1ccc35 +a0 26783e +4 19378f +9a 10b634 +67 1bc82b +c2 ef55b +6b 1bc705 +21 19fa16 +25 19f9e5 +9a 10cbfa +e9 f2593 +e3 f3455 +67 1bb569 +2c 1d7fff +bd 2a1ca4 +da 128229 +7f 6456b +5e 603c3 +5e 1b7bcb +4a 1b729b +4e 1b726a +46 1b7113 +63 63a82 +1c 19b033 +eb f359e +e3 f3447 +8 19a703 +c 19a6d2 +f3 284d38 +52 1f1157 +0 19a5ac +3b 47cab +4 19a57b +9a 10b938 +3f 47c7a +da eeaa3 +ca ee142 +98 d1f0b +13 1d58fb +de 12141a +a5 10f05e +7c 29e2d +a7 110017 +8 41ee7 +8e 10bfc6 +8a 10ad35 +95 29540a +dc 1201b3 +91 2953d9 +9c 25c08a +27 46f20 +94 25bf33 +de 12116c +a 42e9e +15 1cd573 +97 26402f +b7 10f964 +a7 10f003 +37 4686d +de 120158 +14 194152 +eb 2b5bbe +3 1d4f2a +a2 2a24e5 +af 10eeac +a4 26786f +4 1937f1 +5b 25c4f +a7 10ed55 +6b 1eca65 +22 1d938c +20 19e747 +27 45c5e +de 11feaa +1a 4253d +15 1cc2b1 +e3 f3765 +e7 f3734 +62 1b2f9d +c2 ef5bd +c6 ef58c +a1 d6bcd +a5 d6b9c +20 196405 +3a 3f95c +11 19c1d2 +67 2a63d +a8 10f123 +8b 10afd6 +42 26464 +21 da74 +e6 e9ec9 +dc e7cf1 +d0 2b322d +d7 efc96 +e5 f24cd +ef f35dd +e3 f34b7 +e7 f3486 +48 25356 +ce ef435 +c6 ef2de +32 19700d +3a 3f6ae +6f 2a4e6 +6b 2a4b5 +67 2a38f +63 2a35e +4a 2630d +25 d7f7 +21 d7c6 +dc e7a43 +e7 f2472 +a9 2679a4 +8 1d3dc3 +ac 10ef08 +35 e406 +25 daa5 +67 2937b +c4 2b2ba9 +28 45de0 +27 1d910e +e0 eb0f1 +eb 12bcbc +e7 f21c4 +c2 2bac21 +ce ee173 +c6 ee01c +9c d1f3c +35 e158 +94 d1de5 +22 19fcbe +6b 1b3397 +88 d160c +2d d94e +8c d15db +67 290cd +5e 259dd +42 1f1b2a +5a 259ac +4e 2507c +4a 2504b +c0 11f41a +cc 2b2a52 +46 24f25 +c4 2b28fb +7 19bb47 +3 19bb16 +2d c68c +62 1f5f0f +25 c535 +7a 1f677b +1c 8e45 +4 1d3cff +6b 1bc6f9 +18 8e14 +14 8cee +c 84e4 +8 84b3 +4 838d +d8 e6a60 +c8 280639 +3d 6211 +5d 26c99 +30 1d8a86 +38 1d892f +3c 1d88fe +1f 1d47b1 +1e 19b33c +17 1d465a +2c 1d7f9d +f 1d3e50 +24 1d7e46 +7 1d3cf9 +b8 10f7d6 +9b 10b689 +13 19b155 +9e d21e3 +60 1bc856 +68 1bc6ff +60 1bc5a8 +64 1bc577 +47 1b842a +24 19ea26 +2f 46063 +9f 295506 +97 2953af +e8 f35a6 +e0 f344f +c3 ef302 +70 1bbef5 +bd ce038 +60 1bb594 +78 1bbd9e +7c 1bbd6d +5f 1b7c20 +88 10af7a +70 1bbc47 +36 19f07e +26 19e71d +f8 f2c45 +db eeaf8 +59 5803c +51 57ee5 +e8 f22e4 +49 576db +cb ee197 +41 57584 +b8 10f838 +13 19b1b7 +bc 10f807 +9f 10b6ba +9e d2245 +b4 10f6b0 +97 10b563 +a8 10eed7 +3 19a856 +38 466df +1b 42592 +34 465b9 +2c 45daf +28 45d7e +b 41c31 +24 45c58 +6f 21178 +26 da9f +97 10346f +e8 f3608 +ec f35d7 +e0 f34b1 +28 1d94ce +e4 f3480 +a4 d592f +b4 2610f0 +97 25cfa3 +ae d6a3f +7a 2b127 +68 2a4af +4b 26362 +60 2a358 +43 2620b +2e d948 +9f 103318 +94 25bcdb +97 1031c1 +a 9770 +51 581f5 +ea f35ad +41 57894 +d3 279b11 +74 29cd6 +57 1b09e9 +36 d13e +26 c7dd +5c 1ebf8 +51 57f47 +be d60de +b6 d5f87 +ae d577d +f5 124202 +68 291ed +4b 250a0 +6c 1bc6ce +60 29096 +4f 1b8581 +43 24f49 +3e cfe7 +9e 2957b5 +36 ce90 +2e c686 +da e6a59 +8b d15a4 +7f 22d9b +d8 e6a52 +1f 1d4a6f +ca 280632 +9a c9ec8 +7c 1ed3ef +3f 620a +c8 28062b +98 c9ec1 +3d 6203 +4a 1d001 +e6 2b5785 +da 2b3389 +74 1bbc16 +57 1b7ac9 +a 1ccd32 +12 3b5fa +b3 cf1db +2 1ccbdb +dc e6a2f +a 1d40cc +9c c9e9e +8 193609 +a1 29a181 +1f 1d4a0d +cc e60c0 +c8 e608f +9c c9e90 +98 c9e5f +4e 1cfd0 +4c 1cfc9 +4a 1cf9f +10 19b1bb +48 1cf98 +1c d99 +60 1bc848 +d1 2b2218 +6b 63e85 +68 1bc6f1 +60 1bc59a +d1 2b1f6a +6b 63bd7 +32 1d8a7f +60 1bb586 +6b 62bc3 +32 1d87d1 +2a 1d7fc7 +2e 1d7f96 +26 1d7e3f +70 1bbc39 +6a 1f6074 +68 1bb42f +b9 cdd67 +60 1bb2d8 +bd 261248 +b1 cdc10 +6b 62915 +ba 10f7cf +b5 299543 +62 1bc84f +d3 2b221f +d3 2b1f71 +72 1bbeee +62 1bb58d +28 19fb6e +62 1bb2df +3c 19f1ce +34 19f077 +24 19e716 +fa f2c3e +d 1459 +ea f22dd +f5 27c9b2 +90 2953cc +35 1d170e +68 2a4a1 +90 10c798 +d9 11fe71 +60 2a34a +ea f359f +36 46860 +26 45eff +ba 10f831 +be 10f800 +ae 10ee9f +f8 f2c99 +8 39b37 +5c 1ebea +ea 12cf7d +3e 46709 +3a 466d8 +36 465b2 +2e 45da8 +35 1d044c +78 29b40 +66 2a630 +d7 120000 +6d 21171 +24 da98 +95 103468 +78 2b120 +53 1b0a0c +70 1b4b59 +6a 2a4a8 +26 45f0d +75 1b4b7d +62 2a351 +95 1031ba +70 5c33b +53 581ee +60 5b9da +43 5788d +76 29ccf +3b 1d8be3 +66 2936e +2b 1d8282 +34 d137 +24 c7d6 +17 193e4a +ed eb52a +8 41c39 +fa f2ca0 +78 5c1e4 +5b 58097 +ea f233f +68 5b883 +4b 57736 +f5 27ca14 +bc d60d7 +b4 d5f80 +ac d5776 +53 1b0c66 +a4 d561f +78 29e5e +20 45c89 +2c 1d92c1 +7a 29b47 +43 1e109 +42 26218 +25 19fc93 +80 263951 +6a 291e6 +75 1b38bb +28 d91e +48 1f09b6 +3c cfe0 +2c c67f +24 c528 +8c 103c65 +c 3ab6e +8 3ab3d +1c 1cc3fb +7b 63578 +14 1cc2a4 +ac 10f154 +8f 10b007 +46 26495 +b6 d6297 +10 1cc273 +2 1cb919 +73 1ee281 +fe 27cb71 +18 19c574 +25 1d9107 +b5 d755f +e9 12bcb5 +a9 110136 +a1 10ffdf +88 10bf8e +18 19c2c6 +18 19b2b2 +74 29a1a +ca ef706 +18 1d4786 +bd 268336 +1c 1d4755 +eb 12ccc0 +31 19f055 +6b 22409 +ca 2b2a2a +f5 27df20 +e3 12cb69 +8 1d3e25 +0 1d3cce +e5 27d5bf +a5 26787e +4 1d3c9d +a9 10ee74 +7b 1b4f66 +98 10b62d +18 19b004 +61 1bc855 +c0 ef554 +61 1bc5a7 +48 1b8556 +44 1b83ce +da 12978b +23 19fa0f +28 c6b0 +27 19f9de +e1 f344e +c8 ef3fd +c0 ef2a6 +75 1bbec3 +65 1bb562 +5f 1e92a2 +16 1d5bc9 +50 1b7d4c +33 19f35c +2e 1d7ff8 +37 19f32b +23 19e9fb +de 278c78 +27 19e9ca +6 19a822 +1e 42570 +65 1bb2b4 +58 1b7bf5 +5c 1b7bc4 +50 1b7a9e +54 1b7a6d +48 1b7294 +4c 1b7263 +44 1b710c +da 1284c9 +7f 6480b +a 19bc6e +3b 19f205 +2 19bb17 +33 19f0ae +23 19e74d +de 2789ca +27 19e71c +d8 eea9c +28 45bc +ea f360f +c8 ee13b +ab d574b +a1 1102ef +21 47196 +e6 1235eb +dc 121413 +1a 1d4a2d +a9 110198 +2d 47070 +29 4703f +66 1b2f60 +25 46f19 +18 19b066 +21 46ee8 +dc 121165 +87 29e30c +35 47b28 +8a 10bf95 +85 10aea9 +95 29666a +18 19b314 +25 471c7 +dc 120151 +6b 2a757 +0 42d94 +87 29e360 +ed 2b5b86 +a4 2a24ad +a9 10eed6 +ad 10eea5 +59 25c48 +a5 10ed4e +3d 19f1dd +3d 479d1 +35 4787a +22 1d93e0 +6b 1ecab9 +1c 19b035 +eb f35a0 +b 1d538f +28 1d94dc +7 1d5269 +3 1d5238 +69 1eca5e +20 1d9385 +2d 45dae +29 45d7d +25 45c57 +2d 1d925e +21 45c26 +dc 11fea3 +35 1d177e +ad d57e7 +c 41c06 +6b 2a4a9 +e1 f375e +c0 ef5b6 +c4 ef585 +7a 5c4ed +44 2648e +96 10b562 +31 198267 +40 2645d +5a 1b7e9c +38 19715d +62 5b9d3 +30 197006 +7a 5c23f +a5 d592e +b5 2610ef +88 103c40 +8 87b7 +7b 2b126 +18 193f78 +3d d29d +c8 ee19d +a9 267c44 +8 1d4063 +33 1d8a1e +61 2a357 +c7 28177b +95 25bcda +dc e6a83 +31 197fc7 +91 25bca9 +87 295fbe +eb f35ac +75 2af97 +ca ef404 +c5 ee318 +65 2a636 +ae 10ef01 +b7 d6234 +21 3ee48 +a7 d58d3 +86 d172b +13 19af09 +35 1d04ae +b5 29ab13 +de e6a28 +a5 29a1b2 +69 1bc6fe +69 1bb43e +84 29600a +48 2635a +29 1d820b +e5 f21bd +d8 eeafe +28 461e +7d 2ae40 +dc eeacd +2c 45ed +75 2ace9 +ee f3640 +d4 ee976 +eb f360e +24 4496 +cc ee16c +c4 ee015 +7a 5af7d +16 42419 +aa 268f0c +29 3ecf1 +a7 d5625 +7b 29e64 +4b 1b87fe +65 290c6 +ff 2b64ee +b6 2a2e15 +67 1bc88d +58 259a5 +df 280f71 +54 2587f +6a 2a518 +75 1b4bed +af 106e6e +d7 280e1a +4c 25075 +48 25044 +cf 280610 +44 24f1e +4c 1b8525 +40 24eed +c7 2804b9 +2a 19fdb3 +b5 29a865 +70 1b4bb9 +8f 294e53 +2b c654 +2f 19fb35 +23 c4fd +a5 299f04 +58 1af839 +18 192ca8 +7b 29e56 +ec f25c3 +4d 579ba +ed 2bef4a +e1 12b912 +62 1bc85d +9c 103304 +98 1032d3 +94 1031ad +9c 2967b4 +90 10317c +ab ce96f +88 102972 +2b 5878 +db efd58 +8 3987b +70 1b4bad +7b 5c1ea +23 1d93df +27 1d93ae +82 29d06c +4b 1e8910 +2 1d5237 +61 1bc847 +26 1d0e23 +40 1b869f +f5 284fba +1a 19c56d +c0 ef546 +39 d25c +cf 280362 +23 1d9131 +69 1bc6f0 +61 1bc599 +48 1b8548 +40 1b83f1 +37 1d8a4d +27 1d80ec +a1 110041 +12 1d48d6 +61 1bb585 +26 1cfb61 +1a 19b2ab +2 1d5239 +33 1d87d0 +27 1d7e3e +1a 1d477f +61 1bb2d7 +58 1b7be7 +a2 25f4f5 +48 1b7286 +99 c9bbe +40 1b712f +63 1bc84e +42 1b86a6 +f7 284fc1 +21 19fcb6 +80 263943 +c2 ef54d +1c 4382b +3c 469be +6b 1bc6f7 +6d 62bed +42 1b83f8 +65 62a96 +29 19fb5f +21 19fa08 +25 19f9d7 +77 1bbebc +13 1ed9 +63 1bb58c +b3 2a1b77 +e1 f34b0 +29 1d94cd +52 1b7d45 +42 1b73e4 +b5 cef55 +31 19f355 +7b 6458e +bd 2a1c96 +da 12821b +7f 6455d +21 19e9f4 +6b 63c2d +4e 1b8580 +42 24f48 +25 19e9c3 +ad 2a1335 +ca 1278ba +6f 63bfc +25 19fc85 +42 2620a +1c 42569 +6b 1bb435 +63 1bb2de +4a 1b728d +8 19b9c5 +42 1b7136 +63 63a74 +29 19e89d +c4 278157 +2d 19e86c +21 19e746 +25 19e715 +a9 d5744 +3b 1a0777 +22 1cfb20 +9a 264404 +33 d10e +3f 1a0746 +1a 19c5cf +13 1d58ed +a6 107d2c +1a 19b30d +e7 2843b4 +ad d57e5 +e2 2bf068 +de 12140c +2d 1d7f9c +44 26480 +7c 29e1f +8 41ed9 +7b 64848 +e1 f34a2 +91 2953cb +9c 25c07c +fb 2b77e1 +94 25bf25 +61 2a349 +3b 19f4b5 +a7 10eff5 +3f 19f484 +2c 19eb1b +21 1d7e6a +96 10b7ae +86 10ae4d +13 1d462b +1e 19b2dc +27 45efe +de 12014a +69 1f4b60 +be cddf0 +ba cddbf +eb 2b5bb0 +a2 2a24d7 +af 10ee9e +a7 10ed47 +7b 63586 +3f 19f1d6 +9a 10b688 +12 19b154 +e8 285a96 +18 19b304 +d8 eeaf0 +28 4610 +6b 1eca57 +22 1d937e +68 1bc93d +25 1d7e45 +a1 d6bbf +25 da97 +21 da66 +dc e7ce3 +14 19af3e +71 1b4b58 +29 d90f +25 d7e9 +85 295fb7 +21 d7b8 +61 5b9d9 +85 d14e7 +c6 ee2bc +53 1b7a9a +a9 267996 +8 1d3db5 +ab d6a0d +11 19af02 +27 1d9100 +c7 28020b +25 c7d5 +dc e6a21 +56 5efaa +79 5c1e3 +ea 285a9d +eb f233e +69 5b882 +46 5e649 +1a 19b30b +61 5b72b +d9 f000f +14 42412 +a8 268f05 +a9 d57a6 +4 41ab1 +a5 d561e +79 29e5d +6a 1bc944 +6b 291e5 +29 d91d +28 19fdac +2d c67e +8d 294e4c +29 c64d +25 c527 +85 294cf5 +dc e6773 +47 1b86e8 +87 10c182 +a7 107fdb +c7 ef5f1 +1a 19c2bf +7 1d526b +35 465ba +18 3a1dc +27 1d10c4 +d7 2bb5a4 +87 10c174 +a5 107fd4 +a7 107fcd +18 19c572 +9e 10cc39 +1a 43b11 +9f 1048ea +3a 3f96a +5a 26f80 +86 d2741 +fe ebf01 +85 29e305 +7a 22dd9 +a6 ce59a +5b 1ec31 +87 ca3f2 +c4 2b15d7 +3 1d5248 +23 1d10a1 +d3 2bb581 +ed 2b5be8 +a4 2a250f +5 1d4f54 +27 1d1070 +d7 2bb550 +1 1d5241 +3 1d523a +21 1d109a +3d 4f4d +d0 eebf3 +dc 28222b +d1 2bb57a +23 1d1093 +81 d14b6 +8d 264aee +d2 eebec +de 282224 +d3 2bb573 +32 1d87df +d5 2bb549 +27 1d1062 +d7 2bb542 +82 294d20 +19 9127 +40 1f1b21 +c8 e7105 +c 446 +47 1b843a +67 1b4293 +cf ef49a +19 9119 +e2 f34aa +8d 10c024 +de 12975a +8f 10c01d +87 10bec6 +ad 107e7d +fe 1255b3 +a7 107d1f +18 19c2c4 +1f 3b4d5 +be 108a90 +bf 108a91 +3b 3f969 +df f00a7 +7a 22dd7 +de e7d58 +ba 2a1ccd +5a 1ec30 +86 ca3f1 +3 1d4f9a +23 1d0df3 +47 1b83d8 +63 1b4262 +b 1d50e3 +19 90b7 +a6 cd5e6 +1 1d4f93 +1d 8e46 +3 1d4f8c +5 1d4f62 +7a 1ee679 +39 4f10 +8a 294bc9 +21 1d0dec +3 318 +f 193950 +20 4465 +2c 197a9d +d0 ee945 +dc 281f7d +25 1d0dbb +37 1d0771 +32 1d041f +a7 106d19 +df 2ba685 +e4 124b62 +9c 10cc32 +18 43b0a +9d 1048e3 +38 3f963 +58 26f79 +84 d273a +1a a3e1 +6a 1b3396 +17 1d490a +35 1d076a +37 1d0763 +be cf362 +9f cb1ba +bc cf307 +b1 108656 +95 2652ec +dc 2b239f +78 22dd2 +a4 ce593 +87 10aeb2 +a7 106d0b +18 19b2b0 +13 1d48e7 +71 22c0c +33 1d0740 +37 1d070f +31 1d0739 +9d 26418d +c7 128a03 +6d 1bb6bb +37 47b2f +a6 260a98 +32 3e543 +3e 1d1b7b +d9 e7a11 +be 2685dc +c4 2b18e9 +e6 124b5b +97 2966d3 +e4 124b00 +71 1ee2de +a6 10ed48 +7a 63587 +9e 10cc2b +ed f25c4 +1a 43b03 +69 2949c +58 26f6b +a2 ce879 +67 1edc53 +9f 1048dc +bc 108a29 +78 22dc4 +9c d3502 +18 a3da +27 1d10c2 +df e7d4b +fc ebe98 +f1 1251e7 +9d cb1b3 +38 6233 +6d 22185 +c8 e5e43 +7 1d3d09 +65 2202e +37 1d04c3 +2f 1cfcb9 +38 1d9e91 +e7 1235fa +78 1bd300 +70 1bd1a9 +8 41e77 +7b 647e6 +97 10b573 +8 19bc65 +b7 1073cc +f7 2bf9f9 +e7 2bf098 +f8 f4209 +1f 1d47b3 +17 1d465c +5e 1f158f +f 1d3e52 +3f 1d060c +35 1d04bc +37 1d04b5 +2d 1cfcb2 +7e 1ed3e8 +2f 1cfcab +73 2af6f +95 10b56c +8d 10ad62 +de 128498 +87 10ac04 +20 54d9 +e0 f34a3 +2d 45db0 +d7 2bb800 +9d 10cc31 +53 1f26d8 +19 43b09 +56 1afa2a +8a 102c89 +2f 3efcb +fe f2c7d +5f 58074 +f7 2b7659 +bd 108a8a +73 1ee531 +39 3f962 +dd f00a0 +59 26f78 +85 d2739 +78 22dd0 +1b a3e0 +38 e52d +58 1f15c5 +b8 2a1cc6 +9e cb1b9 +b0 108655 +a5 ce592 +1a 2091 +13 1d4639 +7 1d3ca7 +33 1d0492 +a6 106cb4 +37 1d0461 +27 1cfb00 +30 1d9cd8 +c7 12773f +38 47ca3 +28 47342 +ba 2695bf +e7 123598 +dc eeb21 +70 1bd147 +b 1d3e21 +3 1d3cca +e 19a97b +27 46f12 +5 1d3ca0 +3f 1d05aa +31 1d048b +3c 19713c +a4 106cad +1f 192fef +33 1d0484 +3e 197135 +a6 106ca6 +37 1d0453 +7a 1ed3b7 +2b 1cfc7a +2f 1cfc49 +f0 f3e04 +2c 1967db +f 19268e +dc 280cbb +23 1cfb23 +2e 1967d4 +de 280cb4 +27 1cfaf2 +6d 1b336b +70 1ee2dd +2f 45da9 +18 1cc6e8 +3a 3f95a +11 19c1d0 +bf 108a83 +9e 1048db +ed ea274 +79 22dc3 +d7 2820d0 +9d d3501 +f7 123efb +5b 26f71 +78 2b0be +53 1b8fa8 +19 a3d9 +f0 1251e6 +5a 1ec22 +1f 3a4c1 +74 1bbf18 +10 1f35 +31 1d9a39 +7f 63555 +1a 1d5cff +3a 1d1b58 +1a 8e6f +c4 11f44d +da f0015 +7a 1b4fc7 +e6 124b07 +73 1ee2e5 +5b 1b0e1f +87 25c5e0 +9a 10cc08 +74 29cc8 +39 1d8bdc +1a 43aaf +ba 108a61 +9c 10c914 +9f 104888 +3e 3f939 +3a 3f908 +1f 3b791 +14 194154 +bf 2a2d21 +b3 10f6e9 +62 5b793 +de f0046 +66 5b762 +5a 26f1e +fa ebed0 +dc efd83 +fe ebe9f +f3 1251ee +db e7d28 +17 3a0ca +77 1bbf1e +30 6088 +f 1d410e +50 1e9120 +13 1f3b +57 1d539 +15 3a0c3 +86 26397d +a9 260bb6 +8e 103cdc +c9 ef700 +86 103b85 +84 103b7e +c4 11f44b +9a 2957e4 +bb 1089fe +c5 11f44c +b0 2613c1 +5b 1b916d +87 26492e +73 1ee2e3 +0 1cb974 +4 41d5f +21 3eb8e +5a 1b0e1e +86 25c5df +e2 f3448 +53 1ea13c +f2 284d45 +be 108a2e +3e 3f937 +bb 108a60 +9d 10c913 +bf 108a2f +b4 2613f2 +3f 3f938 +34 1982fb +9a 1035f6 +3b 3f907 +30 1982ca +df f0045 +2c 58a1 +fe ebe9d +f3 1251ec +8c 295eb5 +80 10287d +25 3ebbf +7e 22da6 +73 5c0f5 +7a 22d75 +da e7d27 +de e7cf6 +d3 121045 +5e 1ebff +53 57f4e +85 10c10b +3a 1d8b74 +af 106bc2 +45 5e8f1 +5a 1ebce +58 1e8fc9 +1b 1de4 +38 5f31 +30 5dda +f 1d3e60 +50 1e8e72 +13 1c8d +74 1bbc78 +10 1c95 +a 1ccd94 +18 d68 +f4 2b63f1 +4 1ccc13 +56 1afa38 +96 1034d2 +86 102b71 +d6 e6941 +93 10c794 +96 1034c4 +86 102b63 +c7 ef343 +d8 f000e +e7 2b6cf6 +df e7cf7 +9a d3476 +78 1b4fc0 +a4 260781 +59 1b0e18 +85 25c5d9 +3a 198428 +1b 194280 +38 1983cd +8a 294e77 +f8 ebe67 +db e7d1a +9b cb127 +ff 28510a +d8 f0070 +df e7d59 +9a d34d8 +9e d34a7 +93 10c7f6 +1e a3b0 +13 436ff +f8 ebec9 +d9 e7d21 +9b cb189 +ff 28516c +91 2652bb +df eedd7 +d8 2b236e +42 1b86b4 +9f cb158 +95 26528a +dc 2b233d +3e 6209 +5e 1e92a1 +33 3f558 +52 1afa07 +56 1af9d6 +de 11ff0a +b9 260255 +e3 124acb +1a 1d5cf1 +d6 efca3 +58 1b9159 +a2 260a67 +9a 10cb98 +3a 1d1b4a +32 3e4e1 +3e 1d1b19 +f6 ebafc +1b 1cd9a2 +ba 2685ab +d7 e7954 +be 26857a +78 1b4fb2 +a8 110137 +8b 10bfea +59 1b0e0a +ff 2be82c +d9 e7cb1 +5a 1b9160 +c2 128cd1 +18 19c5c8 +da f0007 +89 d28d1 +1c 1dab +a5 10f052 +5b 1b0e11 +78 1b4f5e +38 198421 +db e7cb8 +b0 10f6e1 +bc 2a2d19 +fd 285103 +9e 10cbc9 +d8 f0062 +e7 2b6d4a +1e 43ad2 +1a 43aa1 +58 26f09 +67 1edbf1 +c2 2b18af +ba 108a53 +24 197696 +9b 1048ab +ff 2be88e +9f 10487a +f8 ebebb +dd e7ce2 +3e 3f92b +15 19c1a1 +3a 3f8fa +25 3d909 +c0 ef2fa +1b 3b752 +7f 1f5735 +59 1ebba +da f0069 +dd e7d52 +98 d34d1 +a7 29a1b9 +9c d34a0 +24 3ebbc +91 10c7ef +5a 26f10 +9 97da +1c a3a9 +fe ebe91 +df e7ce9 +f1 125185 +40 1b86ad +9d cb151 +73 5c0e9 +5b 1ebc1 +30 465ea +3c 1d9c22 +3c 6202 +31 3f551 +38 61d1 +65 29374 +86 1028c3 +e 1cbd4f +84 1028bc +59 1b9166 +85 264927 +97 295411 +71 1ee2dc +1b 19c5ce +38 1a071b +c4 e5d1b +d8 278c4c +58 1b0e17 +84 25c5d8 +7d 2ae32 +29 45d7f +ad 2679d3 +fc ebe96 +f1 1251e5 +ce e60c7 +7c 22d9f +71 5c0ee +78 22d6e +1c 192f95 +3e 6207 +33 3f556 +46 1b8429 +f9 ebec8 +fd ebe97 +9a cb188 +9e cb157 +93 1044a6 +b0 1085f3 +7d 22da0 +79 22d6f +1e 2060 +13 3b3af +30 3f4fc +59 1b7e34 +16 1cc2b9 +2b 1d94d4 +51 1b7cdd +6 1cb958 +41 1b737c +67 5b763 +d7 efef0 +69 211a0 +a5 2a11ec +ce 2bada9 +c2 127771 +67 63ab3 +c7 ef58f +bf 1077c1 +1a 1cc433 +9f 10cbca +1e 1cc402 +16 1cc2ab +26 3ebc3 +4 1cb951 +a9 25f646 +6 1cb94a +f6 ebafa +f7 ebafb +84 c918c +9a 2957d6 +d8 278c3e +b 87bf +58 1b0e09 +29 45d71 +a4 107f6f +31 1d174d +f9 ebe58 +d6 eec1f +9b 29db33 +5b 1b915f +78 1bd2ac +19 19c5c7 +da 278c45 +5a 1b0e10 +6a 1bb444 +53 1ea12e +f2 284d37 +98 25c0ad +ed 284564 +96 d208e +91 2652c7 +15 19c19f +1f 90ef +18 1cc686 +3a 3f8f8 +11 19c16e +bb 108a52 +bf 108a21 +9a 1048aa +e9 ea243 +b5 2995b3 +9e 104879 +f9 ebeba +d6 eec81 +e8 f2346 +49 5773d +3f 3f92a +9a 1035e8 +3b 3f8f9 +68 2924f +67 1bc57d +c2 28023b +58 1ebb9 +a5 107d26 +9d d349f +18 192d08 +42 5757e +5b 26f0f +73 5c0e7 +1d a3a8 +a4 298e9b +3c 6200 +31 3f54f +38 61cf +3a 1d8936 +85 10becd +56 581cc +1a 4259f +ea f234d +4b 57744 +8d ca5b2 +d3 121037 +f0 125184 +bd cf2f8 +23 1d93e1 +98 cb181 +a8 d57b5 +9 3abac +91 10449f +da e6a57 +6a 29256 +a 39b3e +5e 1ebf1 +70 5c08d +53 57f40 +af 106bb4 +5a 1ebc0 +7d 29e2e +3d 6201 +16 8f97 +28 c6be +1c 2059 +11 3b3a8 +18 2028 +65 1b453a +dc 2b23a1 +fe 125613 +a3 110048 +d5 281e89 +18 19c2b8 +8 192347 +6b 294f5 +7b 1b4cb6 +44 57862 +e5 eb443 +38 198111 +47 1b86da +ec 12cd5b +75 29a29 +a5 d68ee +16 19b183 +25 19799b +8b 102c96 +96 d208c +2 42da9 +4b 56482 +b 398f1 +ca 2b2a28 +17 1cd88a +17 1cd5dc +56 1b8d9a +76 1b4ea1 +e 39b6f +ec f362b +ca ee3f2 +e6 1248bb +7a 1b4fb9 +15 193eb3 +97 104793 +be 1087e4 +f9 f4208 +97 1044e5 +1b 3b514 +13 3b3bd +b4 2a1b3e +f6 ebdaa +b4 cf212 +53 1eada +1f 1cc40f +fe ebc53 +85 29e057 +fa ebec2 +7a 22b2b +1 1d4f2f +72 229d4 +5b 1e983 +5c 26f48 +30 5e3c +61 1b4509 +d1 281e58 +e7 2b6a4a +ad 107e7b +dc 2b233f +fe 1255b1 +a3 10ffe6 +d5 281e27 +43 1b86a9 +e8 12cd2a +a1 d68bd +c2 27818f +8a 10af73 +95 295648 +72 1bbc40 +65 1b44ca +82 10c0ec +cb 11f7c5 +c0 278188 +3 19bb18 +a8 110199 +23 197971 +d3 281e51 +4 1cb943 +77 1ee2b2 +d2 2b1f70 +d7 281e20 +82 25b5fe +b1 d7272 +65 1b428c +cd ef493 +7a 1bbda5 +c5 ef33c +db 2bb986 +a5 d568e +c 438 +da e6a65 +7f 22da7 +59 582ea +47 1b842c +27 1976f4 +d8 f0000 +e7 2b6ce8 +cc 2b9ae0 +8d d28f4 +dd e7cf0 +98 d346f +a7 29a157 +c4 2b9989 +bb 29ac40 +17 1d5bd8 +67 1bb55b +3 1578 +36 1d1782 +37 1d1a31 +37 1d1783 +57 1b9047 +34 198307 +3a 1d8928 +85 10bebf +76 1b4bf1 +e 398bf +15 19c201 +34 198059 +1a 19b05f +77 1b4ea0 +4 192531 +f 39b6e +56 1b0cf8 +77 1b4bf2 +4 192283 +f 398c0 +56 1b0a4a +14 193eb2 +97 10cae1 +17 3b37e +b6 108939 +b7 10893a +96 104792 +bf 1087e3 +55 60512 +27 1cfe10 +d7 2ba2f0 +b7 10868c +d7 eff50 +95 d33b8 +72 22c80 +df efdf9 +62 1edc2f +47 1b7426 +d7 efca2 +5b 26cd1 +7a 22b29 +8e 25b786 +3d 3f683 +72 229d2 +d6 e7c01 +b2 2a1b76 +94 cb069 +a 19a6fc +67 1b327f +22 1cfabe +d6 e7953 +b2 2a18c8 +5d 26f47 +31 5e3b +10 1c93 +21 1cfdc8 +b1 d7210 +41 1b8402 +30 1d87d8 +6a 5bb8c +45 1b83d1 +34 1d87a7 +6e 5bb5b +61 1b425b +95 2953a8 +65 1b422a +1 19b871 +21 1976ca +38 6231 +72 1b4e00 +25 197699 +8 407 +7b 22d76 +43 1b83fb +26 19ea2d +f5 286032 +54 1f2451 +e5 f246b +67 1b4223 +c2 277ee1 +96 10b570 +31 198275 +b 19b9c1 +49 5fd39 +43 5fbe9 +41 5fbe2 +d3 281e5f +27 197692 +63 5ba42 +25 19768b +8b 102986 +61 5ba3b +f3 27dcb8 +80 25b349 +75 1b3bd9 +30 1d0418 +6a 29504 +95 d310a +b5 cef63 +de e7a3c +42 1b73f2 +25 1966e7 +c2 1289cf +5a 1b8e5e +77 1b3bd2 +75 1b3bcb +6a 294f6 +7a 1b4cb7 +37 197041 +35 19703a +2a c965 +62 1bc5af +c 1cba9a +da 2b20c7 +15 1cd883 +a 431ae +bc 268335 +80 2636a3 +74 1b4e9a +c 39b68 +36 198302 +d9 f006f +e4 1248b4 +9b d34d7 +d8 2ba6bc +3c 19f23e +95 10478c +9d 104635 +95 1044de +38 3f6b5 +67 1ed9b3 +b6 cf20b +97 cb063 +b4 cf1b0 +d4 2b2248 +51 1ead3 +be cf0b4 +4 1934e1 +9f caf0c +bc cf059 +dc 2b20f1 +70 229cd +59 1e97c +51 1e825 +38 19f44b +3a 5f8c +df 1297bb +5e 26f41 +32 5e35 +d7 129664 +71 1b3ba8 +71 1b3b9a +77 1b3b70 +11 19b1b0 +33 197010 +c7 ef2e1 +4e 2633e +31 197009 +37 196fdf +2a c903 +6d 1bb40d +24 19e724 +7 19a5d7 +a 39ba0 +5f 603c2 +17 1cd87c +34 1d19c9 +b6 268485 +80 263695 +74 1b4e8c +7f 5c4c9 +c 39b5a +be 26832e +17 1cd5ce +34 1d171b +b6 2681d7 +56 1b903a +1f 43adf +56 1b8d8c +84 10bebc +1f 43831 +82 26369c +76 1b4e93 +e 39b61 +57 1b0ceb +74 1b4e38 +eb 27d4ae +fc 12536c +15 194153 +a 9a7e +db f0068 +f8 f41b5 +c4 2b163b +e6 1248ad +a4 10edb1 +e4 124852 +57 1b0a3d +74 1b4b8a +99 d34d0 +ff 2b7502 +f3 123eca +a4 107d15 +15 193ea5 +a 97d0 +28 c65c +71 1ee52a +a2 ce5cb +50 26b66 +b6 10892d +3e 19f237 +97 104785 +b4 1088d2 +32 3f805 +be 1087d6 +b6 10867f +bc 10877b +b4 108624 +a1 29a18f +6a 1bc9b4 +32 3f557 +70 229bf +bf 2a1a5f +d6 eff43 +d6 efc95 +f6 ebd9c +d7 e7bf4 +f4 ebd41 +95 cb05c +19 1d5a3b +53 1eacc +70 22c19 +30 60dc +fe ebc45 +85 29e049 +59 1f2888 +a3 29a196 +f6 ebaee +51 1f2731 +df e7a9d +fc ebbea +57 1e90f7 +9d caf05 +95 cadae +7a 22b1d +72 229c6 +5b 1e975 +78 22ac2 +38 5f85 +5c 26f3a +a6 ce848 +30 5e2e +bb 110d4c +c5 12779a +b3 110bf5 +e5 1235f3 +24 19fa48 +6d 1b3121 +65 1b2fca +85 10ac09 +d6 12833f +5e 1b7e6d +35 19f394 +c6 1279de +53 1f11bc +56 1b7d16 +de 12824a +79 1b4f4f +f6 124198 +a5 106a62 +2d 196590 +7e 1b3cc6 +e6 123837 +73 1ed015 +25 196439 +76 1b3b6f +39 47c4e +fe 1240a3 +c7 127793 +e4 12b8e0 +bb 110d3e +b3 110be7 +e7 1235ec +24 19fa3a +6d 1b3113 +65 1b2fbc +85 10abfb +a7 106a5b +2d 196582 +25 19642b +8 8761 +db 2ba6b6 +bb d761e +ef 124c61 +a4 d6943 +ed ea01c +e5 e9ec5 +fb 2b650f +4e 5fa62 +af 1080d0 +6a 1b4656 +34 1d1a29 +35 1d1a2a +35 1d177c +74 1b4e98 +7e 22da8 +73 5c0f7 +36 198300 +74 1b4bea +d 39b67 +37 198301 +75 1b4beb +d 398b9 +37 198053 +95 10cada +d7 2bb552 +9d 10c983 +15 19c44f +e1 2856a0 +40 1f1abf +c8 e70a3 +95 10c82c +c8 27803f +ea eb2b1 +b5 108933 +8e 29cf46 +f7 2b73ab +bd 1087dc +4a 1f0c6b +39 4f1e +25 1cfe09 +d5 2ba2e9 +b5 d7251 +b5 108685 +de 12115e +42 1f0b14 +73 1ee283 +39 3f6b4 +31 3f55d +3c 620e +d5 eff49 +13 a289 +50 1f146e +dd efdf2 +23 1cfdcf +b3 d7217 +a1 26783f +0 1d3c5e +d5 efc9b +1b a132 +38 e27f +58 1f1317 +a2 298c25 +13 9fdb +50 1f11c0 +d4 e7bfa +b0 2a1b6f +96 cb062 +12 1f3a +fd ebc4b +ca 278046 +65 1b3278 +f5 ebaf4 +98 2957cf +27 1966e0 +90 cad2a +55 1ea104 +d7 280bc0 +f4 284d0d +b7 cef5c +f4 2b6141 +79 22b23 +71 229cc +71 643dc +33 5e34 +c5 127738 +b5 cf211 +8e 263824 +7a 5b22b +7e 5b1fa +6e 5a899 +19 1d5cf7 +de 11fefe +ec ea273 +27 45c50 +e5 123591 +20 19fa17 +69 1b30f0 +16 c47 +24 19f9e6 +6d 1b30bf +61 1b2f99 +65 1b2f68 +16 1cd5cf +85 10aba7 +3a 3e69a +3e 3e669 +f8 f2f47 +2e 3dd08 +3d 196e8f +a5 106a00 +a4 cd58b +29 19655f +7a 1b3c95 +25 1963d7 +f7 27df29 +bd cf35a +59 1b7bea +cc ee40c +7b 1b3d06 +e 1cbaa1 +b7 cf20a +f4 2b63ef +6b 1b33a5 +ec ea265 +25 1979a9 +e7 12358a +34 1a0339 +7d 1b3a12 +e4 ea10e +77 1b38c2 +20 19fa09 +69 1b30e2 +29 197821 +7a 1b4f57 +61 1b2f8b +87 10aba0 +5b 5f3df +85 10ab99 +a7 267ac3 +59 5f3d8 +8a 10afd5 +95 2956aa +b 19a6ff +9 19a6f8 +49 5ea77 +3 19a5a8 +1 19a5a1 +fd ebef9 +d3 280b9d +39 196eb2 +ad 29a05b +a1 106a23 +a7 1069f9 +7b 5b238 +3d 196e81 +a5 1069f2 +79 5b231 +5c 25c76 +a6 cd584 +31 196d5b +35 196d2a +2a c655 +29 196551 +25 1963c9 +17 1d5bca +4e 1b858e +42 24f56 +25 19e9d1 +6f 63c0a +9e 25bdd5 +17 1d591c +4a 26361 +21 c7a4 +2d 19fddc +88 263a9a +36 1d1774 +2d 19e87a +a7 2607e9 +25 19e723 +b 39b9f +37 1d1a23 +37 1d1775 +5b 1afafb +21 1cfb2a +75 1b4bdd +53 1af9a4 +ca 2b2cd6 +74 1bd186 +88 d15aa +7c 22da1 +71 5c0f0 +34 1982f9 +53 258aa +b 1d3e2f +5f 1b8ee2 +70 299f7 +7c 1bd02f +27 45c52 +35 1982fa +23 1cfb31 +77 1b4be4 +35 19804c +8a 296145 +ce ef497 +a5 d58da +fa 2bfb20 +23 1976c3 +4c 265d7 +4a 1e9bdf +13 439ab +1b 90be +e3 285699 +42 1f1ab8 +7 1d3fa9 +ad d5783 +a5 d562c +fa 2bf872 +4a 1b84fd +13 436fd +bc d6147 +18 1cc43a +3a 3f6ac +29 dbbd +29 c65b +e7 f2218 +e0 2b57af +a3 ce5ca +b7 10892c +96 104784 +e5 ea11d +33 3f804 +bf 1087d5 +37 1982a1 +92 25bf5f +3b 4f17 +89 264abf +27 1cfe02 +90 10444c +d7 2ba2e2 +b7 10867e +db e6a04 +ca 278038 +65 1b326a +6a 1b4356 +3b 3f6ad +f4 f408f +95 d33aa +a 1cd04e +11 a282 +30 60da +fc f3f38 +6c 1ecaf0 +f4 f3de1 +64 1ec999 +d7 281e22 +9d d3253 +4b 5fd40 +95 d30fc +a 1ccda0 +5b 26cc3 +78 2ae10 +e2 2b57b6 +58 1af8a9 +7a 22b1b +1e 4256e +a7 d5687 +a0 298c1e +16 42417 +30 5e2c +b7 2613f8 +c4 ee2b5 +b5 cf203 +94 cb05b +10 1f33 +67 1b3271 +6b 5bb99 +7b 1ee678 +8a 25b4a7 +25 1966d9 +10 1c85 +b4 26139e +97 25d251 +9f 10cc38 +1d 3a4ba +f 3ae30 +b7 cdc9a +86 ca703 +11 1f34 +1b 1d5cfe +cc 120864 +15 9a1 +c6 2b28a2 +8c 103cd3 +74 1bbc6a +10 1c87 +1a 1d5a51 +42 1f1dc8 +f7 2be6e3 +b6 1073cb +3a 1d18aa +7b 22b1c +10 19c1d1 +d2 efc10 +42 1e87c8 +76 1b4e3f +fe 125373 +53 1b0cc8 +ac d57d8 +e8 f35fa +d3 e7b6f +a6 ce598 +84 25b326 +11 193e82 +15 193e51 +d3 e78c1 +b0 ceed1 +93 cad84 +75 1ee2ab +97 104731 +46 1f1df9 +17 3b63a +13 3b609 +77 1f55ec +ba 1087b3 +ad cd6d7 +50 604e2 +67 1b2f61 +93 1044b4 +97 104483 +79 2b0af +17 3b38c +13 3b35b +90 d3388 +b4 2997ee +52 26dc7 +e 1cbb03 +1a 1d5c8f +90 d30da +52 26b19 +14 9fb2 +f2 ebd79 +d4 efc2c +99 29eb40 +f6 ebd48 +d3 e7bd1 +f5 28501c +8c 103cd5 +44 1b86d0 +76 22c51 +72 22c20 +19 1d59e7 +53 1ea78 +34 60b9 +54 1e9151 +fa ebc22 +f2 ebacb +3a 1d1ae8 +db e7a7a +d3 e7923 +1b 1cd940 +dc f003f +64 5b75b +b0 cef33 +75 1ee30d +8 3ab49 +a9 ce72a +36 198052 +7e 22afa +72 22972 +7a 22d69 +29 5633 +5f 1b0b94 +53 1d55c +7c 1b4ce1 +70 216a9 +d5 128347 +b 3adff +1d 193ffc +11 9c4 +6f 1f4de2 +17 99a +8e 103ccc +63 1f5cc4 +d8 e7a64 +44 1b00e4 +4 193553 +cc e7144 +9f 10c98a +c4 e6fed +da 2b3637 +46 1b00dd +13 4243b +1f 1d5a73 +30 46588 +3c 1d9bc0 +44 1b00d6 +ee f231c +4f 57713 +4 193545 +f 3ab82 +cc e7136 +c6 e6fe6 +9f 10c97c +bc 110ac9 +17 19c448 +c4 e6fdf +da 2b3629 +8e ca5ac +86 ca455 +f2 27df67 +19 1ddd +48 1b020a +e8 eb4f6 +0 193522 +4 1934f1 +78 29ba2 +13 1d5ba7 +36 1d19ce +11 1c86 +ca 2b9d62 +40 1b00b3 +1b 1d5a50 +56 26b3c +13 1d58f9 +3a 1d18a8 +32 1d1751 +36 1d1720 +3a d2c6 +33 1d1a00 +16 1cd827 +b3 1088a7 +15 1d5bd1 +92 1046ff +3b 1d18a9 +76 22995 +33 1d1752 +c0 12098a +58 1b0e19 +84 25c5da +16 1cd579 +b3 1085f9 +3b d2c7 +15 1d5923 +92 104451 +1a 911f +76 1b4e3d +30 1982d6 +88 10c29e +34 1982a5 +7a 1b4d17 +81 10be8e +72 1b4bc0 +a 3988e +76 1b4b8f +d 39b59 +51 60545 +30 198028 +88 10bff0 +34 197ff7 +b0 ceecf +52 1b0cc7 +56 1b0c96 +86 294d4f +e9 f35f9 +44 5f904 +c8 ef451 +10 19412f +23 471ff +55 1b9040 +d2 e7b6e +20 5477 +10 193e81 +14 193e50 +d8 eed9e +f3 eba68 +23 46f51 +55 1b8d92 +d2 e78c0 +27 1966de +94 10c7bb +b6 1088d7 +17 43988 +36 3f7e0 +57 1b9039 +3 1d3f86 +61 222ab +93 10c802 +36 3f532 +b3 108909 +95 10c7bc +b7 1088d8 +96 104730 +37 3f7e1 +16 3b639 +bb 1087b2 +51 604e1 +77 1b4e92 +23 1cfddf +41 1b86a2 +de 280f70 +d3 2ba2bf +b3 10865b +27 1cfdae +d7 2ba28e +b7 10862a +18 3a4fa +96 104482 +3f 3f68a +34 19804d +9a 103348 +37 3f533 +16 3b38b +c9 281650 +24 574a +48 1b019a +f6 ebd46 +53 26dc6 +f 1cbb02 +76 22c4f +bf d638b +72 22c1e +57 26b49 +7e 22af8 +15 9fb1 +46 1b7175 +11 9f80 +d2 e7bd0 +d6 e7b9f +3f 19f482 +90 cb038 +35 60b8 +e 19a6cb +f3 ebaca +25 549d +3b 1d1ae7 +d2 efebe +8e 294bfa +c 144c +4 12f5 +bb 261520 +1a 1cd93f +ce 11f5a9 +d6 e78f1 +dd f003e +58 1af8a7 +fa 124320 +b1 cef32 +e7 2b5788 +ad 106bb9 +fe 1242ef +b5 cef01 +e9 27d4a7 +7f 22af9 +c 18a +77 229a2 +4 33 +73 22971 +35 5e0a +48 1b01fc +42 1b00ac +65 5a74a +dd 1281f0 +2d 3dd10 +25 3dbb9 +d5 128099 +0 193514 +b 3ab51 +6f 1f4b34 +b5 25fe2f +42 5789a +4 1934e3 +d2 279b10 +b9 10fa83 +94 cadbb +4 19253f +94 c9da9 +76 229a1 +44 575b4 +e5 eb195 +fb 2b77df +46 1af0c9 +43 1f1d77 +9 431a8 +14 192e92 +1f 3a4cf +94 cadad +4a 579f1 +d6 e6933 +c6 e5fd2 +96 c9da2 +94 c9d9b +86 c9441 +90 cad8a +23 46eef +55 1b8d30 +b 431b1 +c6 2b15e0 +8c 102a11 +fb 2be85f +c6 2b18ee +91 1046f9 +11 1cd5a4 +a4 107fc3 +57 1b0cf9 +12 19c478 +e9 f2345 +58 1b8eb9 +c0 128a2a +53 1d56a +5f 1b0ba2 +1a 19c321 +3d 469bf +57 1b0a4b +12 19c1ca +35 46868 +d8 efd60 +e7 2b6a48 +51 1b0cc1 +32 1982d1 +13 194129 +30 198276 +18 dca +b7 ceefa +f4 2b60df +17 1940f8 +bc 108779 +d1 e7b68 +78 1b4d12 +d9 f000d +59 1b0b6a +51 1b0a13 +3a 19817a +32 198023 +d7 2bb852 +1b 193fd2 +28 c95e +38 19811f +13 193e7b +d5 2bb7f7 +f8 ebbb9 +db e7a6c +f0 eba62 +d3 e7915 +d1 e78ba +36 1d1a24 +93 cad22 +3c 19f1dc +1f 19b08f +ff 2be890 +95 10472a +b4 d623c +65 1b2f5a +91 1044ad +ff 2be5e2 +95 10447c +b4 d5f8e +b8 2a1a18 +d4 efee8 +d7 e7c02 +92 d3381 +57 1f275b +1a 1de3 +57 1eaa9 +12 a228 +d8 efdc2 +e7 2b6aaa +92 d30d3 +57 1f24ad +29 19fb6d +74 1f65fa +97 29d9ab +5c 26c9a +fb 2be5b1 +91 10444b +c6 2b1640 +58 26c69 +67 1ed951 +c2 2b160f +54 26b43 +50 26b12 +af 108132 +62 2908f +6e 1bc6c7 +d1 e7bca +93 cb032 +f7 285015 +6d 2a77f +c8 ee43d +8e 103cce +97 cb001 +d4 2b21e6 +51 1ea71 +db 281fb8 +36 60b2 +56 1e914a +67 1bb5cb +32 6081 +f5 2be6dc +c 192694 +52 1e9119 +20 1d8125 +17 1f0a +f8 ebc1b +38 1d1ae1 +d9 e7a73 +d1 e791c +19 1cd939 +ba cf083 +39 1d88ce +50 26db2 +de f0038 +8d d2902 +d3 129387 +b2 cef2c +77 1ee306 +9b caedb +b8 cf028 +c9 2b2cd0 +34 19804b +70 2296b +53 1e81e +51 1e7c3 +67 1bb31d +32 5dd3 +52 1e8e6b +f5 2be42e +c4 2bae97 +c 1923e6 +d7 129602 +28 1d7fce +1f 1db3 +20 1d7e77 +e 19a9db +17 1c5c +f5 27ca22 +54 1e8e41 +c6 128d02 +13 1c2b +10 192e61 +1b 3a49e +12 1d5b9a +e9 12ba67 +1a 1d5a43 +12 1d58ec +50 1b8d54 +5b 60391 +36 1d19c2 +13 1cd84b +b2 268454 +17 1cd81a +b6 268423 +70 1b4e5b +7b 5c498 +8 39b29 +74 1b4e2a +51 1b0cb3 +fb f2ef9 +8c 294ead +d1 e7b5a +3a 1d189c +32 1d1745 +1b 1cd6f4 +28 46080 +ba 2682fd +2c 4604f +be 2682cc +13 1cd59d +b2 2681a6 +24 45ef8 +b6 268175 +78 1b4d04 +8c 294bff +f8 ebbab +9a 29d886 +f0 eba54 +92 29d72f +37 1d9a71 +52 1b9009 +56 1b8fd8 +5 19b8a2 +4a 2661d +10 19c471 +1b 43aae +52 1b8d5b +75 633f9 +56 1b8d2a +18 19c31a +10 19c1c3 +1b 43800 +dd e7a42 +98 d31c1 +a7 299ea9 +7b 1ee6e8 +8 1cbd79 +76 1b4e31 +25 1976fb +dc 2b20f3 +fe 125365 +14 1c54 +53 1b0cba +70 1b4e07 +34 198299 +cc 27831c +d3 e7b61 +fd 2b629b +b4 2a2bc2 +7a 1b4d0b +8e d18e4 +5b 1b0b63 +68 294ef +78 1b4cb0 +38 198173 +30 19801c +3b 3f659 +a 976e +cc 27806e +d3 e78b3 +b8 cf01a +b0 ceec3 +7e 1bbdd6 +92 10caa3 +ed 285a74 +e1 f243c +9e 10462d +ed e9fc6 +16 1940f9 +96 10ca72 +16 4397b +a8 d5753 +9 3ab4a +6d 1f4b2d +d8 efdb4 +95 10ca78 +e7 2b6a9c +65 1f49d6 +d0 efc5d +1e 43824 +1a 437f3 +58 26c5b +67 1ed943 +c2 2b1601 +50 26b04 +af 108124 +b2 1088fc +b6 1088cb +3e 19f1d5 +97 104723 +37 1d9d81 +db 2bb6da +36 3f7d4 +de e67ea +f6 2b76aa +39 1d1b50 +55 1ea94 +b9 2685b1 +51 1ea63 +ba 1087a5 +be 108774 +2d c680 +b2 10864e +9b 1045fd +b8 10874a +9f 1045cc +69 5bb92 +fb 27de0f +88 25b4a0 +97 104475 +f8 ebc0d +fc ebbdc +9e 29d8b7 +37 1d9ad3 +3a 3f64c +6a 1bc952 +32 3f4f5 +c 1cbb08 +1e 1d47c0 +21 1d1036 +a3 267af2 +70 2295d +12 1d4638 +eb 12ccd0 +e3 12cb79 +bb 2a1a2e +d2 eff12 +bf 2a19fd +d6 efee1 +85 d27ab +d5 e7bfb +90 d337a +94 d3349 +3b 1d88d5 +52 26db9 +1 9683 +14 a252 +55 1eaa2 +10 a221 +b5 2696df +d2 efc64 +dd e7aa4 +98 d3223 +a7 299f0b +9c d31f2 +c 1cbdaa +d5 e794d +bc 268573 +90 d30cc +5e 26c93 +52 26b0b +5d 1e94b +18 a0ca +27 1d0db2 +82 294a70 +55 1e7f4 +f2 ebd6b +99 29eb32 +f6 ebd3a +bb 29ac4e +d3 e7bc3 +f0 ebd10 +d7 e7b92 +95 caffa +3b 1d1b57 +72 22c12 +4c 1af225 +21 54dc +57 1ea9b +53 1ea6a +d9 281fb1 +34 60ab +1 15e1 +ca 280392 +30 607a +15 1f03 +fa ebc14 +f2 ebabd +b8 cf07c +dc f0031 +b0 cef25 +53 1e90c6 +40 1b83ff +9d caea3 +7e 22aec +23 1d103d +72 22964 +4c 1aef77 +fe ebeff +5f 1e944 +f6 ebda8 +57 1e7ed +f2 ebd77 +53 1e7bc +1 1333 +30 5dcc +d5 281e29 +a3 10ffe8 +ce 2b9d31 +d4 efc3a +44 1e87f2 +4c 1aef79 +94 d30a9 +4 1cbc61 +f5 2be430 +c 1923e8 +52 1e8e6d +d7 129604 +4 192291 +dc e67e3 +2d 19fb9e +21 c566 +1 42fef +c6 11f444 +d6 efc33 +46 1e87eb +4c 1aef6b +52 1ea12f +b0 cf1e1 +46 1aee1b +43 1f1ac9 +9 42efa +84 1028ac +1f 3a221 +94 d309b +4 1cbc53 +c 1923da +2f 19fb97 +23 c55f +dc e67d5 +81 294d26 +5b 26f1d +59 26f16 +9 9a26 +cc e5e74 +e 1d5111 +2 41ad9 +a6 2a11f2 +d5 efee9 +d0 efc09 +67 1bb5bf +cd ef6df +90 d3078 +55 1f2452 +27 19ea2e +8d d2b4e +22 54d2 +0 192260 +85 d29f7 +63 2a66e +34 1d19c7 +8e 10c01a +37 19f07d +6 19bae6 +5f 26ca0 +15 19af41 +30 1d174a +34 1d1719 +31 1d19f9 +b5 261401 +14 1cd820 +96 2642dc +32 196d5f +31 1d174b +35 1d171a +97 d1e41 +90 2953d8 +86 264c3f +b7 2681d6 +39 d2c0 +27 1cfe00 +18 9118 +23 4719d +55 1b8fde +74 1b4e36 +13 19c477 +7a 22d77 +c6 2b15d0 +32 1982cf +8a 10c297 +36 19829e +a5 d58cc +ce ef489 +f0 ebd0e +a1 107fa3 +74 1b4b88 +59 25cb8 +a0 107c82 +85 d1479 +1b 19c320 +38 1a046d +d9 efd5f +29 587f +32 46891 +3e 1d9ec9 +0 1d523e +49 1e8917 +f8 ebbb7 +71 1b4e68 +9 39b36 +12 194128 +16 1940f7 +21 471f8 +d0 e7b67 +15 19c4a1 +92 cafcf +a5 26787c +d8 27899e +50 1b0a12 +58 1d969 +f7 eba99 +84 c912a +57 1b0c97 +1a 193fd1 +3d 3e66f +f9 ebbb8 +d6 ee97f +f1 eba61 +63 1f5c70 +29 470a1 +d8 e7a10 +6f 1b33c6 +21 46f4a +d0 e78b9 +67 1b326f +b3 ceec9 +15 19c1f3 +92 cad21 +67 1ed9a5 +15 43981 +d6 11fda5 +91 10c7fb +15 436d3 +b5 1088d1 +5b 1b0b71 +68 294fd +a3 268db4 +36 196d90 +75 1b4e8b +21 1cfdd8 +d1 2ba2b8 +dc 280f69 +b1 108654 +bc cf305 +d5 2ba287 +b5 108623 +a9 25f8f4 +97 29691f +f4 ebd3f +93 d3380 +d0 2ba565 +90 25bf66 +b2 cf1d8 +55 26df0 +51 26dbf +d 1cbafb +10 192e0d +32 607f +21 196408 +fa 124072 +dd efd90 +2d 58b0 +4 1d526f +4d 1e8948 +73 5c335 +9b d3229 +b8 d7376 +93 d30d2 +74 1b4e8a +20 1cfdd7 +75 1f65f9 +d0 2ba2b7 +59 26c68 +55 26b42 +51 26b11 +fb eac0d +13 9f79 +f1 284d3f +50 1f115e +1c 192ce7 +3e 5f59 +63 2908e +6f 1bc6c6 +ca 280384 +7f 1bc013 +d0 e7bc9 +d4 e7b98 +92 cb031 +96 cb000 +4e 1b725c +71 22c18 +fc f2cca +4a 1b722b +35 197048 +c6 11f692 +54 1eaa1 +30 1d8a16 +16 1f09 +69 1b43b2 +f9 ebc1a +4c 26337 +1e 1d49fe +d6 ee9e1 +fd ebbe9 +61 1b3247 +44 261e0 +16 1d48a7 +39 1d1ae0 +d0 efeb7 +5a 1af8a0 +23 1966af +d3 280b8f +b3 cef2b +af 106bb2 +d7 280b5e +92 d331f +57 1f26f9 +9a caeda +92 cad83 +27 45c60 +74 1ee2aa +b5 2a2e71 +8e 102a0a +96 cad52 +7d 22af2 +79 22ac1 +56 25888 +75 2299b +71 2296a +5c 1e94a +e5 12bbf1 +58 1e919 +d3 e68ad +df 279ee5 +ed 2bf1f8 +e1 12bbc0 +54 1e7f3 +30 1d8768 +3f 5f5a +7c 1ed13f +9a c9c18 +37 5e03 +74 1ecfe8 +c4 2b9c37 +33 5dd2 +1e 1db2 +a7 10f059 +16 1c5b +12 1c2a +5e 1af871 +52 1af74b +d7 efee2 +d5 efedb +cb ef709 +d2 efc02 +42 1e87ba +4a 1aef41 +cf ef6d8 +4c 1aef09 +a5 2a11de +ce 2bad9b +c2 127763 +67 63aa5 +8 193679 +42 1aedea +c7 ef581 +b0 cf17f +46 1aedb9 +9 42e98 +27 3ebc4 +dc 2ba68d +18 192d0a +25 3ebbd +9d d34a1 +86 102851 +1c 192cd9 +84 10284a +d7 e78f2 +2 1cbc29 +57 1f244b +d5 e78eb +90 d306a +73 1ee591 +0 1cbc22 +8 1923a9 +7b 1b4d18 +8d d2b40 +c 192378 +da 2789a5 +c4 2b9bd5 +87 d29f0 +0 192252 +73 1b4bc1 +b 3988f +4 192221 +77 1b4b90 +d2 27884e +13 1d5b99 +17 1d5b68 +36 1d19c0 +16 194159 +37 1d9d1d +92 29d9db +1b 1d5a42 +38 1d9b8f +13 1d58eb +17 19af3a +32 1d1743 +7b 1b4fc6 +a7 260787 +8 192657 +0 192500 +b 39b3d +73 1b4e6f +d3 2bb831 +8f 294e45 +10 1cd851 +37 1a064f +d9 efd51 +29 5871 +16 19c447 +ed f2314 +9e 10c97b +f8 ebba9 +9a 29d884 +33 1d19f2 +37 1d19c1 +92 29567f +12 1cd84a +61 1b31e3 +22 1d7e7e +b7 2613fa +16 1cd819 +71 1b4e5a +9 39b28 +17 19415a +34 1982a7 +f1 ebd01 +58 26ccb +93 29d9dc +17 1d5b76 +16 1d4915 +33 1d1744 +37 1d1713 +92 2953d1 +1a 1cd6f3 +69 1b308c +12 1cd59c +61 1b2f35 +3a 623a +b7 26114c +16 1cd56b +11 1cd852 +13 9cb +1f 194003 +a4 107cc1 +30 4b18 +3c 198150 +d6 ee971 +9b 29d885 +17 193eac +34 197ff9 +d4 eec24 +f1 eba53 +93 29d72e +17 1d58c8 +10 9f81 +72 1b4e60 +25 1976f9 +11 19c470 +fe 12404f +15 19c43f +78 22d70 +5b 1ec23 +30 1982c8 +34 198297 +72 1b4bb2 +12 1cd858 +1e 1d475c +19 19c319 +16 1d4605 +11 19c1c2 +38 198171 +12 1cc2dc +97 10ca73 +9 1cbd78 +73 1b4e61 +b 39b2f +90 25bf56 +10 194121 +20 19e755 +23 471f1 +55 1b9032 +1 1d3f7f +d2 e7b60 +e5 28440d +90 cafc8 +4a 265bb +18 193fca +10 193e73 +23 46f43 +1 1d3cd1 +55 1b8d84 +d2 e78b2 +96 d1de0 +23 1d90d1 +98 cae71 +90 cad1a +36 1d1776 +93 10caa2 +90 295688 +97 d20f1 +b2 1088fa +74 1b4bde +c 398ac +7f 5c21b +96 1044e6 +bf cf0b3 +fc 2b6298 +bb cf082 +16 3b38d +3 1d3f78 +93 10c7f4 +9e d34a5 +a9 d5752 +d9 efdb3 +21 1963fa +bc d60e5 +1d 3b4dc +14 1cc2b2 +36 3f524 +59 26c5a +a3 ce568 +b3 1088fb +b7 1088ca +e 3ae2f +96 104722 +4c 1e297 +41 575e6 +28 1d820c +37 3f7d3 +6c 1bc730 +60 290f8 +e2 12ce26 +bf 108773 +23 1cfdd1 +d3 2ba2b1 +8d 26382c +de 280f62 +96 10b81e +b3 10864d +27 1cfda0 +d7 2ba280 +b7 10861c +18 3a4ec +92 10ca41 +9a 1045fc +e9 e9f95 +12 1940c8 +53 1b7d48 +f8 12c3c9 +9e 1045cb +96 104474 +d6 ee9d3 +9 148a +54 57f17 +61 1b3239 +38 19f20d +65 1b3208 +91 10475b +f5 2be73e +97 29d75f +3f 3f67c +9a 10333a +12 192e06 +37 3f525 +16 426c5 +33 3f4f4 +ea 12cccf +58 1e90b +e2 12cb78 +d7 279d80 +b3 1085eb +96 10b7bc +91 d3379 +fe 2b629f +b 1d533d +53 26db8 +2c 1cfc41 +fa 2b626e +ee 2847aa +21 54da +15 a251 +bc 299707 +f8 f3f07 +68 1ecabf +99 d3222 +9d d31f1 +d 1cbda9 +96 10b50e +1 19a84f +91 d30cb +1a 3b7c1 +a4 298bed +34 5dfb +4e 1e29e +43 575ed +2a 1d8213 +90 cb02a +c 1706 +1 3aa55 +94 caff9 +2b 1d1196 +73 22c11 +6e 1bc737 +62 290ff +a7 106a5d +18 19b002 +2c 19fb9f +20 c567 +14 1f02 +10 1ed1 +56 57f1e +67 1b320f +93 104762 +f7 2be745 +58 1d95b +d2 efeb0 +81 d277a +42 1af098 +46 1af067 +9 43146 +f5 2bfa02 +ce 11f59b +f3 27cca4 +b5 ceef3 +f4 f2e21 +42 1b7382 +d5 e7b99 +90 d3318 +23 1d9133 +98 caed3 +90 cad7c +1a 19affd +77 1b4e3e +94 cad4b +b 431a3 +1b 3b7c2 +7f 1f57a5 +13 3b66b +77 1f564e +16 8ce9 +18 1d7a +ad 2a2659 +a1 10f021 +10 1c23 +ec 12ba99 +97 25d1ef +83 10c151 +84 294cf6 +a6 107f68 +16 1d4917 +33 1d1746 +a3 107faa +59 6069c +c3 ef5c0 +3 4305a +43 264c9 +11 1d58e6 +81 10c14a +50 2584e +5c 1b8e86 +83 10c143 +a4 107f61 +a3 107f9c +a7 107f79 +5d 6066b +a3 107f48 +59 6063a +e3 eb3b7 +7 43029 +e9 2bdf03 +23 3ee51 +d3 129331 +8c 10bfbf +63 222c0 +21 3eb9a +6c 22432 +ef f25bb +be 1087e2 +2e 1cff66 +de 2ba446 +1f 1cc71f +97 10ca7f +12 1cc2e8 +f 1cbdbe +52 1f1469 +fe ebc51 +6e 2a4d9 +7e 29b78 +23 1d80c9 +1a 4284f +6e 29217 +a 41eee +8f 103f89 +ae 106e6f +9f 103628 +8f 102cc7 +3a 3e6a8 +ff f2cde +ce ef747 +94 29665b +7b 29bb6 +4a 2661f +94 295399 +1a 3b7b3 +97 10c825 +69 2114c +5a 25cbe +86 d147f +4a 2535d +8 87c5 +4b 1e2d0 +85 29d043 +bc ce0a7 +ac cd746 +9d c9eff +8d c959e +7a 21b17 +a6 cd2d8 +5c 259ca +1 1d3f1b +20 c559 +2c 19fb91 +9d 295561 +5b 1d96f +87 c9130 +76 229a3 +4b 1d00e +34 5e0b +1e 3b790 +a5 107f72 +a7 107f6b +a1 107f41 +a3 107f3a +5 43022 +7 4301b +27 3ee74 +d7 129354 +cb ef469 +b 42f03 +4b 26372 +89 10bff3 +da 129729 +8b 10bfec +84 10be5a +81 10be9c +83 10be95 +a9 107e4c +fa 125582 +a4 107cb3 +78 5c4f2 +a1 107cf5 +8f ca859 +ac ce9a6 +f2 12542b +ae ce99f +85 10aeb9 +a5 106d12 +af 107e22 +a7 107ccb +5d 603bd +38 196e4f +ef eb291 +3 42d4a +2f 3ed2b +27 3ebd4 +9e c9c4b +c8 e73b1 +d4 e794c +44 1af0d0 +1f 1d4a6d +2f 1cff65 +1e 1cc71e +e 1cbdbd +5f 1b7edc +4f 1b757b +1d 19b344 +47 5fbba +3c 19719c +66 5ba12 +2c 19683b +72 1ed2c0 +85 d2a4b +3a 19f4b4 +8b 10bf98 +2a 19eb53 +f9 286158 +58 1f2577 +6f 1b33d4 +a8 d6a77 +14 19aedc +4e 1e290 +60 5b72c +43 575df +5e 1afb8d +19 2037 +4e 1af22c +1b 1d59e0 +67 5ba13 +ac 1101ca +7 19bb49 +35 ce98 +2d 19683c +73 1ed2c1 +8f 10c2d7 +d6 2820db +b 431af +32 5e33 +10 192bc1 +52 1b8fb3 +8f 10b015 +1f 3a213 +be 1077ce +3a 3e6a6 +50 26b10 +d7 2820dc +8e 103f88 +53 1b8fb4 +bf 1077cf +9e 103627 +8e 102cc6 +53 1b7cf2 +ca 2bb024 +cf ef746 +4b 2661e +6a 22476 +4c 26329 +91 2652c9 +df eede5 +d8 efd52 +e7 2b6a3a +7a 21b15 +5c 259c8 +a6 cd2d6 +6a 211b4 +4c 25067 +ce e73f7 +aa 2a136c +ba d63bd +38 3f901 +1b 3b7b4 +7f 1f5797 +90 25cf7a +de e6a96 +f7 27cc75 +bd ce0a6 +86 c9193 +77 5c364 +5a 1d96e +86 c912f +4a 1d00d +c8 2818fb +8d 10bfc2 +8f 10bfbb +89 10bf91 +7d 1b3d2e +8b 10bf8a +7f 1b3d27 +fb ebe5f +87 10be64 +b0 cf1d1 +5b 606a3 +83 10be33 +77 1b3bd0 +ad 107e1b +af 107e14 +a9 107dea +a5 107cc4 +a7 107cbd +ce 2b19d5 +d 42ecb +e6 124af9 +c4 2b1887 +9 42e9a +fd 2bfb59 +f1 12c521 +5 42d74 +21 1d7e76 +2d 3ed24 +8a 1029db +29 3ecf3 +25 3ebcd +27 3ebc6 +9f 29ee28 +93 10b7f0 +26 3dbbd +bf 29ac81 +b3 107649 +95 10b4fc +8e 102c66 +f6 27df98 +9b c9bb7 +df 282297 +d3 eec5f +66 2102c +c5 ee00a +a4 10f05f +5 41aa4 +e4 f24ce +45 24f13 +9d 29ee21 +91 10b7e9 +24 3dbb6 +9f 29ee1a +93 10b7e2 +5e 1b7bbd +bd 29ac7a +b1 107642 +2f 1d7f97 +bf 29ac73 +b3 10763b +7e 1b3a16 +b 42ea1 +ef 2beeef +e3 12b8b7 +56 1b8fe6 +b5 10f95d +de 12951a +d7 280b6e +9d d1f9f +d2 2bb822 +b7 107618 +bf 29ac1f +b3 1075e7 +62 1f4caf +a7 106cb7 +ff 27e08e +f3 eaa56 +37 3e521 +3f 1d1b28 +33 3e4f0 +27 3dbc0 +d7 1280a0 +2f 1d11c7 +77 22c42 +23 3db8f +df 2bb6a7 +d3 12806f +a9 d6a68 +4 42d73 +ea 124c91 +16 1cd889 +eb 12cfd2 +ed f25b4 +2c 1cff5f +bc d73a7 +1d 1cc718 +d 1cbdb7 +9d d31ff +33 3f7a4 +f8 f2ee5 +e8 f2584 +e 19a9dd +86 295d0f +7c 1b3d2f +e4 1238a0 +fc ebc4a +f7 2be437 +bd 10f868 +8c 10c2d1 +67 1b2fc3 +30 3f7a8 +15 8f9f +7c 29b71 +18 42848 +b7 110978 +8d 103f82 +28 3f002 +50 1b8fae +ac 106e68 +9d 103621 +8d 102cc0 +37 1d19cf +92 29568d +38 3e6a1 +fd f2cd7 +cc ef740 +bf d613f +b8 2996d6 +8e d2ba8 +79 29baf +48 26618 +3b d017 +a 9a80 +8e d18e6 +57 1b8fe5 +58 25cb7 +84 d1478 +f7 f3de7 +67 1ec99f +a 87be +ec eb599 +be ce0a0 +ae cd73f +dc 121163 +9f c9ef8 +bd 29a9cc +b1 107394 +bc ce045 +8f c9597 +ac cd6e4 +36 5e04 +12 1c8c +de 1211ce +b5 107611 +b7 10760a +8f 10ad5b +bf 29ac11 +b3 1075d9 +a7 106ca9 +5 41d60 +f5 27df84 +7 41d59 +35 3e51a +12 193e7c +27 3dbb2 +dd 2bb6a0 +d1 128068 +b8 2695b8 +27 471ce +a7 cd339 +1e 1d4a60 +e 1d40ff +a6 25f7d6 +67 1b4231 +c2 277eef +4c 1b7567 +c4 2b2899 +c 3ae2a +3d 3e3c1 +ae 267c7b +3e 1d08b9 +90 d3076 +d9 e674f +36 1982a0 +2e 1cff58 +be d73a0 +de 2ba438 +1f 1cc711 +3c 1d085e +97 10ca71 +12 1cc2da +f 1cbdb0 +9f d31f8 +46 261e7 +ac 10eea6 +8f 10ad59 +5e 1b7ecf +d6 2b3201 +c6 2b28a0 +8b 10bffa +1c 19b337 +84 10aea8 +94 296669 +c 19a9d6 +84 295d08 +61 1ed97d +7e 1b3d28 +e6 123899 +ae 10eead +4 1ccc67 +7c 1b3ccd +e4 12383e +71 1ed01c +52 1af749 +6c 1b336c +61 1ec6bb +1d 192fe8 +95 d3348 +10 192bb1 +bc 108a8b +a 1ccfec +d 192687 +79 29ba1 +48 2660a +fd f2f25 +9e 10b969 +48 1b01a8 +8e 10b008 +1a 42841 +a 41ee0 +58 25ca9 +67 1ec991 +8f 10bfc9 +d0 120fdb +8f 103f7b +ac 1080c8 +ec eb58b +cd e73e3 +68 22463 +ae 106e61 +9f 10361a +bc 107767 +8f 102cb9 +ac 106e06 +ec ea2c9 +11 1d58f2 +1b 3a4f2 +38 3e63f +d8 e7a72 +63 1f5cd2 +36 3f526 +b 39b91 +d8 1281be +d2 278aee +78 21b02 +74 2298e +16 1d4669 +39 1d18a2 +0 9920 +49 1cff9 +7b 29ba8 +4a 26611 +ff f2f2c +73 1bbbdf +39 d010 +8 9a79 +ce ee477 +7d 5af52 +4c 579bb +9c d2240 +4a 2534f +ec eb537 +e1 124886 +ac ce9fa +ee ea2d0 +fc eabd6 +fd 2b755d +f1 123f25 +86 d2a4f +cf e6128 +ec ea275 +ed 2b6bfc +e1 1235c4 +3a 3f968 +ac cd738 +9d c9ef1 +8d c9590 +78 21aae +34 5dfd +9 468 +9b 10b699 +9f 29eb7a +93 10b542 +bb 1074f2 +bf 29a9d3 +b3 10739b +79 22b15 +db eeb08 +6e 20ed5 +a1 d590b +ad 268f43 +f3 2bf9c8 +84 d1484 +d9 282251 +3a 3e3ec +1b 425a2 +4b 250b0 +63 1f5f70 +99 10b692 +2c 3da5f +9b 10b68b +9d 29eb73 +91 10b53b +24 3d908 +9f 29eb6c +28 19fdba +93 10b534 +9e d21e5 +35 196d38 +dd efde4 +89 10ad31 +da 128467 +7f 1ee719 +73 5b0e1 +8b 10ad2a +8e d1884 +53 1e82c +85 d29e9 +3a 19f452 +b9 1074eb +bb 1074e4 +bf 29a9c5 +b3 10738d +be ce03e +ae cd6dd +19 4259b +9 41c3a +c6 e5fe0 +5a 5f370 +b 41c33 +f 1d5114 +3 41adc +e 878d +44 1b0074 +3c 4f4e +56 1b8d38 +69 2a7b0 +3e 4f47 +d2 2bb574 +29 3da93 +c8 2bad71 +7a 5b1c9 +9f 10b668 +9b 10b637 +97 10b511 +9f 29eb18 +93 10b4e0 +bf 1074c1 +a 3aba6 +bb 107490 +0 3a9f2 +a1 ce5d3 +6a 1f4b58 +b7 10736a +2 3aa4f +af 106b60 +3a 196e56 +b8 110d38 +a7 106a09 +5d 5f0fb +ef e9fcf +f8 f41a7 +1f 42571 +1b 42540 +17 4241a +77 1bcf42 +37 3e273 +2f 3da69 +27 3d912 +30 47aea +8c 10c021 +63 22322 +c6 2babf0 +9a 103594 +1d 1d4a66 +33 47af2 +2c 1cff5d +2d 1cff5e +bd d73a6 +5b 1b8ebf +c 1cbdb6 +32 3f7a3 +9c d31fe +73 2af61 +7c 1b3d2d +88 10bf90 +6c 1b33cc +3e 197195 +38 19f4ad +28 19eb4c +6d 1b33cd +4c 1e289 +e5 2b6cf1 +31 5e3d +3f 197196 +6d 2a4d1 +c8 ee18f +97 cad53 +d4 2b1f38 +50 1b8fac +c7 2b9bdd +8d 10b00e +d4 280e12 +e7 2b6cf8 +ad 108129 +15 c4f +63 1edbd0 +29 3f001 +51 1b8fad +f7 2b6397 +bd 1077c8 +4b 1d000 +2 9927 +e7 2b5a36 +ad 106e67 +c6 2b188e +8c 102cbf +63 1ec90e +29 3dd3f +2b 1d94e2 +51 1b7ceb +c8 2bb01d +49 26617 +b6 10f9b9 +43 1b00ab +60 1b41f8 +68 2246f +48 1f0c64 +a 193610 +b9 110a89 +dd e7a34 +be ce09e +59 25cb6 +85 d1477 +78 21b0e +68 211ad +1b 911e +38 d26b +3a 4f76 +a8 d6d17 +ed eb598 +cc e73f0 +a8 2a1365 +8e ca858 +a0 107cf4 +b8 d63b6 +19 3b7ad +15 9fa3 +fd eac37 +a8 d5a55 +9 3ae4c +9b 25d0c9 +cc e612e +ce 2b9ad7 +99 10c8e2 +bf ce09f +9e c9ef7 +8e c9596 +58 1d967 +48 1d006 +da 282255 +ca 2818f4 +85 29cd89 +9d 10b661 +99 10b630 +9b 10b629 +95 10b50a +9f 29eb0a +93 10b4d2 +8d 10ad00 +8f 10acf9 +dd efd82 +89 10accf +8b 10acc8 +87 10aba2 +57 1e7fb +bd 1074ba +8 3ab9f +53 1e7ca +b9 107489 +b5 107363 +0 3aa48 +b7 10735c +bf 29a963 +b3 10732b +a5 106a02 +a7 1069fb +1d 4256a +19 42539 +a5 d6950 +15 42413 +6a 20e98 +3b 1d1b49 +d 41c09 +f2 eba69 +75 1bcf3b +50 1ea136 +73 1bcf03 +3f 3e3bc +39 3e392 +35 3e26c +37 3e265 +2d 3da62 +2f 3da5b +25 3d90b +27 3d904 +2e 1cff56 +7c 1b3d1f +6c 1b33be +a9 1103e4 +3c 3e3c0 +eb 12ba6e +2f 1cff57 +98 1045a1 +e 1cbdaf +9e d31f7 +7d 1b3d20 +89 10bf83 +34 1d1a2b +6d 1b33bf +31 5e2f +7b 2186a +16 1d5bd9 +6b 20f09 +2c 19682d +e9 f3853 +7c 2182f +60 1ed97c +73 1b4bb3 +56 1b7d84 +19 2029 +4e 1af21e +23 54d5 +60 1ec6ba +67 1b2f6f +e5 12ce51 +3d 19718f +67 5ba05 +2d 19682e +c 192686 +8f 10c2c9 +ac 110416 +ec eb589 +10 192bb3 +1b 3a1f0 +95 d334a +1 19b86f +52 1b8fa5 +be 1077c0 +cf e73ea +95 264036 +53 60290 +ae 106e5f +85 2636d5 +d6 280e0b +dd eedd0 +fc eac28 +a3 1069ca +af 29a002 +ec ea2c7 +3a 3e698 +5c 1afb78 +2a 3dd37 +1 19a5ad +52 1b7ce3 +59 25ca8 +78 21b00 +68 2119f +d7 2820ce +8e 103f7a +17 c48 +ed eb58a +cc e73e2 +ef 285a7b +e3 f2443 +53 1b8fa6 +77 646b4 +a 3ae52 +bf 10776d +b4 260130 +69 22462 +56 1b09ea +48 1e2ba +fd eabd5 +af 106e60 +d7 280e0c +9e 103619 +8e 102cb8 +fd eac29 +ed ea2c8 +d2 2b1f62 +77 1ee2a4 +8c 25b4dd +3b 3e699 +c4 ef32d +da 2bb977 +53 1b7ce4 +ca 2bb016 +1a 3a4f1 +a 39b90 +79 21b01 +30 3f80c +48 1cff8 +12 439ba +7a 21b07 +6a 211a6 +e0 124885 +ef 28456d +4e 1f098c +49 1b7289 +ff eabdc +bd ce044 +b6 10893b +ff ebc44 +ab 106b91 +94 2652f9 +90 25cf6c +de e6a88 +ec 2b6bfb +e0 1235c3 +c6 27815e +8c c958f +32 3f813 +5a 1d960 +4a 1cfff +c8 2818ed +94 25bc79 +1b 3a490 +bc 2999a9 +b7 10f956 +d 41bf9 +e6 2bdd83 +f6 12545c +ab ce9cf +37 4685f +66 1f4c8c +bc 299947 +21 1d90d8 +6f 62bf4 +1b 425a0 +a3 d68b6 +14 19af32 +7e 1bbd66 +1a 1d83 +1a 1d4a3d +78 22d62 +3b 1d183b +1e 1d4a0c +56 25b26 +f0 27cc9e +2b 1d0eda +e 1d40ab +ac 1101bc +35 ce8a +a 850e +15 192be3 +fb f2eeb +3a 1d0896 +1c 1d4749 +3e 1d0865 +eb f258a +ba 1087b1 +c 1d3de8 +da 2ba415 +1f 1cc6bd +f 1cbd5c +d0 eff17 +f4 285fc1 +e6 ea123 +8b 102c34 +a3 10ffd8 +d5 281e19 +f3 27df66 +80 25b5f7 +7b 1bbda4 +4a 1b880d +5b 25a0f +5f 259de +43 1f1b2b +fb f2c4b +ca ef6b4 +53 1f11ca +4a 1b754b +1c 3b4db +bd cf0bc +87 29e060 +43 1f0869 +d5 e7b8b +78 22d60 +1a 1d4a3b +88 d185a +4d 1f0c34 +55 1e7e6 +f4 ebda1 +63 1ed984 +4b 1b04be +3f 1d08c6 +cb e7365 +7e 1b3cd4 +e6 123845 +73 1ed023 +fa ebc20 +2c 55f3 +6a 1b33a4 +4c 1b7257 +5b 1afb5d +87 25b31e +5f 1afb2c +76 1b4b91 +d3 efebd +8f 294bf9 +4b 1af1fc +4f 1af1cb +cb e60a3 +ed eb2dc +b8 ce014 +9b c9ec7 +a8 cd6b3 +6d 1eca8d +89 c950b +9e 29db55 +e4 f24c0 +f4 27dc81 +d7 279b34 +ee f35d0 +bf 10f80d +8e 10c276 +94 d1e3b +3f 46716 +f0 ebd72 +e 4317f +29 1d827b +1c 19c34b +10 8d13 +fe f2c6f +9a 10b946 +3f 47c88 +ee f230e +89 d2b71 +1a 427ed +9 9a7a +e5 2bed8f +76 1bd1ef +8f 103f27 +95 c9aec +de 1294b8 +3c 1981a4 +30 4b6c +da 129487 +ba 10779f +9c 10b652 +84 c9448 +18 427d8 +8f 102c65 +70 229cb +9a 264156 +f7 27df97 +84 25b628 +ad 110415 +28 1cfc7e +3a 3e646 +c4 ee077 +ad 267c75 +c 1d4094 +fb f2cad +ca ef716 +ff f2c7c +ce ef6e5 +c3 128a34 +4f 1af21f +6 19bb46 +7f 29b85 +4e 265ee +43 5f93d +7b 29b54 +4a 265bd +dd 1211c6 +78 22dc2 +da eedb5 +7f 2b0f7 +28 3dcde +7c 22d91 +3b 1d189b +1e 1d4a6c +66 5a4a0 +a5 2a250e +df 2bb70b +d3 1280d3 +68 22461 +ca ee454 +6f 2a796 +98 d221d +a7 298f05 +3d e55f +88 d18bc +2d dbfe +73 64683 +16 19b1e5 +a1 d691f +12 19b1b4 +5a 25c5c +6 19a884 +2 19a853 +4a 252fb +8 8763 +ee eb53e +e3 12488d +cb e73c7 +12 9f7a +fa eac0e +dc eeac1 +fe eabdd +ff 2b7564 +f3 123f2c +db e6a66 +f6 eba9a +cb e6105 +99 c9ece +4c 1b7565 +b4 cef02 +97 cadb5 +d4 2b1f9a +8d c953c +d 445 +9 414 +1e 1d4a5e +a7 d6b97 +c7 2b9c2f +e4 2bdd7c +a9 ce9c8 +86 d178f +47 1f0b38 +64 1f4c85 +ef 2b5be1 +a6 2a2508 +61 2a605 +82 103b54 +f8 2b6267 +ce ef739 +ff f2cd0 +d8 11fed2 +7d 5c214 +e2 f2496 +ee 285ace +dc 2b33b3 +8f 10ad69 +d0 11fd7b +75 5c0bd +e6 285977 +35 60aa +a 3abb4 +2 3aa5d +a4 298e8f +88 103ca4 +ec 2bdc87 +69 2a74e +3c 1d8b9e +93 cafd0 +f7 284fb3 +8a 103c9d +80 103b4d +84 102b6a +8e 103c7a +86 103b23 +ef 2b5b7f +a6 2a24a6 +e6 2bdad5 +c4 e5fd9 +9f 10b976 +60 5b78c +ce e70e9 +6 3aa2c +41 26450 +ef f236f +e8 2b5906 +2 3a9fb +66 1f49de +46 1de9b +8c 103c73 +88 103c42 +cf 2b9ad8 +ec 2bdc25 +f4 f2b1f +55 57f16 +f7 284d67 +c6 2817d0 +ee 2bdc1e +84 103b1c +80 103aeb +c7 2b9981 +e4 2bdace +7b 2b128 +c 3ab7c +8 3ab4b +6c 1f4b2e +74 29a28 +77 1bbc70 +46 1b86d9 +a 3ab44 +4 3aa25 +0 3a9f4 +64 1f49d7 +d0 e791b +b9 261519 +18 1cd938 +1b 1d4a3c +56 25b28 +79 22d61 +cc 11f5a2 +d4 e78ea +1f 1d4a0b +bd 268328 +1c 1d4747 +66 222e2 +53 1b9016 +2 399e5 +e 1cd01d +a2 2a0f75 +a0 260a60 +3f 1d0864 +2b 1cff34 +d 1d3de7 +66 21020 +2f 1cff03 +19 194279 +1e 1cc6bc +64 2a637 +bb 10773c +b0 2600ff +a0 25f79e +f5 285fc0 +4b 1b880c +63 1ed982 +5b 1b7eab +87 26366c +5f 1b7e7a +1e d9e +1f 1cd725 +13 3a0ed +3c 1d1872 +30 3e23a +b7 299806 +4b 1b754a +4f 1b7519 +7e 1b3cd2 +85 10ae49 +6a 1b33a2 +4c 1b7255 +6e 1b3371 +63 1ec6c0 +db 280fa4 +19 19b313 +43 5fb89 +1d 19b2e2 +38 19716b +62 5b9e1 +3c 19713a +28 19680a +2d 45da2 +2c 1967d9 +21 1cfb28 +72 1ed25e +b8 ce012 +a8 cd6b1 +4a 1b04bd +43 1e97db +e2 2843e4 +7b 1b3d04 +2b 45d76 +5d 1b7bb7 +a7 25f4c5 +6b 1b33a3 +4d 1b7256 +6f 1b3372 +5a 1afb5c +86 25b31d +a8 d6a15 +c8 2b9aad +c7 ee32f +e2 124b38 +43 5757d +5e 1afb2b +53 1e8e7a +4a 1af1fb +4e 1af1ca +43 1e8519 +63 5b9e2 +ac 110168 +8f 10c01b +7 19bae7 +73 1ed25f +8 192663 +c 192632 +1 1cb981 +fb eabab +eb ea24a +d9 e7d13 +a9 cd6b2 +8f 10c275 +a 1cbade +95 d1e3a +b4 cdc92 +3b 1d18ab +f1 ebd71 +f 4317e +65 29366 +86 1028b5 +b 4314d +31 19f0a7 +0 19bb10 +36 5e02 +14 192b90 +9b 10cc09 +dc f00a1 +b7 260136 +8f 10afb3 +84 263976 +f7 2862e5 +1b 3a1e2 +ba 10779d +b9 cf329 +96 d20f0 +77 1bd1ee +3e 3e675 +7f 1bd345 +73 29d0d +32 5dd1 +10 192b5f +2e 3dd14 +9b 10b947 +8d 10bfb2 +76 1b4bf3 +49 252e5 +e 398c1 +d3 eff1f +5 98f2 +8e 103f26 +94 c9aeb +24 19799a +8a 102c95 +20 197969 +d0 281e49 +bb 10779e +9d 10b651 +8d 10acf0 +a1 2a0f6f +f5 286022 +af 106e0c +a4 25f7cf +99 cb182 +fd 285165 +3f 3e676 +34 197039 +3b 3e645 +30 197008 +c4 ef2d9 +2f 3dd15 +24 1966d8 +d4 280bb8 +a 39b3c +72 1b4e6e +cc ef420 +ee eb53c +e3 12488b +7b 1b4d1a +8c ca5b3 +b5 29aab3 +d2 121038 +1 98cf +4b 265bc +6e 22445 +63 5b794 +6a 22414 +2c 45df +dc eeabf +25 3d8fd +cc ee15e +ee ea27a +8c 102a03 +c6 2b15d2 +bc ce043 +ff 2863dc +f3 f2da4 +9c c9c52 +b4 29ab12 +5b 25c5b +d1 12002a +dd 2b3662 +a4 29a1b1 +de 2b33ae +b5 2997f1 +d2 11fd76 +77 5c0b8 +66 1f4c7e +4b 252fa +30 197fc8 +7e 21ae4 +62 1edc31 +7a 21ab3 +db eedb6 +6a 21152 +3c 4f4c +20 1d1099 +d0 2bb579 +67 1bc5df +2c 45eb +c0 2bac18 +63 1bc5ae +58 1ec1b +ca e73c6 +ce e7395 +c3 1206e4 +4a 1e26d +cd ee15f +aa d59fa +b 3adf1 +6f 1f4dd4 +ef ea27b +dd e7d44 +ad cd6e3 +3d 620f +2d 58ae +8c c953b +3a d264 +26 1d814f +63 1edc32 +22 1d811e +6b 21153 +73 1ed021 +42 1e9a8a +5a 1d90c +0 1934c0 +4e 1cfdc +4a 1cfab +2d 45ec +c1 2bac19 +c0 ee292 +cc 2818ca +57 1b8d99 +1c da5 +31 1d0489 +0 1ccef2 +1d 1cd72c +11 3a0f4 +27 19fcf0 +de 279f48 +d2 e6910 +77 22c52 +67 222f1 +bc cf04b +9e 296acb +92 103493 +37 3f7d5 +3c 5f54 +96 103470 +9e 296a77 +15 1d4911 +92 10343f +86 102b0f +a4 107d17 +f2 1241c9 +fe 2b7801 +e4 eb186 +16 3a379 +bb ce06e +6 39a18 +56 1d7e8 +46 1ce87 +94 103469 +96 103462 +9c 296a70 +90 103438 +b7 d6236 +84 102b08 +34 3f7d9 +86 102b01 +b9 ce067 +16 3a36b +a9 cd706 +6 39a0a +7c 1bbd5f +18 1d4a36 +b7 2a2b66 +39 1d1834 +1c 1d4a05 +6c 1bb3fe +8 141b +a7 2a2205 +fb 125581 +f9 f2ee4 +be 1074c0 +1f 19b33d +3c 19f48a +38 1d088f +1d 1cc6b6 +d 1cbd55 +28 dbbe +71 1b38fa +e4 ea11c +89 102c2d +79 1bbd9d +48 1b8806 +59 25a08 +f9 f2c44 +c8 ef6ad +cf e7396 +bb d60ac +8a d2b15 +48 1b7544 +e7 285674 +5f 1afb8e +d7 efeee +4f 1af22d +a 19a9ac +94 d20e7 +1b 1d5d00 +d8 eed4c +e7 2b5a34 +c8 ee3eb +df e6a35 +63 2a3b4 +5f 1f158e +cf e60d4 +8a d1853 +4f 1f0c2d +68 1b465f +48 1e2ca +79 21861 +49 1b04b7 +5a 1b7bfc +8 16d7 +39 4c6e +28 197a6c +3d 1d08bf +c9 e735e +ca 2b1a06 +ec f2377 +8b ca7c6 +ef 2847a9 +78 1b3cfe +a4 25f4bf +f8 ebc19 +68 1b339d +fc ebbe8 +85 25b317 +3a 197166 +ce ef437 +7e 1b3a24 +85 10ab9b +59 5f3da +9a 10c94c +e9 f22e5 +1b 192fbe +38 19710b +9e 10c91b +1f 192f8d +31 1d0429 +21 d828 +dc e7aa5 +80 d29c3 +c9 e609c +be d764e +b3 11099d +de 2ba6e6 +6f 1eca86 +9b c9e65 +28 472ee +b 431a1 +8b c9504 +d6 121075 +fc f2c68 +98 10b93f +3d 47c81 +74 1bd1e8 +52 1ead9 +1d 3b78a +bc d6393 +bd 2a2d1a +b1 10f6e2 +b8 107798 +56 1eaa8 +32 1d8a1d +8d 102c5e +2c 3dd0f +dc 1281ef +f9 f2ca6 +c8 ef70f +cf e73f8 +bb d610e +8a d2b77 +3 42d9e +3b cfb5 +a 9a1e +9d 104627 +d8 eedae +e7 2b5a96 +7d 2b0f0 +c8 ee44d +6d 2a78f +df e6a97 +9a d2216 +5f 1f15f0 +7c 1f573d +38 1d1aef +3f e558 +cf e6136 +8a d18b5 +4f 1f0c8f +6c 1f4ddc +10 19b1ad +58 25c55 +67 1ec93d +1e 90ee +a 875c +9d 103365 +c9 e73c0 +a8 ce975 +8f ca7f7 +a1 107c93 +2a 5877 +da efd57 +4a 1e890f +6f 1bb6b2 +f4 eba93 +d7 e7946 +80 d2a25 +c9 e60fe +9f c9e96 +bd 29a96a +b1 107332 +b6 ceefb +d6 2b1f93 +0 98cc +49 1cfa5 +3e e557 +95 cb06a +5e 1f15ef +33 478a6 +2e 45e6 +de eeac6 +c2 2bac13 +c6 ef342 +79 1bbd8f +48 1b87f8 +59 259fa +48 26368 +1a 1d4a2f +a 1d40ce +c6 ee080 +58 1b7e97 +a2 25f7a5 +f7 285fc7 +48 1b7536 +e7 285666 +5 1ccc66 +9a 10b8d6 +8a 10af75 +f2 2862a7 +c8 ee3dd +22 3db80 +54 1af9c1 +2e 1d11b8 +d2 128060 +7b 22dca +10 19c47f +de 2bb698 +11 1f42 +b 1cd041 +8 3adf9 +39 3e390 +aa 267c4a +ae 267c19 +68 1b4651 +48 1e2bc +79 21853 +49 1b04a9 +5a 1b7bee +f5 f2dcc +29 1d7fcd +40 264b1 +8b 103ee8 +ef 2bdecb +e8 eb4f8 +c9 e7350 +ce 128b59 +84 263920 +47 562fa +32 19826f +2a 1cff27 +ba d736f +73 63111 +42 5fb7a +7f 1f6749 +da 2ba407 +2e 1cfef6 +1b 1cc6e0 +3f d296 +38 1d082d +1f 1cc6af +d7 e6692 +36 1d1714 +64 29367 +93 10ca40 +b 1cbd7f +9b d31c7 +78 1b3cf0 +f8 ebc0b +68 1b338f +42 261b6 +a8 10ee75 +8b 10ad28 +74 1b4b7c +1c 1cc409 +3e 3f67b +d1 efea8 +96 104484 +73 1b38f3 +8b 102c26 +f3 27df58 +9e caf0b +96 d3350 +e8 ea236 +21 d81a +81 295fe8 +dc e7a97 +7b 1bbd96 +4a 1b87ff +78 29b4e +5b 25a01 +39 19f1fe +8 19bc67 +da 282263 +bd 268580 +da eeb05 +c 84d8 +7f 2ae47 +cd e738f +b9 d60a5 +88 d2b0e +5a 1b7e9e +4a 1b753d +18 19b306 +25 471b9 +b7 269436 +1c 19b2d5 +84 10ae46 +11 1d4624 +d5 efee7 +4 19bb4d +4d 1af226 +9c 10c912 +73 22c13 +8 19a9a5 +7b 1bd314 +a7 268ad5 +c 19a974 +da 280fa1 +1 1d3cc3 +89 d160f +ca ee3e4 +84 d29f4 +cd e60cd +b7 1109ce +88 d184c +fb f41bb +4b 1b04b0 +68 1b45fd +43 1ce47 +4f 1b047f +61 1ed91b +72 1bbefc +28 197ac0 +d8 281fa0 +3f 1d08b8 +cb e7357 +6f 2243a +35 19f086 +a0 10ed80 +ac 2a23b8 +0 1ccc36 +b5 299551 +78 1b3c9c +71 1ecfba +38 19715f +cc ef430 +ab 107df1 +7c 1b3a1d +3c 19712e +a4 106c9f +31 1d047d +ce 280353 +b8 cf07a +45 1cbd1 +28 1967fe +d8 280cde +19 192fb7 +1d 192f86 +bc 2a1a57 +1 19a5af +23 d821 +83 295fef +82 d29bc +cb e6095 +ac 2a10f6 +a8 cd6a5 +3a 3f6bc +89 c94fd +f9 f2c98 +c8 ef701 +3b 466d7 +a 43140 +79 29b3f +48 265a8 +9e 10b907 +8e 10afa6 +f6 2862d8 +d8 eeda0 +e7 2b5a88 +7d 2b0e2 +1e 42810 +1a 427df +76 1bd1e1 +72 1bd1b0 +48 252e6 +f5 f2e2e +ed 284566 +4c 1f0985 +8b 103f4a +a8 108097 +8f 103f19 +e8 eb55a +ec eb529 +c9 e73b2 +2a 4617 +cd e7381 +5 19b840 +da 129479 +df 2bb949 +d3 128311 +75 29cd5 +96 103224 +1c 1cc6a9 +49 1e259 +ba 107791 +24 1963d4 +b3 d7279 +91 264007 +dc 278c71 +aa 106e30 +a3 d6918 +81 2636a6 +ae 106dff +a7 d68e7 +85 263675 +9b 1035e9 +b8 107736 +9f 1035b8 +b6 10861d +8b 102c88 +fc ebe88 +a8 106dd5 +a3 298ed6 +f7 27df89 +f8 eabf9 +3f 1d8c16 +ec ea267 +f4 eba85 +96 29d760 +b9 29a999 +80 d2a17 +c9 e60f0 +84 d29e6 +cd e60bf +3a 3e638 +15 1d58c1 +6a 1b4346 +18 19c5d6 +da 1281b7 +23 d7bf +1 19a54d +c4 ee069 +c5 2b2b48 +65 2a37a +9b 104849 +6c 21170 +16 a259 +68 2113f +4 98ef +4d 1cfc8 +0 98be +49 1cf97 +fb f2c9f +ca ef708 +cd e73f1 +b9 d6107 +88 d2b70 +7b 29b46 +4a 265af +3d cfdf +c 9a48 +da f0075 +9e 10b665 +1 42d97 +da eeda7 +58 582eb +ca ee446 +98 d220f +a7 298ef7 +3d e551 +9c d21de +24 3d8fa +9d 29eb65 +91 10b52d +8c d187d +ff f41ec +8d 29e204 +81 10abcc +a1 d6911 +5a 25c4e +9 8518 +4a 252ed +1c 90e7 +71 1b4bba +9 39888 +c 8786 +da eedb3 +7f 2b0f5 +4 98fd +4d 1cfd6 +8 8755 +7b 2b0c4 +b4 26817c +ee eb530 +81 29e338 +14 1cc314 +e8 eb506 +e1 124824 +34 19f085 +6e 22439 +63 5b788 +d6 e6693 +76 29cdd +fe eabcf +c 398b8 +ad cd499 +ff 2b7556 +f3 123f1e +ee ea26e +ef 2b6bf5 +9d 10cbd1 +e3 1235bd +f8 eaba5 +fd 2b74fb +f1 123ec3 +f6 eba8c +bb 29a9a0 +82 d2a1e +cb e60f7 +e8 ea244 +86 d29ed +cf e60c6 +ac cd6d6 +ad 29a05d +a1 106a25 +99 c9ec0 +dc e7a35 +40 1b73eb +9d c9e8f +b4 ceef4 +89 c955f +7e 21ad8 +6e 21177 +f2 2be707 +3c 1d8960 +28 45ae +d8 eea8e +1d d98 +9 406 +79 29dfb +9a 10334a +3f 3f68c +89 d185b +9e 29682b +92 1031f3 +37 3f535 +6d 1bc97b +61 29343 +70 22c0b +12 1d48e6 +7f 22afb +de 279c9a +b5 2600dd +d2 e6662 +77 229a4 +f2 2bf9c9 +67 22043 +a 1cbd1e +47 1b7418 +62 1edc21 +3d 3f685 +79 29ded +9a 10333c +52 1af9a3 +3f 3f67e +9c 296824 +90 1031ec +35 3f52e +89 d184d +d5 2820d7 +a3 110296 +36 3e272 +9e 29681d +92 1031e5 +37 3f527 +6d 1bc96d +61 29335 +30 3f55c +b7 29ab28 +17 1cd57a +24 45f06 +18 3a24c +1a 3a245 +9 8756 +1e 1cd726 +12 3a0ee +e 1ccdc5 +2 3978d +9e 103319 +d9 eed3d +1d 1d47ba +9a 1032e8 +96 1031c2 +ff ea930 +60 5ba3c +f2 27dcb9 +86 102861 +a5 299f12 +f6 2b7648 +a 1d50ee +a4 260a3b +e6 2b6ce7 +8c d2b4d +bd d60e4 +8a ca829 +bb cddc0 +aa 260bbe +5e 1e943 +a 39890 +6 3976a +46 1cbd9 +85 264c47 +21 1d913a +72 1f6870 +5f 1b7c22 +7a 1ee42b +14 192bf2 +81 264c16 +9c 103312 +94 1031bb +6b 1bb681 +96 1031b4 +9c 2967c2 +90 10318a +b7 d5f88 +84 10285a +34 3f52b +86 102853 +5a 57092 +7b 29e66 +b7 29aac6 +27 1d10d2 +d7 2bb5b2 +18 3a1ea +a8 260bb7 +3c 46710 +5c 1e93c +8 39889 +70 1b4bbb +a 39882 +72 1b4bb4 +4 39763 +a9 cd458 +6 3975c +c 1ccd6a +54 1e7e5 +0 39732 +e 1ccd63 +2 3972b +19 1d4a35 +2e 47318 +54 25b21 +8a 10c235 +be cddf2 +1d 1d4a04 +d 1d40a3 +38 1d088d +3c 1d085c +28 1cff2c +71 1bbef4 +2c 1cfefb +a8 106dd3 +29 1cff2d +b9 d7375 +a9 106dd4 +49 1b8805 +61 1ed97b +50 1b7a90 +6 1d3fb4 +23 1d0de3 +e8 eb504 +59 1b7ea4 +85 263665 +49 1b7543 +78 1b3cfc +4f 1f098f +a4 25f4bd +b5 10f6af +84 10c118 +7c 1b3ccb +68 1b339b +6c 1b336a +61 1ec6b9 +1b 19b30c +38 19f459 +27 1cfb62 +30 1d9d3a +1f 19b2db +72 1bd1be +2e 1967d2 +23 1cfb21 +9b 264405 +e8 ea242 +e9 eb55b +c8 2782eb +48 1b04b6 +41 1e97d4 +e0 2843dd +a 19391e +3 1ccc3c +a2 267845 +e9 eb505 +e3 eb0fb +c6 ee2cc +3c 1d08be +c8 e735d +15 19af3f +79 1b3cfd +a5 25f4be +7d 1b3ccc +69 1b339c +58 1afb55 +eb 2b6bd2 +84 25b316 +48 1af1f4 +4c 1af1c3 +41 1e8512 +21 547a +3b 197165 +cf ef436 +3f 197134 +a 19265c +34 3e26b +a1 11028f +3 1cb97a +74 1b4b7a +27 c530 +20 1cfac7 +9e 10c98b +c8 e609b +bb ce00c +ab cd6ab +f6 12521c +8d 10afac +f5 2862de +bc 107765 +9 3987a +a8 106e35 +b 976f +e9 284535 +48 1f0954 +d 41eb5 +f2 ebd15 +75 1bd1e7 +d5 12100d +9 41e84 +71 1bd1b6 +3c 3e66e +26 19e77f +38 3e63d +89 d18bd +2c 3dd0d +99 10b940 +28 3dcdc +bd 107766 +9a 25d0c8 +a9 106e36 +4b 1cf9e +2 98c5 +ad 106e05 +8a 25c767 +9c 1035be +20 46f3d +2d 3dd0e +a 193670 +29 3dcdd +d6 279d81 +9c cb1b2 +27 1d9412 +ec eb535 +e1 124884 +79 1b4d13 +6c 2243e +61 5b78d +d9 f0071 +b 9a1d +e9 2847e3 +48 1f0c02 +90 cafd8 +d7 280e6e +f4 284fbb +55 1b0a36 +23 3ebf5 +9b d34d9 +1f 1d475f +24 3ebcc +fc eabd4 +a7 106a6b +52 1ea77 +9f d21e4 +bd 2a2cb8 +b1 10f680 +16 1cc30d +83 29e331 +d9 e7a03 +ba ce06d +be ce03c +a2 29a189 +59 25c54 +49 252f3 +7c 21add +78 21aac +50 1b0cc0 +b 875b +3e 4f45 +22 1d1092 +3f 1d18cc +2 3acfd +33 3e294 +80 d14b5 +8c 264aed +d2 2bb572 +9b d2217 +c2 2bac11 +ae d57df +7d 1f573e +2a 45b3 +e9 eb567 +a8 d6cb5 +ed eb536 +c8 e73bf +cc e738e +c1 1206dd +8a ca827 +8e ca7f6 +83 103b45 +a0 107c92 +6d 2243f +c8 e60fd +a 16ce +63 1f4a00 +46 1b7167 +11 9f72 +f9 eac06 +b8 d6354 +a8 d59f3 +9 3adea +cc e60cc +ff 1240a6 +b6 1109cd +bf ce03d +a3 29a18a +9a c9ec6 +3f 6208 +9e c9e95 +8a c9565 +2f 58a7 +d2 eff20 +8e c9534 +38 d25d +ce 280363 +7d 21ade +61 1edc2b +79 21aad +28 c8fc +6d 2117d +5c 1d936 +88 c956e +67 1bc881 +71 1ed01a +40 1e9a83 +4c 1cfd5 +48 1cfa4 +c3 2bac12 +c2 ee28b +ce 2818c3 +2b 45b4 +52 26e29 +e 43d +a7 298ea5 +a 40c +2b 3ecfa +f9 ebbaa +dc eed7b +c7 ef341 +20 1d0deb +44 1af060 +4b 5fd34 +3b 3e399 +1f 1d49fd +d7 ee9e0 +ce 128b57 +3e 1d0855 +f6 ea838 +2a 1cff25 +73 1bbeed +e6 e9ed7 +59 1b7e96 +49 1b7535 +78 1b3cee +5e 26f4f +68 1b338d +6c 1b335c +d9 280f8f +74 1b4bdc +20 1cfb29 +a5 1102c0 +e8 ea234 +2f 1d8251 +8a 294e75 +a 1cd040 +38 3e38f +bf 29995b +53 1b9008 +70 1bd155 +a2 2a0f67 +c8 2782dd +48 1b04a8 +fd 27cdc3 +73 1ed26f +39 3e6a0 +d8 2bb97e +eb 12ba0c +e0 2843cf +e9 eb4f7 +c6 ee2be +8b 29d1d2 +c8 e734f +3b 1d0887 +cf 128b58 +c4 28151b +3f 1d0856 +2b 1cff26 +bb d736e +a 1cbd7e +9a d31c6 +79 1b3cef +7d 1b3cbe +17 19c4a8 +30 1d19fa +69 1b338e +6d 1b335d +c7 127731 +5 19bb50 +de 1297ba +58 1afb47 +48 1af1e6 +4c 1af1b5 +c8 e608d +62 20fed +6e 1b4625 +9 19bc66 +4 1d3fad +21 1d0ddc +5a 1d652 +a8 ce965 +5b 1b7e9d +78 1bbfea +12 1d5ba8 +4b 1b753c +df 2ba43b +68 1bb689 +7e 1b3cc4 +e5 2bf03f +2d 19658e +6e 1b3363 +db 280f96 +19 19b305 +43 5fb7b +9 19a9a4 +28 1967fc +70 1bd1b7 +2c 1967cb +21 1cfb1a +99 2643fe +a8 cd6a3 +ca 2782e4 +4a 1b04af +78 217fe +ff 27cdca +3b 3e6a7 +c4 ef33b +da 2bb985 +43 1e97cd +e2 2843d6 +80 10be29 +77 1bbeca +88 25b74c +52 1b7d53 +8 193917 +da e67b7 +bd 260232 +eb eb4fe +1c 192f93 +11 1cc2e2 +3e 1d08b7 +a9 ce966 +86 d172d +de 282232 +d2 eebfa +88 ca7be +7f 1b3cc5 +36 1d19d0 +6f 1b3364 +4a 1af1ed +4e 1af1bc +43 1e850b +23 5473 +39 19715e +63 5b9d4 +29 1967fd +1 1cb973 +85 10aba9 +fb eab9d +eb ea23c +a9 cd6a4 +3c 4f40 +8f 10c267 +a 1cbad0 +81 264966 +e8 eb558 +ec eb527 +8b d2b78 +c8 2b9d5d +14 192b82 +5 19b83e +9b 10cbfb +10 192b51 +dc f0093 +1 19b80d +eb eb252 +ce ee423 +cf 2badaa +c3 127772 +6c 22430 +d9 f0063 +68 223ff +a 1d40da +8f 10afa5 +a3 2a1224 +f7 2862d7 +ba 10778f +cb e73b9 +91 264005 +be 10775e +cf e7388 +95 263fd4 +3b 1d05db +85 263673 +fc eabc6 +e8 ea296 +de 278c22 +2f 1d82b3 +7a 1b4d19 +c7 ee081 +e2 12488a +f 1d53d0 +3 41d98 +50 1ea3e2 +73 1bd1af +3e 3e667 +80 10abd7 +63 1b4510 +8c 29e20f +3a 3e636 +4b 1e260 +11 19aeac +70 5c39d +88 29e1de +6d 1f4ddd +5 19a57c +9b 10b939 +dc eedd1 +31 1a0315 +1 19a54b +52 1ea0db +18 3b50c +93 1034a0 +9f 296ad8 +7c 21acf +68 2113d +d5 e68c9 +af 1080c0 +8a 103f49 +1f 19424f +13 c17 +76 1b4be5 +e 398b3 +f0 f405e +ac 298d9a +8e 103f18 +e9 eb559 +c6 ee320 +cc e7380 +8a 102c87 +70 2af67 +2c 1cfca3 +b4 2600ce +48 1e258 +bb 107790 +bf 10775f +9e 1035b7 +f9 eabf8 +9 476 +54 56f03 +fd eabc7 +fd eabd7 +b4 d74fe +d4 2ba596 +e9 ea297 +44 565a2 +ed ea266 +3e 1d8ba5 +f6 f2b88 +c8 e60ef +6d 22431 +f 1d410c +cc e60be +8a 10bff7 +95 2966cc +3f 3e668 +3b 3e637 +c4 ef2cb +e2 eb418 +7d 21ad0 +6d 2116f +30 3f7aa +13 3b65d +77 1f5640 +f5 28501e +54 1f143d +be 29970e +69 2113e +67 1bb2bb +5c 1d928 +ea 12bcbb +f5 2b6390 +4c 1cfc7 +e5 2b5a2f +48 1cf96 +b0 2a18c1 +1a d6f +96 d30a2 +96 1044d6 +e5 e9e6f +8d d2b3e +8 1923a7 +a8 ce9c7 +63 5b786 +db f006a +d 9a47 +2c 589f +21 3ebee +99 d34d2 +3f 479ca +1d 1d4758 +28 586e +fe eabcd +e2 2b6d1a +ea ea29d +95 10b818 +ee ea26c +a5 106a64 +ab cd70d +f6 12419a +50 1ea70 +9d d21dd +b8 ce066 +bc ce035 +a0 29a182 +a8 cd705 +9c c9c44 +5b 25c4d +1a 3b513 +12 43958 +8c c92e3 +ff ebc52 +4b 252ec +62 1edc23 +c0 ee046 +cc 28167e +6a 21144 +1d 90e6 +d 8785 +3c 4f3e +40 1e97d3 +63 1bc5a0 +28 45ac +1c 192ff5 +c3 1206d6 +e0 124823 +25 c7d7 +eb 28453c +fc 12c3fa +4a 1f095b +ad ce997 +4 862f +77 2af9e +88 ca820 +f3 eba5a +d6 eec2b +8c ca7ef +81 103b3e +4 19baed +35 19f084 +6f 22438 +ca e60f6 +77 29cdc +2d 58a0 +88 c955e +73 22963 +2b 1d0ee8 +56 25b34 +c 16f8 +ac 268c88 +a0 d5650 +1 3aa47 +87 10aec0 +d4 2b350a +8 16c7 +85 10ac0b +56 56f0a +e3 2b6d1b +b2 10890a +94 10c7bd +46 565a9 +b6 1088d9 +de e6a26 +8d c92f0 +ce e60c5 +63 1edc24 +5 41ab2 +10 19b15b +7b 21aa6 +36 3f7e2 +32 3f7b1 +0 19a7fa +6b 21145 +73 1ed013 +42 1e9a7c +5a 1d8fe +0 1934b2 +4e 1cfce +4a 1cf9d +88 d28ce +60 1ed918 +45 1b710f +67 2a381 +c7 2b2b4f +3d 4f3f +c0 ee284 +cc 2818bc +29 45ad +3 1d3cd8 +57 1b8d8b +74 1bced8 +1c d97 +40 57831 +e1 eb412 +c4 2babe9 +61 2231b +c1 ef5ab +14 192ea0 +59 26f6c +43 264bb +1e 19c59c +12 8f64 +94 29696b +3 992a +40 1f0b0f +60 1ec968 +f0 f3db0 +e1 eb3b0 +f6 2bf9fa +c5 2b18e8 +b7 1073be +61 222b9 +76 1f6903 +25 5759 +d5 efc39 +45 1e87f1 +e7 eb3da +e5 eb3d3 +87 29d0ae +e3 eb3a9 +db 281fb6 +36 60b0 +14 192e3e +d6 2b1f41 +9c 103372 +e1 eb3a2 +84 10284c +f7 1251bb +58 5708b +a7 ce849 +e4 2b5a2e +5d 26f3b +c7 2b18e1 +f3 12518a +7b 29e58 +a3 ce818 +c3 2b18b0 +59 26f0a +45 26483 +ec 2b5939 +67 222e3 +77 1ee562 +65 222dc +7 1d3fb7 +8c 10bfb1 +63 222b2 +44 1f0ade +e9 2847d3 +4 39755 +77 5c0c4 +64 1ec937 +47 1e87ea +37 1d19d1 +92 29568f +25 574b +f3 ebd78 +18 9126 +27 1cfe0e +2 41ae9 +e 1d5121 +e 1d3e5f +6 1d3d08 +64 2202d +29 1d0f41 +c0 120928 +10 8f6b +1c 19c5a3 +62 1b2f8f +26 1d10d1 +d6 2bb5b1 +af 2a134a +f7 f2dc5 +36 1d0770 +e7 f2464 +b6 10868b +26 1cfe0f +d6 2ba2ef +ef f230d +c8 11f50f +8b 10afe4 +a2 ce817 +25 19fce9 +69 1bb6dc +46 1b8439 +4 19b8a1 +63 1b4254 +11 a230 +46 1b7425 +69 1b465e +5e 1b7c2f +61 1b44a5 +c6 1277a0 +56 1b7ad8 +79 1b4d11 +14 19af40 +c 19a736 +52 1f11bb +4 19a5df +66 1b4540 +76 1b3bdf +ec f2369 +34 197047 +b4 cef62 +24 1966e6 +d4 280bc6 +e6 1235f9 +bf 29a9c3 +b3 10738b +82 103df4 +cb ee453 +99 d221c +a4 106a61 +2c 19658f +72 1ed014 +f7 12d7ab +d7 2ba290 +9d 10b6c1 +d5 2bb7f9 +1d 192d48 +7a 1b3cf7 +15 192bf1 +c7 2b992f +8d 10ad60 +de 128496 +95 d30a8 +5 1cbc60 +5e 1afb1d +53 1e8e6c +5 192290 +56 1af9c6 +b7 10f718 +86 10c181 +66 2a382 +d7 11fd52 +33 465f0 +3f 1d9c28 +2 43059 +8e 10c02a +35 e3f8 +a 42f02 +a3 107cee +86 10aebf +a9 1080f8 +76 29a21 +1e 1d5d30 +12 426f8 +2c d94f +4c 1f09e7 +72 643d4 +9e 10b6c9 +a1 107f3f +b 4313f +96 10b572 +b9 1087ab +8e 10ad68 +29 197a6d +86 10ac11 +a9 107e4a +21 197916 +1a 425a1 +ff ebe92 +87 103e32 +87 10c120 +56 1b0a3c +2 1cb989 +8f 103cdb +3 42ff8 +b 3abb3 +6f 1f4b96 +3 3aa5c +67 1f4a3f +b6 107679 +a6 106d18 +97 1034d1 +87 102b70 +a5 110320 +3e 1d1b89 +32 3e551 +4 41aa3 +77 64412 +ae 106bc1 +44 5e8f0 +e9 f25e5 +9c d1f9e +d6 280b6d +a6 106a6a +97 10b7bf +9f 10337a +97 103223 +87 1028c2 +e7 2bed98 +ad 1101c9 +eb ea29e +80 263953 +d9 28225f +3a 3e3fa +a5 110072 +16 1d4907 +1a 90bb +49 2660b +6 1d3fa6 +1b 3a252 +95 d33ac +1f 1cd733 +13 3a0fb +b5 d5fef +84 d2a58 +bc 2a1cf7 +bb 1089f0 +b3 108899 +84 d27aa +9a 29edf4 +a4 2a11dd +bd ce046 +e4 2b6a40 +9b 29ede7 +6c 1bb70e +84 d1732 +a1 ce561 +8c ca5a5 +4b 265ae +64 2a629 +d5 11fff9 +bb 10772e +ce ee1d7 +25 19e9d3 +4e 1b8590 +42 24f58 +38 47c41 +a4 ce8b1 +62 22321 +9 1d50e8 +44 261d4 +43 1e179 +bd 2999a8 +63 2a60c +46 1b86d7 +62 22073 +fb 286151 +c6 2791e0 +5a 1f2570 +43 26467 +4b 1e022 +f6 eaae8 +9d 29d8af +8d 29cf4e +b3 11093b +de 2ba684 +b4 cdf50 +9c 29daec +44 24f12 +9 1d3e26 +5f 1b0e50 +53 1d818 +4f 1b04ef +43 1ceb7 +85 29cd95 +ee ea030 +88 10c23c +bc cddf9 +7f 1bd2e3 +73 29cab +29 586f +6 8636 +bf 2a1cff +ed f3638 +7a 21869 +1 1d3c6d +b7 2a1ba8 +e5 f34e1 +88 29d1bc +56 1b7d76 +7e 1b4d4a +72 21712 +6a 20f08 +11 a220 +46 1b7415 +fb 12c3cf +ca 128e38 +5b 1d6c1 +7b 1f6a28 +4b 1cd60 +48 576da +e9 eb2bb +52 1af9f9 +40 57583 +e1 eb164 +12 192e68 +6 1d3d06 +64 2202b +d1 efc5e +49 2636b +41 26214 +0 9682 +4b 26364 +43 2620d +63 22066 +b 97d3 +48 1f09b8 +97 d3351 +cd ef431 +ad 29a309 +a1 106cd1 +39 197160 +c1 ef2a9 +ed eb28a +56 1af9c8 +de 11fefc +e5 eb133 +16 192e37 +9e 10336b +6e 1f4b27 +a5 ce5a2 +45 261e3 +5b 1f282d +41 261b2 +65 2203c +7b 1ee686 +25 54ab +45 1e8543 +3b 1d1af5 +cd ef423 +aa d5a5c +28 3efa0 +b 3ae53 +6f 1f4e36 +d8 129480 +c9 ef3f2 +c7 ef2d3 +0 1cb912 +a6 d5936 +24 3ee7a +d4 12935a +c3 ef2a2 +e5 ea173 +ef eb283 +ed eb27c +d0 2b1f69 +d7 ee9d2 +8f 29cf57 +e7 eb12c +e5 eb125 +87 29ce00 +13 439b9 +a0 107f92 +85 d1789 +11 439b2 +8d d2892 +89 d2861 +59 26f7a +c8 11f7bd +81 d270a +33 3f812 +ca 2b1766 +af ce6f2 +ec 2b58d7 +31 3f80b +a 1d3e1e +ad ce6eb +a5 ce594 +84 d2a48 +cd e6121 +c3 2b1602 +ce 2782b3 +c2 12770f +ce 2bad47 +6b 22479 +61 1bb2e5 +e2 f3766 +43 261ab +d7 2bb7fe +6f 2218c +4c 1f0987 +d3 efc71 +1b 1d5c8e +a1 268dad +34 196d89 +2f 55fb +6c 1ec7e0 +d7 128340 +42 1e97ce +8 469 +e4 2b577e +9 41bd8 +5d 26c8b +a7 ce599 +ba 1074f1 +7c 21ad1 +86 10aebd +d5 279b2d +a3 107cec +3b 19817b +f8 124079 +53 1af9f8 +70 1b3b45 +45 5e8ef +af 106bc0 +d7 280b6c +22 1d1030 +80 d1453 +74 22c4a +8c 264a8b +7e 1b3d26 +8a 10bf89 +1c 8e47 +f4 12419f +45 261d3 +8d d188c +a8 108095 +95 10b7a8 +bb 26025c +3d 19f23f +ce 127889 +19 3a1e9 +8a 263aa3 +15 426b1 +4e 5e792 +d0 eff19 +8c c952d +fc ea926 +5 41d50 +97 263fcd +1e 3a4d0 +7d 64564 +29 3dd41 +45 261d5 +e4 f3790 +3d 469af +4c 1aef15 +db 12821a +14 19c4a2 +2b 3dd3a +f8 12c367 +e6 f3789 +5c 1f12e8 +18 1cd69a +1f a103 +a6 298bf6 +87 102852 +5e 1f12e1 +b 1d50f1 +53 26b6c +70 2acb9 +af ce754 +e 3ab73 +15 3b377 +b4 108932 +50 1f11c2 +5a 57090 +13 9fdd +0 3aa54 +87 296020 +d4 e68d8 +ae 1080cf +75 6440d +18 1d5a4a +f8 ebe57 +33 4683c +3f 1d9e74 +9a 29db32 +2 3aa4d +86 103dd1 +b7 107368 +50 5f220 +5c 1f2858 +a6 29a166 +98 10c953 +19 192fc5 +77 64406 +c4 12095b +f5 123ef2 +e4 2b6cf0 +c7 2b2ba3 +19 1d5a49 +72 22c82 +54 26b35 +53 1f11ba +5e 1b7e6b +d 19a735 +70 1f5307 +99 10c952 +d6 278873 +de eed74 +f3 2be458 +c2 2baec1 +55 1f1190 +8d 103c64 +57 1f1189 +49 1f09b7 +29 d91f +4e 57706 +da 1281b9 +eb 124c20 +dc eed6f +7a 1ed169 +dd 2ba68e +ff 12d900 +a3 d6bc6 +81 263954 +5d 1e93d +fc ebef8 +b9 ce015 +6 19a576 +76 1ecfe1 +c6 2b15de +cd 2b9d2d +ef 12cf9f +15 1c55 +b4 cf210 +2a 1d9297 +50 1b7aa0 +be 108a92 +0 1332 +6c 1ec7de +2f 55f9 +bd d6394 +6e 1ec7d7 +db 2ba40a +2e 1d9266 +22 45c2e +54 1b7a6f +fe 12c6a1 +4f 1b048d +43 1ce55 +78 1b3a50 +fd f41e7 +64 1ec687 +aa 10f12a +dc 280f6b +f3 12d52c +d1 2ba2ba +4d 1b7258 +ec 284813 +70 22c79 +65 1bc816 +c0 2804d4 +9c 25d3a2 +90 c9d6a +35 60ac +4a 5ea6f +fb 27de11 +5a 1ea230 +9b 29edf5 +6c 1bb71c +a5 d6bf0 +3e 198459 +32 4e21 +d7 2b34b0 +5e 1b7bcd +c6 12773e +2a 45d85 +5c 1b7bc6 +c4 127737 +ca e6104 +6f 22446 +75 1ee5bb +18 1d477a +52 1b7a43 +50 1b8d62 +c 192376 +34 198309 +f6 123eea +b9 d6115 +88 d2b7e +a6 106a08 +5d 5f0f9 +a7 106a07 +de 1281ea +5e 5f0f3 +2f 3dd09 +d8 1281c0 +58 5f0c9 +d9 1281bf +7d 22d92 +29 3dcdf +6a 1b45f4 +c6 e5d32 +5a 5f0c2 +14 19c440 +2b 3dcd8 +89 d160d +d4 12809a +d8 e7d12 +63 1f5f72 +2 1937c7 +55 5efa2 +75 1f5337 +44 1f1da0 +b 850f +56 5ef9c +fb ebc21 +88 c92b2 +2d 55f4 +7b 22b2a +8 1bb +98 10c8f1 +8c d15dd +18 437fa +c 84e6 +8d d15dc +2f 1d11b9 +23 3db81 +cd 12788f +fc eabc8 +4d 5e798 +38 1a070d +38 4f0f +e1 2b6a72 +9c 10c922 +1d 192f94 +8b d1606 +d6 128093 +23 19fcbd +48 5e768 +1d 1d5cb8 +11 42680 +a0 ce880 +8f 294ba5 +a1 ce87f +6 42d7a +2b 3ed5c +fc 124048 +57 1af9c7 +21 1963a6 +f4 123ef1 +c6 2b2ba2 +5d 1f12e7 +98 d1f0d +a7 298bf5 +5f 1f12e0 +70 2afbb +65 20d7a +7b 1ed3c4 +ec 1236e7 +47 1af066 +6b 221c9 +68 5a5bf +ef 2b5b8b +6a 5a5b8 +78 1b4fbe +af 106e70 +e4 123590 +7c 1b3a1f +4d 1f0986 +88 d15ac +f8 f2ca7 +83 295fe1 +78 29bb0 +3 1cceea +27 daa0 +20 1d1037 +a2 267af3 +44 1b86de +53 1b8cf8 +60 1b44f8 +64 1edbf9 +44 57864 +75 5adfb +b3 cdc69 +bf 2612a1 +82 ca6d2 +47 1e9aac +66 1edbf2 +c3 120984 +e0 124ad1 +47 1e9a4a +e6 284653 +a 3aba4 +b4 25fe82 +8a c9567 +2f 58a9 +6c 1eca8e +6 1d5268 +4f 1e8941 +46 1e979d +96 d1e42 +21 3ebf0 +be 1074be +bc 1074b7 +17 192e36 +19 2099 +fa eabaa +7d 1bc07c +5f 1b0e42 +53 1d80a +7c 1b4f8f +70 21957 +4c 1e28b +7d 21822 +99 caed4 +9e 103317 +54 581c5 +9 1738 +4f 1b04e1 +43 1cea9 +6c 1b462e +60 20ff6 +5f 5f100 +d2 2820fe +62 1bc7fb +f5 2b7404 +6 8388 +28 55c0 +3e 479cb +80 ca41d +ef 12ba3d +e4 284400 +51 1e817 +ea f385b +ec eb27b +d6 ee9d1 +8e 29cf56 +e6 124afb +bc 260285 +49 1b87f7 +2 3979b +e 1ccdd3 +22 1cfb2e +a7 1102c5 +aa 298d70 +89 294bcf +c4 e5cbb +9 1cbad8 +44 1cbc4 +6a 1b4664 +6e 1bb405 +a 1422 +68 22401 +c7 120953 +8b 294bc8 +8a 294bc7 +c1 120929 +41 57832 +ec 285a73 +e0 f243b +cf 281926 +c3 ee2ee +ec f3877 +9 19b9c8 +5a 1b90fe +43 1b7139 +19 1d49c7 +a7 ce8a9 +e4 2b5a8e +b6 2a1b53 +5 3acc4 +a4 d58cd +97 25cf41 +63 1b4260 +84 ca3ec +58 1ec2b +a5 ce8a4 +4 3acc3 +3b 466e7 +96 25cf40 +63 22004 +46 251d5 +69 2240e +9e d3509 +1 3ac93 +60 5a46a +6c 1edaa2 +8a ca57b +43 5631d +4f 1e9955 +79 21a9f +63 5a462 +6f 1eda9a +ca 2b1758 +b9 10f7d5 +88 10c23e +2a 19e8a5 +8 467 +42 1af036 +9f 10b6ca +c6 e5d24 +1 19bb1d +8 1d5395 +b4 cdeee +97 c9da1 +4e 1dff0 +2a 1d7f65 +ae 106b5f +e9 f2583 +2 1d3f75 +94 c9d47 +45 1e878f +c0 2814f8 +2d 197852 +21 421a +7e 1b4f88 +72 21950 +68 1b43b1 +1d 19b338 +9a c9e66 +38 1d1b41 +c9 ee19e +ee 2844fc +c6 2b2b50 +8c 103f81 +f7 2b60e7 +56 1b7d78 +79 1b4fb1 +68 22471 +28 1d0f34 +9d 26568f +91 d2057 +a8 107e3d +20 3ebfb +a7 29a1c7 +9e d2247 +e2 f3704 +ea eb2bf +86 10ac03 +5e 1b7c2d +21 197908 +e2 eb168 +82 295fe0 +ed 1249aa +76 1b4b83 +1e 1cc410 +d3 efeaf +ac 298d38 +2 19bb15 +4b 1af1ee +67 5b755 +5a 1af8a2 +df f0039 +1 992f +db f0008 +fd 285175 +5c 1f1594 +11 43704 +a6 298ea2 +66 1f4a40 +31 4784b +a4 cd2df +87 c9192 +24 41e8 +bf 26832f +1e 1d474e +cc 120802 +28 1d953e +73 1f530f +8 431a9 +39 46740 +89 c956d +d4 11fffa +99 d1f00 +ed eb2ec +4c 5770b +ce ee1c7 +cf 1207fc +26 41e1 +8b c9566 +d6 11fff3 +8a 10bfe9 +95 2966be +f2 27df59 +44 1af0c2 +6 43026 +4f 566ff +99 26541e +3b 3e3fb +da 2bb6d9 +a4 cd2dd +5c 1afb86 +2a 3dd45 +3f 479cc +52 1b7cf1 +bb 2682fe +1a 1d471d +c7 127a4f +4e 5fa54 +c5 1279e6 +89 29cf0f +6c 1edd4e +60 5a716 +84 c919a +75 5c36b +18 4252a +cd ef42f +9f 29daf6 +b7 2681d8 +16 1d45f7 +9d 2653e1 +91 d1da9 +39 198420 +16 19b1e7 +3e 1d0863 +b5 26012f +ff 125368 +6e 1b4635 +62 20ffd +9 1d3dc4 +e6 124b69 +47 575ae +e6 f21b7 +c2 ef5af +ab 2a10cb +41 1b86b0 +35 19805a +c6 1206a4 +c3 120674 +b8 d6108 +31 198029 +c2 120673 +5a 1b0b02 +bb 2a1cce +e9 f3607 +86 294d5d +f4 f2dcd +8f 29d205 +d0 2b2217 +d7 eec80 +55 581c4 +fc ea988 +ac 107e0c +24 3ebca +91 10c7fd +a8 107ddb +8e d28f8 +51 5f21f +5d 1f2857 +a7 29a165 +e9 ea299 +a0 d6bc0 +71 1ed2c8 +2a 19e843 +aa d574c +6f 1f4b26 +b5 25fe21 +bc d7655 +b1 1109a4 +dc 2ba6ed +6 1d4fba +4f 1e8693 +b9 108a59 +35 ce96 +4 98ff +40 1f1ac1 +77 216e2 +ee 124a14 +29 19680b +74 1ed298 +9e 25d3a9 +92 c9d71 +57 1e914b +37 60b3 +b 1d3e1f +28 1d7f6c +e1 2b6d22 +47 5fbaa +38 1d05d1 +9f 25d3a8 +93 c9d70 +bc 2614f5 +b0 cdebd +18 1d5cf8 +2 3acfb +b7 107616 +2b 4308 +25 1cfb5b +1a 43863 +61 1ec967 +f1 f3daf +7c 1b3cbd +a5 25f4c0 +fa ebece +5b 1e913 +2b 3effa +5d 1b0e3b +51 1d803 +9d 103311 +2c d941 +ac d6cf4 +a1 110043 +a8 1080f9 +ee 2b6ea2 +e2 12386a +71 2afbc +e4 124b02 +c7 1209b5 +c 8784 +c3 277ee0 +d 1d50b9 +55 26b34 +1 41a81 +29 1d11ef +23 1d0de5 +6 1d3fb6 +64 222db +63 1ec960 +f3 f3da8 +d7 e6940 +57 1f1499 +30 1d1998 +37 e401 +ee e9fce +b4 cdeec +5b 259ad +d1 11fd7c +dd 2b33b4 +94 c9d45 +3f 19f236 +e 19bc9f +2 8667 +23 5471 +1d af8 +99 1048b2 +fd 2be895 +9f 29d8b6 +14 c4e +a 160 +9f 264434 +bc 268581 +57 1e90e9 +7a 22dc9 +58 1afb57 +f7 27dc87 +84 25b318 +73 1f5361 +42 1f1dca +de eed82 +41 264b4 +fb ebbb1 +7b 1b4d0c +5e 1b7edd +e3 12487d +c6 127a4e +64 21017 +da efd59 +4a 1e8911 +75 2af8b +1a 1d478d +78 22ab2 +8f 263ad3 +ac 267c20 +47 1e8788 +97 10b50f +b7 2a18a4 +86 29e30d +bd 29ac6a +b1 107632 +dd efd92 +a 39b2e +ab cd70f +d9 121133 +87 294a40 +19 3b50d +b8 d6116 +3e 1d1b17 +32 3e4df +94 c9afb +9 16c8 +6d 1bb6ab +5f 1b913c +53 25b04 +ca 128e36 +d5 2b350b +9c 296ac2 +90 10348a +35 3f7cc +78 21aa0 +7 1d5279 +e9 1249db +c6 1277a2 +61 1b44a7 +7d 29e1e +79 217ff +48 1e268 +9e 10336d +0 1937c0 +ff 125314 +3d 3f933 +fc ea91a +80 294d17 +e6 eb3e5 +9c 25c0ec +a9 cd714 +f4 1241a1 +d7 120054 +98 10cc01 +75 5b0a9 +52 1b0a0b +bd 108a28 +f7 124199 +3d 3f931 +77 5b0a2 +c5 2bac4a +17 1cc5c8 +25 d7f9 +3b 1d9e43 +d5 e793f +a5 cd2de +fa 2b7524 +63 1f4c5c +29 4608d +d8 e69fc +ec f35c9 +b9 10f837 +88 10c2a0 +1d 19c5a6 +63 1b2f92 +11 8f6e +ba ce00b +3d 19f4dd +67 63d53 +ad 1080c7 +e7 123838 +7 1cbc67 +30 1a05c4 +79 1b3c9d +7e 1ed138 +5f 1b0dee +53 1d7b6 +ca 120ae8 +6d 1edd4f +61 5a717 +ed 284814 +4c 1f0c33 +21 46eea +a9 108096 +a3 107c8c +86 10ae5d +6f 1edd48 +63 5a710 +47 1ce86 +d4 11fff8 +18 192fc6 +3a 6238 +c2 2b2b73 +f3 2b610a +ad 25f685 +fe 27cdbb +8d 29e210 +81 10abd8 +a9 ce6b8 +8c d1889 +de 2bb946 +d2 12830e +b7 cdf48 +87 103b84 +cc e5e82 +86 26492f +5a 1b916e +c2 128cdf +c 444 +42 5fbe8 +63 1bb2ec +58 1d959 +9a 10b696 +3f 479d8 +8d 103f20 +c7 11f691 +74 1b4bec +c 398ba +d1 eff18 +8c 103f1f +5e 1afb1f +c3 e7270 +d 84e5 +18 19c328 +35 3e50a +c2 ef2ad +6e 5a5eb +95 10345a +87 29e2fe +15 3a363 +5 39a02 +97 25bc7f +e6 f3735 +ee eb2f0 +e5 2846bb +a5 cd5e2 +4 39a01 +96 25bc7e +83 ca6d1 +a0 ce81e +17 1d4918 +71 1b3b46 +58 1d907 +10 192e5f +7a 1ed16b +c4 280203 +ff 12d902 +fa 124074 +ea 2b6e71 +7c 1ed141 +9a c9c1a +16 1d591b +5f 1e8ff4 +3f 5f5c +e9 2b6bcb +4f 5fa53 +8d d188a +7b 21b08 +de 2ba3d6 +10 19b1bd +0 3aca2 +31 3e239 +3d 1d1871 +9f d1f98 +98 29552f +8e 264d96 +82 d175e +bf 26832d +11 19b1bc +b0 cef23 +5b 603f5 +4a 1b729d +17 1d592a +69 1ec810 +97 10c833 +e9 123719 +12 1cd84c +61 1b31e5 +b8 cf018 +ec 1249ab +cf 12085e +c7 2b15df +50 1ead2 +fa 124012 +a 1d538e +bf 2a1ca9 +34 60b7 +25 41db +c4 2814b9 +9 147c +e2 27d606 +3b 1d0825 +f3 ebd16 +da eea95 +2a 45b5 +40 2519b +4c 1b87d3 +41 1f1b22 +20 1976c7 +5b 26f7f +87 d2740 +a8 260909 +65 1ec688 +3d 4c9f +c 1708 +52 5818d +46 1ce79 +b 1cbd8d +13 8d19 +1f 19c351 +30 ce66 +3c 1a049e +38 1d18a3 +13 1d48e5 +73 22c1f +72 1bbc4e +65 1b44d8 +ed 124a0c +28 197812 +0 41ae0 +c 1d5118 +5a 2599e +92 10c7f3 +db 11fecc +f8 124019 +87 10bed4 +c 192626 +da 278c53 +8c 1029b1 +6 41ab8 +f4 27dce3 +46 1e9aab +64 20d77 +51 1e8e73 +2a 3dce3 +5c 1afb24 +45 26491 +8 97cb +bd d60e6 +a8 1103e5 +8b 10c298 +ea 123711 +28 460e +62 1b31dd +75 2aceb +b1 2681a0 +10 1d45bf +a5 cd2d0 +dc 2ba3dd +79 21b0f +24 19f9d8 +6d 1b30b1 +99 104604 +dc 2b3663 +d0 12002b +15 1d489f +21 19e9a2 +4a 1b855f +ae 11016f +d4 ee978 +3a 198178 +78 21b10 +a4 cd2d1 +11 3a092 +1d 1cd6ca +9f 264186 +bc 2682d3 +82 10be92 +cb 11f56b +e8 1236b8 +c8 e73b3 +f9 ea94a +e8 27d748 +d9 eeaf3 +6c 20ec0 +f 1452 +f5 eaae0 +d8 eeaf2 +95 10b7b6 +e7 2b57da +7d 2ae34 +60 2230c +d 19a729 +28 1d0f32 +8a c9257 +65 2a628 +ca ef3f6 +c7 1279df +7a 1ee677 +5f 1b7e6e +e3 eb417 +c5 ef2ca +65 1ed94a +83 ca423 +a0 ce570 +eb 124c2e +af ce9a0 +ec 2b5b85 +8c 1029a3 +ff 125312 +64 20d69 +c2 ef29f +b8 1074dc +b7 299866 +53 5819a +a8 ce967 +fa ea95e +2c 4331 +84 d279c +9a 29ede6 +89 d2b7f +2c 3efcf +99 10cc02 +66 5a740 +df 2bb9ab +d3 128373 +d7 2ba52e +b0 110be1 +32 196cff +0 1ccee4 +31 1d047b +3c 19712c +a0 ce810 +24 41da +6b 29247 +29 1cfc71 +ee 285a7a +e2 f2442 +43 57839 +ea e9ffd +c7 2b9bcd +19 19b2a5 +24 1cfaea +f2 2b6117 +db eea98 +a1 d589b +ca ef458 +ad 268ed3 +33 1982d0 +10 1cc2d3 +9 1cbaca +89 1029d3 +21 3d8ca +2d 1d0f02 +88 294bc0 +17 1cc5ba +66 1b422e +fb 1252e1 +41 5fbe4 +62 5a711 +6e 1edd49 +17 1d48b6 +1f 1cc471 +cb 2b2cc9 +2a 197ac7 +da 281fa7 +10 192dfd +5a 58036 +48 5798a +79 5af21 +4b 1e9bd2 +6f 2a788 +68 1edd1f +97 d20ef +90 295686 +b7 268484 +7a 5af1b +6a 1edd18 +7 1d4fc9 +c0 e6fac +c9 120834 +cd 2bada3 +c1 12776b +65 1ec6ea +7d 1ee402 +40 57833 +71 5adca +43 1e9a7b +67 2a631 +60 1edbc8 +e2 284684 +38 3f653 +1b 3b506 +ba d610f +fd 284ec7 +5c 1f12e6 +a6 298bf4 +2c 433f +96 10b510 +ad cd435 +cb 12082d +e8 12497a +ce ee415 +58 259fb +15 426bf +67 1ec6e3 +fa 2b77d0 +df 280fc7 +2d 433e +c0 edfe4 +cc 28161c +9a 10c8ea +35 47b1c +c3 120922 +6d 5a5f1 +cc 128bb4 +35 e408 +36 1d1712 +7 1d3f55 +97 10c7d1 +f 1cbb10 +18 1d5ce8 +e9 1236b7 +c4 1206ab +0 19249c +4a 576d5 +49 1b74d3 +94 10321b +bd 10877a +17 436da +69 5a5c0 +18 1cd9a8 +33 d16e +3f 1a07a6 +a1 107f95 +79 1b4fbf +a5 260780 +c8 128b83 +65 5a49a +c9 ef462 +6c 5b8b2 +24 196438 +9c 103310 +7 1cb9b9 +10 1d5b91 +fe f2cdf +ff ebe9e +8c c952f +dc 11fea1 +33 47b62 +87 10be72 +18 194288 +da 11fe69 +7b 5b23a +32 47b61 +43 1f1b1b +42 25194 +4e 1b87cc +60 1f5c68 +7 42d7b +e9 2bdc55 +99 29ede0 +2a 46025 +5c 1b7e66 +9a d1f06 +f7 ebd47 +84 c93d8 +47 1cbd8 +2a 196805 +da 280ce5 +f9 27de0a +5f 26c92 +b 41bdf +58 1ea229 +d4 11fd4a +18 192d18 +3a 5f8a +cb ef459 +4 2e1 +e8 27d746 +77 1ecfe2 +46 1e9a4b +7c 1b3d21 +e6 285915 +66 1bc81e +d7 2b21ee +8d c928e +d 197 +88 d15fe +2d d940 +c5 ee2c4 +24 197946 +7 1937f9 +8c c928d +9a d1f66 +c 196 +91 29ec89 +c7 2b2b41 +65 5ba0a +8 41bc9 +70 1bcefb +7b 64538 +8f 29d195 +38 1983bf +cc e6120 +aa d6cbc +ca 2b9d54 +ef eb53d +cc 11f540 +58 25c47 +d0 1212ed +2a dbc5 +9b 103595 +f7 2b739d +bd 1087ce +81 ca6d8 +4a 1f0c5d +90 25bf58 +a4 d6951 +ed ea02a +4c 56449 +c5 ef5e8 +18 19c2b6 +80 10be37 +c9 11f510 +be 110ac2 +a7 ce59b +e4 2b5780 +c7 2b1633 +a4 29a151 +9d 1045d3 +d7 11fd44 +eb eb50c +6b 22415 +45 251bf +82 10be30 +cb 11f509 +63 2a660 +41 1b73ee +d1 efc6a +8c 103c71 +de eeb38 +e1 eb3ae +ef ea26f +a6 d6b96 +bc 1074ab +b8 10747a +3c 1d1870 +30 3e238 +2 1ccee9 +7c 22af3 +b7 299804 +db e7cc6 +38 3e383 +6a 5a5ba +a7 267b23 +4c 1dfdb +e5 2b6a43 +c9 e5dee +80 d2715 +2c 3da53 +fa 124080 +48 1dfaa +24 c536 +41 1e9a82 +b5 d7551 +de eeb2a +e1 eb3a0 +5c 5806e +80 295fdb +b1 299572 +bc 260223 +9 1d40d4 +44 251c0 +bd 268574 +da eeaf9 +58 5803d +b8 d60a6 +cc e7390 +fd ea927 +48 1e00c +23 1d109f +de 279f3a +d2 e6902 +e8 eb24a +ad d5775 +e7 27d5d4 +22 1d109e +4d 2632a +7d 1f548e +4c 1f1ef7 +40 5e8bf +47 1b0398 +99 d31c0 +ce 2803b5 +73 2ad21 +3c 3f682 +8e 1029aa +f6 27dcdc +e3 12ce17 +76 5adf3 +b8 1087aa +9d d1fa1 +b5 2a2e61 +df 281fdb +d3 ee9a3 +66 20d70 +a5 268dde +3f 46708 +f0 ebd64 +e 43171 +b6 25fe25 +9d 29ee13 +91 10b7db +35 465b8 +4 43021 +58 1af899 +21 1966a8 +94 d20f5 +b1 cef24 +b6 107367 +e 1451 +a7 299eb9 +5e 1b7bbf +51 1e9121 +66 1b327e +31 6089 +41 1e87c0 +d1 efc08 +21 5728 +23 c7ab +2f 19fde3 +8a 263aa1 +ad 2679c7 +c 1d3de6 +2a 1cff33 +b5 d628f +c5 127a48 +9b 103587 +22 45c82 +54 1b7ac3 +2e 1d92ba +b5 2681d1 +14 1d45f0 +32 1d073d +5 1d3c90 +23 1cfddd +38 19817f +72 1b38f0 +8f 10c277 +d0 121289 +f6 ea7d6 +b 41eed +18 90b6 +27 1cfd9e +90 10475c +f4 2be73f +2d 3efd2 +dd 1294b2 +ec e9fb9 +1 1937cf +d8 e67b2 +7d 22af4 +75 2299d +1a 193f6f +47 1f1dfa +64 1f5f47 +a5 ce84e +b8 cddc8 +64 5a499 +dd 2bb704 +27 197940 +d1 1280cc +a8 cd467 +6d 1ec841 +61 29095 +6d 1bc6cd +c8 28038b +77 1bcf34 +98 c9c21 +3d 5f63 +65 1bc576 +c0 280234 +9c 25d102 +90 c9aca +55 1e8ea4 +35 5e0c +2d 1d824a +4a 5e7cf +78 21862 +66 1b44de +46 1e149 +77 216e0 +ee 124a12 +8d ca7f0 +d7 eec2c +62 5a4d1 +6e 1edb09 +28 4370 +cd 127b9f +64 1bb2b5 +0 12d2 +18 193fcc +e8 f38a8 +ac 267964 +51 1b8d53 +ad 267c83 +c 1d40a2 +29 1d0ed1 +98 25bdff +13 1cc589 +37 d13f +30 1d06d6 +7f 22aed +36 1d87ae +37 1d0701 +65 2203a +65 1b4228 +c0 277ee6 +fb 1255e5 +40 1b83f3 +4b 5fa30 +c4 279425 +cf e714a +59 1f2828 +8a d28c9 +25 1d0dab +80 294a69 +86 295faf +2e 3dd16 +de 1281f6 +fb f3f01 +c6 e6f90 +48 5fd38 +da 281fb5 +47 1b00ea +2 19b869 +25 45f07 +25 54a9 +25 197697 +bb 108a54 +80 25b355 +24 1d0daa +bc cf0bb +51 1f146f +66 1bb5cc +31 e3d7 +51 1f26d1 +82 d2772 +47 1f1b4c +64 1f5c99 +28 58d2 +d8 efdb2 +1c a0fb +86 294aa1 +43 1e8757 +d2 281e5e +e9 27c1e5 +a5 267b2c +4 1d3f4b +21 1d0d7a +65 1bc5d8 +e2 eb106 +7e 22d98 +5c 1afb26 +2a 3dce5 +74 1bceca +da 1281c5 +b8 cf08a +46 5e63b +c 42eca +75 1b4b7b +21 1cfac8 +8e 10bfba +5d 259c9 +a7 cd2d7 +b3 2681a7 +12 1d45c6 +81 29cdb8 +8c 263a69 +f0 ebab6 +e 42ec3 +e3 284693 +42 1f0ab2 +f4 12c551 +ec eb2dd +82 25b5fc +2 192505 +b9 110afb +e4 27d382 +e2 2b6a7a +c4 2791db +de e6786 +47 1b8438 +3c 1d05a0 +33 4dbe +3f 1983f6 +9a 25c0b4 +88 d285e +ce e5e25 +8 1ccfd9 +39 1d0570 +38 1d056f +e5 1238a1 +26 1d106d +88 295e82 +40 261a3 +84 d1788 +51 1ea0d3 +f7 f40f7 +8e d2898 +d5 12131d +8c d2891 +63 5a780 +6f 1eddb8 +4f 1b87db +43 251a3 +c5 2b2baa +d5 2b1f39 +64 1bc569 +ca 127864 +34 1d0449 +8c 102a13 +95 c9d46 +44 1b740e +3e 1d08c5 +b 4314f +f7 f2e35 +51 1e8e11 +a 1ccfe0 +3b 1d0577 +e2 27d358 +ee ea2de +6 964a +eb e9ffe +62 5b725 +38 196eaf +bd d7646 +9d 2643cd +e6 e9e75 +d9 eeaf1 +a5 268d7e +c2 ef303 +85 295fa9 +9c 2643cc +91 29d71b +1a 1cc441 +9f 10cbd8 +db eea96 +2b 45b6 +21 1976c8 +72 1b4dfe +1 193521 +52 1b0c57 +6b 29505 +70 1bbee7 +7b 63524 +2e 4338 +ad 10f153 +72 22c74 +8a 264ab5 +e 1d410d +2b 1d0f3c +4b 2535e +74 1bbc08 +41 1b8390 +40 26211 +12 1d48d8 +37 47b23 +92 25cf71 +f4 123ef3 +a9 cd466 +ac 268ee2 +a0 d58aa +8f 264d95 +83 d175d +65 1f4c84 +b9 2a1a27 +d0 eff0b +a8 cd465 +c8 ef461 +47 1b8686 +cf 128bba +43 1cc09 +4f 1b0241 +a 19b9c0 +2d 4605e +69 20f00 +44 1b83c2 +7f 1b4d49 +42 1e17a +73 21711 +62 1b450f +f 19bc9e +3 8666 +30 1d174c +be 1077d0 +e2 124aca +b8 260254 +84 10ae54 +a1 107c83 +1c 19b2e3 +39 198112 +1d 19b2d4 +7a 5af19 +b9 2a2f87 +40 1b73ed +8 19b955 +3f 469b6 +50 58194 +42 1b73e6 +57 1afa37 +12 19b1b6 +c9 ee3ea +6c 1b30be +ec e9fc7 +44 1b83c0 +62 1b450d +e9 f2585 +12 1cd5aa +47 1af0d6 +2 19a855 +87 10bec4 +ff f2f2a +4a 2660f +6f 5b8bc +5a 57022 +d5 e7bed +fa 2b77d2 +1b 1d4790 +31 19f363 +ce 2bafe5 +c2 1279ad +5a 1b7e3c +a7 cd5e7 +5a 26f72 +95 25bf26 +8a d1851 +d8 e67a4 +1f 1d47c1 +2 1ccc3d +b7 299558 +c0 12067a +48 25348 +58 1b0b09 +7b 1bd2a6 +46 1b0335 +50 1b7d3e +7f 6455b +5d 1f12e9 +a7 298bf7 +88 c94fc +c3 2814f0 +8 405 +43 1b83f9 +60 1bc546 +11 1cc2d4 +1c 192f85 +e4 28464c +b8 1074e8 +13 192e67 +30 196fb4 +49 1b854b +1a 19affb +c7 ee32d +97 10b503 +49 1dfb9 +55 56f02 +84 25c5e8 +bc d6139 +a 19a69a +18 19aff4 +c5 ee326 +51 58193 +f8 ea957 +cb ef3f9 +e5 f378f +5b 5f371 +7e 1b4ff8 +72 219c0 +19 1d4787 +2e 4706a +54 25873 +8 19a693 +ca ef6a6 +fb f2c3d +3a 1d05e8 +bf 110d7f +e8 e9ff6 +32 19f35d +80 103b3d +77 1b3bde +83 10be41 +30 19f356 +3b 46993 +75 1b3bd7 +81 10be3a +a1 ce5d1 +8 167 +1 1d3c5f +7a 2185b +57 1b8d37 +58 25a09 +67 1ec6f1 +62 290f1 +6e 1bc729 +54 1b7a5f +72 1b3bac +ed f2368 +c6 11f690 +35 197046 +de 280cc2 +41 1b83f4 +12 19aea4 +41 1de62 +56 1f24ac +5a 25cb0 +55 1f24b4 +d2 120fe2 +1 19a53d +78 22b22 +8c 25b77f +9b cae79 +72 1b4b60 +38 5f91 +aa 260910 +2d 1977e2 +21 41aa +c7 e5d31 +5b 5f0c1 +58 1d6ad +36 1d070e +45 1b0391 +c5 ef578 +40 1aede1 +5e 1b0de1 +52 1d7a9 +c 979a +48 1cd4c +21 19796a +d1 281e4a +b6 108629 +26 1cfdad +e7 285668 +ad d6a99 +77 21990 +ee 124cc2 +3a 4cca +91 29e9dd +df 1284f9 +fc 12c646 +a5 d6942 +5e 25c7f +3e 1981ab +32 4b73 +d7 1283a2 +67 2102f +2a 4369 +ec 12bce5 +d3 11fd21 +df 2b3359 +4e 2531e +7a 1ee6d9 +5f 1b7ed0 +c7 127a41 +e4 12bb8e +d7 2b3202 +18 b1c +cc 127b3e +65 5a748 +42 1b00aa +98 10b691 +3d 479d3 +92 10c803 +c 19369c +0 64 +73 229d3 +6f 1ec7d8 +ff 12c6a2 +d8 eeb00 +7d 2ae42 +e7 2b57e8 +77 1ee5c2 +1a 1d4781 +da e7d19 +2d 3efd0 +67 5a741 +b1 269460 +1c 1d4757 +eb 12ccc2 +12 1d4628 +5 1cceb2 +a4 267abb +2c 19e86d +3a 1d0824 +85 103dbb +1 19a85b +ea f384d +35 4787c +12 1d462a +4 1ccec1 +35 1d0458 +37 47875 +84 103dca +b5 107361 +a4 29a15f +0 3aa46 +87 296012 +61 1edc29 +95 c9da8 +d6 2b21df +85 294aa9 +a 42e92 +17 3a378 +25 46f1b +4c 1b7265 +19 1cc677 +8e 29e20a +82 10abd2 +27 46f14 +cd e70d3 +e9 f22e3 +9a 10c94a +12 19c416 +a0 107c84 +a4 260a91 +79 1bbd2f +44 1aedbe +da 12017b +2b 45d84 +5d 1b7bc5 +18 a12c +27 1d0e14 +f9 eaba6 +b0 d74cd +46 1b7107 +19 194287 +75 2acdd +92 10c7f5 +ed 2857c6 +e1 f218e +85 d14e9 +a0 107cf2 +38 198181 +df 128259 +b2 2613c6 +0 2be +c 1938f6 +3d 196e8d +54 26df1 +68 1b433f +48 1b0198 +27 c52e +20 1cfac5 +28 1977ae +8 193607 +1e 19b02e +86 10ab9f +5a 5f3de +5 19a5de +56 1b7d14 +d5 12966b +57 1af789 +12 19af08 +57 1f11eb +29 19e8ab +74 1f5338 +0 1324 +a4 260a3d +87 25c8f0 +c1 ef30b +4e 1b751a +6b 1b4349 +52 1b7a99 +c9 1278c0 +44 1b0330 +75 1b38c7 +e0 1235c1 +ec 2b6bf9 +82 25c612 +2 132b +7b 1bbd36 +46 1aedc5 +c4 128ca7 +20 1cfab7 +98 d3231 +a7 299f19 +70 1ee27b +77 2ace4 +50 57ee6 +c4 e7239 +f5 ea7d0 +e4 27d5ce +40 1deb5 +c7 279481 +6a 20efa +25 1979a7 +16 19aed7 +14 19aed0 +f 1938ee +3 2b6 +91 295687 +56 1b7a68 +6 12fa +55 1f2454 +22 45c20 +2e 1d9258 +54 1b7a61 +72 1b3bae +4 12f3 +9 16d6 +6d 1bb6b9 +12 426e8 +1e 1d5d20 +21 1d108a +5a 1d900 +c4 ef2c9 +a1 107f93 +39 198422 +1 42d43 +63 1edc22 +4a 1b728f +81 103d8a +6d 1b30bd +62 1bc5a1 +59 25c56 +11 19b1ae +5b 603e7 +e3 124b2b +14 1cc5c0 +ad 267c13 +ca ee198 +48 576dc +a8 d5745 +48 1b7288 +50 1b9000 +2 19a5a7 +25 1963d5 +bb 107792 +5a 56d74 +97 d1ddf +65 1b2f66 +16 1cd5cd +ac 268c26 +a0 d55ee +85 294ce7 +7d 5b200 +6 2e8 +8a 10ad27 +2 19a7f3 +95 2953fc +c4 278163 +65 2a37c +12 9fdc +46 1b73c1 +63 1b41f0 +32 1d073f +14 1d45f2 +f1 285fff +75 1f55e5 +b8 cddc6 +8b d2868 +9c 103374 +f4 ebae7 +42 1b0048 +1b 427ec +10 19b1af +b4 cef56 +2 1934b7 +17 1d48b4 +dc eeacf +14 19af30 +5c 259d8 +10 9fd5 +10 19aeff +58 259a7 +67 1ec68f +ac d6a38 +27 471c2 +7c 1bbdc1 +18 1dde +ce 12086b +a5 106cae +82 25c610 +9c 10b660 +5a 26c62 +9e 10b659 +1b 3b760 +10 194123 +7f 1f5743 +1e 42562 +c4 277eb5 +ff 1255b4 +98 10b62f +18 42538 +9f 29db04 +94 10b509 +3c 19718e +66 5ba04 +41 5fb80 +51 1d563 +5d 1b0b9b +2b 3ed5a +53 1b8d06 +16 4240b +31 d167 +ba cf2cd +3d 1a079f +e4 e9e62 +eb eb2b4 +ce ee485 +4a 576d3 +f4 27c9b1 +cc ee16e +1e 1d5a12 +12 423da +8c 10acff +f4 286031 +e3 eb15d +c6 ee32e +42 5757c +c4 ee017 +dc efd81 +88 10acce +5c 26c8a +8 41bd7 +f2 285ff9 +6d 1b461f +61 20fe7 +72 1bcf02 +68 1b465d +84 10aba8 +f0 27dca4 +e 1d50b1 +2 41a79 +ac 299ffa +a0 1069c2 +40 1de53 +c7 27941f +55 5f250 +85 c9129 +a 1d50e2 +2d 3ed22 +58 25999 +df 280f65 +21 3d8d8 +2d 1d0f10 +af 2679cc +1c 1cc717 +a6 107fda +e2 f36f6 +df 2ba6f5 +89 103c41 +ed 2bdc24 +2a 1964f3 +f6 1254cc +d4 e68ca +ae 1080c1 +85 264937 +d6 121325 +38 196ebf +dd 2ba6ee +5a 1af83e +4c 1b7575 +69 1b43a4 +84 c943a +8 19a9b3 +89 25b49f +d4 12131e +d 19a9e3 +53 1f1468 +33 e3d0 +70 1f55b5 +85 10be69 +3d d28d +21 1d93da +1e 8e40 +1b 427de +68 1b43a3 +1c 8e39 +a5 ce5f6 +4 3aa15 +32 4e1f +3e 198457 +78 1b39de +58 1af837 +18 192ca6 +c5 e5fd8 +23 1d913f +de eeac8 +c5 ee078 +d 1d4095 +5e 259d1 +a5 107f62 +98 25c0af +8c d15cf +ff f3f3e +6f 1ecaf6 +85 102af9 +47 1b8684 +96 29ecd0 +c9 ee192 +a1 107f31 +9e 10b6bb +67 5b765 +d8 eea90 +e7 2b5778 +26 3d90f +c5 281774 +7b 1ee6dc +8 19b9b7 +45 1b867d +85 29cd87 +59 1f15c6 +35 4b4a +ac 107e7c +0 19a59e +48 25046 +9c d3200 +c 1cbdb8 +2a 1cff35 +72 219b0 +7e 1b4fe8 +1c a109 +bb 2a1a20 +86 294aaf +56 2587a +1b 1d478e +5c 25c84 +79 22ab3 +fa ea952 +4 193801 +c6 11f3e2 +35 196d98 +98 d31cf +a7 299eb7 +2a 47039 +5c 1b8e7a +50 25842 +af 106e62 +9d d223f +d7 280e0e +e7 124afa +da 278c47 +97 10b821 +ab 298d6f +ce ee167 +95 d2086 +c0 277ee8 +e2 eb15a +e2 eb16a +cc ee160 +68 1ec80f +68 291df +5 19baec +4a 2503f +93 104754 +f7 2be737 +c8 ee12f +7 15a9 +48 25038 +cf 280604 +38 19f1ff +2a 3efa7 +5c 1b0de8 +50 1d7b0 +ee f38e0 +c4 ee009 +ce 278015 +60 1ec6b8 +23 54d3 +d8 280f9c +68 1b464f +30 19f0a8 +a 4314e +3b 466e5 +44 1b0392 +75 1b3929 +2b 1d8274 +b 1d40cd +28 1d821a +d2 eff1e +e5 eb187 +fb 2b77d1 +b6 cdc37 +52 1af6e7 +b4 26816e +c1 277ed9 +81 25b348 +af 2a10fe +f0 2b6110 +f7 f2b79 +c6 ef5e2 +60 1b4508 +2f 45db5 +e0 eb411 +39 3e630 +86 29e05d +cf 2b1736 +64 1b44d7 +47 1b038a +ec 124a0b +2f 3da67 +24 19642a +c9 ef70e +6a 1b4658 +f0 eba60 +60 1b31e4 +e8 123718 +43 1af097 +7a 21859 +14 1d5bd2 +56 1b7a66 +73 1b4b61 +39 5f92 +62 1b4501 +60 1b4506 +f5 1254c4 +98 10b683 +50 1b7cea +3d 479c5 +a8 106b25 +b5 cdf43 +14 3a362 +3 1924a4 +37 1d070d +31 1982c9 +36 1d070c +37 1d1721 +92 2953df +38 3e3f3 +bf 2999bf +27 1cfdac +8c 29e203 +80 10abcb +f3 12d53a +25 46f0d +97 10b501 +53 581fc +a8 ce9c9 +84 295cfa +16 19aed5 +87 264bde +45 1de93 +41 1e9a20 +c3 2804dc +a2 cd5b7 +ae 260bef +25 3dba9 +c1 280235 +ea 1239bf +1e 194002 +12 9ca +d4 128346 +34 1d8809 +c0 ef2a8 +1a 193f71 +24 1d1068 +4 3acd3 +35 3e26a +7 1ccf1b +a6 267b24 +71 22c7a +d4 2bb548 +26 1d1061 +1a 3a49f +56 1af718 +1a 3a1e3 +a4 ce601 +ca ee134 +41 1b0360 +58 1d64b +68 1bb43d +ca 128dd6 +fb 12c36d +f0 284d30 +c1 12098b +df 12824b +fc 12c398 +ff 2be5e0 +c2 127a11 +ce 2bb049 +12 1d4636 +f7 27df27 +7c 29b7f +7 1cceb9 +89 10bf81 +a0 ce872 +d3 2bb583 +8f 294b97 +c7 ef5ef +1a 19c2bd +d6 281e1f +4c 25317 +ca 128b26 +d5 2b31fb +4b 1ccfe +5 19a56e +e2 eb40a +89 29e1d1 +18 d76 +2 39739 +56 1e7ec +e 1ccd71 +49 1b8795 +94 1044dd +c5 ee2b6 +0 961e +49 1ccf7 +3e e2a9 +95 cadbc +5e 1f1341 +60 1b44fa +6b 5bb37 +c7 279171 +2f 45da7 +e0 eb403 +66 1b44d0 +cc 2b1792 +ee 124a04 +6c 1b43d4 +60 20d9c +64 1b44c9 +4d 1f0c96 +83 d2711 +ca 11f7c4 +5a 606a0 +49 1e992b +e8 284534 +20 196407 +a5 d6b9e +c5 2b9c36 +85 10be5b +a7 268d85 +59 6069a +90 29d9c6 +3c 19719e +55 1b0cf2 +23 3eeb1 +3 1937c8 +20 197915 +87 c9440 +a4 cd58d +e4 2b5a90 +4f 1f1eef +43 5e8b7 +c3 e7262 +e0 eb3af +7 193797 +ac 107e18 +b1 cf1d2 +ce 279339 +c2 e5d01 +a5 25f77c +41 1b0352 +eb f2598 +2f 196835 +23 1d90df +98 cae7f +60 22008 +32 1d06cf +9c 26412a +6 19354a +c7 2b15d1 +7b 22d78 +c1 e725b +48 1cffa +e7 eb12a +47 1b0328 +ec 1249a9 +e5 27c30d +52 1b7a97 +45 1b0321 +97 103461 +81 10be2a +b 19a9ab +56 1f1438 +7b 1b3cf6 +19 8e17 +31 1d9cd7 +f5 1241a0 +d2 279b02 +b8 1074da +30 196fa6 +38 19716d +d9 efdc1 +56 1d53a +c3 ef55e +cb e7119 +88 103eee +2a 196555 +5a 1afaee +9e 10b657 +20 196395 +21 1cfdd6 +b1 d721e +2c 19e87b +f 19a72e +72 1f5300 +37 47881 +a6 2607ea +ef 2bdf2f +3a 1d0832 +85 103dc9 +21 1d0dea +5a 1d660 +57 1b7d17 +72 1ee520 +66 1b320c +d3 280e3f +a4 d58db +80 294d25 +87 d178e +2e 19e874 +75 1ed2f9 +87 103dc2 +7 3accb +a6 d58d4 +58 1b8eab +a2 2607b9 +eb 2bdefe +81 103d98 +e5 2bdd7b +62 1b31db +2a 3dcd5 +5c 1afb16 +cd 2b2d01 +c1 11f6c9 +9a 10b626 +99 10b620 +c0 ef30a +65 1b421c +82 10be3e +cb 11f517 +c0 277eda +95 29539a +7b 1b4f58 +98 10b61f +6b 1bb443 +22 1d0de4 +9f 29daf4 +8d ca852 +d7 eec8e +e6 eb3d9 +aa 25f64e +40 1b737d +7e 1bbd74 +91 25bf57 +8f 29d193 +6f 2219a +60 222aa +42 1e87b8 +d2 efc00 +f 19a9dc +2c 19eb29 +72 1f55ae +fe 2b7553 +f2 123f1b +5b 1f1311 +3b e279 +78 1f545e +7e 1b4d48 +72 21710 +20 5719 +d0 efbf9 +96 10b500 +43 1b0359 +cd ee1d1 +e8 1249da +60 1b44a6 +c4 11f3e9 +95 10b4fa +15 42403 +3f 1d1b88 +33 3e550 +d2 2bb82e +0 1d4f90 +49 1e8669 +29 55d1 +32 465e3 +3e 1d9c1b +ec ea01b +e2 124b2c +5 41aa2 +c2 2baecd +8f 263825 +ac 267972 +ac 106b56 +7 1924d5 +c7 e7231 +28 1d9290 +89 c92bf +d4 11fd4c +47 1e13a +93 d3070 +3 1cbc28 +74 1b4e28 +27 c7de +20 1cfd75 +28 1964fc +cd 2b9d2b +57 1ea3b9 +1d 8e38 +f6 284fc2 +45 1e133 +f5 27dcd6 +7 41aab +a6 10f066 +c6 28025e +91 d3069 +1 1cbc21 +9 1923a8 +1a 3a1e1 +bb cddc2 +85 263923 +a7 d6b95 +22 1963fe +67 1b44d1 +84 10ab98 +1c 19b027 +a6 267ac2 +58 5f3d7 +81 d2778 +d2 efeae +42 562ca +4e 1e9902 +22 1cfd6e +1 9681 +87 ca6a0 +c4 2b1885 +2 3ac9b +33 3e232 +3f 1d186a +9a 295528 +c1 e71f9 +41 1e102 +17 1cd828 +5 15a2 +81 ca668 +e5 28464b +f6 27dc88 +bc cf0b9 +2c 19683d +72 1ed2c2 +dc 280d1d +ae 298daf +f6 ea82a +31 e3d5 +9d 29eb01 +91 10b4c9 +1d 1d5a0a +11 423d2 +fd f2c75 +cc ef6de +c1 128a2d +a0 ce5d2 +3f 1a0498 +33 ce60 +76 1ed291 +2b 196804 +73 1b4e01 +39 6232 +9c 29eb00 +90 10b4c8 +35 1d1770 +5c 26c8c +97 29d99d +85 29e2f7 +fe ebef3 +dc e6781 +8 1d50e7 +cc e5e20 +9b 10b635 +90 263ff8 +df f0047 +71 646ea +7c 1ed3ed +35 1a033a +bc cdd97 +9f c9c4a +b8 cdd66 +9b c9c19 +bb 25ffae +8a 25ca17 +b4 cdc40 +97 c9af3 +ac cd436 +15 1d5b6f +8f c92e9 +a8 cd405 +6d 1ec7df +11 1d5b3e +d6 11fd45 +8b c92b8 +c9 1207d2 +9c c9bf0 +b4 29aab0 +4 964f +4d 1cd28 +2 1d3cc7 +94 c9a99 +e1 f36fc +74 216d8 +46 1b0389 +8c c928f +a4 29a14f +de 2b334c +d2 11fd14 +84 c9138 +45 1e84e1 +fd ebc3d +a9 106b8a +fa 1242c0 +43 1decb +2a 19eaf1 +88 c956c +67 1bc87f +2a 1d7fd5 +e7 12484c +da 278999 +24 1976fa +81 d2a26 +49 1b8559 +e7 eb43a +58 6038b +64 222e9 +28 19655e +ad d6cf5 +cd 2b9d8d +a 1732 +3b 4cc9 +f3 12d77a +8 42eeb +42 1f1aba +87 10be70 +99 29d870 +48 1cfa6 +0 1924fe +bc cf0ad +a 19360e +34 1d19b9 +84 103b0c +a6 260a36 +58 5834b +89 103eef +ed 2bded2 +c 193648 +0 10 +9a c9bb8 +1d 19b08a +38 1d1893 +18 1cd6fa +de e7cea +e8 eb2ba +eb f234c +d4 279b2e +a2 107ced +e0 eb163 +28 1d1180 +3b 1d8b75 +69 2a4ae +6 1cbc04 +1 1937c1 +ad 107e0b +97 1034c3 +b4 107610 +27 19fc8c +82 26394a +a5 267870 +4 1d3c8f +22 1cfddc +c8 e5e51 +6d 22193 +ac 107e0a +96 1034c2 +41 1b73e0 +8c 103c63 +1 1cbc2f +5a 1afaec +19 d67 +ae 26796b +f5 2b63f0 +1 15d3 +b 1d539d +1 1ccc43 +a0 26784c +f3 27df5a +52 1ea379 +81 103b4c +8c ca7fd +d2 121282 +4f 1cfdd +e5 2bdb2f +c1 e6fbb +9 1ccfd8 +5c 1d92a +62 22071 +e8 eb2ac +e7 2843a6 +ad d57d7 +e5 eb3e1 +a8 ce71b +4c 265e5 +41 5f934 +1d 19c34a +11 8d12 +9a cae78 +76 1bbc1d +92 1044a5 +ed 27d476 +e1 e9e3e +1c ae9 +89 d2b0d +68 5bb91 +fa 27de0e +df 2ba693 +43 1f1b1d +22 1976c2 +c4 128ca9 +46 1aedc7 +f5 12c240 +c7 2baef1 +da f0005 +c3 128a26 +32 1d19ff +7f 1b4f95 +73 2195d +4b 576d4 +cf ee486 +ea 124c8f +9 84b4 +bd 10f806 +8c 10c26f +2e 19e8d6 +1d 1cd978 +11 3a340 +e2 eb15c +0 3a9e6 +49 25045 +a0 ce5c4 +e0 eb155 +64 1b421b +a5 107cb4 +31 4b0b +3d 198143 +79 5c4f3 +3d 4ca1 +b0 29981f +aa 11013e +d0 ee947 +dc 281f7f +2c 1d11af +20 3db77 +28 1977be +a0 107ce6 +84 103dba +bb 10f7de +4b 1b0202 +68 1b434f +be 10fb10 +23 1cfb2f +60 1b41e8 +a4 107d23 +e9 f2591 +9a 10cbf8 +a0 107f32 +49 1b01fb +ab 267c4b +a 1d406a +bc 10fb09 +3a 1d0578 +e7 1238aa +3 19351a +c5 2bae96 +6e 21183 +20 197667 +4 19379f +35 196d36 +a0 106a30 +ac 29a068 +89 10ad2f +da 128465 +23 da6d +1 19a7fb +a4 107cb5 +78 5c4f4 +27 1cfafe +18 8e16 +3b 1d88c7 +6 1cb956 +1 193513 +e4 f2220 +2c 1d823d +83 ca66f +e7 284652 +2e 196834 +c3 e6fb4 +e0 eb101 +5 19bade +7 1934e9 +8d 10acfe +f5 286030 +6a 211b6 +4c 25069 +41 1b00a4 +eb f22ea +26 19fa41 +6f 1b311a +38 3f8ff +1d 90f6 +e5 2856d1 +44 1f1af0 +40 26203 +c7 2817cf +e4 28591c +32 1d0421 +a4 260a9d +5 1934e2 +76 1bbf1f +12 1f3c +81 103adc +46 24f27 +ce 280665 +99 d3470 +24 1d80f4 +7 1d3fa7 +2c 1cfcaf +30 3f79a +15 8f91 +ad ce6f9 +e7 e9e6a +a9 ce6c8 +80 29e087 +c9 2b1760 +b2 10f6da +be 2a2d12 +9 1d5078 +62 222b1 +eb 12cf6e +7e 5af4a +a0 107f3e +85 d1735 +db eeafa +6e 20ec7 +a1 d58fd +ad 268f35 +22 197970 +46 1af075 +f5 12c4ee +d2 281e50 +14 8f90 +af 106b5e +a4 25f521 +e2 eb416 +43 1de5b +40 1b73dd +bc 299697 +42 1de68 +ac 29a30a +a0 106cd2 +d4 efc9c +10 42681 +1c 1d5cb9 +54 1b09e1 +22 3eba0 +43 1b73e5 +d7 2ba2e4 +60 1bb532 +ef f231b +da eed45 +45 1b00d5 +a6 107cbc +7a 5c4fb +44 1aee22 +1f 1d47bf +29 3da31 +7d 22ae4 +63 1f4a02 +d8 e67a2 +6f 29224 +26 3ebc5 +4c 1cd1b +6b 291f3 +e1 1235c2 +ed 2b6bfa +d1 12938e +8d 1029a2 +73 1b3b4b +e0 28593f +59 1f1318 +a3 298c26 +65 1b2fba +b 1ccda1 +46 1de8d +63 1bc85c +9d 103303 +38 1970fd +e4 eb194 +c0 277ed8 +65 1b421a +1b a37e +f9 285144 +58 1f1563 +23 3ec05 +de efd96 +bc 1074a9 +bf cf2ff +b5 269431 +fc 2b64e4 +9c 103302 +e8 f38b6 +49 262fb +a7 267877 +b9 107735 +6 1d3c96 +8f 103c6b +a6 d5626 +7a 29e65 +76 1bbf2d +a0 107c90 +85 d1487 +ae 268c2d +a2 d55f5 +67 1f49cf +c 41bfa +74 1bcf2c +7f 64569 +99 1032d2 +c3 ef310 +b8 107796 +19 3a1db +c0 e6fbc +43 26219 +2b 5876 +21 19f9a8 +6f 294c4 +68 1eca5b +51 1b8d55 +ac 25f924 +3d 4670f +c 43178 +46 5e8e9 +fd 2be833 +e7 2b57dc +95 10b7b8 +85 c9137 +32 196cfd +2e 55fa +bb cf2ce +c5 e5d1c +98 1032d1 +1 1d4f83 +0 85fc +c 19bc34 +38 3e381 +bf 29994d +f7 eaae7 +5 1d3f3e +a8 d6a07 +bf 29994f +ed eb288 +f6 f2b26 +57 57f1d +30 3f80a +ac ce6ea +6f 1b4634 +63 20ffc +24 54a8 +91 d30db +95 25d24a +76 29a2f +f1 2b73d5 +57 6025d +39 466de +8 43147 +b0 29aae1 +95 2642d8 +2c 197af1 +20 44b9 +fe ea991 +ca 128dd4 +d5 2b34a9 +2a 55c9 +99 d221e +4a 1e8661 +cf 128df8 +53 57eec +a8 ce6b9 +88 c950a +67 1bc81d +c2 2804db +5d 25c79 +a7 cd587 +2b 3efa8 +db 129488 +d0 281e4b +65 1ec936 +f4 2b7405 +28 55c2 +a3 cd556 +af 260b8e +e2 12481c +b8 25ffa6 +85 d2a49 +c 19a6c6 +48 5ea76 +da 280cf3 +8b 103ef6 +ef 2bded9 +26 54a3 +59 5f0c8 +b7 10f9b8 +61 1b41f7 +e6 eb12b +75 1b392b +41 1f1d6e +d1 278848 +ab 29a03f +5f 1b7c2e +e4 eb124 +a9 29a038 +86 29cdff +a9 ce976 +69 29240 +b2 10f988 +be 2a2fc0 +1d 19b096 +47 5f90c +24 549c +f2 ebac9 +16 1cd87b +65 1b3214 +c6 e723e +fb f41af +5b 1b7bfd +fc f41e6 +ad 106b49 +f1 12d535 +8a 295e89 +62 22003 +34 197045 +4 19a56f +77 1bcede +d2 280b9c +e0 eb0f3 +59 1b7bf6 +60 21ffc +ad ce9f9 +e7 27d5c8 +22 5472 +c7 128ca1 +a0 ce562 +a6 10f058 +b6 29a819 +19 19b065 +43 5f8db +20 546b +59 5834c +a7 260a37 +26 c52f +79 647df +88 d160e +ca 11f516 +4d 1f09e8 +2d d950 +73 643d5 +3 1ccc4a +95 1031ac +24 1d105a +8 8517 +31 1982d7 +c2 120921 +9b 10c8e9 +6c 29210 +7 1d5215 +c0 e71f8 +d2 2b34e0 +85 10284b +91 d3387 +86 102aff +1e 192f8e +8e 10bfb8 +a4 106d0f +5 39754 +c2 2b2b7f +ca 2b2cca +fb 2b6261 +1d 1d4748 +3b 1d0895 +c4 281529 +84 c93e6 +aa d6d1e +63 62ac0 +6f 1f60f8 +ef eb59f +c4 1279e7 +60 1b425a +99 10b8ce +37 60b1 +74 1ed296 +c6 ef334 +4 1937f3 +35 196d8a +8a 294e83 +51 1f11c1 +79 1b4ca1 +5c 1b7e72 +66 1bb31e +31 e129 +89 103c95 +64 1b4529 +8e 10b016 +ab 107e45 +64 1bc887 +20 1d1029 +bc cdd95 +13 43957 +8d c92e2 +a0 107f30 +85 d1727 +b8 cdd64 +8a 25ca15 +2a 196803 +bc 10faa7 +21 1cfac6 +b4 cdc3e +86 25c8ef +0 19a84c +ff 12c3a0 +48 252f4 +dd e7a96 +cf e5e88 +d8 f0060 +65 222da +7 1d3fb5 +ff 28612e +f3 f2af6 +bd 2a19f6 +d4 efeda +fb ea961 +5a 56d80 +99 29edee +ac cd434 +7a 1b3d05 +2a 45d77 +5c 1b7bb8 +43 5f8cb +d9 e7a65 +a9 cd404 +d0 efea9 +a8 cd403 +4a 1b01a1 +40 1af091 +2 42ff5 +4b 566ce +1b 1d4a2e +27 54a2 +38 1d8b7b +6e 1bc975 +62 2933d +6a 20ef8 +5 19b8a0 +9 1d3db6 +6e 1b4627 +62 20fef +5c 1b7bb6 +a6 25f4c4 +8d 29e4b2 +81 10ae7a +2c 1d0f01 +20 3d8c9 +98 d21ad +6c 22184 +a7 298e95 +8 39887 +a9 cd468 +db efdc8 +14 c50 +e3 2b6d19 +dd eeb30 +89 c925d +9e 29d8a7 +38 1d05d3 +3f 196e88 +bd 110d6a +96 29d750 +70 1b3ba7 +43 264b9 +d7 1293b8 +60 2a606 +59 1b8eaa +6b 221bd +a1 d6b6b +34 4b47 +6 1937f8 +88 29d1ca +bc cdd89 +1e 1cc6b0 +4c 1dfe9 +28 1d7f5e +b8 cdd58 +ba d767f +73 63421 +7f 1f6a59 +a 8510 +2c 433d +99 d1f70 +b1 2a2e30 +25 41e7 +7a 1ee42d +c4 2814c5 +58 1d905 +63 1bc850 +0 1ccc44 +e9 f25e7 +87 29d04a +4c 26339 +b2 25fe56 +9e 2954f7 +8d d28a0 +d3 129325 +48 26308 +89 d286f +c5 ee06c +1c 193fa7 +10 96f +43 1f0aa5 +98 c9bb1 +af 260b8c +a3 cd554 +59 25c46 +7c 22ae5 +b7 2997f6 +f5 eaa7e +74 1b3b76 +57 1afa29 +56 1f118a +b 19a6fd +28 19e84a +a1 29a191 +7 43019 +7 1d3ca5 +e 42ecf +f0 ebac2 +60 1b3246 +f4 eba91 +4 862d +6b 63ee9 +64 1b3215 +47 1af0c8 +ec 123749 +18 43aa8 +6a 1edacc +df 282227 +d3 eebef +1c 19b025 +2b 1d11f6 +51 1af9ff +14 1940fe +62 1b4253 +10 a22f +c1 e7269 +8c d288f +4b 1e9924 +6f 2a4da +68 1eda71 +87 29e052 +4e 1b750c +43 1f085b +11 1c94 +b 1ccd93 +2f d949 +70 2295b +28 1d0ee0 +aa 26799c +c 9798 +18 192f56 +d7 11fff2 +f7 eaa79 +bd cf04a +8c 10afab +a9 107dda +86 10aba1 +5a 5f3e0 +30 6078 +c6 27917e +5a 1b0dae +43 1aede9 +60 1b2f36 +15 1cc55f +50 1b09b0 +4a 262ff +ce 128e05 +b8 d7624 +4e 1b725e +99 caee2 +5 1cbbfe +a4 d68ef +ed e9fc8 +9e 10462f +9d 2656f1 +91 d20b9 +c9 28038c +24 4486 +5b 1f257f +32 19700f +c6 ef2e0 +da 2b20b9 +29 55c1 +c 8792 +5f 1b7bce +7a 1ee3d7 +9c c9be2 +47 1f0ad6 +8a 10afd7 +55 25b22 +2f 47319 +36 196fde +be 107512 +86 25b62f +48 262f8 +cb 120a87 +8 9767 +4e 1cd2e +81 d1764 +8d 264d9c +d3 2bb821 +fa 124010 +38 19842f +72 1b3ba0 +4e 1aef72 +b8 cf338 +71 5b0da +7d 1ee712 +9d 10c921 +e2 12487e +d7 278810 +56 1b09e8 +bc 2996a7 +58 1b0b69 +9e c9e97 +30 3e53a +3c 1d1b72 +ae 106e0d +85 295cfb +59 1ea53a +37 1d8abf +77 216d4 +ee 124a06 +4f 1f0c9f +a4 25f7cd +22 1966ae +23 1d7e1d +98 c9bbd +77 1bced0 +d2 280b8e +f1 27dcb3 +f 1d50c0 +57 26b3b +3 41a88 +50 1ea0d2 +4f 1b726d +6a 1eda76 +aa 10ee7c +dc 280cbd +5a 26f7e +16 193eab +d7 2b1f32 +ea 1236af +28 197ace +d8 281fae +62 1b323f +cd 2b9d8f +ef 12d001 +80 10281b +8c 295e53 +c7 277eaf +1e 43ae0 +ed 12bce4 +ca 281646 +82 25c8be +28 58d0 +62 1b449f +e5 12383f +c2 2791a1 +34 1a05f5 +7d 1b3cce +a8 106b79 +20 196645 +44 261d2 +62 2231f +12 3b3bc +9 9a24 +99 cb190 +fd 285173 +2b 1d11e8 +51 1af9f1 +9d 2954ff +db e7a0a +67 1b321d +ef 123751 +f0 ebd00 +af d6cee +cf 2b9d86 +ec 2bded3 +96 10c7c4 +88 c924e +ce e5e89 +e6 2b6d49 +65 1b3216 +ed 12374a +16 1cd87d +f5 eba92 +28 196550 +ad d6ce7 +ed 1236e8 +16 1cd81b +42 261a8 +9d cb15f +40 251ef +4c 1b8827 +d7 e68d0 +ad 106b57 +6d 294bd +8e 102a0c +97 c9d3f +a5 2a24a0 +c2 128a25 +ed 284812 +cf 2795d8 +c3 e5fa0 +ec 27d725 +e0 ea0ed +84 ca44c +18 19b064 +42 5f8da +45 1af05f +5b 582e3 +95 d1de6 +ef eb52f +b5 26817b +84 264be4 +f1 2b63c1 +57 5f249 +86 10be63 +5a 606a2 +c0 ef298 +40 261a1 +c7 28176d +44 1b7100 +62 1b324d +87 264bdc +7c 22af1 +95 c9d38 +85 264c45 +f4 eaa7f +d7 e6932 +2c 3ed15 +df eeb39 +fa 125342 +38 623f +72 1b4e0e +a4 106caf +87 102b62 +20 1d80b5 +8 1923b5 +8d d2b4c +d3 1295d1 +8e d15d4 +0 835c +c 19b994 +f3 285ffa +52 1f2419 +f9 eaba4 +46 1b7105 +22 19765e +4 41d51 +96 263fce +77 646c0 +cb e70b7 +94 10477f +5e 1b0ded +52 1d7b5 +8c d15cd +1d 1cd96a +11 3a332 +2 1d4f89 +4b 1e8662 +2b 55ca +ee 1249a4 +4f 1f0c3d +a4 25f76b +bb 110a90 +c8 ef3f1 +a0 ce80e +4b 5fce0 +1a dd1 +f6 2b60e6 +c7 28176f +8d d2ba0 +e7 12359a +ea 124973 +eb f258c +61 21ffd +44 251ce +9c 296ad2 +90 10349a +35 3f7dc +2d 196520 +69 5a8d0 +3 19a7f4 +14 426b2 +c1 11f3b9 +cd 2b29f1 +89 10acbf +f1 285ff1 +61 1b3245 +9 41bc8 +71 1bcefa +c7 1206a5 +68 22153 +e3 ea0e7 +ef 27d71f +f 398b2 +53 6029e +ae 106e6d +d6 280e19 +cd 120803 +a1 25f4ef +9a 1045fe +94 10447b +fe 2be5e1 +15 3a371 +4d 5770c +ec f2315 +8 42e8b +c2 120983 +5a 1b0e12 +17 3a36a +c5 2b9bd4 +20 1963a5 +de e6778 +c2 2b28c5 +69 21140 +20 da67 +29 3ef91 +a4 10effd +87 10aeb0 +3a 1d1b48 +1f 19b33f +97 296671 +53 1d7a8 +5f 1b0de0 +7 192537 +d4 280b64 +ce ee165 +97 10cad3 +e0 27d34f +1f 1cd971 +13 3a339 +ac 2a2604 +a0 10efcc +8f 29e4b7 +83 10ae7f +1b 19b30e +a8 106b87 +3 192506 +20 196653 +7e 22d9a +8a d15a3 +95 25bc78 +4a 2503d +af 267c7a +da efd67 +4a 1e891f +75 2af99 +a 84ac +80 10287b +8c 295eb3 +c9 ef454 +9a d1f04 +9f d2246 +98 2957dd +bf 2685db +77 1ed290 +b9 107797 +6 1d3cf8 +49 2635d +1a 8e0d +89 d15ff +5 39a10 +87 102b00 +7 39a09 +9c 29d83e +73 1b3b3f +39 4f70 +cf 278076 +d8 28224e +55 1af774 +23 3d933 +2f 1d0f6b +56 1b7a74 +7b 21808 +9 192346 +a8 25f901 +5f 1b7e6c +16 1d5b77 +bb 26986c +6b 20ea7 +4f 1b750b +6b 1f6073 +99 29d872 +af d577c +e 1d50bd +2 41a85 +f0 27dcb0 +17 1cc566 +65 1bb554 +1 1571 +34 1d177b +cc e5e1e +89 25b74d +a0 cd55c +ac 260b94 +83 c940f +8f 25ca47 +45 1e141 +4a 1ccff +1c 192d49 +89 264d6d +80 10c139 +2 192257 +87 d29ee +c4 2b9bd3 +84 10c108 +6 192226 +b1 2a2e40 +8a 1029d9 +55 1d524 +f4 eaadf +2f 3ed1b +39 4f0e +cf 278014 +89 10afd1 +b5 10f9bf +e4 eb132 +d8 2ba6be +c7 e6fe5 +7a 1b4f55 +ea eb2c1 +7e 1b3d34 +8a 10bf97 +85 10aeab +b9 108749 +ee 2b593e +c8 120a7f +a9 106b7a +3a 3e388 +8 97c9 +42 1b8398 +6 1924d4 +bd 110aca +97 10b7af +c7 11f3e3 +8d 103c72 +a 1cbd8c +95 d20e8 +6e 1b30b5 +db 280ce8 +41 1e164 +5 193800 +4d 25068 +28 3da20 +f3 2bf9d8 +af 298fec +ae 268f3d +a2 d5905 +20 3ee49 +3 3acfc +67 1f4cdf +d0 129329 +c1 ef29b +14 1cc5c2 +e3 124b2d +70 1b3889 +db f0078 +63 1ec8fe +1e 1d5d22 +12 426ea +cb 2b2a29 +3 12ca +67 1bb2ad +ae 110161 +d4 ee96a +99 29d87e +46 575bd +39 1970fe +42 1b0366 +6d 1bc721 +61 290e9 +ea eb24f +95 10c7ca +35 5dfc +1f 1d5cbf +13 42687 +2a 47047 +50 25850 +5c 1b8e88 +c4 1289f9 +d 3ab7b +ac d5784 +88 294bce +8f d1637 +8e d2b46 +bf d60dd +72 22970 +86 25b5cd +0 1cb972 +c4 e5cb9 +7b 29b56 +a4 106ca1 +ed f3876 +68 1b30df +7a 21aa7 +29 4371 +c8 28164f +2c 1cfefd +74 21978 +5e 5f101 +b2 10865a +76 1b4e91 +22 1cfdde +4 1d3c91 +77 1f6600 +d2 2ba2be +a 3adfe +1c 193ffb +10 9c3 +b4 2600dc +97 25bf8f +18 194286 +52 1af9f7 +6 1d3f46 +29 1d117f +7 1cbbf7 +a6 d68e8 +ef e9fc1 +78 22ac0 +8c 25b71d +4d 1b72ba +ca e5de8 +ba 1074e1 +32 196fad +25 5749 +f0 123f24 +fc 2b755c +80 25b5f5 +81 294a6a +dd 281f80 +ab 11013f +d1 ee948 +39 19f44c +4 1924db +4e 57714 +39 4698c +20 197657 +c1 2814f7 +41 261a2 +85 10ae55 +96 29ec60 +58 1d6bb +a7 d5935 +a0 298ecc +1 1cb911 +b4 10760e +8e 25ca46 +82 c940e +64 1ec935 +12 436fe +22 19639c +60 1b424c +6b 5b889 +a1 299ee1 +cf 280354 +42 1f1b1c +76 1bbc7f +56 1af71a +89 d2b6f +ef 2b6ba1 +e3 123569 +20 420b +2c 197843 +73 1bcf01 +1a 1cc6df +be 108a22 +0 12c2 +87 25c88e +28 3da84 +af 299050 +19 1cd9a9 +3d 1d05b1 +54 1ea95 +d8 278990 +53 1e911a +d 192695 +33 6082 +9d 10b6c3 +76 1bbc0f +12 1c2c +60 2206a +32 1d0731 +c6 128a02 +9c 26418c +51 1b900f +a0 2a0f6e +8e 263ad2 +e7 27c368 +46 1e8787 +4a 1f1f1f +ff 2be83a +43 1b0367 +5 41d5e +a7 107d2d +9f d34a6 +e6 2bdb37 +b1 110942 +dc 2ba68b +10 436a3 +49 25037 +ad ce9a5 +f3 12542a +e7 ea116 +d5 279ddb +a3 107f9a +3b 198429 +85 d2a57 +8e d15d6 +d5 12005b +a7 107f69 +9a 25c0b6 +11 1d4622 +1c 19b2d3 +d2 2ba560 +98 10358f +77 1f68a2 +15 1d45f1 +33 1d073e +86 103dc1 +d4 2b31fa +87 10abb0 +b0 108901 +11 3b346 +46 1e853b +95 d20f8 +a7 11006b +85 29cdf9 +e1 eb402 +dc 279f33 +d0 e68fb +aa 1080f2 +3f 1983f8 +33 4dc0 +cf ee484 +81 264968 +30 e3d6 +79 21aaf +31 197007 +7b 5c240 +18 90c4 +6a 1b30e8 +9e 2967c9 +92 103191 +15 1d4663 +ef f387f +f9 124018 +c8 120a81 +a7 ce8ab +6 3acca +29 19654f +ec 27d475 +e0 e9e3d +19 42537 +8c ca54f +d2 120fd4 +c6 e5cc0 +7a 22dcb +35 196d8c +1 1d51cf +4 19a829 +21 197658 +dd 2b3600 +d1 11ffc8 +60 2a5f8 +67 222e1 +ce 2795d7 +c2 e5f9f +8d 29e1a0 +81 10ab68 +b8 2685b2 +19 19aff7 +ee f3632 +b6 10f6a9 +5e 1b0e4f +52 1d817 +ea f3601 +13 1d4637 +d5 e661b +af 107e12 +3c 1d05a2 +d 1cbaa7 +99 d1f62 +ea f2599 +4b 57990 +f5 27cc6e +38 196e5d +dd 2ba68c +c8 e70a1 +5 1ccf22 +20 1d0d79 +5 19a570 +a4 267b2b +43 1b73e7 +e8 12ba68 +8e 103c6a +13 1d45c7 +66 1bb2bc +82 103b44 +3d 3e671 +1a 193fd3 +d0 128317 +dc 2bb94f +0 19a84e +73 1bd1bd +d2 2ba2b2 +98 1032e1 +77 1f65f4 +d6 e693f +14 1d490e +31 1d173d +a1 107fa1 +ca 120a7a +fb 124011 +39 198430 +dd 121412 +da 129789 +86 103b13 +5a 58352 +0 19a53c +32 1d0491 +b7 110c28 +e5 f3781 +38 6241 +84 294a9a +b5 261153 +14 1cd572 +96 26402e +b0 110bef +46 1f0829 +f9 1242c8 +8a 2637f3 +7e 1b4fea +72 219b2 +19 1d4779 +88 d28c2 +cd e7143 +a5 107f70 +ba cf32f +98 25c0bd +39 e280 +6e 1bb475 +64 1bb563 +47 1b7416 +80 103deb +18 19427a +6c 1b311e +3a 3f65a +3c 1d1b82 +30 3e54a +29 1d0f43 +71 229be +df 281fe9 +d3 ee9b1 +66 20d7e +a5 268dec +8e 1029b8 +f6 27dcea +d0 2ba567 +93 d3382 +2d 1d0f12 +21 3d8da +75 2298d +17 1d4668 +41 5e920 +4d 1f1f58 +ca 120a86 +9b d1f05 +8d d18ee +9 3ab3c +a8 1080f7 +78 1b4d10 +e4 27d5c0 +51 1b8d61 +6a 211a8 +43 1b00b9 +17 4240c +b6 10f9c7 +46 5e8f7 +63 5b726 +9 8764 +85 10becb +d6 129601 +92 25bf5d +37 19829f +9b d3477 +26 1d80fb +c1 2804e3 +2e 1cfcb6 +dc eeddf +f9 ebc0e +b6 25fe27 +ec e9fb7 +7e 1ed13a +17 1cc31a +c3 2b2b72 +72 1b3b4c +38 4f7d +b0 1088a1 +46 1e84db +b4 107602 +ea eb2b3 +95 10c82e +17 1d4606 +84 d1486 +a6 107f6a +f2 27cca3 +53 1af6e8 +ca 2b2a1a +ef e9fcd +44 1de84 +a4 1069f3 +78 5b232 +be 1074b0 +2c 3da61 +c9 ee130 +2d 1cfcb0 +7e 1ed3e6 +ed f362a +cb ee3f1 +c6 120952 +35 198308 +d6 e6691 +ad 107e19 +36 197ff2 +72 5c3a2 +93 d331e +28 1967aa +8a 10bfeb +61 2230b +8b c9256 +12 1cc588 +a1 107cf3 +ac ce9a4 +e5 f34d3 +cf 2818d2 +c3 ee29a +38 5f93 +5f 1af8e0 +dd 1297c2 +7e 1b4d3c +72 21704 +82 10be94 +a7 106ca7 +3f 197136 +6a 21146 +29 1cfc0f +6d 1bb46d +ea e9f9b +18 1cc6e6 +3f 19f4e4 +7c 1b3a0f +ad 29a30b +a1 106cd3 +f9 ebe66 +46 1b83c7 +6d 1bc72f +61 290f7 +ea eb25d +e5 ea171 +49 1f1c19 +22 19e74e +c 192386 +2e 55f8 +9b d322b +e0 27d5f1 +2f 1d7f95 +d0 2ba2b9 +4 19222f +26 54a1 +da 120187 +93 d30d4 +75 1f65fb +b9 110a8b +ee 2bdc80 +84 103b1a +61 1b4259 +40 1aede3 +2 42d47 +4b 56420 +1b 1d4780 +38 1d88cd +5a 1afafa +e7 f24d4 +41 1e84b0 +d 8793 +5f 1f2850 +53 5f218 +e5 ea10f +17 1d4916 +3 1d4f8a +2 8603 +e 19bc3b +86 29cdf1 +b4 2681de +ee eb592 +d9 effff +9e 1045db +61 2a3ad +2b 58da +82 10ab70 +8e 29e1a8 +5f 1f285e +53 5f226 +f2 27dca9 +c4 e5f67 +37 3e511 +85 29e0b9 +d6 2bb7ef +e3 eb409 +14 192e9e +27 3dbb0 +c6 2bae8e +bc 29ac6b +b0 107633 +c1 ef299 +f4 f2b11 +9a 10c8f8 +95 10b80c +35 47b2a +68 1b307d +b8 cf2c6 +c1 e6fad +8b 103c3a +bc 108a27 +e6 eb199 +58 1f12b7 +1b a0d2 +de e67dc +19 19c5d5 +53 1b7d46 +70 1bbe93 +11 a290 +a7 cd5e9 +6 39a08 +72 1b3b3e +38 4f6f +99 cae80 +ce 278075 +97 d208d +60 1b4496 +f 19a730 +50 1af742 +2a 1d0f39 +2 1ccef7 +66 1b4220 +f5 ebda2 +98 d1f61 +a7 298c49 +3d e2a3 +65 21018 +8e 10acf8 +f6 28602a +48 1b854a +2f 1d1227 +23 3dbef +55 1afa30 +4a 2535b +84 10ac08 +67 1b4541 +7 19a577 +22 1d0d80 +58 1d659 +96 29d9fe +c7 1277a1 +d5 2b325d +6d 1bb3fd +0 1921ee +9e 10cbd7 +2d 3ed14 +88 1029d2 +73 1b4e0d +0 19249e +b6 29aab9 +88 ca7cc +4b 1cfac +e1 2bdafe +d6 eff51 +95 29ea1a +84 ca3ea +58 1ec29 +89 ca7cd +ed 2847b0 +92 10b7df +9e 29ee17 +cf 279576 +c3 e5f3e +a4 2a0f9f +46 1e97ab +ce 1278f7 +69 1b45fc +9e 29edb7 +92 10b77f +9e 103309 +dc efd91 +bc 29ac69 +b0 107631 +4c 1e8949 +f7 123f4f +1d 3a21a +54 1ea93 +0 399e0 +3d 1d05af +c 1cd018 +8e 263ad4 +c5 279418 +24 1cfae8 +9c d3262 +44 1f0b3e +ca 281648 +2b 436a +ed 12bce6 +69 1b4650 +c4 ee007 +75 1bcf2b +21 1d7e78 +f2 eba59 +8f 102a19 +1a 192d0f +98 10cbf1 +25 3ee6b +8c 296161 +80 102b29 +18 192fb8 +b7 2610e8 +99 c9bb0 +8c d289d +d2 129322 +c6 ee00e +c4 128a5b +d4 128098 +15 a25f +67 1b4283 +ec e9fc5 +e5 27d5c1 +6 1d526a +37 1d8801 +2 1ccc4b +c3 ef2a0 +f6 f2b18 +37 47b31 +ad ce74d +c 3ab6c +52 1b0c65 +c7 27815d +6a 22416 +22 19796e +4e 1f1c42 +42 5e60a +8 42e99 +81 d2718 +e3 27d5f7 +9 1cd048 +2d 1cfc50 +75 216cb +44 1e134 +43 1e87b9 +d3 efc01 +d9 1294e5 +23 5721 +61 20d39 +6d 1b4371 +c8 27802f +a6 10ed56 +45 1b83c3 +e4 28597e +16 3a0bd +83 10c0e1 +8b 103c9c +a8 107de9 +66 1bc88e +ff 2b7562 +f3 123f2a +c2 120993 +30 3f4fa +15 8cf1 +8c 10c023 +dc 2ba43f +26 19667b +93 2642ae +60 1b2f28 +40 1b838f +f7 125211 +34 1d177d +91 10caa9 +6d 1b3111 +fc 12404a +96 10c824 +68 2114b +df 11fefd +da 280f95 +d 19a737 +28 1d0f40 +a3 298c18 +6e 1bb467 +4 1301 +39 e272 +bf cddf3 +8b 10c236 +1e 3a212 +cc 2bada2 +c0 12776a +65 63aac +fc f2c76 +d8 2b20c0 +df eeb29 +5d 5806d +ff 284ebe +c2 ee2ef +ce 281927 +87 295fb0 +8b d28ca +c8 2b9aaf +9b 10c94d +22 1d10a0 +d2 2bb580 +53 1b8d08 +19 a139 +6e 1ec7d9 +48 1aeed8 +f6 eaa84 +4 1303 +dc efde5 +14 1cc312 +81 29e336 +4 1ccf23 +35 1d04ba +c1 e6f59 +26 1d10c3 +d6 2bb5a3 +d1 e7bbc +94 d1e39 +1b 1d5a52 +e 19a6cd +9c 25d3b0 +90 c9d78 +55 1e9152 +35 60ba +4a 5ea7d +bb 10fade +8 8515 +42 1b70e4 +ec 27d779 +e0 ea141 +95 10c7cc +62 2200f +77 1ecff2 +27 1d1060 +18 a378 +82 294d1e +40 1e163 +29 1cfc7f +93 104444 +64 20d6b +7a 1ed3b5 +4 19a5cf +4c 25077 +cb 1207cb +11 9fe2 +a8 cd713 +9 158 +a6 107f76 +33 1d1754 +da 1284c7 +8e d1638 +5d 1f1597 +7f 64809 +91 d30d9 +db efdba +14 c42 +dd eeb22 +89 c924f +c9 12785e +b6 10f957 +87 10bed2 +a8 29a2d9 +a0 107fa2 +df e67e9 +f7 ebda9 +9a d1f68 +5f 1f1342 +7c 1f548f +38 1d1841 +3f e2aa +95 2655a6 +4b 1f09b0 +2b d918 +68 1f4afd +27 1d0db4 +82 294a72 +a8 1080eb +d4 e668c +ae 107e83 +e9 f38a7 +75 63149 +44 5fbb2 +9c d3260 +d6 281e2f +94 1031b9 +de eed84 +fb ebbb3 +83 10c141 +1b 19c5d0 +7a 1b3a49 +ff f41e0 +d8 1213e2 +c9 ef3fe +8e 10c2d8 +bf 10f86f +e4 e9ed2 +2c 1cfeef +3b 1d9b87 +48 1b84e8 +cd ef741 +4 3aa17 +4d 25076 +e6 eb18d +41 1f1dd0 +25 1d0db9 +a7 267875 +2d 3da60 +c1 ef2a7 +88 1029e2 +dc e7a95 +67 1f5cf5 +30 5e3a +73 5c333 +8e 25c79a +82 c9162 +64 1ec689 +27 54a4 +b1 108902 +47 1e853c +1f 1d49ff +4d 26338 +9 1428 +6d 1bb40b +12 4243a +1e 1d5a72 +a1 25f79d +60 1b4258 +f5 28500e +96 10346e +ff 27e0ee +f3 eaab6 +f5 284cfe +c4 281767 +25 4489 +46 1af0d7 +f5 12c550 +a5 298e8e +c2 11f413 +ce 2b2a4b +eb 2b5900 +99 10b8dc +1f 1cc463 +b3 cf177 +54 1b0a43 +22 3ec02 +53 5f288 +5f 1f28c0 +32 3f555 +10 1cc2e3 +e7 2b6a9e +95 10ca7a +37 19f0e1 +8d 10c270 +90 10c79a +d6 278abf +9c c9ef0 +27 1d8150 +c9 ee12e +28 1977b0 +ec eb597 +4d 1dfdc +6e 2a4e7 +81 ca6ca +e5 2846ad +96 1044e4 +c7 ee2bd +ec 2b6e47 +e0 12380f +cf 2b2cfa +c3 11f6c2 +c0 2791aa +8 413 +43 1b8407 +39 3e63e +27 19e780 +6e 21175 +20 197659 +56 1d7da +1b 1cc6ee +8f 2637c3 +77 21982 +c5 28152a +a5 268d8c +c2 ef311 +1b 42530 +96 10b502 +1a 1cc67f +48 1dfb8 +38 196e4d +e5 ea17f +1f 19c353 +13 8d1b +10 1d48d1 +96 1031b2 +99 103334 +1 98bf +af d57de +a8 298d75 +e 19bc3d +3f 19f1d4 +2 8605 +95 10320e +cc 127b3c +85 d1725 +28 3da22 +af 298fee +5e 259cf +f8 27cb47 +66 1bb55c +24 3d8fc +61 2200b +76 1f6655 +45 1b866f +a3 cd308 +af 260940 +fc 27cb16 +a 431b0 +3b 46747 +d6 120001 +6c 1b43e2 +60 20daa +5d 1ebf9 +f4 27c9bf +d7 278872 +e 191 +2a 1d9537 +50 1b7d40 +5b 5f37d +ba 10f7dd +10 1cd597 +0 15d2 +89 c92b1 +14 19c1a0 +7f 22aeb +2b 3da38 +c7 ee07f +c4 e6f7b +ae 268edb +a2 d58a3 +3 3ac9a +67 1f4c7d +e7 ea124 +8 1cbd85 +2f 19eb83 +98 d31cd +6c 1b30ae +d9 280ce1 +31 1d0729 +c5 1289fa +0 1334 +24 1cfe08 +fd ea929 +b4 d7250 +d4 2ba2e8 +e9 e9fe9 +44 562f4 +6 1cb9ac +1a ac1 +8d d2890 +c9 e7104 +10 1f41 +59 26f08 +a3 ce816 +1e 3b4e4 +44 1b867c +b8 1089f8 +4e 1e8632 +40 1b0041 +86 d14df +55 1f143e +5b 1b7e3d +74 2298c +20 3d8d9 +2c 1d0f11 +3 3978c +f 1ccdc4 +ae 2679cd +66 1ec682 +71 1bbee6 +a3 2a24e6 +36 1d04c2 +b7 cdf4a +16 3a369 +69 1edad2 +69 291ec +1a 43853 +20 3eb8d +a7 29a159 +41 1aedf0 +3b 197167 +15 1cc551 +d6 e68dd +c 1938e6 +0 2ae +28 58e0 +d8 efdc0 +9 1d3e18 +86 2636cd +a0 11028e +e9 123967 +f4 ea7c3 +5 19253e +84 10becc +c3 ef5b2 +8d d2bb0 +d3 129635 +4a 1e2c3 +7b 2185a +10 19af0f +7c 1b3cbf +a2 cd553 +25 19ea25 +ae 260b8b +50 1f140e +13 a229 +ba 2996df +45 1b73af +4 19b891 +4d 1aef6a +cb ef6a7 +5e 1d683 +ba 10fa7d +25 1d0e0d +b5 107673 +3 1cbbd4 +4c 1b8517 +40 24edf +c7 2804ab +c0 280236 +65 1bc578 +6e 1bc6d5 +62 2909d +81 25c8b8 +28 19654e +ad d6ce5 +6f 5b8ac +4 1d4f61 +a9 268c56 +b7 10766a +e6 285923 +d 9799 +ab 10f12b +dd 280f6c +ff f41de +e9 12bd17 +30 ceba +3c 1a04f2 +20 1976bb +e3 eb109 +c8 ee43f +6d 2a781 +18 192d16 +42 5758c +8f 29d1a3 +d7 eec1e +2 1d3cc9 +c0 2bac1a +5b 1d65f +9b 25be07 +9 39b8a +24 19768a +f2 27dcb7 +4a 1f1c11 +4f 1b8583 +43 24f4b +85 d2749 +d0 279db9 +1f 1d475d +41 1b83f2 +c 42ebc +82 295fe2 +b3 299579 +be 26022a +4d 5fa5c +0 1570 +ce 2b2d09 +c2 11f6d1 +ff 2b62a0 +2e 3ed2a +54 1d533 +c1 ef557 +c9 e7112 +ac 29a06a +a0 106a32 +f4 ebae5 +11 1cc520 +0 1326 +b5 cdc41 +6f 62946 +6 193796 +be 1077c2 +a4 25f77b +87 25b62e +19 192d09 +41 1b8400 +48 1e8918 +6a 5bb8a +26 1d105f +ed f25c2 +9e 10cc29 +19 a387 +4e 1b757c +6b 1b43ab +a4 107f63 +9 42e8a +69 1bb42e +93 25bcb0 +26 3ebd1 +93 10c804 +9d 29eb71 +91 10b539 +af 106b52 +42 1b83fa +65 62a98 +58 1af89b +dd f0032 +5 43020 +6c 1bc97c +d7 1280f6 +60 29344 +4f 1b882f +43 251f7 +53 1b09b8 +68 20eff +76 1bbebd +12 1eda +25 19e787 +dc 121411 +69 291de +b2 10f926 +be 2a2f5e +9f 25d098 +93 c9a60 +a9 106b88 +fa 1242be +fd eabc9 +b4 d74f0 +43 1f1dcb +7d 1f6752 +71 6311a +b5 cdf51 +14 3a370 +7b 22d6a +8d ca7fe +d3 121283 +c7 e5f6f +4c 1af227 +94 d1dd9 +77 1ed300 +39 19f20e +ca 127858 +53 26b18 +2d 19fb90 +21 c558 +aa ce6be +a8 2a2635 +9 1d507a +b0 108891 +95 d2088 +9d c9c43 +c6 128cae +12 436aa +4b 2503e +84 10be68 +cd 11f541 +fb 12d62f +f0 285ff2 +71 1bd156 +4a 1ccef +e6 27d317 +80 d2707 +c9 e5de0 +f7 f2b27 +e7 eb43c +46 5785b +de e67da +94 c9aed +bf 1074bf +8e 103f28 +ca 128e28 +40 1b0043 +11 1d48d0 +80 29d073 +b4 cdc32 +4b 5fd42 +a0 ce870 +59 1b7be8 +15 8f8f +4e 25070 +46 1e84e9 +c 19a6d4 +b8 2996ca +ce e70e7 +c6 2b9980 +91 10c78b +5 862e +83 103df3 +a0 107f40 +6e 1eda9b +62 5a463 +bd d6384 +28 3ecf2 +b 3aba5 +79 1f570d +aa d57ae +6f 1f4b88 +f7 ebd39 +a3 106c86 +af 29a2be +eb 27c1de +99 d21ba +3c 196e8e +a4 1069ff +1c da7 +a4 299ea1 +da e6a05 +5d 1b7ed7 +2b 46096 +78 1ee6e0 +61 1b44f9 +8a 29e1d9 +13 19aea7 +a 1420 +5 98f0 +ac 298da6 +2 1d3f77 +1 1cbbcd +cc ee47e +e9 eb2ad +44 1b70fe +62 1b324b +42 261aa +bf 26154f +b3 cdf17 +30 3e4da +3c 1d1b12 +89 10af7b +f1 2862ad +85 d29e7 +0 192250 +1e 194250 +12 c18 +5 1d4f60 +2 12c9 +17 426c6 +18 8e68 +52 1b7a37 +85 103b0d +3a 1d0576 +e7 1238a8 +95 10b81a +ea ea29f +a9 298d68 +a5 268dee +4 1d520d +4d 1e88e6 +7c 1b4f8d +70 21955 +59 25998 +7d 29e2c +61 1f5f79 +9e 10337b +d9 eed9f +0 1937ce +60 1b41ea +69 1bb43c +20 1d0ddd +51 26e21 +e7 ea17a +46 56599 +fe eabdb +61 2230d +3e 1983f5 +32 4dbd +e6 ea115 +9 42efc +c6 e72a2 +f7 ea839 +5a 60632 +ab 10ee7b +dd 280cbc +4a 1cd61 +2d 1967dc +cb ef45b +4c 1e8639 +6e 5b8ab +91 2642a5 +db 1294de +83 103ae3 +e7 2bdac6 +9b d1f69 +7d 1f5490 +8d 1029b0 +1b 1d49ce +49 26307 +7a 64849 +48 1b8558 +ae cea01 +c 192688 +9 19a9b2 +54 1f143f +10 1cd7f1 +17 a25a +be 299710 +f9 285134 +e8 e9ff8 +61 1b2f27 +f 1d53c0 +3 41d88 +2c 1d950d +20 45ed5 +13 1cd549 +0 1d3cc2 +73 1f6631 +84 294cf4 +b5 2696ed +d2 efc72 +8e c9286 +ff 12d652 +31 e439 +dd 2ba3e0 +40 1b70cf +14 19b1ee +31 19801d +51 1b09b1 +4b 26300 +44 1cbc2 +9b 29d877 +dc 1281f1 +c 193948 +0 310 +41 1b713e +6a 5a8c8 +5a 1b7b8e +28 d910 +99 1032e0 +3f 1d0858 +6d 22191 +c8 e5e4f +21 1d9138 +5a 259ae +ea ea2ab +2 9617 +4b 1ccf0 +23 dacf +1 19a85d +cf 2b9a76 +e0 f3751 +db eed44 +7e 1b3a18 +24 1979a8 +e6 123589 +65 1b2fc8 +24 197944 +cc 2b1a40 +ee 124cb2 +c5 281528 +87 10ac10 +58 26f77 +e9 f25f3 +a0 107f94 +d7 278abe +99 d1f0e +19 1cc42b +d1 efbfa +21 571a +0 19b860 +49 1aef39 +1d 1dba +80 ca3b9 +95 10b508 +6d 1bb45f +ea e9f8d +88 ca51e +8c d2901 +b5 2a2e01 +d2 129386 +c6 ee072 +44 575b6 +be 107760 +b7 d7248 +95 263fd6 +c 193638 +0 0 +87 25b5cc +bf cf361 +b5 269493 +14 1d58b2 +fc 2b6546 +5b 1b7bf1 +ce ee413 +94 10b7a9 +2c 4605d +d3 2b1f63 +8d 25b4de +4d 2633a +82 25c8c0 +b3 25fe57 +8 192665 +2a 58d7 +79 1f6a31 +e5 ea181 +44 565a0 +27 1d7eae +d6 27881d +68 2948d +aa 25f64c +40 1b737b +1 1d3cc1 +c 19a972 +20 1d7e07 +a4 106a01 +87 1028b4 +cc e70d4 +66 1bc570 +d7 2b1f40 +33 1d87de +2 1d5247 +1f 1d5a81 +13 42449 +9c 25d34e +90 c9d16 +e4 eb3de +71 1b4bbc +0 15d4 +31 4b6b +3d 1981a3 +e2 2b6d18 +c7 28050f +64 2203b +64 1b4229 +47 1b00dc +4d 1b0478 +41 1ce40 +36 1d06fe +a6 106ca8 +54 1afa31 +22 3dbf0 +2e 1d1228 +e3 124ac9 +fc ea928 +64 290b9 +93 10c792 +b 1cbad1 +80 25b605 +a2 ce877 +58 26f69 +b9 d7677 +4d 25316 +d 19a6c7 +ac 267c82 +28 1d0ed0 +c0 2814e8 +99 d3230 +a9 d59f2 +4e 1e86a2 +29 42ff +7d 22d94 +89 d159d +f4 27df2f +d7 279de2 +a8 cd3f7 +40 1b839f +6 2e6 +22 4210 +2e 197848 +86 c93dd +25 41d9 +c4 2814b7 +3e 1981a9 +32 4b71 +1b 19b05e +38 19f1ab +99 1035f0 +28 dc20 +cc e7382 +fd ea919 +26 19642f +4b 1b0210 +1f 42563 +be 10fb1e +9e 264185 +7a 21b09 +1 1d3f0d +21 196396 +15 42411 +6a 20e96 +1d 1d5a18 +11 423e0 +e 1d410b +2b 1d0f3a +51 1af743 +a 41c32 +1e 43832 +e0 124b31 +c5 ee328 +75 1b4e37 +18 19aff6 +64 1b3213 +3f 1d8ba4 +c6 ef5f0 +f7 f2b87 +47 1b83ca +e6 285985 +90 10475a +96 29d75e +6b 63edb +64 1b3207 +37 47873 +15 1d4601 +6a 1b3086 +20 5717 +ea eb56f +cc ef422 +6f 20ed8 +e2 2b5a56 +78 2b0b0 +89 102971 +11 1cd596 +b0 26819f +be 25ff7e +2c 3dd01 +58 1d8f9 +cc e70d2 +4c 1b0485 +40 1ce4d +68 1b43af +b8 107478 +58 1b7ea5 +84 263666 +65 63d58 +f7 285fd5 +a8 cd3f5 +9 1ccd8c +a8 267995 +87 26391a +c 19b986 +0 834e +58 1f1319 +1b a134 +f6 27dcda +a2 298c27 +42 1e977a +8 3abab +60 2230a +83 294a71 +16 426b9 +b4 10f95e +41 1b0050 +97 10b811 +17 192e98 +63 1b324c +45 1b70ff +24 1976f8 +7f 5c4bd +8b 10acc6 +f3 285ff8 +58 1af845 +5e 25c8d +7b 22abc +a8 260bb5 +5d 25c77 +a7 cd585 +58 1b9167 +84 264928 +a8 ce6b7 +58 1afaf5 +7a 22d67 +c6 e5f6e +5b 1b7bef +78 1bbd3c +c6 28176c +36 1d8802 +2d 1977f0 +21 41b8 +c2 ef2a1 +45 2648f +5a 1b7e3a +19 90b5 +7c 22ae3 +28 3da30 +b 398e3 +9b 1035f7 +2a dc27 +35 1982fc +9f 10cbd6 +1a 1cc43f +41 1b86a0 +6 1ccc7c +7d 1b4f8e +71 21956 +9a 10b636 +4e 2532c +6b 2215b +bb cf330 +16 3b63b +98 103333 +3d 3f675 +10 192dff +ad 2a10f5 +c4 ef5d9 +88 d28c0 +c5 ef586 +40 1aedef +7b 5c4ee +32 4b11 +3e 198149 +7a 5c4f9 +eb ea23e +a2 d6b65 +c2 2b9bfd +8a d15a5 +e7 eb3e6 +df eed75 +fd 2bf849 +8 1c9 +f1 12c211 +10 1cc581 +2d 5602 +4 1d4fc1 +4d 1e869a +56 5f258 +73 5c087 +7c 1b4d41 +70 21709 +f7 27ccd5 +f8 ea94b +3f 1d8968 +29 3da21 +52 1e90c7 +18 3a4f8 +eb f22dc +7e 1b4d3a +72 21702 +2a 1cfc87 +3 19a544 +14 42402 +b5 d5fe3 +23 1d7e0f +98 c9baf +6 1d3ca4 +d1 efbfc +49 26309 +1 19b861 +67 2936d +60 1ec904 +4e 1b72cc +19 a0d7 +82 103af0 +5 1d4fc2 +f7 2862d9 +56 1f26f8 +ea 27d74f +48 1aeed6 +ac 10812a +35 4df8 +95 d1dd8 +4c 25315 +da efd65 +3 19b86a +4a 1e891d +48 1cce8 +2c 1d0f03 +20 3d8cb +a7 298e97 +56 25886 +c 144a +a5 299eb2 +59 1ec2a +3a 196e54 +85 ca3eb +8a 25b4a5 +c8 ef3ef +71 1bbc38 +40 1b86a1 +4b 5fcde +2 1cb979 +87 10c110 +13 8f67 +1f 19c59f +dd eed6e +e6 eb137 +87 103b14 +5b 259a1 +fa f2f5c +5b 58353 +de efd8a +4e 1e8942 +ae 110471 +d4 eec7a +21 dad6 +cd 2b9a7d +de e7a9c +8a 1029e9 +b5 cdedf +d6 e6685 +11 19c47e +69 2a4a0 +a7 25f7d7 +6 1cbbf6 +34 19f0e7 +49 1f0c03 +c0 2804e4 +ed 12cd5a +65 1bc826 +c2 27944f +c9 ef69e +5c 1d67a +ea 12ba0d +f5 2b60e2 +18 3a1e8 +b9 cddc9 +71 1f662c +2d 1cfc40 +ee e9fcc +fb f2c4d +b4 107360 +86 296011 +4c 1b0487 +40 1ce4f +7d 1b3a1e +bc ce037 +bd 29a9be +b1 107386 +d 41c07 +f2 eba67 +75 1bcf39 +ef f237d +a6 107d1e +e1 eb404 +fd 12c645 +4e 1af1cc +3 1ccef8 +68 20ef3 +67 1b4221 +c2 277edf +3e 4c97 +ab d6cbb +6b 1b45f7 +88 10acbe +e8 124c88 +cd ee47f +17 1d48a6 +99 10b690 +d0 eff09 +b9 2a1a25 +88 29e48e +c 192384 +59 259a6 +70 1b38f7 +f5 f408e +8 9769 +4d 1dfea +1e 19b02c +c4 281527 +55 1af9c0 +2f 1d11b7 +23 3db7f +19 1cc6e7 +2e 3efca +54 1d7d3 +a3 107c9a +59 6038c +68 211af +a 19a69c +67 1b44dd +af 107e76 +c2 27819b +2a 197817 +e 1938ed +2 2b5 +16 99b +83 d29bf +61 5a469 +6d 1edaa1 +a8 ce6c7 +cf ee1c8 +c8 2b175f +2b 19e842 +9 19366a +e 1cbaad +53 1b7aa6 +1e a102 +65 1b3206 +2b 4618 +8e 29cee6 +b5 1088d3 +e1 124b32 +79 1b4fc1 +a5 260782 +c8 ee19f +6d 2a4e1 +37 1d879f +6 1d5208 +de 280cb6 +58 25997 +df 280f63 +36 3f534 +29 1d94dd +71 2af58 +4 19a57d +6f 20ec8 +a4 260a2f +a7 25f529 +6 1cb948 +1 1ccee3 +dd 280f6a +ab 10f129 +a0 267aec +c2 e726f +33 1d0732 +61 2206b +1f 1d5a11 +13 423d9 +b6 29ab1b +68 211a1 +67 1b44cf +c2 27818d +88 ca82e +29 587d +6f 1b4626 +63 20fee +24 549a +c6 ef2d2 +4a 1e9b71 +7b 1ed108 +be 10776e +99 c9c12 +b9 25ffa7 +88 25ca10 +a6 1069f8 +c8 e5ded +40 1b73df +4b 5ea1c +4a 1b84ef +c3 ef2b0 +cf ef48a +c8 2b2a21 +e3 f21e7 +ef 28581f +19 1cd6fb +54 1e7e7 +b8 268304 +d 1d50a9 +1 41a71 +c 1d3df6 +54 25871 +13 1cc2db +37 ce91 +30 1d0428 +1e 192f8c +7b 6459c +14 8ce0 +f8 ebeb9 +1 f +d 193647 +68 20e8f +ef 27c45b +7 1d3d07 +65 2202c +2a d979 +9b 103349 +35 19804e +44 5e642 +e9 f2337 +97 26427b +c1 2b1609 +cc 2782ba +ff 2b6294 +b6 2a2bbb +5a 1b7b8c +8f 10acf7 +a3 2a0f76 +f7 286029 +8c d18ed +a9 ce71c +8 3ab3b +0 39794 +c 1ccdcc +15 1940ff +cd ef421 +eb eb56e +a6 d694a +ef ea023 +75 1f68a9 +4e 56442 +c5 ef5dc +10 1edf +7a 1bc045 +18 192fb6 +73 5adc3 +7f 1ee3fb +42 5782c +96 d33b2 +e8 ea298 +a9 ce974 +ef 27d71d +e3 ea0e5 +cb 128dd7 +5e 56db3 +27 448e +dd 128252 +75 1b4e99 +18 19b058 +25 46f0b +5b 1f2891 +b0 2613bf +a5 107cc2 +ba cf081 +98 25be0f +8a d1605 +59 1f1564 +b6 29ab29 +6f 2a4d8 +4d 1b7266 +68 1eda6f +5d 259d7 +41 1f1b24 +12 439ac +63 1edbc0 +c5 ee016 +32 478a5 +7b 5af7e +50 1af744 +2a 1d0f3b +94 d310b +e5 27d31f +c2 2814f1 +2f 19784b +23 4213 +e5 12bb8f +87 10c180 +33 196fae +a9 cd406 +be 2a1a50 +40 1b02fd +ce ef6d5 +c3 128a24 +5b 1b8eb3 +e0 12cb71 +58 26cbb +f0 f2d9c +fc 2863d4 +28 4362 +d1 11fd1a +dd 2b3352 +65 21028 +27 197690 +82 25b34e +a3 ce878 +59 26f6a +e7 f24c6 +e0 2b5a5d +e8 f3846 +1c 193fa9 +10 971 +4a 5e761 +5 1d520e +94 c9a8b +56 1b9048 +ad 298fe7 +ca 11f56c +6f 5b8ae +e4 eb432 +96 104794 +d9 f0061 +9e 10463d +5a 1afafc +64 2a37b +d5 11fd4b +29 1d928f +f5 2b60e0 +c4 2b2b49 +6a 2215a +15 436d5 +8a 10c2a7 +bb 10f83e +28 1cfebe +a7 ce847 +e4 2b5a2c +79 1ee681 +aa ce722 +63 5a4c4 +1d 43ad8 +6f 1edafc +af 11016e +d5 ee977 +f5 284d0c +c4 281775 +25 4497 +1b 3a500 +30 19802a +d5 2bb859 +1a 192f5d +be d6140 +3c 3f684 +96 10cae2 +1b 19affc +1 1ccef1 +36 1d87a0 +f7 eaadb +56 56efa +e8 e9f94 +5 8380 +62 22313 +9 1d50da +9f 2656f8 +93 d20c0 +a6 d5688 +75 1f55e7 +80 ca3bb +e4 28439e +34 1d0457 +bc 299699 +42 1de6a +a0 106cd0 +ac 29a308 +bd 299948 +da 11fecd +7f 5c20f +e4 f372e +e0 124b33 +f4 27cc6d +eb eb570 +4a 5798f +d7 278b20 +9 1ccd38 +7d 29b70 +4c 265d9 +20 54cd +6 1cceb8 +a7 260a99 +c4 ee319 +ce ef429 +8 165 +96 c9a90 +73 643d3 +51 1f1161 +e 3ab83 +49 265a7 +2c 19fb2f +20 c4f7 +65 20d78 +5 1cbbf0 +2 15db +33 4b72 +3f 1981aa +a4 d68e1 +ed e9fba +8 97d9 +49 1b8549 +40 1af03d +62 222af +6 3acda +37 3e271 +26 1d106f +73 22c81 +d6 2bb54f +33 19f04e +c5 11f698 +f3 12d786 +11 436f8 +a6 298e96 +2 1d4f8b +8 16d5 +4e 1b047e +42 1ce46 +fb ebe6d +88 c94fe +d8 11fe70 +ce ef6d7 +ff f2c6e +66 1b44dc +8d 10c016 +42 1e8766 +8 39b97 +b4 2997fe +97 2956b1 +6e 1f4de3 +44 1af06e +66 222e0 +d3 eff13 +4b 1b7290 +af cea00 +a5 268b32 +4 1d4f51 +ec 2b5be5 +84 10ae48 +72 1ed020 +f7 12d7b7 +46 1ce77 +8e 296168 +82 102b30 +27 3ee72 +1a 192fbf +ac ce6e8 +96 c9da0 +28 42fe +69 22402 +94 25bc85 +26 1cfd9d +a5 d6b8e +20 1963f7 +3e 1983f7 +32 4dbf +c7 e5cc1 +8d ca550 +d3 120fd5 +ab ce6c1 +6b 1eda77 +8f d2897 +69 211ae +cc 2b9a7c +85 d1787 +95 25cf48 +52 1b7d47 +75 1b3b75 +e0 12386f +ec 2b6ea7 +2 15d9 +58 1e96d +f0 eaa4e +fc 27e086 +d3 e6901 +df 279f39 +28 430c +56 26df8 +60 1b3236 +ac 298d44 +29 19e83b +b5 cdf4f +2b 1d9296 +51 1b7a9f +7a 5b229 +20 197907 +2 3a9ed +66 1f49d0 +4b 2504c +c1 11f41b +cd 2b2a53 +48 1b7224 +42 1f085a +50 26b74 +c 188 +a5 298bf0 +c1 2814e9 +b9 d60b3 +88 d2b1c +ce 2818c5 +ff 284e5c +c2 ee28d +4 19b83f +6f 2218a +2 1577 +f8 27cb45 +a6 29a158 +29 3da2f +f8 ea959 +dd eed7c +6 98f6 +4f 1cfcf +a0 260a6e +2 1cb987 +87 10c11e +de 279c8c +b5 2600cf +d2 e6654 +77 22996 +b7 10766c +2a 1d9545 +50 1b7d4e +cd ee41b +e0 eb3a1 +9c cb15e +27 1d93be +c0 281796 +2b 55c8 +6f 29216 +68 1ec7ad +9c d1f30 +7f 1ed457 +b 15f +59 5f12c +9b d21b3 +25 196437 +76 1b3b6d +58 1b7b85 +1 1cce81 +c0 278196 +e2 eb408 +b2 2a18d6 +86 103e33 +b7 1073ca +a6 29a1c8 +37 4def +85 264997 +d6 2820cd +8 3ae4b +42 1e9a1a +73 1ecfb1 +2d 19652c +eb 12bcae +9d 103373 +58 1b90f7 +45 24f1f +cd ee161 +25 4487 +c4 281765 +86 10abad +81 d276a +26 197691 +62 5ba41 +10 43951 +62 1ed975 +b1 d625e +bd 269896 +22 c560 +2e 19fb98 +9f 295568 +a4 ce5f3 +c1 2b9957 +a1 d68bf +cc 280608 +85 d1795 +74 22c3a +2c 1d11bf +20 3db87 +f 1cd072 +3 39a3a +79 22dd1 +dc 2bb69f +d0 128067 +4f 579b3 +15 1d45ff +6a 1b3084 +2a 4305 +ce 12085d +a5 106ca0 +c5 e722a +7a 1b3c93 +f7 f2dd5 +b9 cf089 +e2 27d5f8 +ce ee1c9 +4c 5770d +8 157 +d3 28210f +8f 25b723 +b6 10766b +cd 2b2c9f +c1 11f667 +96 103460 +e9 2b6bc9 +9d 10b65f +5b 26c61 +3 98c6 +aa 298d7c +e1 28468c +40 1f0aab +42 1e9a7a +2f 45db7 +7c 1ee401 +70 5adc9 +f7 2b6395 +b4 107352 +2 1ccc49 +17 1cc558 +ab ce96d +88 102970 +9 1cbd16 +91 265575 +2c 58ad +21 3ebfc +66 2101e +df 282289 +d3 eec51 +9d 29ee11 +91 10b7d9 +c0 e6f4c +7 1d4f69 +6 15a8 +11 1d58f4 +cb 2818f5 +5e 1af8d1 +f5 12d504 +c4 280205 +af ce752 +45 26481 +ec 2b5937 +84 10ab9a +58 5f3d9 +f7 12d509 +c 1458 +46 1cbc9 +85 264c37 +2e 197a96 +d8 128222 +22 445e +de 281f76 +d2 ee93e +ec ea2d5 +4 9641 +4d 1cd1a +86 10ae5b +d5 279acb +a3 107c8a +28 1964ec +66 1b2fc2 +31 5dcd +14 8f9e +b4 1088d0 +50 1f1160 +26 3ee81 +d6 129361 +10 1d4623 +6d 2921d +24 3ebbe +d7 ee9e2 +f2 1251eb +94 295655 +9e c9be7 +82 295d34 +e1 f3440 +4f 26341 +c2 2baebf +89 103f51 +ed 2bdf34 +ed eb27a +8f 29cf55 +9d cb1c1 +43 261a9 +70 1b3897 +53 1af74a +d 1707 +53 5818c +ad 268c97 +c 1d50b6 +0 41a7e +a1 d565f +fa ea950 +87 10abae +6c 1b461e +60 20fe6 +40 1ce41 +7d 1b3a10 +4c 1b0479 +eb 12ba0e +d1 12103e +55 1f2702 +86 d27a3 +31 1a05c5 +a 15e +a8 ce973 +ee 27d71c +65 1bb5b6 +e2 ea0e4 +90 10b7e8 +9c 29ee20 +ee 27c1ac +3c 5f52 +1e 1cd9d2 +12 3a39a +88 102c8e +67 1f5fa1 +59 1f1566 +7b 647d8 +8a d1607 +e7 eb448 +3a 198116 +bc cf049 +2c 1967cd +dc 280cad +21 1cfb1c +1e 1942b0 +12 c78 +c7 ee011 +e2 12481a +7a 1b4ca9 +bf 2a1c9d +ed f35d6 +7a 21807 +28 c64e +6d 20ecf +1b a0d0 +f9 284e96 +58 1f12b5 +7 1d4fbb +9a cb126 +1d 19c5f8 +11 8fc0 +83 d2771 +e9 e9f97 +a0 d68be +65 1f5c98 +c0 2b9956 +4c 1cd19 +e5 2b5781 +3a 198118 +d3 12830f +df 2bb947 +cc e5e10 +74 1b38c6 +fc 27cb08 +90 10b53a +9c 29eb72 +a8 ce6c5 +9 19a694 +b5 10f711 +84 10c17a +2b 46024 +5d 1b7e65 +a7 25f773 +11 1cc582 +e8 eb256 +dd 2bb94e +d1 128316 +29 19655d +74 1ecfea +9e 25d0fb +92 c9ac3 +57 1e8e9d +37 5e05 +7e 1b3d36 +47 562ec +e6 1238a7 +e1 2b6a74 +47 5f8fc +19 3a4f9 +e 84df +11 1d45c0 +24 1963c8 +f2 27c9f5 +9e 25bdd7 +30 1d047a +78 5af22 +12 436fc +5b 56dd5 +7b 1ed16a +4a 1e9bd3 +60 5b72a +45 24f21 +b8 d73ca +5b 1f156b +8c 10acf1 +fe ebef1 +dc 278c7f +aa 106e3e +ff 12d660 +f4 286023 +cb 2bad79 +26 3ee73 +f5 28627e +ce e5e17 +28 1cfebc +6c 1b4680 +60 21048 +14 9a2 +81 d29c6 +5 41ab0 +b4 cdee0 +52 5f219 +5e 1f2851 +b0 cf17d +46 1aedb7 +0 e +c 193646 +cb ef715 +62 20db1 +6e 1b43e9 +8b 296146 +45 5e641 +f4 eaa71 +b9 299985 +50 1b09b2 +4a 26301 +1e 19c59e +12 8f66 +d2 1295d2 +1a b21 +dc 12849d +b3 299589 +57 1d7e7 +ce 120b19 +cc 281680 +c0 ee048 +65 2a38a +8 1d4065 +7b 1f69d4 +4c 1b01d7 +40 1cb9f +c7 27816b +c8 e5de1 +10 3b3b5 +97 296981 +2 42d9d +b7 10f6b8 +c7 ef57f +42 1aede8 +a9 267c42 +2f 1d7ff7 +de 278966 +3b 5f8b +78 1ed170 +1c 1d5a0b +10 423d3 +c5 ef2d8 +97 29d99f +55 56c54 +e9 27d747 +75 1ecfe9 +44 1e9a52 +92 104761 +17 1d4608 +d8 1294e2 +0 1924ac +22 571e +c1 edfe5 +cd 28161d +d9 efd53 +9 19b9b8 +eb e9f8e +85 29e307 +96 c9af2 +3c 6210 +31 3f55f +19 1cc439 +21 3d93a +2d 1d0f72 +72 5b070 +7e 1ee6a8 +15 1d5b71 +6a 1b45f6 +48 262fa +1c 19c597 +10 8f5f +2 39a3b +e 1cd073 +3f 1d060a +bc 260233 +9f 25c0e6 +17 4240a +b1 299582 +2f 3efd7 +55 1d7e0 +b5 107663 +68 1b43a1 +4c 1b0477 +40 1ce3f +f2 27c9e7 +a0 cd54e +ac 260b86 +46 261db +b 1d50ef +ef 2b5bef +a6 2a2516 +7 1d4f5b +1d 19c598 +11 8f60 +39 196eb0 +8 193919 +da e67b9 +bd 260234 +49 26609 +5f 1f15fe +1e 19424e +12 c16 +27 1d9110 +9c caeb0 +48 1ccf6 +cf 2782c2 +84 10aba6 +67 1b44df +ef 124a13 +c2 27819d +2a 197819 +cc 128e00 +4e 1aef1e +fd 12c397 +10 192b4f +b8 cf2c8 +6 3aa1c +a7 ce5fd +e4 2b57e2 +6a 221ca +d7 278acc +86 d273f +cf e5e18 +a6 107d2a +d6 279ad3 +9c caf04 +27 1d9164 +8b 103f58 +1c 3b78b +ef 2bdf3b +1a 3a1f1 +8d 10bfc0 +41 264b2 +4d 265e6 +60 1b4498 +23 1d938d +98 cb12d +c0 2baec8 +5b 1d90d +f1 2be45f +1d a108 +77 1bbc0e +46 1b8677 +71 5c08c +e2 285946 +94 d1de7 +e5 f24bf +f5 27dc80 +c8 1207d1 +38 4f7f +8b 10c2a6 +b5 25fe91 +14 1cc2b0 +c6 1279ec +e3 12481b +5e 1b7e7b +7b 1b4caa +a8 cd457 +37 19703f +e4 eb3d2 +a9 29a2e6 +5b 25cbd +87 d147e +a8 25f647 +8 16c9 +39 4c60 +28 197a5e +9c d1f90 +d6 280b5f +ae 106bb3 +6a 2a50a +75 1b4bdf +e1 eb102 +f6 2bf74c +26 3d901 +c5 281766 +e9 e9ff7 +12 19412a +ec ea2d7 +4 9643 +bb 26986e +1a 1d5c8d +7a 21805 +b9 269873 +68 20e9f +ef 27c46b +f 19a97a +9f 10330a +3a 197104 +41 1b70ce +84 d1786 +53 26e1a +b 1d539f +94 25cf47 +8e d2896 +a4 cd5ed +5 32 +c2 27945d +86 ca69f +73 1ed2cf +95 29ecba +dc 2789c3 +aa 106b82 +c9 28164e +24 5748 +91 d337b +58 1b7be9 +a2 25f4f7 +8d 29610e +81 102ad6 +59 26cbc +ee 27c45a +16 1cc559 +44 1de92 +79 2ae03 +ee eb5a0 +ae 1080cd +d4 e68d6 +2e 58a8 +23 3ebf7 +19 3b74b +4e 1e8940 +5c 1b8edc +2a 4709b +50 258a4 +7b 63276 +4a 5fcdf +f2 2b7679 +d7 280e70 +1b 1d82 +ce 128e59 +d 1d3df7 +2f 47069 +55 25872 +75 1bbc07 +44 1b8670 +d6 2ba28d +7f 1b4ff7 +73 219bf +ac 2608d8 +a0 cd2a0 +2f 47325 +55 25b2e +19 1d89 +4e 1aef7e +cc 128e60 +25 3ee7b +d5 12935b +bc 25ff75 +60 29088 +6c 1bc6c0 +62 2104f +6e 1b4687 +c 97a8 +52 6022d +46 24f19 +b 1d3e2d +4c 25323 +69 22152 +df 279c29 +d3 e65f1 +9 141a +a8 ce9d5 +ea 2bf1cd +4b 1f1c12 +f2 125429 +d7 eec20 +12 3b3ae +b6 29aac7 +1 1cbbbf +0 19250c +2d 1d11c0 +21 3db88 +75 22c3b +4a 57745 +a9 106b18 +fc ea918 +60 1bb2e6 +bd cdd8a +63 1b2f2e +1e 19c352 +12 8d1a +80 295fd9 +0 15e0 +e9 ea2a5 +1 9611 +2a 197ac5 +46 1e87e9 +11 3b5f4 +b8 cddb8 +d4 279dea +a2 107fa9 +84 10be5c +58 6069b +18 193f6a +4 1cb9b3 +c5 ee008 +18 ac8 +28 197abe +c 19b988 +0 8350 +73 2acbf +87 26391c +cb e710b +e8 eb258 +76 1ee551 +26 1cfd9f +ea f38af +dc 28221d +d0 eebe5 +85 d27a9 +8 39879 +a9 cd45a +40 1b0351 +f5 27cc6c +67 5ba03 +ce 2b2cf9 +c2 11f6c1 +5a 1afb50 +2b 1cfc78 +9 19b956 +8a 10c243 +35 e14a +95 296918 +cd 128dff +4f 1aef1d +58 1b90f5 +20 5469 +a7 260a35 +a6 298be8 +7a 1ed427 +32 1d0483 +b7 110c1a +2f 3efc9 +55 1d7d2 +ef 2bf19d +e3 12bb65 +7 39 +bd cdd96 +8c ca7ff +d2 121284 +74 22c48 +65 20d6c +7b 1ed3b6 +66 1b4290 +1d 19c2e8 +11 8cb0 +19 19b057 +1 9621 +63 1b4500 +c7 2804ad +8d d18de +91 c9a67 +9d 25d09f +ea 1236b1 +40 1e171 +a8 106b19 +42 1de5a +2b 562c +68 1ec811 +7f 1b3d35 +88 103c94 +31 196cf7 +0 193760 +6f 1bb404 +b 1421 +b 1d40db +46 251c7 +69 22400 +3d 5f61 +1f 1cd9e1 +13 3a3a9 +9 19a704 +54 1f1191 +c8 2818ef +f9 284e86 +25 19fce7 +a2 ce815 +4f 1cd2f +58 26f07 +cc ef6ce +59 1b8eac +b4 108678 +2 1ccbd9 +a3 2607ba +4b 5fa94 +a0 ce5c2 +9 39b98 +6d 20ec1 +f4 f2e2f +42 1b7390 +55 25874 +2f 4706b +d1 2bb57c +8d 294b90 +c8 28038d +61 29097 +6d 1bc6cf +89 102c8f +ed e9fb8 +a1 ce871 +16 c49 +f8 12407b +b4 cf202 +2a 1d9289 +50 1b7a92 +5b 5f0cf +3d 1d18d3 +31 3e29b +0 3ad04 +64 1f4ce7 +63 63a80 +be 108a84 +28 47040 +b 42ef3 +e1 27d350 +2c 1d11b1 +20 3db79 +5a 1e982 +31 4dc5 +3d 1983fd +98 25c0bb +77 1b3b6e +e2 123868 +ee 2b6ea0 +c8 e73c1 +f9 ea958 +78 21800 +ff 27cdcc +5f 1b9130 +53 25af8 +84 ca44e +9a 296a98 +43 26457 +ea 2b590d +43 1b86b5 +65 1b452c +82 10c14e +cb 11f827 +36 196fdc +2b 1cfc88 +7f 1b4d3b +73 21703 +42 1e16c +40 1aed81 +1b 1d471e +c2 2814ff +5 19a81c +5d 1b9135 +2b 472f4 +51 25afd +34 46867 +da 2ba409 +a9 106b26 +84 d279a +cd e5e73 +c5 ee2b8 +11 19ae9e +88 29e1d0 +4 19a5d1 +77 1bcf40 +80 ca41b +3e 3e677 +4 863b +21 546a +24 19e9d2 +7 19a885 +5f 60670 +e4 2bf03e +2c 19658d +72 1ed012 +d5 2ba537 +f7 12d7a9 +27 19fcee +0 1ccef0 +4c 5e799 +18 8e06 +9f 2643d2 +11 1d48de +d4 279ddc +a2 107f9b +86 29d0ad +a3 299edc +5b 25a11 +21 c814 +2d 19fe4c +73 1f68d1 +a8 106b17 +37 1d06ff +16 426b7 +b7 d6298 +b0 29982f +6b 1b3085 +e6 124b6b +1a 8e71 +ec f2307 +2d 47320 +43 1f1ab9 +e0 f36fd +e 1cbb11 +c3 ef5b0 +3e 1d8905 +cc 2818cc +fd 284e63 +c0 ee294 +ef 2b593f +21 19e994 +4a 1b8551 +6d 62bef +61 20d47 +6d 1b437f +c8 27803d +b0 cf171 +1 42d41 +53 1b8d5a +70 1bcea7 +18 d66 +19 8e07 +da eea97 +b 16cf +a1 2a2221 +cd 28167f +c1 ee047 +9 1d4064 +20 1976c9 +c5 2baef8 +a4 d562d +80 294a77 +87 d14e0 +a5 268b40 +4 1d4f5f +4d 1e8638 +7c 1b4cdf +70 216a7 +42 1b0358 +f7 27cc73 +86 1028b3 +f8 ea8e9 +56 26dea +3f 1d8906 +94 d33b9 +a8 260907 +e5 27d5cd +50 1af9f2 +2a 1d11e9 +da 2bb6c9 +1f 193fa1 +c6 127a40 +13 969 +8a 103c9b +34 1d8ab7 +e3 1235c9 +ef 2b6c01 +7b 5c4fc +14 c40 +ae 2a260b +d4 280e14 +a2 10efd3 +6b 221cb +5d 1b0b39 +2b 3ecf8 +51 1d501 +c8 120833 +71 1b3896 +40 1b02ff +e9 ea237 +a0 d6b5e +68 5b881 +4d 25078 +a8 ce719 +c5 e5f76 +f3 f4064 +af 298da0 +88 c92b0 +2d 55f2 +21 41a8 +2d 1977e0 +88 25b49e +b8 110d9a +3a 196eb8 +98 104851 +df 2ba6e7 +bf d764f +fc 2be834 +d0 281e57 +fc f3f8c +4a 1b84ed +c3 279450 +2d 4332 +c0 edfd8 +cc 281610 +ea 27d75d +59 1e96e +29 430d +1a a133 +61 1b3237 +2b 1d8212 +9c 26442c +e7 2b6d58 +8a 29cf17 +b1 108904 +82 29d06e +9c cb150 +27 1d93b0 +7c 1bd2dd +70 29ca5 +5f 1b9190 +b 1d40dd +53 25b58 +78 21860 +e6 2856d7 +90 1044ac +64 1b2f59 +9c 25d0a0 +90 c9a68 +55 1e8e42 +b6 2997f7 +e4 eb130 +59 1e90c +f8 ebec7 +c5 128ca8 +47 1aedc6 +62 1edbc1 +28 3eff2 +ba 26126f +50 1b8f9e +2 19a545 +d 9a55 +53 604da +6c 1edd50 +60 5a718 +4f 1e9c03 +43 565cb +45 24f11 +94 c9d39 +73 1b3bad +2f 1d9257 +23 45c1f +55 1b7a60 +54 26b33 +c 1d50b8 +0 41a80 +87 29d04c +62 1ec95f +ef 12bceb +e4 2846ae +d6 2b3203 +9c 104634 +14 194100 +cd ee40d +29 3ed55 +7a 5c48b +48 1b01fa +34 d145 +da 280ce7 +c5 2b9928 +e7 12cb9a +a9 d6a14 +76 1b4be3 +22 1cfb30 +a7 1102c7 +13 19aea5 +64 1ed949 +3d 4f4f +aa 1103ec +d0 eebf5 +dc 28222d +21 3ebfe +2c 58af +dc efd8f +72 5c334 +c4 279427 +f5 27c9be +40 1b00a3 +d2 280b90 +98 c9bbf +77 1bced2 +c6 e6f92 +97 10b81f +b 3ae61 +6f 1f4e44 +14 1cc55e +b5 26013f +42 1b86a8 +73 1bbc3f +53 1af6f6 +d1 1295d8 +9b 29d879 +7f 21829 +4e 1e292 +14 19aede +e3 f3449 +64 21025 +8f 296107 +83 102acf +d1 27883a +8e 29d202 +ab 29a031 +b2 299836 +8e 294e54 +d6 e68cf +19 42529 +85 c9199 +8a 10bf87 +cf ef748 +95 29665c +c 1cbaa8 +c1 ef547 +2e 3ed1a +54 1d523 +bd 2612aa +10 3a091 +1c 1cd6c9 +b1 cdc72 +6b 62977 +98 d1f5f +3d e2a1 +91 103437 +9d 296a6f +60 5a4ca +ce e5e27 +6c 1edb02 +b6 107359 +d2 129640 +96 265290 +28 3da2e +af 298ffa +56 1e7fa +32 1d876f +bc 1074b9 +28 45ba +9f 10336c +d7 e6622 +de 282226 +d2 eebee +8d 264af0 +81 d14b8 +d 43177 +e1 2b5a60 +47 5e8e8 +40 26205 +f5 f2b20 +c7 2817d1 +6b 1ec809 +19 427e5 +0 1934b0 +15 1d48ad +6a 1b3332 +a1 298c11 +c1 ef549 +54 1d525 +2e 3ed1c +c0 e7268 +c 192624 +50 1b9010 +80 103adb +77 1b3b7c +4f 1e98f3 +e2 123876 +43 562bb +ee 2b6eae +e5 eb433 +45 1b710d +db 1284ca +bd cf306 +af 298ffc +f7 eaa77 +37 1d0761 +c3 e7200 +ed 2b593a +a4 2a2261 +28 19e89e +6d 1b311f +51 1eac5 +61 20d9d +6d 1b43d5 +a7 267815 +59 5f12a +3e 196e85 +19 4283b +b9 ce007 +e2 f3758 +94 10b4fb +be 29ac80 +b2 107648 +8c 1029af +9e 1048e9 +48 1d008 +e7 eb138 +47 1b0336 +5e 1b8e8d +52 25855 +87 10ac12 +d4 2b325c +8 1419 +bc cf2f7 +94 10b817 +b1 108646 +58 1b7b87 +f 19b98e +3 8356 +85 295d5d +bb 10fae0 +86 102b6f +5d 1b8ee9 +2b 470a8 +51 258b1 +da e7a17 +af 108122 +d5 e692b +48 1b84e6 +18 192fc4 +29 1cfebd +7e 1b4ce6 +72 216ae +b1 26971c +6d 1b4681 +61 21049 +b4 108622 +7 1cbc59 +97 d30a1 +28 19e8ac +81 103dfa +e5 2bdddd +e5 eb123 +87 29cdfe +be 108782 +f9 f41a6 +b8 cf088 +28 19680c +d8 280cec +d 192633 +53 1e90b8 +35 19f0e8 +c6 127732 +8c 10bfc1 +f5 f2dce +68 1b30ef +ed f3886 +50 26e22 +c 436 +ce 2b2a5b +c2 11f423 +a5 298e9e +7e 1b4f94 +72 2195c +2a 46095 +79 1b4d05 +5c 1b7ed6 +11 970 +1d 193fa8 +c4 127a47 +8a d2865 +95 25cf3a +20 577b +88 29e1e0 +d0 efc5b +c8 e60ff +6d 22441 +8e 10acf6 +9d 104881 +49 250a9 +44 24f20 +fb 28514b +5a 1f156a +4a 576e1 +eb eb2c2 +36 1d0762 +9f 10462e +28 587c +38 cfaf +15 19c4af +47 1b86e6 +86 26397b +ec eb289 +cf e713c +4e 26332 +52 1b7a35 +c1 280227 +83 d29bd +16 999 +5b 1b7b8d +d6 129673 +fe 125305 +9b 10cbf9 +4e 1f0980 +b8 110d46 +bd 110d68 +2 2b7 +3f 196e86 +e 1938ef +cd 28035b +29 3dd31 +63 1ec900 +49 1b7225 +a7 107fcb +e7 27c304 +15 19c44d +5a 259a0 +0 41ae2 +c 1d511a +ca 2795b4 +e5 f3471 +24 1d0e1c +81 10c148 +db 1281b8 +19 19c5d7 +f5 ebd40 +98 d1eff +a7 298be7 +7b 1ed426 +ec 27d723 +e0 ea0eb +28 197810 +dc 279c93 +d0 e665b +aa 107e52 +5a 1b7eac +86 26366d +2d 19e8ce +aa cd3fc +28 1cff2e +70 219a9 +7c 1b4fe1 +41 1e875e +29 1964ed +a4 107fd3 +1d 42568 +86 d27a1 +cf e5e7a +55 1f2700 +9f 10b906 +66 1b4292 +64 1b4289 +8c d188b +a9 ce6ba +84 103b0e +58 5834d +cd ee16d +56 1b0cea +2 1cbc37 +57 1f2459 +92 1044b3 +1c af7 +89 d2b1b +cf 2818c4 +c3 ee28c +20 41b7 +2c 1977ef +3 6a +f 1936a2 +2f 19e8d7 +70 1b38e9 +4f 1b751b +6a 1edd24 +e4 f21b0 +c0 ef5a8 +a9 2a10c4 +25 471c9 +e8 ea2a4 +0 9610 +49 1cce9 +5d 1f1595 +8e d1636 +ad ce75b +c 3ab7a +28 1cfc10 +1a 19427f +c6 ee32c +e3 eb15b +81 264c14 +14 192bf0 +40 24eef +4c 1b8527 +2a 4615 +79 1f576f +e8 eb566 +49 1dfab +97 10b7b1 +49 1e267 +88 25b75a +82 25b350 +73 1ee521 +67 1b320d +96 1034d0 +d4 12960a +56 1af728 +89 d2b7d +20 4219 +2c 197851 +72 1ee2d6 +51 1b9001 +16 1cd5dd +46 251d3 +63 22002 +66 1bb56a +82 103df2 +1a 194281 +39 5f30 +6e 1b3125 +32 1d072f +c6 128a00 +3b 3e3ed +85 d1485 +da 2bb6cb +3 3ad0a +67 1f4ced +d6 ee97d +8c ca541 +1c 8e37 +0 1d4f84 +37 465bf +6 43028 +1b 1cc432 +3f cfe8 +38 1d057f +b8 1087ac +ac cd498 +b7 107608 +5 1d3c9e +78 1b3a40 +a5 ce840 +b9 cdd59 +8e 10c274 +d1 e78ac +19 19aff5 +94 10cadb +64 5a49b +3c 198140 +30 4b08 +b7 2600d4 +fa f2c4c +50 1b0a06 +5b 58043 +c2 e6fb3 +a5 260a2e +4 8381 +77 2acf0 +bb 2685ac +1a 1d49cb +d5 27886b +a3 106a2a +af 29a062 +88 ca572 +a 39b9e +31 ceb9 +3d 1a04f1 +ba cf01f +0 9922 +e4 e9e70 +c7 e5d23 +6 19a886 +29 197abf +6f 1bc6d4 +63 2909c +e5 2b6aa3 +ef eb2e5 +4e 57704 +25 c529 +3b 1d8b73 +ae 106b5d +f7 27ccc7 +cd ef6cf +48 1aef38 +84 d1794 +a1 ce5c3 +a6 106a06 +ba 1074e3 +4b 5e760 +95 c9a8a +be 10faae +41 1af090 +8d 294b9e +db 280cf4 +36 4dee +5d 25c85 +41 1f1dd2 +72 1ecfb0 +2c 19652b +af ce6f0 +ec 2b58d5 +a5 cd5e0 +46 1af065 +fd 12d65b +99 10cc00 +d6 278b21 +df 2bb9a9 +d3 128371 +c7 2804bb +8d d18ec +fc 2bfaf6 +f0 12c4be +79 1b39df +48 1b0448 +e5 28440f +44 1f082e +20 3eb9b +e 16ff +3 3aa4e +71 1f55b6 +ae 268c8f +a2 d5657 +67 1f4a31 +a7 29a167 +ed eb2de +be cdd8e +a2 299edb +14 3a0c4 +81 10c0e8 +23 19e74f +89 103ca3 +da 1213d9 +ed 2bdc86 +a3 2607b8 +b4 108676 +de efd98 +4e 1e8950 +e1 eb410 +2f 47077 +55 25880 +75 1bbc15 +44 1b867e +cc 128bb2 +dd eeac2 +ad d6a37 +3a 4c68 +df 128497 +26 1cfb53 +2a 1d11f5 +50 1af9fe +45 1b83c1 +63 1b450e +31 1d173f +14 1d4910 +e3 12ce7b +42 1b7144 +0 3acf4 +9f c9be8 +4 1cceb3 +35 1d044a +b8 cf026 +4e 1dfe2 +ae 106b51 +d0 efc6b +8c c927f +38 1d081d +90 295678 +6c 5a5f2 +38 4c5f +bf 26022b +e8 27d49a +d2 129330 +1a 192fbd +b6 10f70b +3a 1d0888 +68 221c1 +e3 ea155 +ef 27d78d +31 1d0737 +4 15a1 +6d 1bb71b +ea ea249 +95 2652f8 +5a 1f1312 +69 22410 +bf 107515 +26 1cfdab +f1 ebd03 +93 29d9de +21 197968 +e7 27c312 +e8 e9f88 +70 1ecfb7 +77 29a20 +46 26489 +2f 1d7fa5 +3f 1d1b18 +33 3e4e0 +81 29e088 +80 d1701 +8c 264d39 +3 4304c +ff 27dde0 +f3 ea7a8 +2a 19fb67 +9b 295537 +10 19ae9d +44 1f0830 +e9 284525 +14 436d4 +b9 d73c9 +ca ee136 +2f 1d0f19 +77 22994 +23 3d8e1 +7 1d3f45 +45 5fbb1 +d7 281e2e +68 20e9d +ef 27c469 +6b 62c25 +37 1d87af +2c 3efd1 +dc 1294b1 +66 5a742 +bb 110a9e +eb ea2ac +3 9618 +e1 2843de +40 1f07fd +2f 1d7fa3 +46 26487 +e0 27d5ff +39 1d081e +d1 efc6c +8d c9280 +ac 106b4a +46 1de8b +a6 1069fa +7a 5b239 +6f 5a5ea +4 1d3c9f +a9 267994 +b6 107609 +36 1d1a22 +e6 284661 +d 84d7 +6 193798 +b5 110c11 +37 196d2f +2b 1d9536 +51 1b7d3f +2b 47046 +5d 1b8e87 +51 2584f +c8 128b81 +d9 eea91 +37 465b1 +6 4301a +87 10c172 +63 1ec660 +29 3da91 +7a 5b1c7 +c8 2bad6f +c4 ee317 +ce ef427 +c2 28179d +5f 1b7bcc +44 1b83d0 +f7 27df37 +bd cf368 +40 1b8401 +0 1ccc34 +84 295fb8 +b5 29954f +83 10bea3 +14 19b1e0 +1f 4281d +e4 e9e6e +84 294ce6 +7d 1bbdc0 +4c 1b8829 +40 251f1 +fa ea8ee +87 29d03c +4c 2632b +6a 22478 +6a 1bc6f8 +50 1b7a2e +af 29904e +b4 107600 +4a 1b04b1 +7b 1b3a48 +3c 1d1b20 +30 3e4e8 +1f 1cd9d3 +13 3a39b +8e 10c2c8 +53 1f1158 +d 19a6d3 +40 1b713d +7b 6483c +9d c9be1 +e5 2846bd +44 1f0adc +ae 298dad +ed ea2d6 +5 9642 +45 565a1 +d7 27881e +73 6468f +9a 295536 +c1 e7207 +d6 2bb851 +35 1d0768 +c8 ee12d +d9 eeaff +6 1ccc1a +41 1b863e +1c 194257 +10 c1f +8 1ccd2b +ae d6d4f +8 193925 +42 1af096 +f9 12d68c +a0 106c70 +ac 29a2a8 +49 25357 +28 19fb0c +9c 1045d2 +ad 2608d7 +a1 cd29f +a8 29a02b +31 196cf9 +28 55ce +39 4cc4 +80 d2709 +63 1edc30 +6c 1b462c +d7 11fda6 +60 20ff4 +75 22c49 +18 8e08 +27 1cfaf0 +e4 f3472 +a9 2a2386 +bc cdd87 +a6 107f78 +38 1d081f +d 1cd017 +1 399df +54 1b7d6f +7c 1b4d43 +70 2170b +44 1e132 +2d 1cfc4e +6f 1bc736 +63 290fe +1c 19b335 +cc 127890 +98 d1efd +47 1b7176 +30 19f0b6 +6a 2246a +b4 cf1a2 +5 42d72 +d6 128341 +8b d18b4 +9 3adf8 +a8 d5a01 +6d 1f4ddb +bd 1074b8 +8c 103f21 +e 3ab75 +c1 ef5b9 +64 5ba09 +f6 27dc86 +48 1e25a +79 217f1 +68 1b45ef +43 1b86a7 +d7 2bb5a6 +60 1bc7f4 +c6 e6f82 +3e 1d88f7 +c8 e5e41 +6d 22183 +f 1d3e5e +cc 2818be +fd 284e55 +c0 ee286 +b 84ad +81 10287c +8d 295eb4 +c7 27942d +8d d15ce +58 1d8f7 +5 9900 +bc cf2f9 +d 42ec9 +22 3dbe2 +54 1afa23 +2e 1d121a +c9 e70b0 +d2 1280c2 +b5 2a1b3d +de 2bb6fa +b5 260131 +14 1cc550 +c5 280204 +ee 12398e +c8 ef453 +df e7a49 +9a d31c8 +a 1cbd80 +53 5ef6a +5f 1f25a2 +35 e3fa +fb 28615f +5a 1f257e +af 108130 +d5 e6939 +2c 197a8d +20 4455 +da 279f15 +e0 eb0ff +eb 124c30 +e5 eb435 +b6 cdee5 +cc e5e12 +50 1e7c2 +d7 279d8e +9d d1f2f +cf 127b98 +81 29e07c +c5 11f3ea +29 58d3 +8c 29e1a1 +80 10ab69 +f3 12d4d8 +e0 27d5ef +8 1427 +14 192be2 +81 264c06 +99 d1f60 +c7 127a51 +14 8ce2 +73 643e1 +87 29d03e +45 562f3 +9d d1f93 +b8 10879c +30 198268 +34 1d0705 +25 19fc95 +42 2621a +bc 2a1a49 +88 ca830 +b9 cddc7 +e 9a4d +a8 260bc5 +d3 1295d3 +1b b22 +38 4c6f +3b 196eb7 +b9 110d99 +a 193920 +dd 12849e +c0 edfd6 +cc 28160e +84 295fa8 +9 19a6f6 +db 2ba418 +36 3e512 +ef 124a05 +84 29e0ba +f4 27cc5d +f3 27cc96 +9d 2653ef +91 d1db7 +fe ea91f +90 10ca48 +e2 2b6a6c +1d 19b098 +38 1d18a1 +3a 1d189a +1f 19b091 +a4 10ed4f +87 10ac02 +44 1ce70 +64 1bc817 +d5 2b21e7 +ca 127b12 +90 10ca9a +12 192bb8 +97 d334f +d4 2ba534 +8a 10c237 +bb 10f7ce +9c c9bee +27 1d7e4e +6a 5b87c +20 196643 +c7 27917f +fc 27cb06 +ba 29998b +1b 1cc3d0 +c2 2791b1 +5d 1b0de7 +2b 3efa6 +51 1d7af +c8 120ae1 +80 c90f9 +8c 25c731 +4f 1f1ca3 +43 5e66b +6c 1f5df0 +60 627b8 +d8 eea9e +e7 2b5786 +82 ca6d0 +64 1edbf7 +d2 280e3e +dd 2ba442 +27 19667e +ff 12d6b4 +98 c9e6d +77 1bd180 +1b 42594 +58 25cb5 +53 26b0a +b 1d508f +d8 2ba410 +22 19664c +cb 2b9ab7 +26 3dbb1 +ee e9fbe +ee 285a6c +e2 f2434 +43 5782b +ea e9fef +95 10b56a +e6 12484d +bc 25ffd7 +61 1b2f97 +66 5b756 +a9 268f04 +3c 196ee0 +81 10be9a +8c d2b4b +19 19c329 +d2 1295d0 +9e 29d847 +3b 4f79 +da 282257 +a9 cd3f6 +69 1b30ee +6e 5b8ad +24 196674 +91 2642a7 +65 1f4c86 +35 197ff8 +90 25bcb6 +89 10bff1 +da 129727 +1 19babd +c6 277f12 +22 1cfd6c +4b 1b8550 +68 1bc69d +3c 4c90 +a9 d6cb4 +90 2953da +97 d1e43 +b2 10864c +22 1cfdd0 +98 1032df +77 1f65f2 +8c 26382b +d2 2ba2b0 +25 549b +25 197689 +80 25b347 +86 25c88d +12 1cc2ea +97 10ca81 +9 1cbd86 +44 1ce72 +a7 107cbb +7b 5c4fa +9a 25be08 +8 39b8b +3e 1d0857 +6c 22190 +d9 efdc3 +cd e7135 +85 29e359 +35 19f0e6 +c6 127730 +4 19bb4f +5 19a880 +82 c93ae +20 1d1089 +8e 25c9e6 +bf 25ff7d +71 646ec +2d 3dd00 +9e 104887 +21 1d1098 +64 1bb2a7 +0 12c4 +34 4df5 +da 278997 +37 1d8aaf +66 1b4222 +c8 e735f +f9 ea8f6 +cb 2795a7 +c1 ef2fb +9f 10cc2a +bc 110d77 +e8 27d4a6 +6d 5a89f +11 194130 +c9 ef452 +3d 198141 +31 4b09 +0 1572 +64 1bb555 +64 22039 +36 1d0700 +45 1b0383 +ef f25c9 +86 10bed3 +92 104753 +96 10c834 +e8 12371a +a4 cd2cf +4f 5e7a1 +40 1b02f1 +71 1b3888 +11 8cbe +1d 19c2f6 +57 1b7a67 +84 25c8ea +b5 25fe81 +19 19c327 +53 1b7a98 +70 1bbbe5 +a5 ce5a0 +16 192e35 +1d 8e48 +38 3f651 +f2 27dcab +96 10cad4 +e5 f246d +6d 1b30af +23 1966ad +ce e60d3 +5c 25c78 +a6 cd586 +19 3b7bb +19 8e09 +b8 d63c4 +e 19394f +2 317 +f7 2bf997 +b7 d5fea +16 42409 +b0 299581 +9e 25c0e5 +17 19c1fa +7d 1b3a20 +34 1a0347 +69 1b30e0 +6 19bae4 +4f 1af1bd +61 1ec659 +30 196cf6 +cd 2bafed +c1 1279b5 +33 19700e +c7 ef2df +de 281f84 +d2 ee94c +88 ca510 +40 1af08f +fc ebc3e +4a 1b019f +93 104452 +64 20d79 +33 1a0310 +c5 12095a +7a 1ed3c3 +a9 107de8 +86 10abaf +79 1bbfdd +44 1af06c +dd 2bb9b0 +d1 128378 +95 263fc8 +40 1af09d +62 2105f +6e 1b4697 +87 10ae4e +3a 1d1ae6 +1f 19b2dd +bd cf058 +ac ce9f8 +e6 27d5c7 +69 20e9e +1a 3b505 +71 1b3ba6 +3c 3e670 +80 ca667 +94 10478b +d6 2b34b1 +9c 1048e2 +ab ce6bf +96 c9d3e +4 15a3 +35 4b3a +d 1d4105 +8a 102c33 +24 197938 +66 1ec930 +24 1979a6 +1 41d1f +d 1d5357 +c1 127707 +cd 2bad3f +83 d2a21 +c0 2b9c06 +65 1f5f48 +93 10caa4 +24 41e6 +9d 265451 +91 d1e19 +e9 eb257 +c6 ee01e +44 1ce80 +a9 298d76 +c0 e725a +7 1d5277 +95 10321c +15 c41 +4e 1cd22 +81 d1758 +8d 264d90 +c7 11f701 +89 294bc1 +6b 1b4657 +9 9778 +e2 f24a4 +ee 285adc +4f 1b8521 +43 24ee9 +c2 279451 +f3 27c9e8 +68 29241 +88 c925c +67 1bc56f +c2 28022d +3 1d5246 +96 c9af4 +9c 25d340 +90 c9d08 +e4 eb3d0 +a3 299eda +86 29d0ab +16 436db +99 26415e +bb d73d0 +68 5a5c1 +2 42d9b +4b 56474 +27 1963ce +a5 1102b0 +98 2643fd +a 398f2 +35 4de8 +e6 2b6ce9 +ac 10811a +6e 20ec9 +e0 eb401 +bd 261558 +10 3a33f +b1 cdf20 +1c 1cd977 +9e 264433 +e5 124853 +fa ebc12 +d8 2789a0 +5 1cb9b2 +4a 1b0201 +21 196644 +ac cd426 +f0 f3e12 +84 103e2c +b5 1073c3 +a4 29a1c1 +5e 1e951 +85 10be5d +3a 1d88c6 +e7 12bbf8 +60 1b31d4 +44 1aedb2 +d0 279dab +1f 1d474f +79 21b03 +30 e42a +c6 281530 +25 41e9 +c4 2814c7 +3b 1d0833 +ab 268c5f +bc 110b1d +a 1d507e +43 25207 +4f 1b883f +ba d60ad +ba 108a5f +1b 3b4a4 +2d 197a8e +21 4456 +4a 1e013 +25 daa7 +a6 106a5c +69 1b43b0 +4d 1b0486 +41 1ce4e +7f 21ad9 +36 e400 +56 1f1498 +5 1d5270 +82 103d9e +23 19796f +a4 298e9d +1d a3aa +87 294d50 +a8 ce729 +61 5a4cb +6d 1edb03 +40 1b035f +19 a3db +61 1b4507 +19 8e6b +67 2101f +1 1d4f91 +0 860a +c 19bc42 +f3 2862a8 +52 1f26c7 +40 261b1 +43 1f0859 +4e 1b750a +60 1f49a6 +ac 260b92 +a0 cd55a +9d 29eb63 +91 10b52b +9c d21dc +15 8ce1 +35 19f076 +4 19badf +c6 2babe2 +63 22314 +8c 10c013 +d2 28210c +f8 ea8f5 +ca 2795a6 +80 d2769 +c9 e5e42 +48 1ccf8 +16 1d45f9 +bd 2685e4 +1c 1d4a03 +39 1d1832 +1e 1d4750 +7b 2b11a +c9 ee13c +2e 1d82a6 +36 1d8ab0 +db eeb06 +1a 3b7b5 +14 3b632 +b5 cf213 +7e 1f5798 +21 1966a6 +6b 5b8df +c5 e5cba +40 24ee1 +4c 1b8519 +6a 1b4666 +d6 129603 +8b d2b76 +a8 d6cc3 +c8 2b9d5b +22 4212 +2e 19784a +d9 eea9d +29 45bd +3e 1d8c07 +26 1963cd +93 264000 +6a 20ea6 +9b 264157 +9 41eda +b4 299550 +57 56efb +65 1b427e +82 10bea0 +cb 11f579 +36 196d2e +25 1963c7 +86 25b5cb +e4 eb184 +4b 2504e +63 1f5f0e +3 19a5a6 +20 19e6f3 +71 1bcf0a +2d 19651e +e4 f3720 +6a 5a5c6 +ec eb2db +4d 1b7574 +ca e60a2 +17 19aed6 +82 10abd0 +8e 29e208 +89 10ad21 +1 19a7ed +96 25bf2c +5b 259af +4a 2636f +21 c7b2 +2d 19fdea +88 263aa8 +13 19af07 +6a 22408 +30 19f054 +41 1aede2 +1f 19b02d +64 1b2f65 +fa 124322 +ec 27c1a7 +69 1bb6ea +a 3adf0 +ab ce9d1 +87 295d02 +5b 1ea541 +0 2b0 +c 1938e8 +3d 196e7f +3a 4f78 +63 222b0 +3e 197197 +a6 106d08 +9f 2967c8 +93 103190 +42 1f0858 +2e 3efd6 +54 1d7df +bf 25ffe1 +1e 1cc400 +d 97a9 +80 29e327 +2a 58d9 +1c 194247 +10 c0f +8a 10c299 +bb 10f830 +4d 1b87d4 +41 2519c +3a 198426 +1e 19c2f0 +12 8cb8 +4 1ccc05 +d2 2b3232 +4d 1b7264 +a5 29a150 +c2 1206d5 +31 196fa7 +5a 1b0b64 +49 1b7295 +2f 1d9265 +23 45c2d +55 1b7a6e +5e 1af86f +1d 4382a +b8 cf328 +45 1ce7f +85 10c109 +7 192227 +c6 e6fe4 +4 41 +cf 2b9d88 +1a 1cc68b +37 1d8811 +31 19f303 +5a 1b8ec0 +ab 10f17d +dd 280fbe +a6 260a34 +83 d2a1f +65 1f5f46 +c0 2b9c04 +a8 ce727 +4f 265ed +5 19a88e +20 1d1097 +3d 1983ef +5a 1e974 +31 4db7 +c7 277ebd +80 25b603 +32 1d1753 +b4 cef64 +c9 120a80 +ca 1207ca +39 198180 +42 1b035a +73 1b38f1 +d1 12128a +42 1ce48 +4e 1b0480 +7f 1b3a17 +15 19aecf +63 1b4502 +80 10abc9 +8c 29e201 +de eead4 +41 26206 +94 2966bd +84 d2a56 +cd e612f +88 d18ae +fb f421d +2d dbf0 +8f 264d33 +83 d16fb +65 1bc568 +c0 280226 +9c 25d0f4 +90 c9abc +35 5dfe +2d 1d823c +4a 5e7c1 +ee 2bdeda +75 1ee55b +a6 ce5fc +a 1ccd92 +8e 296116 +5 1d3fb0 +d6 e7b91 +82 102ade +bf 2996ad +3b 3f95b +40 1b70cd +aa cd70c +c9 e70a2 +9c 104880 +d6 11fff1 +85 1028bb +fe eac3f +16 9fab +68 20e91 +a6 268d84 +58 60699 +ef 27c45d +a1 11028d +34 3e269 +6 1ccf1a +9e d1f35 +82 29e082 +4a 1aeedf +65 1bb2a6 +1 1d3f7d +6a 21154 +b4 10f950 +22 1966ac +c8 e7351 +f9 ea8e8 +5 40 +ea 123713 +ff 27e07e +f3 eaa46 +ed f3884 +68 1b30ed +7a 21ab5 +ac ce75a +f2 1251df +21 19fcc4 +e6 e9ecb +41 1f0b0e +21 da76 +3 19a854 +5a 1afb4e +7c 22d93 +88 d159c +85 d14d9 +13 19b1b5 +30 19f302 +83 103d91 +e7 2bdd74 +ed 12cfa6 +6f 1b30c4 +c 1938f4 +0 2bc +28 19781e +eb e9f9e +65 1b44c8 +c0 278186 +7b 1f69c6 +e7 27d636 +8a 2637f5 +50 1e7b4 +f 97a2 +8 1ccd39 +6b 63ee7 +74 1b3b74 +f8 12533b +dd eeb32 +20 1963f9 +a5 d6b90 +70 1b3ba5 +41 1b73ec +67 22041 +68 20f01 +67 1b422f +c2 277eed +e9 e9f89 +a0 d68b0 +b7 2997f8 +e5 eb131 +32 3f503 +5 1d5210 +36 1d0450 +18 19b056 +42 5f8cc +23 45edb +2f 1d9513 +55 1b7d1c +32 1d0481 +4 98f1 +35 ce88 +95 295656 +8a 10af81 +2b 1d9544 +51 1b7d4d +e4 eb3e0 +c7 e7293 +a7 298ea3 +6c 22192 +24 1976ea +cc 2bafee +fd 2be585 +c0 1279b6 +e 19262b +52 1b9017 +25 19fa39 +a2 ce567 +d3 eebed +df 282225 +df efd89 +8b 10acd6 +61 63d8b +f4 27dcd5 +6 41aaa +78 1b39ee +c1 2b9c05 +34 4b49 +a1 d6b6d +3d 5f53 +23 1d7e71 +98 c9c11 +f4 eaa7d +78 1bbd90 +39 19710c +83 d270f +84 ca698 +3c 1d18d4 +30 3e29c +c8 e7103 +9d 10cbcf +e3 1235bb +ef 2b6bf3 +f 19a6cc +1c 192ce5 +86 10be61 +cf 11f53a +53 1f1416 +19 42847 +48 1dfba +0 193512 +84 25c896 +b5 25fe2d +44 1b0082 +f7 2b764b +bd 108a7c +6a 1b3394 +85 10ae47 +38 1d1adf +1d 19b2d6 +ff f2f1c +3e 1d08c7 +b2 108908 +13 3b34d +46 1f1d99 +77 1f5330 +c6 e7240 +f7 ea7d7 +25 197937 +42 1debc +f4 2b638d +b7 cf1a8 +64 1b4227 +fa 1255e4 +e0 e9e31 +ec 27d469 +60 20d46 +6c 1b437e +48 56418 +e9 e9ff9 +a0 d6920 +56 1afa36 +2 1ccc3b +86 295fbf +b7 299556 +f8 ea949 +e 1d53cf +2 41d97 +3f 1d8966 +58 25ca7 +c1 e6f4b +a2 298c19 +7f 1bbdd5 +42 25206 +4e 1b883e +41 1b73de +6 1cb9ba +e6 ea125 +41 2518e +4d 1b87c6 +69 22162 +4 19a88b +21 1976ba +26 1cfafd +93 29d730 +ae 298d3f +3a 1d05da +84 263672 +bf 110d71 +45 1b83cf +4c 1e88e7 +6e 5bb59 +db 12978c +7 1ccf29 +e1 124ad0 +de 12825a +23 1d7e6f +c8 ee13d +5 1d3f4c +6c 1b4372 +60 20d3a +50 60542 +ad cd737 +e7 27c306 +34 1d044b +49 1b8557 +25 1cfaf7 +bf d764d +9d 2643db +82 10be32 +76 1b3bcf +d9 128221 +23 445d +2f 197a95 +8a 25b753 +b5 cdf41 +2b 1d9288 +51 1b7a91 +c5 2804b2 +d 192385 +a1 ce80f +9e d1f99 +e0 eb3ad +37 47883 +18 194278 +c4 ee325 +e1 eb154 +52 1f272b +73 299fd +7f 1bd035 +42 26466 +d5 12106f +bc 2a1c95 +21 4464 +2d 197a9c +4a 1e021 +a4 298e8d +e3 eb3a7 +14 192e3c +5e 58075 +7 19bae5 +a9 268c58 +8 1d5077 +b1 d7520 +78 1b3a4e +fd f41e5 +85 c93d7 +64 1b44d5 +21 5718 +ec 27c1a5 +da e69f7 +5d 1b7ec9 +2b 46088 +a2 298ec7 +3f 1d18da +33 3e2a2 +2 3ad0b +66 22034 +95 10ca6a +17 192b88 +82 102882 +8e 295eba +c7 ef335 +c0 2b28cc +89 10297f +f 1d50be +3 41a86 +f1 27dcb1 +d5 2ba535 +30 196d06 +99 d1efe +f4 ea7cf +c6 279480 +6b 62c27 +6d 2920f +43 1f0ab5 +9 41ee6 +97 c9a91 +b7 25fe26 +86 25c88f +94 295647 +4c 1af217 +21 54ce +22 197660 +c7 2bae8f +59 259fc +59 1b7b86 +88 c9250 +41 264c2 +fb ebbbf +2f c687 +7c 1b4cd1 +28 1cfc1e +70 21699 +f7 27cc65 +94 d33ab +1a 3a251 +92 10c7a1 +cb ee135 +20 1966a7 +21 1d7e16 +75 1bcec9 +d0 280b87 +2a 1977b7 +c3 1279ae +cf 2bafe6 +88 103ef0 +b9 107487 +8b 296138 +24 1963c6 +91 263ff9 +2e 3efd8 +54 1d7e1 +88 10bff2 +1d 19c2f8 +11 8cc0 +70 1bcf09 +2c 19651d +99 264150 +34 196d27 +1b 43854 +38 479a1 +40 1deb7 +f5 ea7d2 +3c 196e7e +18 90b4 +a1 d6b5d +34 4b39 +9b 10cba7 +1d 19b026 +80 ca6c9 +23 1cfabf +e 1936a1 +f7 2bf6e9 +2 69 +c4 1279e5 +31 1d9a9b +38 1d1b51 +17 426c8 +2b 1cfc16 +6d 1b462d +61 20ff5 +12 3b65c +de efd88 +8a 10acd5 +ff f2c7e +60 63d8a +ae 25f61b +f2 286007 +1a 1cc6ed +e0 27d351 +46 261d9 +35 e15a +30 3f55e +69 20ef2 +b2 10763a +be 29ac72 +e8 27d4a8 +4e 26330 +48 1e9b78 +4f 265e1 +79 1ed10f +5a 1b90fc +43 1b7137 +60 1bb284 +3 9928 +40 1f0b0d +1c a3b5 +6e 1b43d9 +62 20da1 +58 1ebc9 +94 c9a97 +58 1ea539 +b 41eef +12 1d58fa +4b 1b728e +68 1bb3db +78 1ed172 +3b 5f8d +7f 1b3a27 +fd 12d909 +75 1b3b67 +4a 1e8671 +cf 128e08 +3c 5f60 +1e 1cd9e0 +12 3a3a8 +20 3d92b +2c 1d0f63 +8e c9288 +aa 2a262e +d0 280e37 +db 128474 +75 1bd179 +b5 26973f +24 1d0dba +7 1ccc6d +39 196e50 +db 129738 +8c c9281 +ff ebbf0 +1c 1cd9d9 +10 3a3a1 +1d 1d5a7a +11 42442 +9a 1045a8 +95 1034bc +ac 298d46 +f4 ea7c1 +6b 62c19 +1 98c1 +42 1b00b8 +b 1d50e1 +28 1d922e +3e 1d8959 +29 430f +fd 284eb7 +cc 281920 +c0 ee2e8 +65 2a62a +ca ef3f8 +57 1f1437 +23 3eba3 +7 19b839 +ef 27c4cd +4e 1e88ec +7 1d5207 +28 1cfc0e +24 19fcf6 +6d 1b33cf +3c 1a074c +30 d114 +1f 19c5ff +13 8fc7 +b 9771 +48 1f0956 +e6 27d5c5 +21 3eb9c +a5 2a2252 +f 1700 +af 268c90 +2 41a77 +e 1d50af +f0 27dca2 +a3 d5658 +5 1d5200 +dc 279f41 +d0 e6909 +aa 108100 +8c 10bfb3 +4e 1aef0e +b8 cf2d4 +af ce99e +a5 268ad0 +ec 2b5b83 +12 1cc598 +a9 d6d16 +76 1b38c1 +39 479a0 +6d 294cb +8e 102a1a +c9 ee43e +53 57ede +d7 eec90 +f2 125499 +b6 2610e9 +88 103f50 +1d 194256 +11 c1e +ec eb279 +8e 29cf54 +d6 279d8f +27 1d9420 +9c cb1c0 +2a 1977b5 +20 1966a5 +6a 5b8de +30 1d1988 +15 19b17f +1d 192d3a +1b 43b02 +38 47c4f +29 1cfc81 +7d 1b4d34 +71 216fc +40 1e165 +f5 eaa80 +50 1e816 +8 1ccd9b +c9 ef3f0 +69 2a75c +8e 10c2ca +bf 10f861 +37 19f32d +17 8f98 +32 3f7a1 +10 1cc52f +b5 cf1a1 +8a 103cab +e4 e9ec4 +fa 2b650e +6b 63c3b +4 837f +49 252f5 +f1 284feb +1 19a84d +4b 5fa86 +6c 20ece +d9 eeb01 +2a 1cfec3 +49 1b0209 +38 19842d +1c 19c2f7 +10 8cbf +72 1b3b9e +c 97a6 +b8 ce074 +19 ab9 +39 196e4e +8 1938b7 +84 295faa +b5 299541 +4e 5ea4c +6b 5b87b +0 1d4f30 +ef f262d +18 b2a +9e 25d347 +15 19b1e1 +92 c9d0f +30 1d19ea +c5 ee06a +bb d761c +81 d2a18 +24 1976ec +55 1d532 +2f 3ed29 +8a 1029e7 +81 d2708 +64 1b2f57 +ab 2a2381 +d1 280b8a +b9 ce013 +6 19a574 +85 c9439 +8f ca549 +57 1b7a75 +65 1bc5ca +e2 eb0f8 +84 d2738 +cd e5e11 +5a 1af840 +a1 107c91 +29 c95f +1 19bb11 +ee 1236f0 +e8 1236b6 +f5 eaad4 +43 1af035 +e 1cbb0f +23 1d0d83 +6 1d3f54 +29 1d118d +3d 469bd +1a 19c31f +24 197688 +1e 19c600 +12 8fc8 +63 1b31dc +40 2518d +4c 1b87c5 +68 22161 +4b 1e014 +e3 ea0f5 +ef 27d72d +20 1976b9 +2 1cbc27 +db e6748 +92 d306f +20 41a7 +2c 1977df +74 1b3b68 +52 1e7c9 +b8 107488 +9b 10333b +0 43042 +9f d1f36 +83 29e083 +97 10b7bd +41 1b864c +e6 f3727 +64 1b3205 +63 1b323e +d 19b997 +1 835f +9b d1f07 +39 1d9be2 +65 1bc878 +e2 eb3a6 +24 197936 +5e 1b0b33 +52 1d4fb +49 265b7 +23 1d0d81 +6 1d3f52 +3e 3f67d +d1 efeaa +28 c90a +9f 10b6bc +1 19bb0f +ee 1236ee +66 1b44ce +6c 1b43d2 +60 20d9a +e7 27c366 +48 1b04aa +79 1b3a41 +63 22074 +47 1b86d8 +64 1bc825 +44 26490 +75 29a27 +96 29ecc2 +98 d1f6d +3d e2af +c5 e5f68 +48 1b04b8 +79 1b3a4f +8b 102978 +49 25355 +9f 10b658 +b8 107486 +13 192e05 +8a 296137 +7f 2182b +36 e152 +56 1f11ea +11 192e60 +9d 29eb0f +91 10b4d7 +99 10b62e +0 9672 +49 1cd4b +a4 268d8b +ed 27c464 +31 1d9ce7 +a 39880 +ab cd461 +44 1aedb0 +e4 f3782 +6a 5a628 +4c 1aef07 +2b 4306 +d7 2ba53e +9d 10b96f +cc e70e2 +bb 10fa7c +96 cadb4 +ec 27d717 +e0 ea0df +9c c9e9c +27 1d80fc +38 19841f +1c 19c2e9 +10 8cb1 +97 26427d +2a 1d94d5 +50 1b7cde +ba 25ffaf +28 3dd32 +a4 25f7db +5 192220 +14 19c202 +2b 3da9a +19 8e77 +53 1b7a46 +c4 e6fdd +27 1cfb52 +18 8e6a +2a 197a65 +65 1b2f58 +66 2a390 +c6 27815c +61 1b2f89 +1 2bd +d 1938f5 +53 1ea37a +9c 26417e +62 2a3c1 +11 1cc590 +44 1b0322 +75 1b38b9 +4b 2535c +27 1d814e +d6 278abd +19 1d4717 +85 25b387 +24 1cfaf6 +9c 2643da +db eeaa4 +40 1b0353 +71 1b38ea +6b 1edd25 +a8 298d69 +40 1b86af +71 1bbc46 +2c 1cfc4d +60 20da8 +6c 1b43e0 +3c 1d05ae +72 1b3b4a +99 10b684 +89 d1601 +38 1d05df +bd 110d76 +67 2a63f +7 1d5217 +19 dd7 +3 3979a +f 1ccdd2 +e9 124979 +c6 127740 +9e 10330b +0 19375e +d 1d3df5 +66 2102e +a4 107cc3 +87 103b76 +89 103c33 +67 1b2fc1 +15 8f9d +f7 2be435 +c6 2bae9e +8c 10c2cf +b9 ce075 +6 19a5d6 +4e 2507e +51 1f115f +7e 21828 +eb f384c +39 198110 +1c 19b2e1 +2c 19fddd +20 c7a5 +65 21026 +89 d28c3 +2c 3ed13 +72 1b4e0c +99 10c946 +8e 10ad06 +f6 286038 +73 1bbbed +39 d01e +8 9a87 +7f 1bbdc7 +4e 1b8830 +42 251f8 +1d 1d4756 +76 2198f +ac 107e1a +8f 103ccd +c7 e6f83 +6 1cceba +37 1d0451 +8c d163d +c6 28020c +bf 26028d +75 21987 +ec 124cb9 +9e 10b667 +18 a3e8 +27 1d10d0 +5e 1b9191 +52 25b59 +f3 ebd08 +ea 1249d5 +7f 1b4cdb +73 216a3 +9c d1f2e +8e d2ba6 +21 1966b4 +4 31 +6b 5b8ed +22 1cfdce +42 24ef6 +4e 1b852e +45 261e1 +17 1d48a8 +47 261e8 +1a 1cd9a1 +8e 10ad58 +6 19a824 +29 197a5d +a 1cba70 +bf 261551 +1e 1cd970 +12 3a338 +b3 cdf19 +b3 cf1d9 +af 2a13ac +f7 f2e27 +f0 2b63be +9c 296814 +90 1031dc +35 3f51e +24 1cfaf8 +7 1cb9ab +88 d2860 +6b 1edd87 +cd e70e1 +27 46f22 +58 1b7b95 +69 22470 +cc 2bad3e +c0 127706 +39 466d0 +8 43139 +a5 cd340 +76 1b4e9f +e 39b6d +d5 278ab7 +af 29a2ae +a3 106c76 +3b 197105 +34 1d0459 +17 1cc30c +3f 3e3ca +83 ca3c1 +e7 2843a4 +9b d21b5 +39 1d9e90 +b6 10735b +7a 1b3a47 +c5 e6fde +62 21051 +6e 1b4689 +2e 1d11b6 +22 3db7e +54 1af9bf +f5 284d70 +54 1f118f +69 20e90 +9c 2954fe +e0 124821 +c5 ee018 +3d 19712f +5a 1d6b4 +41 26452 +24 1cfda4 +71 1b3b98 +2a 472f5 +5c 1b9136 +50 25afe +58 1d6b9 +df 278c85 +d4 e7b8a +80 102ad7 +8c 29610f +bd 2996a6 +8 1ccd8b +37 3e263 +6 3accc +1 19b80f +4f 2532b +20 1cfdd5 +37 47b21 +15 1d48af +6a 1b3334 +1d 1cc46a +61 1b41e9 +44 1b73ba +74 1b3b66 +ca 120a78 +39 19842e +73 1b3b9f +1a 3a4ff +ed ea01a +93 25bf5e +6b 1b43b9 +a4 107f71 +87 103e24 +8 193677 +69 1ec812 +20 1d9139 +2 8665 +e 19bc9d +c0 e6f4a +7 1d4f67 +41 1b86ae +7 19a5d9 +19 3a497 +22 1d0de2 +9a 2656c6 +d 19a6c5 +24 41d8 +9d 265443 +91 d1e0b +1e 3a222 +8b 10c246 +37 4b43 +d4 e667e +ae 107e75 +bc cf057 +64 1b3275 +31 1d9a29 +62 20d4d +6e 1b4385 +49 5fd3b +81 10be38 +e4 123592 +c7 11f445 +fa 28514a +5b 1b7b8f +89 10bf8f +a4 106a63 +44 1b0072 +89 ca511 +ed 2844f4 +1a 42591 +be 2a1caa +a4 298be1 +78 1ed420 +5c 1b0b2a +2a 3ece9 +50 1d4f2 +8 1cba77 +f 84e0 +50 1b0a14 +33 1d0730 +c7 128a01 +53 25858 +5f 1b8e90 +c0 ef2fc +72 22c72 +50 1afa00 +2a 1d11f7 +0 41a72 +c 1d50aa +da 2bb6d7 +24 1d0db8 +33 3f564 +4 39753 +a5 cd334 +bb 29997e +de 280f64 +d3 2ba2b3 +20 3d8d7 +2c 1d0f0f +48 25098 +41 56570 +4d 1e9ba8 +cf 280664 +ec 2847b1 +70 22c17 +62 1b425f +98 d347d +ea 27d4a1 +9e d1f37 +ea f25ed +68 5bb31 +11 1d4630 +4b 579e4 +f5 27ccc2 +8e 264a86 +82 d144e +66 222f0 +d8 282250 +23 3d935 +2f 1d0f6d +d6 e662f +9a c9e64 +1d 19b336 +47 5fbac +7a 1ed109 +39 4cd2 +d8 281fb0 +1a 90bd +6 1d3fa8 +29 1d11e1 +d5 279d79 +a3 107f38 +f5 2bf6f2 +c 1936aa +0 72 +24 1d1066 +c8 ef3ff +28 430e +cd 127b3d +cf 1278f8 +ba 26986d +d7 efc40 +47 1e87f8 +a2 260a65 +58 1b9157 +bd ce036 +a1 29a183 +2b 3ed4c +51 1d555 +5d 1b0b8d +23 1d1091 +8b 29e486 +1e 1cc462 +49 262f9 +f8 ea8e7 +64 1b2f67 +b1 cf1e0 +47 1aee1a +6e 1b33d3 +39 61de +25 1d10c9 +c8 1278c1 +6d 63c03 +74 1b38c8 +57 1af77b +4e 1e8640 +45 57863 +e4 f246c +c0 2b18b6 +c7 ee31f +d7 279ae0 +56 26b4a +60 1b2f88 +e5 f371f +5e 26ca1 +3e 1d1b79 +32 3e541 +74 1b38b8 +7f 5c20d +14 1d58c2 +b9 2695b7 +49 1b7287 +8 19a6f5 +45 1b73bb +26 1cfaef +9e 2643d3 +93 29d722 +8c c953d +de 2b35fa +d2 11ffc2 +74 21986 +7 1d3f53 +3e 1d05a7 +1f 1942b1 +13 c79 +3c 1983fe +30 4dc6 +7 1d3c97 +81 29d066 +1b dd0 +38 4f1d +17 1d45f8 +13 1d4629 +1e 19b2da +7c 21821 +e9 f3845 +d4 e662a +ae 107e21 +70 22c6b +28 1d11f0 +62 1ec961 +f2 f3da9 +cc 2803bc +ac 29a05a +a0 106a22 +38 196eb1 +bd d7648 +a6 29a156 +3d 19f1db +c 19bc44 +0 860c +2f 1cfcb7 +4e 1e88ee +9 1ccd9a +44 1de86 +a8 2679a3 +fb 27e0b1 +5a 1ea4d0 +44 24f10 +6e 1b4695 +62 2105d +f1 2b63bf +c9 ee3ec +81 263944 +cb 128b7d +9d 29d84d +f6 eaa86 +21 c4f6 +2d 19fb2e +3 1d3f76 +20 1d80c3 +8 147b +42 1b004a +13 1d48d7 +30 1d8a24 +69 2949a +1a 43b01 +20 3ee3b +52 1ea3db +11 1ed2 +46 1af0c7 +4b 1f1c1e +fd 12d6bd +c 19a6c4 +64 1b4219 +60 1b424a +8 1cd047 +23 c80d +2f 19fe45 +60 20d38 +6c 1b4370 +ad 260b95 +ca e711a +a1 cd55d +b6 2a1ba7 +64 5a749 +bc cddf7 +f6 27c9c6 +3c 1983ee +30 4db6 +c6 277ebc +26 1d0db1 +92 d205d +9e 265695 +64 222e7 +8d c952e +d1 eff1a +38 4f0d +ce 278013 +22 3d8d0 +54 1af711 +2e 1d0f08 +e5 eb3df +88 d159e +fb f3f0d +6b 1ecac5 +84 d2746 +cd e5e1f +36 1d1722 +71 1bd146 +64 29375 +93 10ca4e +6f 1b30b6 +44 1aee14 +6 42d78 +4f 56451 +3a 1983c6 +4 19a87d +69 22154 +4c 25325 +c7 ee2cb +64 1b44c7 +16 1d465b +2c 1977e1 +20 41a9 +9 9768 +67 222ef +a 84ae +a 3ab52 +81 294d18 +6e 1f4b35 +55 60264 +2 1cb97b +87 10c112 +89 d285f +cf e5e26 +d8 efffe +6f 1bc974 +63 2933c +32 3f563 +a4 cd333 +ba 29997d +8e 10c268 +bf 10f7ff +1 41ae3 +d 1d511b +8a 103c49 +ee 2bdc2c +8d 264d8e +81 d1756 +8b d2866 +a9 d57b6 +8 41bd5 +8f 29d1a1 +28 4300 +2a 4307 +cf 127b36 +b4 29a866 +af 107e84 +45 5fbb3 +c3 1289c2 +7c 1b4cd3 +70 2169b +bd ce098 +f7 27cc67 +79 1ed3bf +30 1d9ce6 +aa cd460 +7d 1bbd5e +4c 1b87c7 +40 2518f +1d 42816 +6f 1ec83a +83 10be31 +1b 19c2c0 +3c 198142 +30 4b0a +b7 2600d6 +a5 d68e0 +79 2b11f +cd ee40f +93 29d9d0 +c1 ef309 +87 10be62 +13 8cb9 +1f 19c2f1 +5b 606a1 +4c 1b7503 +9e 296ad9 +92 1034a1 +37 3f7e3 +d2 e7bc2 +8a 296147 +bb 2996de +c8 12785f +98 25d0c3 +7 3acd9 +80 103add +77 1b3b7e +27 1d93bc +e0 eb39f +b6 cdc39 +11 1d487c +7 19a887 +22 1d1090 +b4 107354 +9b 29db27 +c9 ef460 +a 41c40 +35 d136 +8f 10bfb9 +e0 f36ef +be cdd90 +19 1d49d3 +1 19250d +80 10be9b +cd 2792cf +c1 e5c97 +26 1cfe01 +ff ea922 +b6 d7249 +d6 2ba2e1 +1c 1db9 +a5 10f060 +e 1cbaaf +c3 ef54e +56 1d52a +95 265598 +36 1d0452 +d 1ccd69 +1 39731 +c 16fa +3d 4c91 +20 4457 +2c 197a8f +5b 1b0b01 +fa 27e0bc +d0 ee937 +dc 281f6f +2c 1cfc4f +74 216ca +e1 f36ee +a6 107cca +5d 1f25a9 +51 5ef71 +d0 278849 +f2 ebabb +aa 29a040 +7a 1b4d09 +ee eb2f2 +ac 260be6 +a0 cd5ae +4b 5ea80 +2c 19682f +dc 280d0f +8d 295e60 +81 102828 +8c 264a7f +80 d1447 +f3 f3db6 +63 1ec96e +c5 e5cc8 +4c 1cd27 +d9 eed4b +5 39762 +85 102859 +7f 1b3cd3 +88 103c32 +7b 2ae16 +34 196fd5 +7e 5c20e +d5 279ad9 +a3 107c98 +59 6038a +78 1b4fb0 +2b c966 +36 197ff0 +11 439a6 +8c 2637c9 +74 21988 +96 10c7d2 +cf ee166 +a0 298c10 +94 c9af9 +9b 10b8d5 +62 1b4261 +3e 1d05a9 +25 1cfb59 +76 1ed28f +45 1b8671 +e 1ccd61 +2 39729 +a3 cd30a +af 260942 +41 1cbf4 +4d 1b022c +97 1031b3 +6c 1b3110 +97 2956bf +c9 ef6ac +5c 1d688 +79 5c491 +88 c92c0 +67 1bc5d3 +40 1e97d5 +7a 1b39e7 +d3 129395 +8f 1029a9 +a3 298c28 +f7 27dcdb +54 1af713 +2e 1d0f0a +22 3d8d2 +5f 1f1590 +33 1d0420 +2 1cce89 +85 d1733 +3b 3e69b +da 2bb979 +3e 46717 +cb e5de7 +82 d270e +4 9651 +1a 1d5c9b +66 1b4530 +a 19a6fe +67 1b453f +58 1d657 +df 278c23 +63 1bc5a2 +ad ce6e9 +8f 296169 +83 102b31 +f4 ebd31 +ac 29a2b6 +a0 106c7e +44 1b866e +60 2200a +43 1debd +d6 2b21ef +9c 103620 +ab cd3fd +7a 21aa5 +c8 28164d +64 1b4537 +ab 107e53 +41 5fb82 +3c 197190 +a4 106d01 +f5 27dce2 +7 41ab7 +b6 cdee7 +c5 2b9926 +30 4b6a +3c 1981a2 +1 1cb91f +94 25bc87 +b6 ceef9 +ae 106bb1 +d6 280b5d +8 9775 +4e 1b851e +42 24ee6 +76 1bcf33 +9b 1032d9 +c 41c08 +74 1bcf3a +44 1b73ac +ae 25f67d +6c 1b4380 +60 20d48 +4f 1b0233 +43 1cbfb +ad cd745 +e7 27c314 +cd ee46f +e5 2b6cef +9 1cba76 +85 29d035 +fe eac31 +41 1deb6 +e5 27d5cf +48 1dfac +35 196d28 +4 193791 +c6 2b2894 +8c 103cc5 +d2 279dbe +0 1cbc20 +d9 e6741 +90 d3068 +5 19a88c +8d d187c +96 103214 +40 1e10f +87 d172c +24 3d90a +87 294cee +4c 1dfdd +34 1d0767 +c0 e7206 +5c 1af86a +2a 3da29 +eb eb2c0 +b0 110c43 +32 196d61 +90 1046fa +d7 2ba590 +a9 267c50 +f4 2be6dd +54 1b7d0d +be 25ffde +47 1ce78 +d4 278b1a +ae 29a311 +a2 106cd9 +d5 2ba289 +f7 12d4fb +43 1b73f5 +1c 192ff7 +8e 103c78 +f7 eaa85 +f4 ea7d1 +d7 e6684 +c6 ef2d0 +b4 107362 +20 4463 +2c 197a9b +97 103215 +3a 3e38a +28 3dd40 +2a 1d94e3 +50 1b7cec +2e 197a94 +22 445c +d8 128220 +7d 64562 +27 1d106e +6a 1b43aa +18 a386 +82 294d2c +49 1e00d +5c 1b7e64 +11 9fd4 +a6 25f772 +30 196d58 +7b 2b118 +59 1b7ea6 +85 263667 +5f 1b8ef0 +53 258b8 +ea f258b +f5 27cc60 +1 192251 +60 1b2f26 +40 1b0361 +71 1b38f8 +eb ea000 +4a 5641f +1c 1cc469 +89 29e48d +cc e7134 +ef 2857cd +e3 f2195 +57 1b0c89 +fc 12530a +2e 197af6 +22 44be +71 1f5618 +e0 eb40f +41 1de54 +cc 1278f0 +3d 3e3c3 +79 22dd3 +d0 128069 +dc 2bb6a1 +81 ca3ba +e5 28439d +42 5e8b8 +7f 1f5487 +4e 1f1ef0 +af 2a109c +f7 f2b17 +c6 ef580 +a5 cd57e +ce e713b +5a 1d962 +cd ef731 +a6 106a68 +1c af9 +89 d2b1d +cf 2818c6 +c3 ee28e +82 d2770 +51 1f26cf +cb e5e49 +e8 e9f96 +fb eab9f +b2 d74c6 +86 2636cf +98 10358d +77 1f68a0 +a9 260908 +8c 263ad9 +d2 2ba55e +30 1d048a +1e 192fee +b5 110c21 +11 192bb2 +d 19b987 +1 834f +ca e5e58 +ad 25f8d3 +2f 19e875 +70 1b3887 +a6 260786 +7a 1b4fc5 +81 10c13c +e4 ea11e +c7 e5fd1 +72 1b388e +e1 27c080 +6e 2218b +d8 2ba40e +22 19664a +2b 19781a +b6 1073c9 +6c 1b4620 +60 20fe8 +41 1b0042 +e0 27d5fd +17 19b1e8 +32 1d19f1 +9 84a6 +d4 e692c +ae 108123 +37 4df1 +85 264999 +94 103459 +98 1048b1 +9e 29d8b5 +6c 1b335e +c2 2804dd +cd 2b9ae1 +ef 12cd53 +88 c950c +67 1bc81f +47 2648a +71 1ecfb8 +40 1e9a21 +a 1d5390 +3b 1d8927 +60 1b2f8a +e5 f3721 +28 197820 +c1 127a17 +cd 2bb04f +10 8f6d +1c 19c5a5 +62 1b2f91 +e7 f3728 +d2 281e52 +f5 12c4f0 +30 1d072a +c4 1289fb +1a 3a48f +bb ce070 +97 2953a1 +a4 ce8af +5 12f4 +e4 eb440 +45 1de85 +f4 2be42f +a9 2679a2 +98 104603 +10 1940cf +77 5c0b6 +6c 1b30b0 +21 196646 +4a 1b0203 +60 1edc2a +ff 27cb1e +2c 197aef +20 44b7 +cc ee40e +cb e5df5 +94 1034bd +4c 1b7505 +ca 280634 +7d 1bbd6c +4c 1b87d5 +40 2519d +6f 1ec848 +48 1cd58 +cf 278324 +c4 e7229 +ad 298d45 +30 3e29a +3c 1d18d2 +97 25bf2d +1c 193f99 +10 961 +16 19c1fb +68 1b30e1 +ed f3878 +6f 1bb412 +26 1d0db3 +b7 cdc9c +16 3a0bb +83 10c0df +3c 1983f0 +30 4db8 +8c c92ef +c6 277ebe +2c 19651f +21 d81c +68 5a8cf +fa 27cb4c +48 1b0208 +70 1b38eb +4a 57991 +7b 5af28 +f5 f4082 +6a 1edd26 +f2 27df57 +72 1b38f2 +f7 f4089 +d0 12128b +c 198 +24 1d1058 +23 1d93ed +de eed76 +66 5a492 +a8 107e49 +8d d1640 +a5 2a2500 +df 2bb6fd +68 1bc94b +d3 1280c5 +5 1cb950 +b7 cdc3a +b4 2997f0 +31 4dc7 +3d 1983ff +5a 1e984 +1c 192cd7 +6c 1b30bc +80 103d89 +3d 19f1cd +c 19bc36 +0 85fe +2f 1cfca9 +73 2af6d +42 1aed88 +8 1b9 +ac 25f8d2 +8f 25b785 +f5 27dcd4 +7 41aa9 +a1 298c21 +28 19fb60 +ae 107e13 +d4 e661c +99 295530 +61 20da9 +6d 1b43e1 +ba cddc1 +2a 472e7 +5c 1b9128 +50 25af0 +8f 29e457 +83 10ae1f +8b 1029da +fc ebbda +a8 106b27 +c 84d6 +89 1029e1 +da 120117 +fa eabfe +84 10ac0a +30 5dca +b7 261396 +64 1ed94b +82 ca424 +47 1e97fe +9c 29d84e +39 4f80 +45 1b7171 +ce 2792d7 +c2 e5c9f +60 1ed97a +d8 28225e +23 3d943 +2f 1d0f7b +a9 cd3f8 +40 1b02ef +41 1b7130 +21 1d80b6 +4a 1f1c73 +7 2e7 +e2 2b5a64 +43 1e84a9 +4d 1b04da +41 1cea2 +ea eb56d +4b 1dfb2 +1d 1d49f8 +1c 4381b +b 19b95d +bd d73fc +45 1f082f +f4 27cc5f +f7 ea7cb +26 19f9df +6f 1b30b8 +9b 10460b +ef 124c5f +e4 27d622 +8b ca518 +ef 2844fb +6 38 +77 64404 +55 1f1192 +61 20d3b +6d 1b4373 +c8 278031 +38 5f21 +b3 cdeb5 +bf 2614ed +1d 1cc408 +27 c7dc +20 1cfd73 +9f 29edb8 +93 10b780 +5a 1d65e +99 2656cc +28 3ece4 +a3 106c78 +af 29a2b0 +b 1d406d +61 2205d +46 1b73b5 +ce 1278e9 +69 1b45ee +19 1d7b +b8 cf336 +4e 1aef70 +e5 12cba3 +0 19b86e +49 1aef47 +32 cec1 +3e 1a04f9 +5c 1d68a +c9 ef6ae +58 1b0b5b +b 8511 +8f 29e455 +83 10ae1d +1b 19b2ac +26 1cfaf1 +b6 10862b +3c 1d05b0 +f1 f404f +a9 d6a06 +22 1cfb22 +a7 1102b9 +52 1b7a45 +18 8e76 +45 1ce71 +61 20d9b +6d 1b43d3 +b 87bd +56 5f24a +c8 28163f +a 1d50e0 +c6 ee2ca +e3 eb0f9 +14 192b8e +40 1aed7f +b2 10865c +46 562fb +85 29e369 +b8 cf32a +2e 1cff64 +f9 ebebc +41 562c2 +4d 1e98fa +88 ca520 +cf 2803b6 +ec 284503 +8 1d5079 +d 19b9eb +8a ca519 +1 83b3 +49 1b84e7 +e 1d53c1 +2 41d89 +3f 1d8958 +35 198298 +52 1e81d +a 1ccda2 +cb ef3f7 +6f 5a5f8 +91 cad7d +96 1031c0 +4 1ccc75 +26 19768f +93 104700 +64 21027 +8b 10af74 +f3 2862a6 +2e 1cfc48 +43 261b9 +ec 1236e9 +86 10bec3 +cf 11f59c +b8 cdd56 +58 60639 +68 1edad3 +f9 27cb46 +c8 2795af +ee eb59e +4f 1dfe3 +27 3d910 +63 22320 +c6 2babee +6c 1bc96e +60 29336 +f2 27df65 +53 1b09aa +68 20ef1 +ef 27c4bd +c5 e5fca +cf e70da +60 1b2f98 +e5 f372f +6 3a +c1 ef2fd +8 159 +7b 22ac8 +41 24ee0 +4d 1b8518 +6b 1b4665 +1c 193f9b +10 963 +fe ea92d +61 2205f +ab 29a2ed +d1 278af6 +6b 2a763 +1e 193fa2 +12 96a +27 1979a2 +83 29cdcf +d7 281e82 +f4 285fcf +18 aba +a 3ae60 +b4 26013e +4c 1aef17 +6e 22189 +db efdbc +8b d2b16 +1e af2 +18 193fd8 +8 1429 +6c 1bb40c +4e 1b01d2 +42 1cb9a +14 192be4 +81 264c08 +53 26e28 +e1 f3750 +2c 1cfcb1 +ee ea2ce +a0 2607b2 +d1 e65f8 +dd 279c30 +ab 107def +4a 1e2c1 +10 19af0d +48 1ccea +a4 1069f1 +3c 196e80 +cd ef6dd +48 1aef46 +12 192e66 +5 19379e +4a 1ccf1 +1c 192d3b +89 264d5f +c5 ef5ea +7a 1bc053 +58 5f129 +a6 267814 +1d 194248 +11 c10 +88 103f42 +a4 cd57f +c3 ef550 +56 1d52c +1b 1cc440 +2f 19eb85 +70 1b3b97 +bc 2996a5 +34 5e09 +5a 1d970 +79 1bcff1 +44 1b0080 +da 12143d +d 3ae29 +88 294e7c +8f d18e5 +ac d5a32 +47 5659a +91 2652b9 +a4 298bef +87 294aa2 +e4 eb122 +86 29cdfd +67 1f5f3f +88 102c2c +af d5a2a +70 1b38f9 +f5 f4090 +9b 10b627 +1e 1cc710 +67 5a493 +fa 125580 +df eed77 +13 a227 +ba 2996dd +f1 284fed +50 1f140c +a 42ef4 +bf 10f80f +88 264d5e +b9 2682f5 +e3 12cb6b +f 1cd01e +3 399e6 +9d d3503 +38 3e391 +1b 3a244 +3b 1d05d9 +a 1cd042 +bf 29995d +63 1f49ae +d8 e674e +29 45ddf +c4 11f699 +b4 299540 +1e 3b4e2 +98 d34df +20 1cfd65 +2f 1977e7 +23 41af +9f 10c928 +ab 106b83 +94 2652eb +c4 e6f8b +40 1b7131 +4b 5e76e +a4 ce8a1 +8f d2b45 +88 10c290 +a 1923ae +a1 10ffe1 +cc 2b9d2a +ee 2b6e40 +e2 123808 +5 19b894 +4c 1e8947 +82 ca3c2 +e6 2843a5 +5d 1b0dd9 +2b 3ef98 +51 1d7a1 +69 5a870 +20 47197 +3 4304a +50 1b8cfe +8f 25c9e5 +83 c93ad +94 263fc7 +84 d14e8 +75 646b9 +3d cfed +c 9a56 +1 42da5 +52 604db +ec ea027 +df 279ed7 +d3 e689f +ee 124cb4 +b4 2a1900 +2 1cceeb +33 1d0482 +3e 197133 +5 2e0 +46 1b86e7 +77 1bbc7e +21 420a +2d 197842 +e 19b98d +2 8355 +84 295d5c +ff 27ce2c +17 19c198 +69 1b307e +e4 124b64 +5 19bb4e +88 d286c +ce 281615 +c2 edfdd +ac 29a05c +a0 106a24 +d 1d5365 +1 41d2d +84 102af8 +1c 192f87 +68 1b308d +37 197ff1 +92 25bcaf +38 4cc3 +1e 1cc6ae +bf 26028f +ec 124cbb +1c 90e5 +52 1b0a19 +bf d6141 +1e 42560 +b8 2996d8 +46 1b7417 +11 a222 +1d 2068 +57 1d7d9 +d4 27886c +ae 29a063 +a2 106a2b +a 398e4 +bd 2a1a58 +d4 eff3c +3d 1d085d +5a 56de2 +69 2948c +b3 107647 +bf 29ac7f +9f 25d346 +93 c9d0e +28 587e +6f 1bb714 +d8 efd5e +40 1b00b1 +9 3ae5a +e7 2bf036 +a7 d5689 +6 41aa8 +f4 27dcd3 +a0 298c20 +8e 25b784 +bf 10faad +e4 ea110 +a9 299024 +8e 10ad04 +89 d28c1 +e6 ea117 +8d 29cede +ab 29902b +28 1cfc70 +73 1bcf11 +2f 196525 +ad 110407 +53 1f1168 +19 42599 +42 1f0b08 +db 2bb978 +96 d1dde +1b 19affe +fb ea8ff +86 d147d +5a 25cbc +30 60e8 +dc 2b208f +9f caeaa +44 1b710e +f6 eaa78 +9d 29d83f +bb 29998c +8 84a5 +8f 263a71 +48 25036 +cf 280602 +c7 2bae9f +8d 10c2d0 +eb f384e +7e 2182a +23 1cfd7b +a7 107d1d +33 4b74 +3f 1981ac +ff ebf00 +8c c9591 +89 d18bb +d4 128348 +27 41e0 +c6 2814be +9c d3510 +24 1cfd96 +91 29d9c9 +bf 107523 +55 5f252 +21 197976 +16 43989 +99 26440c +bb d767e +6d 1f5df1 +61 627b9 +2f 197849 +23 4211 +e5 12bb8d +c2 2814ef +7d 1bc01c +34 1d06f7 +6a 1b43b8 +38 4c61 +bf 26022d +8b 10bf88 +8d 29e4b0 +81 10ae78 +19 19b307 +f1 2be70d +1 1d3f6f +bd 2a1a48 +8c 29e4b1 +80 10ae79 +f3 12d7e8 +25 471bb +20 3db85 +2c 1d11bd +5a 56d82 +de f00a8 +66 1ec92e +d3 2ba561 +2e 19fb36 +22 c4fe +a4 299f05 +67 20d7f +99 d1f0c +6f 1b43e8 +63 20db0 +f7 284d15 +bd d6146 +8c d2baf +d2 129634 +da f0077 +62 1ec8fd +c0 2817a4 +4b 1dfc0 +4d 1b04e8 +41 1ceb0 +5b 1e921 +5d 1b0e49 +2b 3f008 +51 1d811 +80 c915b +8c 25c793 +7a 22ac7 +20 1cfdc7 +8e 25b724 +7c 1b4fef +70 219b7 +23 1d0d91 +9e 2957b3 +7d 1b4ff0 +d4 2ba286 +71 219b8 +a0 cd302 +ac 26093a +9a 10b698 +3f 479da +39 4c62 +ac 260bf4 +1 1 +d 193639 +a0 cd5bc +51 26b03 +9 1d5088 +30 3e4e6 +3c 1d1b1e +2f 19e873 +ea e9fff +1c 1d5a19 +10 423e1 +97 29d9ad +b4 25fe20 +ab ce723 +a 3ab42 +a1 d68af +46 1f0ad7 +0 1937c2 +31 196d59 +30 e128 +79 21801 +45 1f0add +c6 e7292 +af 298dae +62 5b731 +47 24f28 +ba d73d1 +c8 28037d +98 c9c13 +3d 5f55 +43 1f0b07 +23 da6f +60 1f4c54 +12 1d48e4 +2f dbf7 +28 1d118e +70 22c09 +fd 2be5e7 +cc 2bb050 +c0 127a18 +65 63d5a +4f 1b8591 +43 24f59 +63 1ec6b2 +11 4268e +1d 1d5cc6 +84 10aeb6 +a1 107ce5 +ac ce996 +1c 19b345 +39 198174 +9e 264125 +74 1bd1da +c 41ea8 +da 1284d5 +7f 64817 +75 646bb +50 1f1470 +13 a28b +45 575b5 +e4 f21be +c0 2b1608 +c7 ee071 +87 29e300 +5 1924ce +c8 e609d +ca e6094 +4d 1b7566 +20 d829 +69 20f02 +c3 ef304 +3e 1d1869 +32 3e231 +72 216a2 +7e 1b4cda +2 1d3c67 +1a dcf +95 29d6ea +b3 299837 +52 1af9a5 +18 dd6 +37 1d87ad +6 1d5216 +17 42418 +31 ce57 +0 98c0 +3d 1a048f +98 26414d +f0 ebd02 +92 29d9dd +37 1d9d1f +41 5f8d2 +c1 1289c9 +40 5f8d3 +c7 1289f3 +b7 ceefc +60 1b31e2 +d7 2b1f94 +f4 ebd33 +b3 29a83d +96 29da0e +b9 29ac47 +45 5f903 +78 1b39e0 +9c 25c0de +95 cad4c +4b 5fa22 +5f 1b7bbe +78 217f2 +cb 128b19 +4a 5fa23 +0 19a7ea +39 1a04d0 +ca 128b1a +f8 ebe59 +9a 29db34 +46 575ad +e7 eb18e +49 5fa29 +c9 128b20 +48 5fa2a +af 2a23b2 +a3 10ed7a +d5 280bbb +c8 e5def +c8 128b21 +4a 1e9931 +21 1cfd74 +1 9931 +cf 128b4a +fc 2b6238 +bf cf053 +fc ebe8a +bb 29a994 +9e 29db65 +4d 5fa5a +cd 128b51 +39 1d0880 +4c 5fa5b +b2 299578 +4 19baeb +4d 1af1c4 +37 1d9ac5 +cc 128b52 +38 1d0881 +48 1e98c8 +e9 27d4a9 +4f 26331 +5 19a5d2 +8e 25c738 +82 c9100 +20 1d0ddb +98 2656bf +88 10acc0 +e5 124b01 +fa ebec0 +d8 278c4e +e9 2b5907 +9c 2957ae +be 108a20 +95 265296 +19 8e79 +9f 265696 +93 d205e +31 1d9d39 +f 1d40aa +f1 27cc9d +1 1924ff +59 1e91a +84 295fb6 +35 197fea +90 25bca8 +89 10bfe3 +1 19baaf +34 197feb +1d 1d47b8 +9a 1032e6 +9 192656 +9f 1045da +95 1034ca +8 1d50db +15 1cc5c1 +59 1b0b5c +a 41eec +2f c935 +28 1cfecc +7c 1b4f7f +70 21947 +78 21a9e +62 5a461 +bd d7656 +6e 1eda99 +43 1e117 +da 11fecb +bd 299946 +60 5a468 +6c 1edaa0 +7d 1bd02e +71 299f6 +40 2645f +63 5a4d2 +6f 1edb0a +a1 1069cf +ad 29a007 +96 10b564 +40 1e101 +29 1cfc1d +c7 ef2d1 +1b 4284e +4a 1dfc1 +2 193519 +25 3dbb7 +c4 2bae95 +4c 1b04e9 +7d 1b3a80 +40 1ceb1 +12 193e7a +35 3e518 +d4 2bb7f6 +7a 22ac9 +32 198021 +d7 e6630 +47 1f1aea +65 1bc824 +45 1f1af1 +80 d2717 +f5 f3de0 +65 1ec998 +44 1f1af2 +e9 2857e7 +7c 1b4d35 +70 216fd +6a 5bb38 +56 1e90e8 +f7 27ccc9 +12 3b34c +b3 cef2d +de 278c76 +c1 128a2b +cc ef6dc +59 1b8eba +41 1af09e +c0 128a2c +40 1f1b23 +5c 259d6 +43 5e609 +4f 1f1c41 +8a d2867 +d1 1212ec +88 d286e +40 5e611 +4c 1f1c49 +13 1cd859 +d2 278840 +78 21854 +e8 12ccc8 +9 9a78 +43 1b8647 +51 26db3 +4b 1f1c72 +68 1f5dbf +75 1b4b89 +c9 128b82 +41 1b864e +49 1f1c79 +3e 3f689 +d1 efeb6 +8d 294bf2 +0 19bb1c +49 1af1f5 +32 d16f +3e 1a07a7 +5 2ee +5a 1ea534 +48 1f1c7a +1 42fe1 +24 1d0dac +b5 cdc95 +3 1921f6 +14 3a0b4 +81 10c0d8 +3b 1a0713 +5 43012 +97 26528f +4b 1b8552 +57 1b7ad9 +72 1ee2e2 +aa 298d6e +8e 103c6c +2 1d3f15 +cd ee1cf +43 5fb79 +11 8f6c +63 1b2f90 +1d 19c5a4 +ea 1249e3 +1 1921ef +a0 25f7aa +57 1b7d15 +c2 127a0f +1b 193f70 +ce 2bb047 +14 1d48a0 +8e 10c01c +66 1ed944 +f7 ea82d +56 56c4c +c3 128c70 +8 1729 +73 1b388f +42 1b02f8 +98 2643fb +39 1d088e +f7 ea7d9 +25 197939 +42 1debe +b7 cf1aa +d7 2b2242 +34 197fe9 +8 192655 +dc 281fd1 +d0 ee999 +88 29cf1e +21 45c28 +75 2acdb +2d 1d9260 +bc cf369 +f6 eaada +9d 29d8a1 +21 19e754 +fe ebc43 +aa 106b90 +dc 2789d1 +5d 1b7c27 +da e6755 +2b 45de6 +c6 11f6a0 +b6 299547 +38 5f23 +3 192258 +81 10c13a +19 19c5c9 +1 1d5231 +9b 29eb47 +6c 1bb46e +f1 12d4d3 +8 148b +59 1ebc8 +4a 5672f +3 967c +40 1f0861 +d7 280e1c +9d d224d +25 1cfda7 +4e 1e9964 +42 5632c +66 1edbf0 +4e 1cd30 +23 1cfd6d +9f 29db02 +0 42d42 +87 29e30e +94 10472b +34 ce89 +63 22072 +47 1f1d98 +45 1f1d9f +d5 278879 +a3 106a38 +af 29a070 +47 261ea +7a 22abb +c3 128cd2 +5b 1b9161 +69 22160 +3b 1d0827 +43 1f1dc9 +7d 1f6750 +40 5fb81 +71 63118 +60 1f5f16 +30 19801a +59 1b9168 +cc 2b19dc +85 264929 +dc 279c85 +aa 107e44 +d0 e664d +17 1d466a +b7 cdee6 +43 2621b +37 196fdd +72 2af6e +4a 1ccfd +d4 2bb7f8 +1c 192d47 +89 264d6b +91 d20c5 +9d 2656fd +42 1e97dc +6b 20ea5 +6f 20ed6 +77 29cce +2f 1d8253 +70 1ed265 +33 6080 +7f 1b4ce7 +42 1e118 +73 216af +cf ee1d8 +ea 1249e1 +28 58de +62 1b44ad +13 1cc527 +b2 d7218 +fb ea8f1 +22 1d7e70 +6a 1b4604 +26 1d7ea1 +3a cfb6 +b0 107385 +bc 29a9bd +a9 ce968 +4e 1b048c +42 1ce54 +6a 1b43b6 +6b 22469 +0 19bb1e +ce 2bad37 +c2 1276ff +31 19f0b5 +20 d7b9 +69 20e92 +24 d7ea +6d 20ec3 +66 1bc82c +46 26497 +77 29a2e +62 20daf +6e 1b43e7 +56 1d7e6 +2e 19fde4 +22 c7ac +a4 29a1b3 +79 1f54c1 +67 2102d +b4 29ab14 +f2 12d779 +3a 4cc8 +77 2198e +4f 57705 +ee 124cc0 +13 3b5fb +77 1f55de +be 108a30 +6a 1bb436 +0 12d0 +fa 12c670 +b4 cf1ae +28 3da92 +f7 eaad9 +af 29905e +21 5478 +4a 2509f +2d 19eb1a +45 1b8423 +c2 e6f51 +ad 106bbb +fe 1242f1 +af 2a235e +f7 f3dd9 +a3 10ed26 +d5 280b67 +58 1ebc7 +2 967b +29 55cf +36 1d8a4e +f 1cd080 +3 39a48 +67 20d71 +2d 5600 +67 1bc5d1 +88 c92be +47 26498 +a 97d2 +8f d15d5 +d0 279b09 +5c 1d67c +c9 ef6a0 +ac 106b58 +8f 102a0b +7b 2ae0a +46 1de99 +22 1d7e0e +cd ef6d1 +48 1aef3a +84 d1796 +0 3a9e4 +a1 ce5c5 +cc 27830e +4c 1b04e7 +40 1ceaf +4a 1dfbf +63 22010 +c4 2b9925 +6d 1b468f +61 21057 +6b 22167 +2a d917 +9b 1032e7 +71 5c39c +35 197fec +81 ca42a +4a 1f09af +90 25bcaa +6f 22198 +ac 106bba +cf 279584 +c3 e5f4c +e5 eb185 +80 ca429 +aa 2a238e +d0 280b97 +75 1bced9 +98 10b8db +ea 2b58ff +e 1cd011 +3f 1d05a8 +2 399d9 +d8 280cee +e5 12cba1 +71 299f8 +7d 1bd030 +6a 1bb6e4 +0 157e +ec 27c1b3 +21 5726 +36 1d0760 +33 cec0 +3f 1a04f8 +2 9929 +8f d1883 +ad 2a2357 +f5 f3dd2 +a1 10ed1f +d0 279db7 +42 1e116 +bc 299945 +58 5f377 +7b 2b0b8 +46 1e147 +22 1d80bc +0 3ac92 +a1 ce873 +a6 106cb6 +60 1ec656 +67 290bf +c7 2b188d +63 222be +bb 107482 +0 9674 +b5 d5f8f +87 264c40 +2f 19eb21 +80 ca6d7 +e3 f3703 +76 216df +b5 26974d +aa 2a263c +b 1d5081 +d0 280e45 +25 196685 +42 1cc0a +4e 1b0242 +fd 12d6bb +4e 1cd20 +75 1bd187 +29 1d7f6d +7d 1bd020 +40 26451 +71 299e8 +60 1bc7e6 +89 d159f +6a 29494 +75 1b3b69 +5 838e +31 1d8a17 +5a 1f25d4 +36 1d1a30 +95 264028 +7a 29df5 +29 c6bf +f7 285017 +56 1f1436 +19 8e15 +23 1d102f +6a 20ea4 +62 1ed983 +b1 d626c +bd 2698a4 +a8 d57a7 +e2 2b6d28 +9 3ab9e +f5 ebd32 +a1 106c7f +ca 12083c +ad 29a2b7 +63 5b9d2 +ed ea028 +16 19415b +d 9a57 +53 604dc +22 1d0d82 +c5 2b28fc +cb ee137 +47 24f26 +a0 298c12 +ea 124971 +cf ee168 +6 3aa1e +20 1cfb27 +a5 1102be +98 26440b +31 d115 +5a 26cd2 +3d 1a074d +4f 2507d +57 25887 +77 1bbc1c +46 1b8685 +a5 10effc +ce 128bb9 +30 1d0488 +b5 110c1f +9d d1f3d +bd 2682d2 +80 d1703 +8c 264d3b +99 d1f6e +b9 268303 +88 264d6c +c7 ee01d +61 2a5f7 +26 3ebd3 +82 103b46 +cb ee1a5 +8d ca542 +d7 ee97e +14 994 +81 d29b8 +8e 1029b6 +f6 27dce8 +89 ca573 +ed 284556 +1b 1d49cc +44 1b00d4 +1c aeb +89 d2b0f +24 1cfb58 +5 863c +5a 1f2882 +41 1f07fe +2 19a5a5 +4a 2504d +6e 1b4633 +62 20ffb +76 2198d +aa d6a0e +f1 125493 +ca 2b9aa6 +1 3acf5 +93 25cf72 +ef eb28f +47 251d4 +ce ee175 +cf 281934 +c3 ee2fc +8e 102c64 +57 1b09db +f6 27df96 +89 ca821 +ed 284804 +3b 3f6bb +44 1b0382 +7b 1bbda6 +1 961f +6a 2a756 +75 1b4e2b +aa 29a2e0 +d0 278ae9 +5 9650 +4f 1b851f +43 24ee7 +9 9776 +d 97a7 +53 6022c +6c 1b468e +60 21056 +6a 22166 +25 5757 +6e 22197 +43 261b7 +8c 29e44f +80 10ae17 +bd 2a19e6 +5 19a5e0 +20 1d0de9 +98 2656cd +4f 2633f +ee f25ca +4f 579c1 +ef 2bef51 +e3 12b919 +a4 ce59f +ef 27d72b +e3 ea0f3 +a0 ce5d0 +e6 e9e67 +ac ce6f6 +85 d2747 +3b 3f6af +c7 ee00f +8d d289e +23 3ee43 +d3 129323 +b2 299826 +7d 632a0 +4c 5fd09 +a7 d6949 +85 2636d7 +a0 299ee0 +4 2ef +cb ef467 +97 1044d7 +20 5725 +27 1d0e16 +db 129736 +d0 2820f9 +ca 2792f8 +ed 123996 +24 5756 +42 1e97cc +91 d3389 +1 98cd +c6 e5d22 +5 98fe +41 1f1ac0 +62 222bd +66 222ee +8f c9535 +d3 eff21 +9a 296aa6 +43 26465 +da 128219 +bd 2a1c94 +47 26496 +1 12c3 +a0 ce87e +85 d29f5 +0 19225e +3b 3f95d +95 25cf9c +c7 ef58d +c0 128cd8 +42 1aedf6 +4d 1af218 +4 19bb3f +7d 29b7e +4c 265e7 +b2 260104 +41 5f936 +20 54db +47 1aee28 +c5 128d0a +c3 ef5be +9c d3254 +ac 106bac +19 b1b +f2 27cca5 +aa 260bbc +aa 10ee6e +dc 280caf +8b 10c244 +1e 3a220 +85 10ae57 +8d 102a12 +de 120148 +2 3aca9 +3f 1d1878 +33 3e240 +22 1d103e +44 1af05e +a 3ae00 +3b 3e397 +30 196d5a +72 22c10 +50 1af99e +2a 1d1195 +b5 d74f1 +81 ca41c +e5 2843ff +27 3ebd2 +27 1d0dc0 +18 a0d8 +82 294a7e +4d 1f1ef6 +41 5e8be +50 1af750 +2a 1d0f47 +b5 d72a3 +a2 10efc5 +ae 2a25fd +d4 280e06 +ac 106e5a +16 3a377 +e9 27c1d7 +66 5b754 +44 1e84e2 +c6 2bae9c +28 4360 +62 1b2f2f +37 3e51f +d6 2bb7fd +14 1c62 +7e 1bbdc8 +1a 1de5 +5e 1af87f +dc 129761 +a7 106cb5 +5d 5f3a7 +c6 27941e +6b 62bc5 +2 3a9f9 +55 1b09e2 +23 3eba1 +bb 110a92 +86 103b21 +82 103b52 +a7 107cc9 +8 39b99 +5d 603bb +c6 2babe0 +63 22312 +62 1edbbf +47 1b73b6 +cf 1278ea +ec 12ba37 +44 1e97a4 +55 1b0c90 +23 3ee4f +33 1d8a7e +82 103e00 +a7 107f77 +5d 60669 +4b 1cd52 +2 9679 +7f 1ed3e7 +0 19a5a0 +73 1bcf0f +1f 1d5a1f +13 423e7 +4c 1b01d9 +40 1cba1 +1b 4253e +10 19af01 +44 1cbd2 +be 2a2f6c +b2 10f934 +ba 1074ef +8f 10ad05 +84 2636c8 +f7 286037 +8b 10ad36 +c4 e5cc9 +9f 10b666 +94 264029 +53 1b8fb6 +19 a3e7 +9b 10b697 +5b 1f2571 +c7 2791e1 +36 3e51e +49 1e2bb +e0 27c081 +d4 281e26 +eb 1236be +d5 e6629 +af 107e20 +a4 2607e3 +a5 106d10 +c9 28037e +f1 27df5f +f 1d536c +3 41d34 +8 41e85 +5c 26f38 +33 198024 +8 8507 +42 1b70d6 +60 2104a +6c 1b4682 +cb ee1a7 +69 221b4 +ae d5a8d +8 1cba69 +bd 26154a +1c 1cd969 +10 3a331 +b1 cdf12 +6b 62c17 +7b 1ee3d8 +b6 107615 +88 2637ec +70 219ab +7c 1b4fe3 +25 1976ed +8b 1029e8 +87 10ae5c +ad d6a45 +e7 f21b6 +7a 22ab9 +58 1af847 +8f 29e4c5 +83 10ae8d +45 1b8433 +c2 e6f61 +d2 2b2210 +8c 25b78b +3 42d48 +b6 cf1a9 +d6 2b2241 +7 42d79 +be cf300 +96 10b820 +b3 10864f +de 2b2398 +f1 ebac3 +f 42ed0 +4 19b893 +a6 107cc8 +40 1af09f +62 22311 +14 19b1de +79 22ab5 +5c 25c86 +3c 1981b2 +30 4b7a +d5 1283a9 +83 10bea1 +e5 f2211 +38 4cd1 +dd 128500 +8b 10bff8 +d4 eff4a +e8 27d498 +71 1bd1a8 +9 41e76 +93 10cab0 +1d 90f4 +6f 1b3118 +3b 1a0467 +cd 120ab1 +26 3ee7f +93 10cab2 +83 10c14f +ef 27d47d +e3 e9e45 +2c 197841 +20 4209 +a7 25f7d5 +ce 2792d5 +45 1b716f +c2 e5c9d +eb e9f9c +2f 4339 +9f 10cbc8 +1a 1cc431 +10 192bbf +4d 1b7512 +21 196406 +72 1b3b3c +86 10be71 +63 5b734 +eb 12cf70 +7e 5af4c +c8 128e2f +4a 1aef4d +cf ef6e4 +d4 129608 +56 1af726 +dc 12975f +5e 1af87d +e5 12ce4f +67 1b2f6d +46 1b0337 +f5 12d7b0 +77 1b38ce +64 21019 +93 1046f2 +e 19268d +f7 2be6d5 +4e 1b048e +42 1ce56 +fd 12d907 +7f 1b3a25 +ff 27e08c +f3 eaa54 +10 192e6d +3d 1d1b21 +31 3e4e9 +5a 580a6 +14 992 +81 d29b6 +7b 5c24e +ef f388d +7b 1bbfe4 +46 1af073 +3c 3f932 +18 1cc3ca +be d63ee +8e 29cee4 +2b 4616 +76 5b0a3 +5f 1d690 +c4 2bac4b +42 1af0a4 +56 1af9d4 +52 1afa05 +67 1b321b +6b 5bb39 +ea 28452d +80 ca3c7 +a9 ce6c6 +e3 eb107 +eb eb25e +39 1a0460 +4 1934ef +a0 d6b6c +e9 ea245 +9a 1048ac +94 104729 +fe 2be88f +0 193520 +7b 1bcff8 +46 1b0087 +ea 2847db +80 ca675 +a1 ce81d +e3 eb3b5 +39 1a070e +4 19379d +7d 22da2 +89 d15ab +41 1af02e +59 25a0a +e6 2bdac7 +cb ee143 +22 3ebf4 +0 1cb982 +54 1b0a35 +e2 2bedc8 +11 19c170 +5f 25c8c +43 1f1dd9 +30 1d0736 +ac 25f616 +ea 2bef1f +b2 cdc6a +be 2612a2 +34 196d35 +bc 25ff77 +1b 43862 +a8 107e3b +8d d1632 +40 1dec5 +30 196d66 +b5 d74fd +27 3dbbe +b8 25ffa8 +3c 196e8c +83 294d2d +48 1e01c +2d 1cfefc +4a 56481 +38 196ebd +bd d7654 +62 5b733 +1d 19b034 +66 1b2f6c +d3 280b9f +c 1ccd5c +0 39724 +73 5c093 +ee 27c1ae +98 d21bb +ea 27c1df +b1 2600fe +6e 1b30c3 +49 5ea79 +36 4df0 +db 280cf6 +3e 1d1b27 +32 3e4ef +41 1e172 +6a 1b30f4 +ef f388b +76 1b38cd +fe 27cb0f +72 1b38fe +84 d1726 +f7 f4095 +fa 27cb40 +a5 2a0f9e +47 1b7114 +43 1b7145 +6 1ccc0c +1c 194249 +10 c11 +4f 1b726b +4b 1b729c +24 1963d6 +2d 47072 +34 196d37 +ca ee144 +40 57893 +df e6787 +45 1b741f +c2 e5f4d +ff 27cb1c +60 1edc28 +ce 279585 +1f 3b4e3 +14 193ea6 +4a 1e9923 +99 d34e0 +21 1cfd66 +d6 1293c5 +8a c9503 +d 19a9d5 +6a 1ec7a8 +25 45c4b +24 196682 +6e 5b8bb +b3 d7527 +91 2642b5 +34 196fe3 +7e 5c21c +1b 43b10 +7d 1b4d42 +71 2170a +40 1e173 +30 197014 +80 d29b5 +c9 e608e +7a 5c24d +5 19a82a +a4 ce591 +4f 5fa63 +d4 ee9d8 +bd 2685d6 +1c 1d49f5 +4e 1b726c +66 1b321a +8d 10ad54 +d3 280e4d +76 1b3b7b +8a 29d1d1 +9d 10b6b5 +47 1b73c2 +43 1b73f3 +1a 19b05d +1 2af +d 1938e7 +57 1b7d23 +c2 127a1d +ce 2bb055 +e 1cbd5b +9 193918 +53 1b7d54 +1b 1d5cf0 +38 1d9e3d +a0 ce56e +ab 1103eb +d1 eebf4 +3e 3e3c7 +dd 28222c +dd eed70 +ab 10809f +6b 62969 +81 d2716 +89 d286d +6 1d5278 +37 1d880f +c3 ef2ae +5f 26f4e +15 19b1ef +30 1d19f8 +66 1b2f5e +d3 280b91 +2 1cbc35 +20 41b5 +2c 1977ed +53 1f241a +48 250a8 +47 1b83d6 +4b 1b855e +a0 ce81c +24 197698 +7 19354b +14 9a0 +81 d29c4 +65 1bc886 +e2 eb3b4 +21 1d1028 +1e 1d47b2 +4a 1e8601 +20 197975 +40 1cb93 +4c 1b01cb +39 3e3f4 +22 19790e +d8 2bb6d2 +3 1d4f98 +a0 25f4f0 +20 54cb +a7 260a97 +58 1f2827 +11 193e74 +16 1cc2b7 +19 193fcb +1e 1cc40e +46 1e139 +2f 1cfc55 +42 1e16a +2b 1cfc86 +66 20d72 +6 1ccec8 +37 1d045f +a 1cbd2a +bc 1077c9 +62 1b449d +2 1ccef9 +33 1d0490 +ff eabd0 +b6 d74f7 +d6 2ba58f +e 1cd01f +56 1ea9a +2 399e7 +3f 1d05b6 +52 1eacb +a 1cd050 +3b 1d05e7 +e3 f36f7 +76 216d3 +10 1c31 +c5 ef32e +7a 1bbd97 +59 5f376 +bf 29ac1d +b3 1075e5 +11 194122 +fe ea92f +76 1b38bf +12 1cc596 +4d 25324 +2d 197850 +21 4218 +c0 2814f6 +2f d947 +d 19a6d5 +28 1d0ede +fa ea960 +e4 e9e60 +85 102b5b +20 196397 +3b 1d9b89 +6 1ccc18 +a2 110295 +d4 2820d6 +eb 12396e +23 1d0df1 +59 60638 +3b 1d9e37 +6 1ccec6 +87 294d5e +ec 285ad5 +e0 f249d +41 24ee2 +4d 1b851a +88 10ad2e +6b 1b4667 +80 263697 +cf ef6e6 +61 63d89 +6c 1eca8c +25 19f9d9 +f3 286006 +42 1b8406 +95 c9d9a +42 1b86b6 +73 1bbc4d +2e 1cfc54 +dc eed7d +f9 ebbac +9b 29d887 +ea 2b6e7d +cf 280674 +4b 1e98c2 +2a 1cfc85 +d5 eec25 +af 11041c +14 19b1ec +31 19801b +36 1d045e +cc ef482 +14 194160 +be 2996a0 +59 1b9158 +12 3a0fc +1e 1cd734 +32 1d048f +b7 110c26 +ba 2996d1 +1c 19b343 +39 198172 +16 19af39 +3e 1d05b5 +bf 110d7d +70 21949 +7c 1b4f81 +ae 25f61d +21 c504 +2d 19fb3c +7c 1b4ff1 +70 219b9 +88 2637fa +73 1f65c1 +3 1d3f84 +61 222a9 +3d 197191 +a5 106d02 +46 1b0079 +5e 603b5 +83 10be3f +e5 f24c1 +3c 1983fc +30 4dc4 +26 1d0dbf +6 3975a +a7 cd33b +b8 ce006 +9 41bd6 +71 1bcf08 +22 1d0df0 +d0 27883b +aa 29a032 +1 1325 +c2 e6fc3 +a5 260a3e +28 1964ee +dc eed6d +12 1cc278 +b3 25fe59 +cf 2b9d24 +af cd73e +52 60549 +68 1b307f +80 10be99 +c9 11f572 +be 110b24 +30 196cf8 +29 5625 +28 1cfc72 +5a 1b0b62 +31 196fa5 +40 1aed8d +7b 5c48c +41 1aed8e +7f 1b3a19 +36 1a0340 +41 261a4 +e0 f375f +cf 2b9a84 +48 1aeee4 +32 ce5f +3e 1a0497 +68 2114d +96 10c826 +e5 f21bf +fd 27e077 +f1 eaa3f +73 229c5 +2b 1d0f4a +a4 cd331 +11 1cc280 +a3 10ed78 +d5 280bb9 +af 2a23b0 +e6 123839 +4d 1b8528 +41 24ef0 +f 1d53ce +3 41d96 +17 426ba +19 1cc3d7 +95 29d996 +28 3ef9e +d 8795 +5f 1f2852 +53 5f21a +22 1cfac0 +7 19a575 +bc 29a9bb +b0 107383 +4c 1e869b +2a 1cfc17 +0 1cce90 +7 98f9 +31 1d0427 +48 1b0456 +79 1b39ed +f 9a50 +50 1ea62 +8 1ccfe7 +39 1d057e +84 d2748 +c4 277ea7 +9 1d5326 +3d 196ee3 +a5 106a54 +c5 277ea8 +34 1a039b +7d 1b3a74 +c6 277eae +19 1d4725 +7e 1b4f96 +72 2195e +4d 1b72c8 +68 1edad1 +ca e5df6 +f 1d5122 +50 1ea134 +3 41aea +d1 281e5a +8d 25b46e +20 45c27 +2c 1d925f +3 41ada +f 1d5112 +d3 281e61 +8f 25b475 +65 1b452a +ce 278005 +24 4494 +9d 2656ff +42 1e850a +91 d20c7 +ca 278036 +63 20d40 +6f 1b4378 +25 4495 +7a 1ee6db +c4 281773 +82 26495e +cb 278037 +8 1d40d3 +7d 1b3d30 +34 1a0657 +1e 1d5a82 +12 4244a +f7 ebd3b +9c 25bdce +9d 25bdcf +35 4df6 +92 2652bf +db 278998 +99 25be00 +52 1d507 +5e 1b0b3f +67 22035 +9a 25be06 +2 1579 +33 4b10 +3f 198148 +3d 19f47b +5a 25a00 +fe 285119 +4d 1e28a +e4 27c050 +86 10aeb1 +a9 1080ea +1f 1d5d21 +13 426e9 +3c 1d9e6e +30 46836 +42 1de5c +cc ee1c0 +15 19aedd +4f 1e291 +61 5b72d +d 16f9 +ad 268c89 +c 1d50a8 +0 41a70 +a1 d5651 +57 25b35 +ce 128e67 +c3 11f3b2 +cf 2b29ea +11 19af0e +4b 1e2c2 +68 2240f +90 d2064 +9c 26569c +e2 27c088 +a7 106d17 +53 25856 +5f 1b8e8e +cb 2b2a1b +1 311 +d 193949 +57 1b7d85 +17 3b62c +b6 d6235 +4a 1dfb3 +49 265a9 +a7 267b25 +6 1d3f44 +b4 2600cc +5f 1f159e +84 25c888 +b5 25fe1f +1d 205a +dd 28222e +3e 3e3c9 +d1 eebf6 +ab 1103ed +c4 279419 +f5 27c9b0 +28 1d827c +56 1b09dc +5d 1ebeb +fd 28617b +f1 f2b43 +19 208b +fa eab9c +7d 1bc06e +1e 3a4ce +b0 25fe51 +8c ca851 +c6 279420 +f7 27c9b7 +1b 2092 +84 294a38 +38 61df +7f 1bc075 +11 19c1c4 +58 1e9277 +b2 25fe58 +5f 1ebf2 +71 5c08e +b9 110a97 +80 c93a7 +8c 25c9df +bd 25ff76 +41 1dec4 +28 19eaea +a5 110010 +98 26415d +31 ce67 +3d 1a049f +b7 268486 +16 1d48a5 +f 1d3dee +c0 27944a +f1 27c9e1 +e4 2bdb30 +4e 1cfde +6 192536 +59 1ec1c +29 45bb +e 1d3def +f0 27c9e2 +bf 108781 +b4 261144 +b5 107671 +f2 27c9e9 +b7 107678 +c0 e5f38 +cc 279570 +fd 27cb07 +c5 277eb6 +3f 1d1b26 +33 3e4ee +c1 277ee7 +aa cd70e +6f 1ecae8 +d8 121132 +86 294a3f +13 19c1cb +5a 1e927e +cc 27800c +84 264934 +cd 27800d +a8 cd715 +fd f3f37 +a3 ce56a +6d 1ecaef +84 294a46 +88 10bfe2 +7f 1bc083 +8 39b35 +a9 cd716 +85 294a47 +80 264965 +c9 27803e +b2 d5fb8 +be 2695f0 +81 294a78 +8c 25b729 +0 3aa56 +64 1f4a39 +a0 25f79c +4b 1f0c6e +8 3abad +6c 1f4b90 +2d 1d7ffe +aa 106b2c +dc 27896d +b8 ce076 +b3 ceecb +7d 1ed450 +94 2953a7 +18 3a496 +6 19a5d8 +b9 ce077 +40 26213 +c7 2817df +93 2953d2 +9e 25c083 +9f 2954f8 +b0 cf1d3 +9 9a16 +94 d1e49 +e5 27c05d +96 2966c4 +c2 28022f +88 c925e +67 1bc571 +47 261dc +e6 f3797 +40 1e9773 +87 10aebe +2c 3ed21 +99 10c954 +83 29d07b +48 2636a +c3 ee2fe +cf 281936 +4d 1e298 +39 cfae +8 9a17 +e4 27c05e +c7 277f11 +14 8f9c +66 1b2fc0 +31 5dcb +cd 281681 +c1 ee049 +e1 27c08e +5b 1f155d +c7 2781cd +a5 298bee +c1 e5c89 +cd 2792c1 +db e6756 +82 29cdc0 +9c caea2 +27 1d9102 +97 10b813 +49 1e2c9 +46 24f17 +e0 27c08f +a1 298c1f +51 1b8d01 +ac 25f8d0 +12 4394a +9c d1fa0 +ed 27c1b4 +92 1031e3 +9e 29681b +24 c7e4 +ca 280386 +26 45c51 +39 cfbc +8 9a25 +7f 1bbd65 +4e 1b87ce +42 25196 +f3 2bf72a +af 298d3e +ec 27c1b5 +a3 d6b64 +36 4b40 +90 2642a6 +9b 10b8e3 +f7 2bf6eb +e 1936a3 +2 6b +bd 110b1c +52 26b6d +35 1a05e8 +4e 2506e +e8 27c1e6 +52 1b09b9 +18 1dea +a 1cbae0 +5e 1b0b93 +35 196fd6 +52 1d55b +bc ce0a5 +1d aea +f6 27cc74 +86 295fb1 +b7 299548 +c4 ef32b +b6 299549 +d7 278b2e +92 29d721 +37 1d9a63 +f 1d3dfc +c0 279458 +f1 27c9ef +b2 29957a +80 295fe9 +87 d2a52 +b1 299580 +bc 260231 +5e 1b912f +52 25af7 +d 19b9f9 +1 83c1 +3d 19712d +5a 1d6b2 +fe 27cdcb +64 1bc5cb +6f 63c08 +d5 2b1f9b +8a 296139 +bb 2996d0 +2 12cb +66 1bb2ae +69 1b4340 +4c 1b7511 +41 1f0860 +21 d7c8 +6 12fc +e8 27c1d6 +5e 1b8e8f +52 25857 +50 1b9002 +5b 6063f +28 19655c +ad d6cf3 +e 1453 +1b 437f2 +40 1de55 +b9 29a99b +1f 43823 +60 1b2f96 +e5 f372d +4e 1dfe4 +4d 265da +86 ca3f3 +12 3b66a +20 3ebed +8e ca54a +0 2 +c 19363a +48 579ea +da 279c67 +56 1b7a76 +69 294ee +79 1b4caf +9f 10c91a +c4 e6f7d +89 295e91 +40 1af02d +f9 12d62a +ee 2bdc8e +b9 110a99 +96 103222 +5 193552 +e1 eb100 +52 1af995 +1 19225f +c1 278187 +53 1af996 +f8 124017 +ca 2b2cc8 +a1 ce56f +12 192e04 +6e 1bb413 +81 25b5f6 +a 1cbad2 +8f 10c269 +92 10c793 +af d6a40 +f0 eba52 +37 1d9a6f +92 29d72d +60 1b31d6 +e4 124854 +c7 120707 +16 1d591d +65 1bb2b6 +71 1b3b36 +8c 263a77 +70 1b3b37 +d4 281e7a +eb 123712 +80 29cdc7 +25 1d9109 +18 427e6 +6a 1ec80a +ef 12cfa1 +73 1b3b3d +2a 1cfc79 +33 196fac +11 1cc52e +80 25c8b9 +b1 25fe50 +b0 d721f +f9 ea8f8 +c0 1289ca +41 1af03c +7f 1b3cc7 +36 1a05ee +95 d1de8 +b0 1085f1 +38 d2bf +7 19a823 +71 1b4bac +54 1b7d7d +2b 1d1194 +51 1af99d +e3 f2495 +ef 285acd +cf ef738 +c8 2b2ccf +6 1d4fca +1c 19c607 +de 1281e8 +10 8fcf +33 1d06ce +64 2a389 +c4 278155 +60 2a3ba +c3 27818e +44 1f0890 +eb 2b6e80 +84 25b5c4 +78 217f0 +ff 27cdbc +36 1d1784 +85 25b5c5 +22 1d0df2 +7a 217f7 +b9 269865 +20 3d939 +2c 1d0f71 +66 5a494 +d3 278aef +9e d31f9 +e 1cbdb1 +94 d20e9 +e5 27c2fd +1c 1d47b9 +a1 267aeb +eb 12cd24 +e4 27c2fe +df e7aab +9a d322a +53 5efcc +5f 1f2604 +70 63119 +7c 1f6751 +e1 27c32e +c9 2bb020 +5c 1e8ffc +e0 27c32f +d4 2820d4 +eb 12396c +c0 e5c96 +cc 2792ce +e2 27c336 +ef 1249a5 +a4 25f76d +32 196fab +ca 2b1768 +f5 27cc5e +23 3eea5 +8a 10af83 +de f0036 +d3 129385 +f0 12d4d2 +bc ce097 +f6 27cc66 +4f 1f097f +b9 110d45 +39 1d88c0 +4 1cb94f +a5 25f530 +f 1d409c +f1 27cc8f +e 1d409d +f0 27cc90 +fb 1242cd +f2 27cc97 +b6 2600d5 +78 5af20 +4a 1e9bd1 +ff 2b64ec +60 2205c +e7 27d628 +a 1d406c +67 2a391 +1 1d3ccf +29 1977af +c 19a980 +f3 284fe6 +52 1f1405 +b3 260105 +4d 265e8 +3 1d3cd6 +44 251cc +61 21ffb +b2 260106 +38 1d0571 +e5 1238a3 +3b 46739 +a 431a2 +bf 10fabd +c5 278164 +88 ca574 +ec 284557 +48 26306 +1a 1d49cd +77 2acf2 +c1 278195 +af 29a2bc +a3 106c84 +d5 278ac5 +ca ee3f0 +4e 1b04e0 +42 1cea8 +6e 1bb715 +4 15af +39 e520 +2 19a543 +b4 d5fe2 +a1 298ecd +4a 1aeedd +fc ea97c +a8 25f645 +e5 27c30b +96 296972 +e4 27c30c +4c 1b022b +40 1cbf3 +c7 2781bf +e2 ea148 +ee 27d780 +e1 27c33c +46 251c5 +e0 27c33d +52 1b0c67 +18 2098 +5e 1b0e41 +a 1cbd8e +52 1d809 +5a 1e920 +ef 2bf1ff +e3 12bbc7 +5c 1b0e48 +2a 3f007 +50 1d810 +9f 25bdd6 +0 1ccee2 +c0 ef308 +92 29d9cf +37 1d9d11 +78 22b16 +b3 299827 +4d 5fd0a +e0 eb161 +b2 299828 +b1 29982e +1a 42593 +6b 1ec7a7 +d 19a973 +83 29e32f +16 1cc30b +eb 28452e +4a 1f094d +81 ca3c8 +e5 2843ab +ef 28455f +4e 1f097e +85 ca3f9 +89 ca51f +ed 284502 +92 10b531 +9e 29eb69 +68 1b4341 +3b 1d8bd5 +37 1d9ad1 +60 1b3238 +6a 1b4348 +15 1d58c3 +b7 10767a +1 1ccbe1 +d 1936a9 +1 71 +5e 1b0ddf +a 1cbd2c +52 1d7a7 +10 9fe3 +3 43058 +31 1d8a85 +18 a13a +27 1d0e22 +69 1b434e +46 1b7115 +bf 10fb0f +53 1afa06 +1e 1d5a74 +12 4243c +db efdca +63 1ec650 +29 1d0edf +6 1d3ca6 +31 4b17 +0 1580 +3d 19814f +98 25be0d +cb 1278b9 +e8 12ba06 +46 1b0329 +77 1b38c0 +13 1cc597 +ef 2b5931 +9d 10b90d +9f 10b914 +94 2642d7 +c4 e5f77 +3d 1d0613 +81 25c60a +9b 10b945 +b2 cf178 +35 1a064a +65 222ea +3f 1d061a +2 42dab +80 c90f7 +8c 25c72f +3 42dac +81 c90f8 +8d 25c730 +cc e60ce +78 21852 +ff 27ce1e +83 c90ff +8f 25c737 +c5 279426 +88 25c760 +89 25c761 +c4 279169 +69 62910 +1f 1d5d2f +13 426f7 +ee ea27c +ef 2b6c03 +e3 1235cb +c5 27916a +82 103e02 +bf 29a9d1 +b3 107399 +21 577c +84 29e04a +58 1f2889 +a2 29a197 +c6 279170 +6b 62917 +ad 2a1095 +c4 ef579 +f5 f2b10 +e4 28590e +6a 1ec7b4 +6c 2921e +9b 10c8f7 +46 5659b +c0 e6f5a +a1 2a24df +34 1d04bb +c0 27919a +6d 1f4b2f +c1 27919b +c 9a46 +1 42d95 +c3 2791a2 +c0 e5c88 +cc 2792c0 +c2 e5c8f +45 1b7161 +ce 2792c7 +11 9d2 +1d 19400a +c3 e5c90 +cf 2792c8 +2 3aca7 +c8 2792f1 +86 103b15 +5a 58354 +cf ee174 +cb 2792f9 +57 26df7 +a8 cd459 +e5 ea11f +96 104786 +d 19b995 +1 835d +e9 eb259 +84 263982 +a1 2607b1 +48 1e00e +83 294d1f +4e 1b756e +19 a379 +a4 d6b9d +ed ea276 +9e 1048dd +a3 cd2a6 +af 2608de +ca 2782f2 +94 d30fd +e5 27d311 +3e 5f5b +e4 27d312 +b7 10f70a +86 10c173 +96 d3104 +e8 e9fea +e7 27d318 +48 1af1e8 +4d 1b8526 +41 24eee +96 29ec6e +15 1cd813 +b4 26841c +e0 27d343 +eb 124980 +a5 107fd2 +90 d3326 +e2 27d34a +49 1b7233 +a7 107fd9 +9e d325b +e3 e9e37 +ef 27d46f +c4 279177 +c5 279178 +c0 2791a8 +44 251be +7d 1ed3ee +ae cd48f +c1 2791a9 +aa ce9d0 +86 295d01 +5a 1ea540 +a8 ce9d7 +61 5a779 +6d 1eddb1 +82 295d32 +5a 56de4 +3d 1d085f +83 295d33 +87 d27a2 +80 295d39 +a7 268b37 +c8 2792ff +81 295d3a +c9 279300 +1c 19c5f9 +10 8fc1 +99 c9e5e +83 102821 +8f 295e59 +8b 295e8a +88 ca7ce +b9 cdd65 +42 261b8 +bc 2a19e7 +e4 27d320 +c7 2791d3 +a8 106b7b +d 1d50b7 +1 41a7f +a6 299eaa +7a 1ee6e9 +12 1cc58a +40 1dec3 +5b 1f281f +c7 27948f +a5 299eb0 +aa cd6ac +2d 19eb7e +db e7a18 +c0 2b18b8 +e2 124b2a +c7 ee321 +e0 e9e3f +ec 27d477 +c3 e5cf2 +cf 27932a +b9 ce005 +d5 278809 +a3 1069c8 +af 29a000 +46 1cbcb +b 1cbadf +85 264c39 +c 145a +52 57edf +4d 1af1b6 +4 19badd +24 54aa +44 1e8542 +94 1034cb +f8 f2f55 +59 2599a +57 6026b +2c 5601 +72 5c086 +a7 cd2d9 +44 1de94 +fb 27e0bf +5a 1ea4de +87 294cfc +4c 1dfeb +84 d29e8 +b5 d5f7f +2a 1cfc23 +80 ca3c9 +45 1e97a3 +c7 28025f +e4 2843ac +84 ca3fa +5a 1e912 +ef 2bf1f1 +3a 1d1af4 +e3 12bbb9 +2a 3eff9 +5c 1b0e3a +8 1cbd87 +50 1d802 +8c d2b3f +bd d60d6 +a0 d589c +ac 268ed4 +70 22969 +f7 27df35 +84 25b5c6 +74 2299a +8b 294bd6 +8c ca551 +d2 120fd6 +a4 ce5a1 +1a a37f +c4 2b1639 +d6 e7bf3 +8e 296178 +bf 29970f +82 102b40 +27 3ee82 +d7 129362 +41 1f0aac +c2 e7261 +ab 298d7d +ac ce6f8 +8f ca5ab +f2 12517d +cc 2b1790 +cc ef6d0 +fd f2c67 +e0 f242d +ec 285a65 +99 10b93e +80 25c609 +c8 e70b1 +7 3aa1d +a6 107fd8 +1 19375f +30 4b78 +3c 1981b0 +d4 1212ba +b4 cf204 +2 193765 +41 1b02f0 +ab cd3ff +f4 ebd95 +42 1b02f6 +17 1cc2b8 +bf 1074b3 +10 a283 +61 1b4497 +5e 1b7c21 +c6 127792 +12 a28a +63 1b449e +41 1b02fe +fc eac38 +4a 1af199 +14 9fa4 +1 1cce8f +97 29564f +5c 1e93e +de 1294aa +10 a291 +84 25c886 +7d 2b0e0 +29 4602d +fd f2f15 +3c 1d08c0 +80 25c8b7 +43 1b7385 +9 87b6 +c0 127a16 +cc 2bb04e +19 193f77 +e9 eb507 +a1 260a5f +10 3a0f5 +1c 1cd72d +fe eac2f +b0 261113 +c4 279417 +69 62bbe +c0 279448 +c1 279449 +c0 279456 +0 1921fe +22 5470 +c1 279457 +e1 27d5f0 +de eedd8 +5c 5831c +e1 27d5fe +e8 27d756 +4e 265de +43 5f92d +60 63a7a +61 63a7b +8 84a7 +65 222e8 +62 1b41f1 +28 5622 +a3 cd5b6 +af 260bee +6b 21147 +22 da6e +9f 296a76 +93 10343e +b5 108677 +42 1f0b06 +8 1923b7 +2a 5629 +79 1f6783 +43 26459 +ea 2b590f +c2 2bac1f +40 1aedf1 +62 22063 +63 22064 +ad 267c21 +ca ee1a6 +6f 2a4e8 +68 221b3 +e3 ea147 +ef 27d77f +62 2a5ff +df 2b3607 +d3 11ffcf +48 1aef48 +6a 221ba +45 1aedbf +b8 261268 +0 19a53e +73 1bcead +8c d18df +a3 29a188 +13 43949 +6d 1bc6bf +61 29087 +ff 2b64e0 +b6 2a2e07 +4 19b83d +4d 1aef16 +65 1b44d6 +8 19a695 +7b 1bd004 +de e7a48 +f 3ab74 +ae 10812f +d4 e6938 +10 19ae9f +9 97cc +76 1bcf41 +d5 129609 +57 1af727 +72 1ee522 +38 3f953 +c2 11f421 +a5 298e9c +ce 2b2a59 +12 19aea6 +80 102827 +8c 295e5f +60 1b2f34 +68 1b308b +48 1b7226 +19 193fd9 +a6 107fcc +4a 1b722d +71 22c1a +65 1edc5a +8 1d3e19 +7b 1f6788 +11 19b14c +88 29e47e +70 1b3895 +78 1b39ec +41 1b70dc +6a 5a866 +a4 ce84d +c2 120675 +31 19802b +40 1b70dd +8b d1852 +d6 1293c3 +2f c685 +28 1cfc1c +48 1b7234 +b4 d622e +15 3b625 +f6 27c9b8 +bc cdde9 +23 1d103f +37 ce8f +6 98f8 +30 1d0426 +59 1b7b94 +3f cfe6 +e 9a4f +38 1d057d +54 1b8d21 +6b 5a5b9 +0 1d3c6e +99 d220e +a9 268f06 +8 1d5325 +3c 196ee2 +a4 106a53 +50 25840 +5c 1b8e78 +8 1d3dc5 +b7 cf20c +16 3b62b +69 1edad4 +f0 286000 +ac 25f614 +12 1d45c8 +4b 57982 +11 1d45ce +2b 1d8276 +9b 10c94b +b8 110a98 +c0 e6fae +7 1d4fcb +f8 ea8f7 +fb 27cb3f +ca 2795a8 +f5 27dc74 +a6 10f004 +ae 106bbf +d6 280b6b +b6 107607 +18 1d4726 +4 19a87f +be 25ff7c +70 646eb +2c 3dcff +99 10b932 +9a d21b2 +c9 ef702 +58 5f12b +e4 27c04e +21 3eeac +dc f003d +d1 12938c +e6 27c055 +e9 285a97 +7c 1b3a73 +23 3eeb3 +de f0044 +d3 129393 +ba 25ffad +50 1b7cdc +d6 efc41 +46 1e87f9 +e0 27c07f +b5 cdc93 +84 ca6fc +4f 1e9ba1 +43 56569 +fa 2b6260 +fd ebbdb +a9 106b28 +43 1de69 +73 1f686f +82 26369e +63 63d90 +2e 1d11c4 +54 1af9cd +22 3db8c +10 1d48dd +8c 2637bd +c8 127b6d +6d 63eaf +69 2924e +f4 27c9af +1 3aca1 +6b 1f4e07 +95 263fc6 +7 19b89b +4e 1e894e +19 3b759 +9b d2215 +b8 d6362 +7d 1f573c +af 2679ce +e 1d3ded +f0 27c9e0 +8f 10ad67 +a1 d58a9 +ca ef466 +ad 268ee1 +93 263ffe +11 1cd542 +78 22ab4 +1a 1d478f +92 263fff +73 646f1 +9d 26411d +3a 1d1b56 +bd 2695ea +10 423d1 +b1 d5fb2 +1c 1d5a09 +1e 1d5a20 +12 423e8 +9c 26411e +d8 1284ce +8c d163f +7d 64810 +3e 1d1b25 +e7 12bbea +32 3e4ed +f8 27cb37 +16 1c5d +d 41ea7 +25 1d10cb +d5 2bb5ab +9c 10b650 +7f 1b4f89 +73 21951 +b0 2a18cf +ea 124c83 +c1 2814f9 +fa 27cb3e +99 26414e +c3 1289c4 +f5 27df90 +7 41d65 +98 26414f +a5 110002 +79 64841 +9b 264155 +19 1cd699 +c5 2801f6 +f4 124201 +e3 27c343 +8 9a15 +e4 27c05c +c4 2801f7 +9 1ba +e2 27c344 +c7 2801fd +c6 e6f84 +8b 295e98 +e0 27c08d +f7 2bf999 +2 319 +3f 196ee8 +bd 110dca +e 193951 +8f 10bfc7 +84 26498a +85 10aeb7 +38 1d1b4f +1d 19b346 +c3 28022e +e7 f3796 +41 1e9772 +cd 28034d +cc 28034e +3 98c8 +40 1f0aad +ce 280355 +f5 ebd42 +c4 e5fcb +ce e70db +e8 27c1e4 +4a 1aef3f +cf ef6d6 +e1 12cb72 +b5 ceef5 +25 c7e3 +7a 1f6a29 +cb 280385 +60 2205e +46 1e9a49 +e7 27d62a +20 5779 +e 1d3dfb +f0 27c9ee +89 d18ad +2c 196581 +28 3ed54 +50 1b8d00 +f7 ebd9b +a3 106ce8 +af 29a320 +fc 27cb14 +ab 10ee6d +dd 280cae +4a 1cd53 +2d 1967ce +b 42ef5 +df 280cb5 +2f 1967d5 +1f 1d5ccd +13 42695 +d9 280cdf +29 1967ff +35 d144 +db 280ce6 +2b 196806 +ff f424e +59 1ea22a +cc ee41c +e9 eb24b +a6 298be6 +a2 298c17 +38 e271 +b4 29954e +8c 294b9d +40 5fbe1 +df eead5 +c2 ee29b +ff 284e6a +ce 2818d3 +f 1cd010 +3 399d8 +34 4de7 +d9 280ced +d4 280b56 +29 19680d +ba 2996cf +48 1b7542 +40 261af +12 1d4876 +43 5f92f +22 54d4 +c7 128d03 +8 1cbd15 +a9 25f8f6 +28 5624 +a3 cd5b8 +e 1cd00f +2 399d7 +af 260bf0 +2a 562b +cf 128e5a +87 29cd8e +5b 1f15cd +62 22065 +a3 298ec6 +68 221b5 +5a 1f15ce +6a 221bc +8 19366b +bd 25ff86 +8e 263a70 +97 29d6ef +48 1b01a6 +9e 10b967 +5a 1b7bf0 +8f 296115 +83 102add +a5 107d16 +31 4b6d +3d 1981a5 +93 29d720 +9e 2643d1 +9c 2643d8 +35 1d9a6a +9f 29d846 +f9 eabfa +b0 d7521 +a5 cd2e0 +67 20d81 +70 2af59 +30 196fa4 +7a 5c1dd +98 29d87f +31 46589 +3d 1d9bc1 +1 19a7eb +45 1af06d +b8 261516 +0 19a7ec +2d 47062 +73 1bd15b +97 10b571 +2f 1d11c5 +23 3db8d +55 1af9ce +4a 252f9 +c0 11f6c8 +cc 2b2d00 +10 19b14d +7b 1b4fba +98 10b681 +3d 479c3 +67 1edc61 +a 1d3e20 +31 3f80d +13 19b153 +8a 29e485 +57 1af9d5 +10 1cd5a3 +37 1a03a1 +4a 1f09b1 +89 29e1df +71 5c39e +19 a0c9 +4e 1b72be +70 1b3b43 +41 1b738a +e6 f2465 +47 5785c +ab 2a1379 +ee ea020 +29 dbcb +40 1b738b +c8 1278bf +6d 63c01 +b4 cdf40 +3b 1d1b59 +32 1d06cd +9f 10463c +a1 298ebf +e 1d3e4f +37 d13d +30 1d06d4 +77 1ed044 +46 1e9aad +e7 27c056 +a6 10eff6 +4c 1b022d +40 1cbf5 +f6 f2dc6 +57 581bd +4 41aaf +a5 d5690 +bb 2a1cda +fe ea981 +39 e52c +13 1d4875 +20 1d1027 +5 19a81e +d3 280e4b +95 d309a +5 1cbc52 +bc 1087dd +a 1ccd3e +d 1923d9 +b6 2600d3 +45 5f905 +25 1cfaf9 +e4 27c2fc +e3 27c335 +cb 2bb027 +5e 1e9003 +b9 2a2cd9 +f0 f40b2 +2f 1cfef7 +fa 125336 +51 26b65 +9 1d50ea +e6 27c303 +71 2acac +b5 cdeed +de e7aaa +ba 2a1a1f +d6 efeef +e0 27c32d +84 263914 +f7 286283 +86 26391b +26 41df +9f 26544a +93 d1e12 +75 1f5339 +80 263945 +8b 10af82 +e7 2bed8a +ad 1101bb +42 2620c +25 19fc87 +f3 2862b4 +96 103216 +7d 1b4ce0 +71 216a8 +40 1e111 +af 267c7c +e 1d409b +f0 27cc8e +f2 27cc95 +94 264275 +c4 2804a5 +96 26427c +86 294ab1 +93 2642ac +11 1cd7f0 +92 2642ad +37 1a05ef +e4 27c30a +2c 47071 +96 295412 +54 25881 +2e 47078 +d3 280e3d +82 ca422 +51 1ea381 +e9 eb4f9 +d3 eec4f +df 282287 +8b 29d1d4 +66 1f4a32 +31 4783d +2f 1d1219 +23 3dbe1 +55 1afa22 +4a 2534d +5a 1b0b0e +a2 298ec5 +38 e51f +b7 10735a +86 103dc3 +cf ee422 +b6 2997f5 +b4 2997fc +df eed83 +b7 d6296 +11 1cc272 +b0 29982d +f7 eba8b +af 29a010 +a3 1069d8 +59 5f0ca +86 29d03d +c4 e5d1d +da 2b2367 +49 265b5 +81 29d074 +dc eeb23 +4d 265d8 +21 54cc +f7 2be427 +c6 2bae90 +8c 10c2c1 +e 1923df +a5 110012 +bb 108750 +1 43053 +f4 27dce1 +6 41ab6 +1 9673 +bf 29a971 +b3 107339 +62 1f4a01 +c4 ef2d7 +96 29d99e +95 29d9a4 +2c 19fdeb +20 c7b3 +97 10b565 +41 26460 +49 1e01b +bf 2685dd +1e 1d49fc +d6 ee9df +ad 110167 +3a 3e398 +c5 ef5da +58 26cbd +93 29d9ce +64 1b427b +2f 3ed1d +f4 eaae1 +42 1af042 +91 29d9d5 +31 5e2d +a3 110294 +d5 2820d5 +36 3e270 +b5 110973 +45 1b0081 +41 1cba0 +4d 1b01d8 +8 19b957 +c1 ef5a9 +c 1cbb0a +94 10c7cb +15 192e3d +80 102b37 +8c 29616f +45 251cd +27 d7f0 +5 19a57e +20 1d0d87 +68 2215f +3a 1d0826 +b1 29aaf2 +17 4397a +60 1b41f6 +41 1b839e +49 1b84f5 +cb ef405 +93 2966a2 +64 1b2fc9 +c4 278165 +e6 eb3d7 +56 1b7d86 +73 1b4bb5 +48 1b84f6 +43 1f07f7 +9 1d5086 +62 222bf +e5 f21af +8b 10bf96 +24 1cfb5a +bd 10fab4 +73 1f52ff +42 1f1d68 +8 43199 +9a 265416 +87 d2a50 +80 295fe7 +a7 268de5 +e4 27d310 +b9 10fae5 +4b 1f1c82 +a0 2607b0 +e0 27d341 +1b 1cd702 +28 4608e +56 1e7ee +44 1b0384 +75 1b391b +e2 e9e36 +65 1bb308 +ee 27d46e +0 15e2 +ce 1207fb +31 4b79 +3d 1981b1 +a5 29a160 +c2 1206e5 +46 1b038b +77 1b3922 +d5 1212bb +25 19e777 +a2 cd2a5 +ae 2608dd +89 10c293 +7f 21839 +f2 2b63b7 +51 1b8d63 +d 192377 +ac 25f932 +de f009a +f4 ebaf3 +30 3e4d8 +3c 1d1b10 +5e 1d68f +cb ef6b3 +e0 124acf +c5 ee2c6 +34 4df7 +fb f3f6f +2d d942 +84 d27a8 +cd e5e81 +18 b1a +52 1af6e9 +c5 2814b8 +c9 ef400 +81 264958 +c8 ee191 +6d 2a4d3 +c7 2814bf +82 264960 +ce 128bbb +a5 10effe +75 22c3d +81 d1446 +8d 264a7e +2 42d49 +8c d28f1 +c6 2814c0 +27 41e2 +e8 124c26 +cd ee41d +77 22c44 +83 d144d +8f 264a85 +3c 5f62 +85 10c179 +88 264ab0 +c9 ee44e +8a 264ab7 +ad 10f155 +73 1bbefb +2e 1cff02 +f9 ebe5a +9b 29db35 +a0 d590c +d 19b989 +ac 268f44 +1 8351 +c1 edfd7 +cd 28160f +53 26b7a +e 3ab81 +3 42ff6 +c9 281640 +33 4658e +3f 1d9bc6 +2 42ff7 +a 3abb2 +b4 25fe90 +c8 281641 +29 4363 +4 1924cf +cb 281647 +e4 27d31e +e4 123840 +c7 11f6f3 +6e 22437 +4c 1af1c5 +a6 299ea8 +5f 1b7ede +7a 1ee6e7 +c6 2801fe +8c d162f +a4 299eaf +2c 19eb7d +44 1b73ae +ae 25f67f +4c 1aef69 +b8 ce004 +c3 e6f52 +6 1ccf1c +37 1d04b3 +c5 2814c6 +cc 2b19de +ee 124c50 +c0 edfe6 +cc 28161e +a0 298ebe +2d 4340 +34 3e519 +fb 12d691 +34 196d29 +87 29e050 +5b 1f288f +9c 26568e +90 d2056 +82 102b32 +8e 29616a +bf 299701 +af 1080ce +d5 e68d7 +23 5783 +86 29e051 +5a 1f2890 +b5 d5fe1 +84 d2a4a +a 398f0 +1c a3b7 +6e 1b43db +62 20da3 +28 5632 +4f 1aef7f +cd 128e61 +ea 124c1f +cf ee416 +a0 298ec0 +ef 1249a3 +21 578a +84 29e058 +83 29e081 +82 d16fa +8e 264d32 +60 2206c +a3 298ed4 +68 221c3 +80 ca42b +e4 28440e +98 10b8dd +ea 2b5901 +b1 299820 +f7 284d07 +8c d2ba1 +bd d6138 +a0 d58fe +ac 268f36 +88 ca582 +41 56324 +4d 1e995c +ec 284565 +ad 2a10f7 +c4 ef5db +f5 f2b72 +6a 1ec816 +ef 12cfad +e4 285970 +9b 10c959 +cc ef732 +fd f2cc9 +e0 f248f +ec 285ac7 +c8 e7113 +99 d21ac +75 1b3b77 +6a 294a2 +73 5c397 +8b 29e1d8 +be 1074b2 +20 197905 +d8 2bb6d0 +22 19790c +9f 10b65a +1 19baad +47 1af074 +ba 26151d +52 1b7aa5 +8 193669 +8c 25c9ed +80 c93b5 +bd 25ff84 +45 1b032f +74 2299c +0 992e +49 1d007 +7 19a825 +22 1d102e +27 da9e +5 19a82c +20 1d1035 +4b 1f1f30 +a0 260a5e +4 19b89f +4d 1aef78 +56 25b36 +73 22965 +9c 25d092 +90 c9a5a +53 1b7a36 +ca 2bad68 +e4 27d5be +86 295d73 +6e 20ed7 +e4 27d5cc +a4 29a15d +87 26397e +a2 29a187 +6 1d4fc8 +4f 1e86a1 +73 5c095 +8e 10afb4 +ab 107de3 +66 22040 +8f c9287 +d3 efc73 +38 4cc1 +72 1b3890 +46 1b73b3 +6e 1b4387 +62 20d4f +25 45f05 +4b 1aef40 +2 19b867 +cb 2b9aa7 +ab d6a0f +6b 62bb5 +66 20d80 +de e7a9e +b5 cdee1 +21 1d90ca +6f 62be6 +b7 2a1b46 +e5 f347f +35 46866 +5b 1af8a1 +12 19c1c8 +db 2ba408 +2b 1cff28 +bb d7370 +c3 edfde +cf 281616 +30 5dd8 +e5 f34d5 +fb 2bfb1f +cb e5e57 +6a 1edada +43 1b8397 +f5 f3e36 +db e67b8 +fb 27cb4d +ca 2795b6 +e6 e9e77 +2a 1cfec5 +f6 ea7d8 +eb eb260 +cf 2795e6 +c3 e5fae +21 3d93c +2d 1d0f74 +72 5b072 +7e 1ee6aa +8c 25c9eb +80 c93b3 +32 e121 +7b 217fa +a8 25f8f3 +a9 10f122 +99 10b682 +11 19b14e +ad 106b59 +91 d3317 +c6 28050c +ea ea2ad +2 9619 +47 1de9a +5 1ccc12 +87 2636ce +a4 26781b +3f 1d9ed6 +33 4689e +46 1b8675 +62 22011 +78 1b3a42 +fd f41d9 +66 22042 +19 d75 +22 1976c0 +6c 1b4690 +60 21058 +6a 22168 +60 2231a +6 1d4f66 +4f 1e863f +f3 f40c6 +25 da99 +cf e70e8 +41 1b738c +63 2a5fe +a4 ce5f5 +47 1e8796 +9f 264436 +ba 29ac3f +8 3adf7 +a9 ce9d8 +85 295d09 +b7 10f9c6 +e6 eb139 +6f 1bb716 +5 15b0 +24 5758 +d4 efc38 +44 1e87f0 +f3 12d4ca +a 1482 +4e 1aef1c +cc 128dfe +59 1f25dc +a3 299eea +95 1044d0 +47 1e148 +a4 ce84f +87 ca702 +c4 2b18e7 +e6 eb3e7 +75 5c36d +81 10ab76 +8d 29e1ae +28 197acc +62 1b323d +c 19b996 +0 835e +8 84b5 +39 61d0 +6e 1b33c5 +72 219be +7e 1b4ff6 +3a 1d8bd6 +85 10c16d +12 19af06 +5 193790 +6c 5a5f0 +c9 ee1a0 +16 19af37 +30 47af8 +79 5b1d1 +5e 259df +a2 107c97 +d4 279ad8 +25 1d9169 +8 398eb +7 1ccc19 +bb cf084 +1a 3b4a3 +9c d1f3e +98 d1f6f +da 11fe77 +5d 1f1349 +a7 298c57 +3d e2b1 +0 3ad02 +10 3b663 +8c ca543 +52 1ea389 +18 3b7ba +de eead6 +a5 2a2260 +78 22b14 +bd 268582 +da eeb07 +7f 2ae49 +21 546c +4 863d +6b 1b43b7 +40 1f07ff +6 1d4f68 +cb ef407 +64 5a747 +f6 27c9c4 +dd 2bb9b2 +d1 12837a +bd cdd88 +8c ca7f1 +de e67e8 +b9 cddb9 +88 ca822 +ec 284805 +77 2afa0 +62 1b44ff +0 9620 +8 9777 +46 261e9 +4f 1aef71 +b9 cf337 +6 19b898 +4a 5ea0d +eb f25ee +69 5bb32 +4e 26340 +c6 ee010 +cc ee41a +e9 eb249 +8c d289f +d2 129324 +3d 4f41 +60 1ed91a +67 2a383 +40 57585 +88 d28d0 +55 1af782 +23 3d941 +2f 1d0f79 +f 1ccd70 +3 39738 +9d d3255 +7 39769 +b1 d7522 +10 43941 +1f 1cd6d1 +13 3a099 +7e 22aea +5c 1af878 +2a 3da37 +5 19b892 +82 ca3c0 +87 102860 +12 192b56 +90 10ca38 +d7 efee0 +8f 29e465 +83 10ae2d +a5 110066 +21 3ee3a +2d 19652e +9 19b9ba +f0 27c9f0 +56 25878 +e 1d3dfd +9f 29edc6 +93 10b78e +b6 107369 +80 d276b +7a 1b3a55 +c5 e6fec +1 399d1 +d 1cd009 +ac 267c12 +98 25be01 +7 39a17 +d8 28225c +90 2652b8 +d9 278991 +1f 1cd97f +13 3a347 +9b 1045a9 +6c 20ed0 +24 196428 +1e 1cc472 +d3 eff11 +8b 29e496 +e8 27c1d8 +54 1af9cf +76 22c41 +2e 1d11c6 +22 3db8e +7b 22dd8 +de 2bb6a6 +d2 12806e +26 3dbbf +d6 12809f +36 3e520 +87 102b0e +ad ce6f7 +e7 e9e68 +8f 296177 +83 102b3f +fc ebeea +a8 106e37 +4e 1b01e0 +42 1cba8 +fd 12d659 +42 1e178 +bc 2999a7 +b6 107617 +7a 1b3d03 +c5 e729a +18 193f68 +3 3a9fa +67 1f49dd +7 3aa2b +2c 1d1221 +20 3dbe9 +dc 2bb701 +26 19793d +d0 1280c9 +75 6440b +5c 1b0b3a +50 1d502 +2a 3ecf9 +16 1cc2a9 +b7 25fe8a +87 103b22 +f4 27cc6b +20 3eeab +d0 12938b +85 102b69 +8f 103c79 +84 25c63c +43 1e16b +60 222b8 +16 3a0cb +83 10c0ef +1c 192fe7 +77 5adf4 +46 5785d +e5 e9ec3 +8b 103caa +1c 3b4dd +ef 2bdc8d +d4 279b3c +a2 107cfb +58 603ed +c9 2792f2 +3 3aca8 +67 1f4c8b +c6 128a54 +87 103dd0 +83 103e01 +14 3b634 +e7 2bdde4 +22 3dbee +54 1afa2f +2e 1d1226 +48 56728 +1 9675 +38 d01f +65 2101a +66 1b453e +2e 196524 +72 1bcf10 +32 3e54f +3e 1d1b87 +8d 2637bc +75 2197b +1a 4253f +e5 ea111 +f5 eaa72 +c8 28037f +bb cf020 +1 9923 +1e 1d5cce +12 42696 +2a 1cfc15 +6f 1b33d6 +81 d2a1a +b6 2610e7 +91 10ca9d +8e 102a18 +ac d6a46 +f2 12d4cb +cc 2b9ade +27 471d0 +9e 103379 +c 1d53c8 +3d 1d895f +ba 10748d +0 41d90 +a 42ea0 +f0 ebac4 +e 42ed1 +37 3e513 +85 29e0bb +82 10bea2 +e4 f2212 +b5 d74ef +8a 10bff9 +c4 2babe7 +61 22319 +e6 f3479 +d1 280b96 +ab 2a238d +cf 2792d6 +c3 e5c9e +df 279c37 +d3 e65ff +b7 10f966 +37 1d8abd +5 19222e +f8 eabf7 +3f 1d8c14 +f5 f2b1e +c4 ef587 +a8 2a10b7 +15 192b8f +80 102889 +8c 295ec1 +11 192bc0 +1d 192ce6 +89 d1859 +2c 19652d +30 196d68 +d5 2ba597 +66 1ed942 +b5 d74ff +18 dd8 +cf ef428 +c1 128cd9 +43 1aedf7 +d3 efc0f +43 1e87c7 +ba cf331 +b 42f01 +dd 129760 +5f 1af87e +9d 2656ef +91 d20b7 +d4 e693a +ae 108131 +94 103467 +fb ebe61 +e4 12ce50 +66 1b2f6e +e7 f3736 +89 ca7bf +ed 2847a2 +ad 2a23b9 +a1 10ed81 +f5 f3e34 +4f 1b72cf +6a 1edad8 +4c 1e8699 +f7 f4097 +db eed52 +fc 12d908 +7e 1b3a26 +e6 123597 +ee 2b6c00 +e2 1235c8 +ff f41ee +cd ef73f +42 1b7138 +8 19b9c7 +33 3f7a2 +a0 cd5be +ac 260bf6 +f2 2b767b +84 10be6a +5 1924dc +a0 2a120e +90 10c7fc +c9 ee190 +11 192e6e +b4 cef00 +b0 cef31 +75 1bced7 +d0 280b95 +e8 f2338 +fe 2b622f +30 197016 +43 1af0a5 +48 566c6 +a0 d6bce +1 9613 +e9 ea2a7 +f6 eba98 +66 1b321c +ee 123750 +76 1b3b7d +fe 1240b1 +6 1ccf2a +37 1d04c1 +c3 e6f60 +ec 27d787 +e0 ea14f +99 c9e6c +a2 2607b7 +58 1b8ea9 +37 1d9d7f +5 1934f0 +ea 27c49b +2 19b807 +48 1cd5a +47 1b0088 +56 1d538 +c3 ef55c +66 1b4230 +e5 284659 +a5 cd332 +a0 1069c0 +ac 299ff8 +a4 ce841 +37 1d076f +c3 e720e +e2 27c334 +ce 281625 +c2 edfed +88 ca512 +ec 2844f5 +de 281f86 +d2 ee94e +85 25c887 +da eeaa5 +a1 2a222f +6b 20ef9 +0 19a5ae +42 1b7146 +5a 1b7bfe +67 63ab1 +b9 cdd57 +88 ca7c0 +ec 2847a3 +42 1b73f4 +52 1b7d55 +e2 27d5f6 +46 1b83d5 +ce 281617 +c2 edfdf +61 1b424b +44 1b741c +42 24ef4 +4e 1b852c +1a 1cd9af +74 1b3bc8 +94 295657 +b6 1088c9 +1d 19c35a +11 8d22 +36 1d8810 +c2 ef2af +3e 1d8967 +cc 28192e +fd 284ec5 +c0 ee2f6 +65 2a638 +ca ef406 +1c 194255 +10 c1d +c5 ee31a +6b 221bb +0 19b870 +46 1b83d7 +42 1b8408 +88 ca580 +46 1b8683 +36 1d8abe +f3 f2af4 +ff 28612c +c2 ef55d +9d 29eb11 +91 10b4d9 +f3 2b63b8 +ad 25f933 +4c 1b8589 +40 24f51 +e2 2b6d26 +43 1e976b +c7 28051d +2e 196836 +de 280d16 +44 261e2 +fb 28640d +5a 1f282c +7 1cb957 +b1 269710 +10 1d5b2f +3 1cb988 +f 1cbaae +d0 efc69 +ab 1103dd +d1 eebe6 +dd 28221e +3e 3e3b9 +64 290c7 +93 10c7a0 +13 1cc2e9 +4 19a88d +21 1976bc +8f 29e463 +83 10ae2b +a5 110064 +26 1cfaff +61 1bb523 +c 19a9e4 +29 197813 +b4 1073c2 +e3 f36f5 +76 216d1 +2e 1cfc56 +69 1bb67a +6 19354c +b5 1109c5 +36 1d0460 +71 1bbe84 +0 19b862 +b 42e9f +da eed53 +45 1b00e3 +fd 27cb15 +cc 27957e +c0 e5f46 +79 1bbfdb +45 1b867f +cd 128bb3 +8 19b9b9 +41 1cc02 +4d 1b023a +7 1cbc05 +cd 2b1791 +84 29e0b8 +21 41b6 +2d 1977ee +88 25b4ac +67 1b2f5f +3 1cbc36 +22 44c0 +2e 197af8 +5e 58067 +e5 e9e61 +8b 103c48 +ef 2bdc2b +ce e6127 +80 25c60b +16 3a0c9 +83 10c0ed +26 1d0dc1 +61 1bc7e5 +50 1af752 +72 229c4 +2a 1d0f49 +31 19f2f5 +5a 1b8eb2 +7d 63550 +83 103d9f +e7 2bdd82 +9b 10459b +6c 20ec2 +fd 27e079 +f1 eaa41 +48 1e8916 +1 19b863 +16 1d4607 +63 1f4c4e +d8 e69ee +29 4607f +39 1d1840 +8a 264d65 +bb 2682fc +1e 1d475e +5 8630 +20 3ee39 +d6 efedf +bf 2a19fb +8e 29e464 +82 10ae2c +a4 110065 +9e 29edc5 +92 10b78d +20 c505 +2c 19fb3d +3 83b8 +f 19b9f0 +72 1f65c2 +8b 29e494 +1e 1cc470 +33 1d16e4 +16 1d48b5 +39 1d1aee +bb 2685aa +82 10be40 +76 1b3bdd +2 1d4f99 +20 1cfab9 +30 1d041a +32 46581 +3e 1d9bb9 +18 4259a +fd ebe8b +9f 29db66 +69 1ec7ae +16 1d58c9 +63 1f5f10 +d8 e7cb0 +29 47341 +bb 2695be +a8 cd707 +8 39b27 +a9 cd708 +85 294a39 +6e 1b33d5 +39 61e0 +d0 2bb57b +8c 294b8f +90 2953ca +35 1d170c +9c 2954f0 +9d 2954f1 +99 295522 +82 103af2 +5 1d4fc4 +b 19bc6d +56 1f26fa +7b 1b4fb8 +a5 298be0 +79 1ed41f +48 1cd4a +ec 27c463 +47 5e63a +e1 2b57b2 +f1 2bf723 +ad 298d37 +b4 299542 +18 1cc3d8 +3a 3f64a +1f 8e41 +b0 299573 +1b 8e72 +58 1d6ab +fc 27cdc4 +57 5ef9b +f1 2b6113 +88 296132 +b9 2996c9 +62 1b2f2d +15 194161 +cd ef483 +20 1966b3 +6a 5b8ec +37 4b41 +6 15aa +e8 27c484 +f1 ebd0f +e8 1249dc +7d 1b4ce2 +71 216aa +1b 43aa0 +b9 29ac49 +1f 43ad1 +68 1b434d +60 1b3244 +77 216d2 +2f 1cfc57 +46 1e13b +b 1cd04f +0 9612 +e8 ea2a6 +aa 26090e +40 1b863d +b7 cdc38 +86 ca6a1 +c5 2b15d8 +8f d28f9 +88 295e90 +c4 2b9c35 +11 192b5e +a3 d5656 +af 268c8e +86 c943f +6c 294be +9b 10cb97 +32 3e233 +3e 1d186b +c9 ee44c +9b 2967f9 +6c 1b3120 +41 1aed80 +e0 27c33b +3d 4c93 +aa 110130 +d0 ee939 +dc 281f71 +fa 27e0be +8d 264d9e +81 d1766 +7a 5af7b +34 1d06f9 +fb 2bf871 +f9 27cb38 +c8 2795a1 +fa eac00 +95 c9afa +d6 2b1f31 +10 9f73 +f8 eac07 +94 c9da7 +9d c9c51 +20 3d93b +2c 1d0f73 +32 60e1 +10 192e6f +de 2b2088 +bb cf022 +c2 ef30f +a5 268d8a +a0 1069d0 +f4 eba83 +ac 29a008 +83 102883 +8f 295ebb +ac 106baa +e6 2b5779 +94 10b56b +e5 2b577f +c2 2b9951 +dc e7a33 +88 102980 +67 1f5c93 +af d6aa2 +e 42ec1 +f0 ebab4 +a8 29a039 +e4 2bddde +31 196d07 +68 2949b +df 12824d +a6 cd5e8 +d4 12100c +e1 2b57b0 +ec 27c461 +2 3a9eb +a3 ce5cc +99 cb120 +ce 278315 +ca 2b9aa8 +f1 125495 +91 29d9c7 +21 3ee4a +d1 12932a +90 29d9c8 +58 1e97b +d3 e690f +df 279f47 +50 26dc0 +c 1cbafc +c6 2b2b42 +8c 103f73 +f7 2b60d9 +81 10c0e6 +14 3a0c2 +b5 cdca3 +ab 25f64d +bc 10750b +f6 2b60da +f2 2b610b +ac 25f686 +b6 cdf49 +c0 2b2b7a +c7 ef5e3 +f1 2b6111 +fc 27cdc2 +58 26f17 +a 1d3dbe +67 1edbff +c2 2b18bd +ce 2b2c99 +31 197017 +c2 11f661 +ff 2b6230 +89 10c23d +1c 3a219 +bd cddfa +22 3eeb0 +54 1b0cf1 +25 45c59 +fe 2b6231 +fa 2b6262 +7 1ccc7b +e1 124822 +69 294f0 +1d 1942b8 +11 c80 +5f 1b0b40 +53 1d508 +1a 43801 +1c 1d5d29 +10 426f1 +61 1ec905 +3c 1d0850 +16 1cd5db +6f 1b4388 +63 20d50 +1 860b +d 19bc43 +53 1f26c8 +47 1b73b4 +4 1d3f3f +77 1f68ae +d2 2ba56c +a2 cd307 +ae 26093f +71 1ed266 +4c 1b04d9 +40 1cea1 +4a 1dfb1 +19 427d7 +85 c9447 +c6 2b187e +c7 2b187f +c7 e5ccf +d0 efea7 +c5 2b1886 +65 290b8 +e0 e9e2f +ec 27d467 +cf 279338 +c3 e5d00 +d7 2b21e0 +d3 2b2211 +b3 cf179 +8d 25b78c +1b 1cc67e +f3 27c9f6 +c2 27945f +48 1e2c8 +96 10b812 +b9 108a4b +40 1b0051 +e7 2b5a26 +d8 eed3e +9d 29daed +54 1d531 +c1 ef555 +2e 3ed28 +9b 10c95b +c2 2b9bff +dc e7ce1 +88 102c2e +67 1f5f41 +e7 e9e78 +f0 f4050 +e3 2b5a57 +44 1e140 +79 2b0b1 +e2 2b5a58 +42 1b70d4 +f4 f2b73 +ee 12cfae +9a 10c95a +e1 2b5a5e +2b 1cfec6 +f7 2b6387 +a5 298c52 +f2 2b63b9 +ac 25f934 +45 251c1 +6e 5a897 +20 1d0d7b +4d 25318 +2d 197844 +21 420c +c0 2814ea +cb 128b27 +28 1d0ed2 +cc 28035c +43 24ef5 +4f 1b852d +1a 43855 +69 291ee +67 1ec691 +6f 1b43dc +1d a3b8 +63 20da4 +6a 1eda6a +18 4385c +69 1eda70 +46 1f0837 +18 1d7c +eb 28452c +80 295d2b +81 295d2c +80 c93a5 +8c 25c9dd +85 c918b +80 102819 +8c 295e51 +b5 cdc31 +84 ca69a +81 10281a +8d 295e52 +89 295e83 +15 19b1ed +a5 299ea2 +73 1ee2d7 +56 1f14a8 +79 1ee6e1 +a4 299ea3 +78 1ee6e2 +cf ef42a +a1 1069c1 +ad 299ff9 +7b 1ee42e +5e 1f15ff +a5 ce842 +35 4b48 +4 15b1 +3d 19713d +5a 1d6c2 +20 44c5 +2c 197afd +57 1b7d79 +72 1ee582 +63 5ba36 +75 216d9 +44 1e142 +b5 cdc3f +84 ca6a8 +ad 2a1343 +ca 1278c8 +f5 f2dbe +6c 294cc +9b 10cba5 +90 265568 +32 3e241 +3e 1d1879 +79 1bd29d +34 1d0769 +b0 2681ad +ea eb561 +95 10cadc +c6 2b2892 +c7 2b2893 +e8 eb568 +9 9a88 +f4 2b76b3 +28 5870 +d8 efd50 +7a 21867 +48 57988 +e9 eb569 +c5 2b289a +ca e6096 +4d 1b7568 +c2 2b28c3 +34 1d9d17 +ae cd491 +7d 1ed3f0 +86 ca701 +c3 2b28c4 +c7 ef333 +c0 2b28ca +e7 2856c8 +c1 2b28cb +c0 e5f44 +cc 27957c +f3 f3e18 +3b 1d9e35 +c5 e5d2a +51 1f2733 +d 1cbd47 +c0 11f3b8 +cc 2b29f0 +81 d2a28 +24 3ee78 +91 10caab +c9 2b2a22 +16 192e99 +4d 1f1c48 +41 5e610 +37 1d8a5d +ac 107e6c +e6 2b6a3b +94 10c82d +15 192e9f +cd ee1c1 +e5 2b6a41 +ea ea23d +6d 1bb70f +b7 d5fe8 +b0 29957f +86 d2a51 +cc ee1c2 +e4 2b6a42 +c7 2b28f5 +49 1f0955 +95 2642d6 +68 2a75d +df 12950f +a6 ce8aa +9e 10c97d +ed f2316 +f9 eab96 +21 197978 +e3 123559 +ef 2b6b91 +46 562eb +e7 e9ecc +f8 eab97 +3f 1d8bb4 +49 5e767 +e2 12355a +ee 2b6b92 +9c 10c984 +1d 192ff6 +e1 123560 +ed 2b6b98 +23 19e99b +34 46859 +6d 5a8a1 +24 471c8 +87 c93de +ca 2bad6a +14 19c1f4 +2b 3da8c +44 562f2 +e5 e9ed3 +13 1d5889 +fb 2b651d +e0 123561 +ec 2b6b99 +c3 11f414 +cf 2b2a4c +98 10cb9f +ea 2b6bc3 +39 4cc2 +8 172b +73 1b3891 +71 1b3898 +c4 2804b3 +69 63ee0 +34 196fe5 +fb 28615d +43 1e9a19 +10 439b3 +49 25347 +59 1b0b08 +61 1edbc7 +5e 1f1351 +63 22012 +47 1b8676 +ce 2b1727 +0 19250e +22 5780 +c6 2b2b40 +c7 e6f91 +c4 2b2b47 +b6 cdc99 +c2 2b2b71 +c3 e6fc2 +c7 ef5e1 +c0 2b2b78 +e7 285976 +d4 efc8e +91 10b777 +9d 29edaf +e7 eb13a +cc ee470 +21 19f9b4 +5c 1af876 +2a 3da35 +ed ea268 +a4 d6b8f +dc eeb31 +e1 2b6d20 +2b 1d1188 +20 dad7 +cc 2b9a7e +69 211b0 +0 1d3c60 +73 1f65cf +65 1edbf8 +8 1d3db7 +7b 1f6726 +dc 2ba3df +30 e438 +79 21b11 +a5 cd2d2 +3b 198179 +10 1d45c1 +c5 127a4a +7a 1ed107 +7f 29b77 +4e 265e0 +78 1ed10e +4b 1f094e +8c 294e4d +d4 e68c8 +ae 1080bf +1e 1d4a6e +3b 1d189d +85 264935 +4a 1f094f +71 5c33c +11 1d486e +ac 1080c6 +39 1d18a4 +6f 1b43ea +63 20db2 +14 1cd5d4 +45 1b73ad +6d 1b4381 +61 20d49 +12 3b3b0 +76 1f5393 +59 1f12b6 +9e 10c929 +b1 108648 +94 10b819 +e5 2b5a2d +a8 298d67 +b0 299571 +f5 2b638e +b8 2996c8 +58 1f15c7 +1b a3e2 +f7 2bf6f7 +80 29cdb9 +f3 2bf728 +25 1d90fb +e5 2b6d51 +88 29cf10 +34 1d0707 +fb 2bf87f +21 45c1a +2d 1d9252 +95 29d6e8 +78 22b24 +b3 299835 +f3 12d778 +a 1730 +85 29e04b +59 1f288a +a3 29a198 +91 29d719 +9c 2643ca +90 29d71a +35 1d9a5c +39 4f72 +9c 29d840 +ba 29998d +f5 2b76b2 +98 29d871 +a0 1069ce +ac 29a006 +e6 2b5777 +cd ee47d +af d6aa0 +8d 26382e +a8 29a037 +0 19376e +31 196d05 +78 2ae02 +8 19a705 +7a 1b39e5 +c5 e6f7c +27 1d7ea0 +d6 27880f +4b 250ae +dd eedde +f2 2b6109 +ac 25f684 +d5 e78dd +b6 cdf47 +b2 10f996 +13 423db +be 2a2fce +1f 1d5a13 +cc 2782ac +9 9a18 +15 993 +b4 cdf4e +11 423e2 +1d 1d5a1a +fc 2b6236 +bf cf051 +c7 2b991f +7b 2b0c6 +c3 2b9950 +ce 280601 +ac 29a318 +f4 ebd93 +a0 106ce0 +46 1b0089 +f5 12d502 +a5 106d04 +c0 2b9958 +65 1f5c9a +24 3dba8 +c9 2b9aae +a9 d6a16 +3 1cbbc6 +b5 107665 +4a 1e2cf +c5 2babea +95 d20f6 +e5 e9e71 +96 1044d8 +34 3e509 +23 19664b +d9 2ba40f +30 1d06c6 +e5 27c04f +53 1b8d6a +f 19237e +8d 10c260 +0 1d3f0e +73 1f687d +a 19b95c +bc d73fb +5f 1f159c +ae 1101d1 +d4 ee9da +1c 1d49f7 +4f 1b852f +43 24ef7 +7b 1f677a +53 1f1406 +d 19a981 +9e 10463b +96 10ca80 +cf ee414 +ed 2beee8 +e1 12b8b0 +84 263974 +5b 25cbf +84 29d036 +f7 2bf9a5 +94 29d997 +39 4697e +a5 cd5ee +e6 2b5a25 +e7 e9e76 +2b 1cfec4 +98 d21ab +cc 2b172e +20 5787 +f6 2b6386 +a5 298c50 +60 22318 +de eeb36 +a1 ce811 +e2 124828 +65 1f5cfa +c7 ee01f +f3 12c26a +c2 128cd3 +ff 2bf8a2 +c3 2b9bfe +36 4b42 +a3 d6b66 +2b 1d7f74 +7f 1bd027 +42 26458 +73 299ef +62 1bc7ed +61 2a65b +d3 2ba55f +8d 263ada +8 1cbac9 +b3 d74c7 +8f 1029b7 +1a 192cad +98 10cb8f +f7 27dce9 +84 25b37a +d1 2ba566 +1d a3b6 +6f 1b43da +63 20da2 +4b 1f1c10 +49 1f1c17 +4d 1b858a +ca e70b8 +41 24f52 +48 1f1c18 +50 604e0 +ad cd6d5 +f1 f40c1 +12 1cc27a +81 29e07a +1a 1cc3d1 +a4 2607ef +13 4370b +a0 107ce4 +85 d14db +9b 29db25 +88 29e1d2 +b0 26845b +11 19aea0 +45 1e9ab3 +45 1b7101 +e4 2846bc +ad 2a13a5 +f5 f2e20 +9b 10cc07 +d1 282108 +32 3e2a3 +8d 25b71c +3e 1d18db +79 1bd2ff +19 3a489 +4b 26370 +ce 2bad99 +c2 127761 +a5 2a11dc +40 1b713f +e5 e9ed1 +e0 12355f +ec 2b6b97 +32 3f803 +10 1cc591 +37 19f38f +74 1b38ba +c7 2babe1 +aa 1103de +dc 28221f +d0 eebe7 +ce 2b2cfb +c2 11f6c3 +ff 2b6292 +c5 2babe8 +3 8604 +f 19bc3c +28 3dd30 +62 1ec8ff +cc 28035a +7c 1b3a11 +1 19bb1f +c3 127700 +cf 2bad38 +e0 124883 +c5 ee07a +db 2ba6c4 +d 1d4097 +24 3ee6a +c9 2bad70 +10 1d487d +7b 5b1c8 +85 c93e5 +8e 102c58 +f6 27df8a +3c 3f930 +76 5b0a1 +19 3b49d +4e 1e8692 +b8 108a58 +9d d224f +c4 2bac49 +b8 cddba +77 1b3924 +43 1f1d67 +84 c918a +c6 277eb0 +8c c92e1 +94 d2087 +ad 110409 +2f 196527 +1a abf +6 1cb9aa +a0 cd5b0 +ac 260be8 +e 1cbb01 +52 26dc5 +d6 279ae1 +27 1d9172 +9c caf12 +46 1b7423 +63 1b4252 +11 a22e +f8 eab95 +3f 1d8bb2 +20 197977 +e2 123558 +ee 2b6b90 +e7 e9eca +b6 10868d +a9 2a2636 +3c 1d0612 +f1 f40b1 +f7 27dc89 +bd cf0ba +4a 1b7549 +e6 27d629 +8e ca548 +84 c9438 +9e caea9 +94 c9d99 +1a d6d +69 2a502 +6 1cbc58 +df e6779 +96 d30a0 +74 1f68a8 +29 19fe1b +ee ea022 +49 1f0c65 +29 dbcd +f7 27c9c5 +c6 27942e +8c ca85f +d2 1212e4 +c6 e5fd0 +6b 2a509 +49 1b7297 +e7 27d5d6 +ad cea07 +f3 12548c +e7 ea178 +5e 1d931 +25 1d10bb +a5 ce5f4 +84 264998 +ef eb2e3 +84 ca6fa +77 1ed2f2 +f 1d5120 +3 41ae8 +41 1b7132 +a5 ce8a2 +21 1d0d88 +a3 267844 +5a 26f0e +12 4370c +52 25b67 +5e 1b919f +c6 128d10 +a4 cd5df +73 22c73 +d6 2bb541 +2b 1d11f8 +14 1cc560 +36 3f7d2 +db 2bb6d8 +35 19f334 +52 258b9 +5e 1b8ef1 +c6 128a62 +e6 27c057 +ac cd488 +50 1af6e0 +2a 1d0ed7 +f 19a6ce +4b 5ea7e +50 1e824 +d7 279df0 +d7 280b60 +9d d1f91 +26 1976f3 +d7 281e30 +9d d3261 +33 3f806 +8a ca7c5 +d 19bc97 +1 865f +e6 27d5d5 +d 144b +ac cea06 +d0 2b2219 +d7 eec82 +f2 12548b +48 1b7296 +e6 ea177 +1d 1dac +f6 27df36 +bc cf367 +58 1b7bf7 +cc 2bada0 +c0 127768 +65 63aaa +c7 28177d +8d d2bae +d3 129633 +ec 2b6ea9 +e0 123871 +26 1979a1 +82 29cdce +d6 281e81 +d7 2820de +9d d350f +25 1d1069 +42 575ee +e6 27d319 +ac ce74a +85 d279b +1a b23 +dc 12849f +1 9921 +5c 1f1596 +18 1cd948 +1f a3b1 +11 43706 +a6 298ea4 +9 8754 +1e 1cd724 +12 3a0ec +b6 299805 +71 1f6878 +4a 56411 +eb e9ff2 +a2 d6919 +88 1029e0 +67 1f5cf3 +f8 ebe65 +1e 1d5cc0 +12 42688 +1a 3a243 +be 29995c +79 1f69cf +aa d6a70 +63 62812 +6f 1f5e4a +c4 127739 +ef eb2f1 +c4 ee2b7 +89 29d1cb +cc e5e72 +b8 cf2d6 +4e 1aef10 +ce 120ab7 +ae 11040f +d4 eec18 +99 29db2c +67 1f4a33 +dc e67d3 +cd 28161f +c1 edfe7 +86 10ae4f +a9 108088 +c9 ee13e +48 1e266 +96 10b7b0 +91 263ff7 +d9 eea9f +61 222b7 +86 103e25 +b7 1073bc +a6 29a1ba +2c 55f1 +99 d3224 +e 1cd071 +2 39a39 +3f 1d1b7a +33 3e542 +5f 26ca2 +80 d1763 +8c 264d9b +d2 2bb820 +ce e70d9 +c4 e5fc9 +d 1d3e59 +8a 102987 +de e7a3a +9b 1048b9 +ff 2be89c +ab 106b2f +94 265297 +ff ebbe2 +86 102b61 +1e 192ff0 +80 d2777 +c9 e5e50 +be d7402 +5d 1b8edb +da e7a09 +51 258a3 +2b 4709a +9 1d3e28 +a7 106d09 +3f 197198 +a0 2a0f60 +a 40e +86 103b75 +12 9cc +1e 194004 +b7 10f6a8 +86 10c111 +55 1b0ce4 +23 3eea3 +86 103e23 +12 c7a +1e 1942b2 +10 43705 +61 1ed919 +10 1c25 +e3 2843d5 +49 25099 +92 10b7e1 +9e 29ee19 +1c 19c359 +10 8d21 +29 c8fb +d9 e7cbf +8a 103ca9 +e4 e9ec2 +4 19221f +6b 1edadb +e4 f245e +d7 279ad2 +a9 2a1372 +bb 10f7dc +8a 10c245 +ec ea019 +92 10ca4f +9a 10460a +cb ee3e3 +6e 1b30b7 +ac 298da8 +f4 ea823 +ee 124c5e +14 192b80 +7b 1ee43c +f4 f2dbf +b9 2a1cd3 +9a 10cba6 +fc ea97a +4a 1e866f +cf 128e06 +f5 284d60 +c4 2817c9 +a6 106a5a +9c 104882 +ad d5a23 +b6 1073bb +80 d1455 +8c 264a8d +2e 1d0f78 +22 3d940 +c1 2817a5 +54 1af781 +3b 3e38b +41 2645e +8a 103f57 +e4 ea170 +4 1924cd +6b 1edd89 +9a 1048b8 +6e 1b3365 +a7 299eab +14 192e2e +7b 1ee6ea +e1 ea0e0 +ed 27d718 +18 1d4788 +b6 107669 +41 26204 +49 2635b +a4 d568f +ac d57e6 +fe 2bf8a3 +f2 12c26b +8 1cbad9 +50 1d554 +5c 1b0b8c +2a 3ed4b +52 1b8cf7 +24 197998 +c5 ef32c +20 3eb8b +5 8382 +a4 d593d +0 1cbc30 +54 1b0ce3 +22 3eea2 +4c 1cd29 +4 192281 +4 19a81d +77 1bd18c +c 1923d8 +14 19b17e +89 264d5d +1c 192d39 +aa 2a10cc +46 575af +2d 196580 +67 5b757 +10 9d1 +1c 194009 +c3 28179e +56 1af77a +cf 128b4c +b6 2a18f9 +4c 1cfd7 +2 7 +e 19363f +23 3d8d1 +2f 1d0f09 +55 1af712 +f4 27cccd +4 19252f +5c 1d938 +12 968 +1e 193fa0 +83 103b53 +14 3b386 +e7 2bdb36 +14 192e90 +24 daa6 +6d 2117f +25 1966d7 +35 197038 +92 10cab1 +fd 124049 +cc 120ab2 +56 1b7ad6 +0 62 +c 19369a +ba d635b +25 1976eb +46 1b00db +25 196675 +42 1cbfa +4e 1b0232 +c3 ef312 +b 1d532f +ca e7356 +41 251f0 +4d 1b8828 +69 221c4 +26 1cfb5f +7d 21830 +4c 1e299 +7a 29bb7 +25 197999 +5 9652 +1b 1d5c9c +58 1d649 +67 1b4531 +24 19e9c4 +2c 19657f +42 1ceaa +7f 1b3a79 +4e 1b04e2 +34 19f325 +8a c9255 +d 19a727 +9a c9bb6 +1d 19b088 +20 4217 +2c 19784f +57 1b7acb +72 1ee2d4 +3a 1d05e6 +85 103b7d +d6 1212b3 +39 5f22 +1c 90f3 +6e 1b3117 +8d 103cd4 +de 12140a +16 1d592b +71 1b3b44 +11 8d20 +1d 19c358 +70 1ed01b +5e 1afb7f +33 5e36 +77 1b38d0 +f5 12d7b2 +9b 104857 +6c 2117e +22 41ae +2e 1977e6 +9 4319c +24 1966d6 +32 4b0f +3e 198147 +19 43afd +7c 21adf +d5 279b3b +a3 107cfa +34 197037 +28 3da82 +62 1ec651 +da eeaf7 +bd 268572 +82 c93ac +8e 25c9e4 +5 19a87e +13 8cb7 +1f 19c2ef +8c 25b46d +d0 281e59 +b7 29aab8 +4 838f +1a 1d49d9 +31 6079 +66 1b326e +2c 19e8dd +72 1f5362 +3a 1d0894 +85 103e2b +14 8cf0 +1 866d +d 19bca5 +53 1f272a +ea e9ff1 +70 1f6877 +11 8fce +1d 19c606 +70 1ed2c9 +57 1b7d77 +74 1bbec4 +8a ca517 +1 83b1 +d 19b9e9 +6c 22440 +61 5b78f +b1 d61fc +da efdb9 +bd 269834 +82 ca66e +5 19bb40 +44 1aee12 +3a 1983c4 +22 3d932 +2e 1d0f6a +c1 281797 +54 1af773 +e3 ea157 +42 56576 +ef 27d78f +4e 1e9bae +e2 2bdb06 +2e 1d9506 +22 45ece +54 1b7d0f +be 25ffe0 +ea 2bdc5d +cb e70a9 +2e 1d1218 +22 3dbe0 +54 1afa21 +f1 27df53 +f 1d5360 +3 41d28 +65 1b3268 +6a 1b4354 +1b 1cd9b0 +3f 1d05b8 +56 1ea9c +75 1b3bc9 +7a 1b4cb5 +81 10be2c +d4 2bb54a +71 22c7c +89 264abd +fa f2eec +65 1b427c +f5 eaae2 +54 56f01 +43 1af043 +1a 1d81 +1c 1942a9 +10 c71 +6 1ccc6c +52 1af997 +18 dc8 +17 426b8 +b1 299830 +2 3978b +e 1ccdc3 +5b 1f12af +c7 277f1f +d0 2820f7 +4b 5ea72 +ee eb2e4 +49 1f1f27 +3b 61d7 +31 1a0309 +7f 29e25 +78 1ed3bc +b9 cddbb +18 3a1da +9f 2957a6 +64 1b2fb9 +1a 1cd701 +74 1bbeb6 +e3 284683 +10 1ed3 +e9 285a95 +7c 1b3a71 +14 1d5924 +ca e5de6 +4d 1b72b8 +2b 45dd8 +da e6747 +5d 1b7c19 +a7 25f527 +69 221b6 +3a 4c66 +26 1cfb51 +10 3a341 +1c 1cd979 +9e 264435 +26 1d80ed +c1 2804d5 +2e 1cfca8 +1b 8e0e +91 1031dd +9d 296815 +62 20d3f +6e 1b4377 +64 1b3267 +50 26b02 +8 1d5087 +6d 1b43e3 +61 20dab +c2 e5f3d +ce 279575 +45 1b740f +b9 10fad7 +a 19265e +5a 1b8e5c +69 22464 +94 25bce7 +3a 4f14 +70 22c7b +88 264abc +26 1cfdff +1b 90bc +46 1b007b +8e 294b96 +d2 2bb582 +17 1d4908 +34 1d8a55 +41 24f42 +ca e70a8 +4d 1b857a +18 192cb6 +3a 5f28 +3c 198450 +30 4e18 +fb 284e8f +91 cad29 +5a 1f12ae +c6 277f1e +1d 3a4c8 +26 1d0e13 +92 d20bf +9e 2656f7 +53 1b7ce6 +19 9117 +61 2206d +c2 e71ff +45 1b86d1 +18 192f64 +3a 61d6 +26 1d10c1 +63 2a3b2 +41 1b7140 +6a 5a8ca +39 47c40 +a5 ce8b0 +eb e9f90 +a2 d68b7 +88 10297e +67 1f5c91 +c2 2b994f +e4 2b6cee +48 1e88b6 +6a 5bb28 +4f 2531f +50 1af6e2 +2a 1d0ed9 +20 1cfdc9 +f9 ea8ea +b0 d7211 +80 10be39 +74 1b3bd6 +e3 eb169 +51 1af98f +2b 1d1186 +85 d2a59 +70 1b3b99 +7b 5b1d6 +56 1e8e9c +c3 2baec0 +54 1e8ea3 +c1 2baec7 +55 1e90f0 +86 c9191 +9e c9c49 +e3 eb40b +42 5782a +e1 eb0f4 +c4 ee2c5 +cc e5e80 +f1 eba55 +ae 11041d +d4 eec26 +ed 12cfa8 +6f 1b30c6 +78 1bd29e +c4 e729b +f5 ea832 +e4 27d630 +11 3b656 +dd 280f5c +ab 10f11b +55 1ea3b2 +86 ca453 +b7 d5f86 +86 d29ef +5d 1ea509 +51 56ed1 +8e ca5aa +e9 e9feb +a0 d6912 +f5 12c552 +43 1f0ab3 +56 1afa28 +88 294e6e +a8 d6a69 +8 192345 +88 d1600 +e5 eb441 +38 19810f +4b 1f0c60 +49 250a7 +92 10b7ef +9e 29ee27 +75 1ed297 +a6 cd338 +9c cb160 +fc ebc3c +a8 106b89 +25 197945 +42 1deca +bc 2996f9 +bd 26123a +b1 cdc02 +6b 62907 +b8 1074ea +e4 e9ed0 +e6 2bdd75 +ec 12cfa7 +6e 1b30c5 +f4 ea831 +3c 1d084e +af 1101d0 +d5 ee9d9 +1d 1d49f6 +f5 284d6e +c4 2817d7 +4b 2630e +4d 1b8836 +ca e7364 +41 251fe +5b 26c6f +80 d14a9 +8c 264ae1 +ee eb28e +45 1cbc3 +e4 ea17e +2 1934b9 +b1 110932 +fe ebbef +6e 1b3373 +63 1ec6c2 +df efd97 +6 1d5276 +4f 1e894f +13 1d48d9 +41 26212 +a8 107e4b +9c 10cbd0 +ee 2b6bf4 +e2 1235bc +41 264c0 +3d 1d88fd +c 1d5366 +54 26de1 +0 41d2e +42 1e84b8 +8 398e9 +8f 294eb5 +52 1e8e19 +18 3a24a +22 c80e +2e 19fe46 +9f 295816 +40 1cb91 +4c 1b01c9 +43 1b7147 +5f 1e952 +1a a0d1 +61 1b31d5 +4b 1b729e +17 19c446 +2 41adb +e 1d5113 +5b 1b7bff +eb eb250 +ce ee421 +a 3ab50 +b4 25fe2e +97 25bce1 +c 1cd078 +0 39a40 +2d 1d1220 +21 3dbe8 +c0 2baec6 +44 1b0320 +3d 1d1b81 +31 3e549 +d0 2bb827 +53 1b7d56 +9e 1045d9 +94 1034c9 +5a 582e2 +fb ebec3 +63 1ed922 +29 3ed53 +58 1e9217 +5f 25c80 +7a 5c489 +51 1b8cff +8 415 +43 1b8409 +10 9d3 +1c 19400b +4b 1b8560 +a5 107d24 +ce 1207fd +31 4b7b +3d 1981b3 +21 3eeaa +43 1b86b7 +de 11fe9a +10 c81 +1c 1942b9 +79 1bd29f +44 1b032e +ad cd437 +ff 2b74f4 +f3 123ebc +62 1ec65f +28 3da90 +50 1b7a3c +af 29905c +bf 2999bd +b8 ce068 +43 1f0807 +9 41c38 +c4 ee2c3 +e1 eb0f2 +dc efdf3 +5c 1b0b38 +50 1d500 +2a 3ecf7 +86 25c8f1 +b7 25fe88 +2c 1d121f +20 3dbe7 +ac cd428 +3c 1d1b80 +30 3e548 +d 1d53c7 +8a 103ef5 +1 41d8f +a8 107dd9 +8d d15d0 +40 1de63 +1d 1d5d28 +9a 104856 +11 426f0 +d4 e6628 +ae 107e1f +be 108780 +76 2197f +2e 1cff04 +7b 21b16 +de 2ba3e4 +15 3a0b5 +b4 107670 +1 42da3 +c 9a54 +f3 f40ba +52 604d9 +1a 3a253 +c7 2babf1 +8d 10c022 +5 19baee +de 129758 +e6 f21c5 +47 575bc +20 3eea9 +1 43051 +5 3aa16 +a4 107fd1 +fe 124041 +30 4e28 +3c 198460 +84 10c16a +6 192288 +2f 1d0f0b +f4 27cccf +23 3d8d3 +42 1cba6 +4e 1b01de +20 19e9f5 +5d 1b7e73 +2b 46032 +63 20d4e +6f 1b4386 +ca 278044 +65 1b3276 +4e 1dff2 +27 1976f2 +a6 268de6 +ef 27c4bf +4e 1e88de +40 1cc01 +4c 1b0239 +18 ab8 +9f 25c084 +27 1979a0 +ee 2b58d0 +26 19e9cb +36 19f32c +e9 f22d5 +7c 1b4d33 +70 216fb +28 1cfc80 +ad 110417 +c8 ef69f +f9 f2c36 +38 1d05e1 +bd 110d78 +64 1b2fc7 +ca e5df4 +4d 1b72c6 +73 2acb3 +8d ca5a4 +6e 21185 +26 1966dd +7e 21ae6 +36 19703e +95 d1e4a +b0 108653 +30 1d0738 +17 19b1e6 +34 19f333 +53 1b7a44 +ca 2bad76 +6e 22199 +1d da6 +26 1976f1 +86 294ced +e9 f3597 +70 229bd +28 1d0f42 +1c 1d5cc7 +10 4268f +62 1ec6b3 +bd 2685d4 +e7 12ce4a +41 24f50 +ca e70b6 +4d 1b8588 +6e 22447 +63 5b796 +26 19799f +cd ee1c3 +18 ac6 +6a 1ecac4 +23 19fa11 +b9 2685a3 +2b 4368 +76 5adf5 +e3 12ce19 +53 1b8d68 +23 19fcbf +2 39737 +e 1ccd6f +dd e6780 +94 d30a7 +88 10ad20 +6b 1b4659 +ec ea029 +39 4f1c +55 1af720 +23 3d8df +2f 1d0f17 +8a 294bd5 +25 1cfe07 +b5 d724f +cd ef485 +18 1d88 +91 265567 +5a 1d650 +99 2656be +0 39792 +c 1ccdca +ec eb2eb +c0 e6f58 +a1 2a24dd +34 1d04b9 +c8 e70af +a9 2a2634 +3c 1d0610 +8a 102985 +d 1d3e57 +ff 28516e +95 cb008 +5e 1f158d +33 47844 +54 1af71f +22 3d8de +2e 1d0f16 +24 1cfe06 +6b 1b30e7 +19 90c3 +15 43983 +67 1ed9a7 +b1 26970e +24 1d0e1a +90 d20c6 +9c 2656fe +5b 2599f +1 41ae1 +8a 103c47 +d 1d5119 +ff 286430 +52 5f217 +f3 f2df8 +5e 1f284f +a7 268de7 +6 1d5206 +4f 1e88df +24 1d10c8 +35 1d0708 +5e 1ea2c5 +52 56c8d +20 1cfd67 +2f 1977e9 +23 41b1 +53 1e8e0a +f2 2b63c5 +29 4301 +30 1d06c8 +27 1cfb54 +3f 4c9a +d2 ee940 +de 281f78 +80 10be8b +77 1bbf2c +84 29cd86 +21 44b8 +2d 197af0 +32 1d19f3 +90 d1e16 +9c 26544e +a8 d5a63 +9 84a8 +3b 198427 +18 1cc42a +1f 192cdf +9d 10cbc1 +10 1d486f +f3 123f1c +ff 2b7554 +c2 120985 +b4 d629e +30 3f4ec +15 8ce3 +8c 10c015 +8e 25b722 +d2 28210e +7 1d3cfb +1b 8e10 +47 1b7178 +62 1ed981 +da 282265 +1 85fd +d 19bc35 +6f 1b437a +63 20d42 +67 20d73 +16 1d5b69 +ed 12ba36 +22 d7c0 +6b 20e99 +26 d7f1 +6f 20eca +25 1d0e1d +2b 197ac6 +76 1ee553 +11 8f5e +1d 19c596 +e8 f25f4 +49 25039 +20 1cfb1b +27 1963d0 +a5 1102b2 +71 219aa +29 1cff2f +7d 1b4fe2 +30 1d047c +37 196d31 +b5 110c13 +8c 263acb +33 3f502 +bf 261241 +b3 cdc09 +b5 cdc33 +6f 62938 +bb cdd60 +62 1bb2ed +13 436a9 +98 295521 +5e 1b0b31 +52 1d4f9 +a 1cba7e +e5 e9e63 +ff 27ddd2 +f3 ea79a +f5 ea7c4 +e8 f3598 +2 83b9 +e 19b9f1 +a3 ce87a +2 3ac99 +a1 ce563 +84 d1734 +df 281f79 +d3 ee941 +c2 ef301 +a5 268d7c +f2 12517b +d7 ee972 +15 19b17d +80 10ae77 +8c 29e4af +73 1ee583 +20 3eb99 +5 8390 +1b 1d49da +32 1d1a01 +6b 1b3395 +9 84b6 +21 1d9376 +36 1d1a32 +28 3ecf0 +d 84e7 +80 29d065 +10 3a0f3 +1c 1cd72b +4d 1b7504 +21 1963f8 +20 d7c7 +69 20ea0 +1a 3b507 +14 3b384 +b5 cef65 +7e 1f54ea +25 196429 +24 d7f8 +6d 20ed1 +7 1d4fbd +2f 1d9505 +23 45ecd +55 1b7d0e +82 c9410 +8e 25ca48 +bf 25ffdf +75 216db +ec 124a0d +1 19a59f +49 25047 +82 c90fe +8e 25c736 +5 19a5d0 +11 19af00 +59 259a8 +f9 eab98 +b0 d74bf +95 264274 +df 1294ad +2b 1d7fc6 +22 3ee4e +54 1b0c8f +89 d15ad +a8 107de7 +8d d15de +c2 128a23 +a5 2a249e +df 2bb69b +d3 128063 +b8 108748 +9d d1f3f +d2 129384 +b5 2a2dff +92 1044a7 +f6 2be48a +fd 27ddd9 +f1 ea7a1 +86 103b77 +cf ee1d6 +e8 124978 +cd ee16f +60 5b9d8 +45 251cf +b8 d7678 +ef 27c4cb +7 19b837 +8 8509 +7b 2ae78 +d0 278ae7 +aa 29a2de +8f 263ad5 +47 1b83c8 +48 2509a +ea 2b6e6f +cf 280666 +3b 4ccb +da 281fa9 +fd 12c647 +a 1d5080 +f0 2be70e +0 1d3f70 +73 1f68df +f 19b9f2 +50 1b0a04 +3 83ba +9 850a +20 d81b +69 20ef4 +4e 1b04ee +42 1ceb6 +16 1d5bcb +ed 12ba98 +65 1bb564 +22 d822 +6b 20efb +49 2509b +4b 250a2 +32 3f811 +ae ce6f1 +a4 cd5e1 +62 5a46f +6e 1edaa7 +5b 25a03 +21 c806 +aa ce96c +2d 19fe3e +be cf052 +de 11fe9c +19 1d5c95 +de 2b20ea +2 1924a3 +b4 cdf42 +b0 299821 +bf 2612a3 +1e 1cd6c2 +12 3a08a +b3 cdc6b +52 1b0cb9 +80 29d067 +f3 2bf9d6 +65 1f49d8 +6e 5a5e9 +db 12821c +cb ee199 +fb 2b7533 +94 25bc77 +b 1d40cf +18 8e78 +27 1cfb60 +2a 470a9 +5c 1b8eea +50 258b2 +f2 2b7687 +53 1ea0cc +d7 280e7e +fd 27de3b +f 41c10 +50 56c22 +5c 1ea25a +f1 ea803 +6b 29493 +3a 3f6ba +ac cd48a +7b 29df4 +0 1937d0 +c2 11f3b1 +31 196d67 +ce 2b29e9 +e2 124ac8 +c7 ee2bf +9d caf13 +62 1b41ef +a 1cba7c +bc 10751b +8d ca860 +d3 1212e5 +f0 125432 +ac cea08 +f2 12548d +e6 ea179 +8d 29cf40 +3a 196eb6 +85 ca44d +ac ce74c +6 1d5214 +4f 1e88ed +b5 cf1af +76 21981 +8e 2637c2 +a6 267876 +b8 107734 +a7 106a69 +3a 197164 +85 ca6fb +a4 ce8a3 +e6 eb43b +81 10abca +8d 29e202 +84 d14da +f7 f3e49 +33 4682e +3f 1d9e66 +9a 29db24 +a4 299eb1 +87 295d64 +8c d1631 +ff f3fa0 +1d 1942aa +11 c72 +ed 27c1a6 +9c d1f92 +4f 1dff1 +3d 19f22f +c 19bc98 +ba cdd5d +0 8660 +73 2afcf +5e 1d93d +10 193e21 +8c d28f3 +4f 1b883d +43 25205 +d4 ee968 +29 461f +8c 29ceed +5f 1f15f2 +d1 2820fa +3e 1d18cd +32 3e295 +af 107e74 +d5 e667d +cf 120ab8 +f5 27ca12 +c4 27947b +cb ee3e5 +42 1aed86 +f4 ea825 +ee 124c60 +87 10ae50 +41 1e110 +56 1f275a +b6 1073bd +20 197967 +be 107514 +df e7a3b +ee eb282 +e4 ea172 +fe ebbe3 +8a d28c7 +59 1f2826 +42 1af034 +f4 eaad3 +19 192d17 +a6 106d0a +d4 efc2a +29 58e1 +80 10ab77 +8c 29e1af +3 42fea +22 dad0 +6b 211a9 +ce 2b9a77 +f5 125464 +6b 29255 +f6 27c9b6 +54 1b0a37 +22 3ebf6 +94 1031ab +1 3a9e5 +a0 107fa0 +85 d1797 +19 1d5a4b +f6 27cc64 +e4 eb434 +c4 ee06b +ba d761d +da 2ba6b5 +ae 1101c3 +d4 ee9cc +5c 1af868 +2a 3da27 +8e 10ad5a +29 197a5f +49 1af1e7 +0 19bb0e +ff 12d662 +79 29b4d +48 265b6 +21 1966b6 +ef 2b58cf +83 29d06d +48 2635c +fd f2c77 +c3 ee2f0 +cf 281928 +cc ef484 +e9 ea235 +5 192282 +ed f2306 +89 10afdd +5e 1b0ba1 +52 1d569 +35 196fe4 +7f 5c21d +99 d34de +a2 298ed5 +f6 27df88 +73 1f65d1 +39 47a02 +89 ca82f +d4 1212bc +3 8 +f 193640 +f5 27ccce +5 192530 +15 192e91 +a 87bc +b4 cef54 +1e af0 +8b d2b14 +22 41b0 +2e 1977e8 +6a 5bb98 +9b d3475 +a3 107cfc +1 31f +d 193957 +53 1ea3dc +f2 284fe5 +77 2af92 +70 1ee529 +d4 27887a +ae 29a071 +a2 106a39 +f6 ebaec +20 44c7 +ee 1236e0 +2c 197aff +72 1ee584 +d0 ee9a7 +dc 281fdf +63 1f5f1e +d8 e7cbe +29 4734f +76 1b3bd1 +73 1f687f +39 47cb0 +9a d21b4 +5 193544 +ed f35c8 +52 1e82b +35 1982a6 +90 25bf64 +bd 29ac18 +b1 1075e0 +da 12119d +56 1e8e4a +89 10c29f +c6 2781c0 +7a 217f9 +cf 2bb048 +c3 127a10 +5b 1b7e9f +ec 2bf195 +e0 12bb5d +47 1b8678 +cf 128bac +ec 12ccf9 +14 a260 +66 1b4284 +3d 19f48b +5a 25a10 +5 1937f2 +66 1b4532 +c 19a728 +73 29a5f +42 264c8 +7f 1bd097 +1c 19b089 +67 1b4533 +84 10abfa +0 83c0 +c 19b9f8 +52 1f247d +56 1b7aca +2 8357 +e 19b98f +90 cb03a +55 1ea414 +4a 5fd3f +55 1b7a62 +2f 1d9259 +23 45c21 +f4 28501d +0 83b2 +c 19b9ea +45 1aee13 +3b 1983c5 +90 10caaa +e1 285940 +74 1b391c +7 1d3f47 +97 10c7c3 +45 1b8425 +c2 e6f53 +a3 2a24d8 +36 1d04b4 +41 24f44 +4d 1b857c +ca e70aa +ab 2a262f +d1 280e38 +3e 1d060b +45 1af0c1 +fc ebc4c +4a 1b01ad +fa f2f4e +78 5c492 +5b 58345 +95 d1e48 +5e 1d681 +cb ef6a5 +62 20d41 +6e 1b4379 +21 1d0d8a +6f 5a8a6 +b9 2695c5 +db f0006 +74 1b3bca +31 1d16eb +7f 5b207 +94 29540b +b6 10867d +3a 4f16 +88 264abe +64 1b427d +6f 5b8ba +7 1d5209 +19 dc9 +6f 1bb474 +3a 5f2a +5a 1e8fc2 +df 129759 +3c 198452 +30 4e1a +da 129735 +5a 1f12b0 +c6 277f20 +26 1d0e15 +38 4f71 +ce 278077 +b5 25fe83 +14 1cc2a2 +64 1b452b +83 d2773 +ca 11f826 +6f 5bb68 +20 1cfb19 +8e 25b476 +3a 61d8 +5a 1e9270 +44 1b7162 +4f 5e79f +3a 1a0714 +4c 1b72b9 +85 102b07 +2a 45dd9 +5c 1b7c1a +a6 25f528 +77 22c50 +1a 8e0f +17 19b184 +82 10ae7e +8e 29e4b6 +e 1d3e51 +42 24ee8 +4e 1b8520 +1f 4256f +d0 e7bcb +fe 27cb0d +44 1b7410 +22 45f30 +2e 1d9568 +54 1b7d71 +11 1d5892 +5f 5f3ae +16 1d4909 +39 1d1b42 +9b 295529 +44 1b8424 +4f 5fa61 +40 24f43 +4c 1b857b +4f 5fd0f +23 1d80bd +98 c9e5d +2a 45de7 +5c 1b7c28 +c4 127799 +af 107e82 +d5 e668b +1d 1cc6a8 +ee eb290 +e4 ea180 +e6 2bdb29 +b1 110934 +fe ebbf1 +23 1d937f +98 cb11f +5 19bae0 +d3 28210d +1 63 +d 19369b +57 1b7ad7 +b 41ee1 +e4 eb442 +3d 3e661 +d 1ccd5b +a0 106cde +ac 29a316 +1 39723 +ef 27c1ad +c4 ee079 +c 1d4096 +da 2ba6c3 +cc ee1d0 +4a 2630f +7d 1bbdce +fa ea8fc +4c 1b8837 +40 251ff +5a 26c70 +e1 eb156 +c4 ee327 +51 5f221 +5d 1f2859 +8e d28fa +cc ef492 +f5 f2b80 +c4 ef5e9 +1d 42808 +9 398ea +1d 1d5cc8 +63 1ec6b4 +11 42690 +19 3a24b +50 1eac4 +39 1d05e0 +8 1cd049 +65 20d6a +d 1cd079 +1 39a41 +1b 3b4b2 +10 193e75 +1d 1cd9da +11 3a3a2 +a4 106d11 +1 42ff1 +1 1d523f +c6 11f446 +f4 27c9bd +6b 1f4e15 +20 3ebfd +a7 29a1c9 +a4 107d25 +2b 1d0f48 +51 1af751 +61 1ec657 +1c 1d5a7b +10 42443 +f5 ebd34 +97 29da0f +8c 10ad61 +f4 f3e35 +42 1b8396 +ed 2b58d6 +9c 10b6c2 +8e 10bfc8 +0 42da4 +87 29e370 +8 42efb +83 10ae8f +d7 eff42 +d0 2b34d9 +8f 29e4c7 +ed 27d468 +e1 e9e30 +e9 e9f87 +45 1aee21 +67 2a62f +60 1edbc6 +45 1b73bd +cd 1278f1 +b8 269866 +65 1b4538 +8 19a6f7 +7b 1bd066 +db efd66 +2 1d5245 +4b 1e891e +8c 102c5d +55 1b09d4 +23 3eb93 +6 41d64 +f4 27df8f +d9 eedad +e4 1235f2 +ed 27d716 +e1 ea0de +86 10bec5 +26 1966df +f5 27dce4 +7 41ab9 +54 1ea103 +d6 280bbf +36 197040 +a 19b95e +51 1ea3e3 +43 1cba7 +4f 1b01df +45 1af0cf +db f0014 +72 216b0 +7e 1b4ce8 +e6 124859 +74 1b3bd8 +c1 ef5b7 +64 1b428b +9d 10b8ff +64 1b4539 +e8 f22d6 +e 19a72f +f8 f2c37 +44 1b7170 +4c 1b72c7 +d2 121044 +b5 29aabf +2e 1d92c8 +22 45c90 +54 1b7ad1 +e0 f3441 +2a 3efa5 +50 1d7ae +5c 1b0de6 +51 1ea135 +f0 284d3e +44 1b8432 +2b 3ed4e +19 a12b +f2 2862b5 +ae 25f8c9 +53 1b8cfa +31 1d0419 +0 1cce82 +45 1b7411 +ce 279577 +c2 e5f3f +ff 27cb0e +27 471c0 +5 1d3f4e +d 1cbb09 +ff 27ce20 +b6 269747 +5e 1e923f +16 426c7 +33 3f4f6 +e5 f245d +b4 108684 +54 1af721 +22 3d8e0 +76 22993 +2e 1d0f18 +69 1bc93c +9a 10b8e4 +5 1ccc74 +f7 27df8b +56 1ea3aa +a4 26787d +7b 1b39e6 +4a 1b044f +1 39793 +d 1ccdcb +ff 27e0e2 +52 56ec9 +f3 eaaaa +5e 1ea501 +ac 2679d4 +f2 2be459 +24 1d10ca +d4 2bb5aa +c5 e728c +7a 1b3cf5 +4 1d3d01 +15 19b18b +80 10ae85 +8c 29e4bd +b4 ceef2 +5f 603c4 +c 1d3e58 +7a 1b4fb7 +5e 1b8e81 +52 25849 +4 1d4fc3 +80 d2779 +c2 120681 +45 1f1b53 +35 4dea +ac 10811c +a2 110287 +eb 123960 +d4 2820c8 +5b 1b8eb1 +78 1bcffe +0 1cbbce +54 1b0c81 +22 3ee40 +7 8637 +55 1b0c82 +23 3ee41 +20 1966b5 +ee 2b58ce +67 21021 +33 3f7b0 +ce e713d +a5 cd580 +bf 2614ef +b3 cdeb7 +b7 cdee8 +28 470a2 +21 1d93d8 +8f 264d35 +83 d16fd +ad ce9a7 +f3 12542c +e7 ea118 +ff 27e080 +f3 eaa48 +af 110410 +d5 eec19 +27 1d0dc2 +59 1b8eb8 +20 3ee47 +5 863e +ca 11f508 +39 196ebe +8 193927 +14 1cd882 +12 3b65e +76 1f5641 +7d 1b4f90 +71 21958 +e8 124c8a +0 9680 +49 1cd59 +ce e714b +a5 cd58e +25 197947 +42 1decc +bc 2996fb +23 1d811d +99 c9c20 +ed 27d726 +e1 ea0ee +92 104755 +f6 2be738 +fd 27e087 +f1 eaa4f +3f 19845a +96 29d6f0 +33 4e22 +d2 282100 +1f 19c601 +13 8fc9 +5f 1b9192 +53 25b5a +27 1d0e24 +13 8f65 +1f 19c59d +8c 25b71b +d0 282107 +b5 108617 +df 1281e9 +1d 19c608 +11 8fd0 +6b 22169 +43 25195 +4f 1b87cd +23 1976c1 +e5 2bf03d +6d 1b4691 +c4 2b9927 +61 21059 +33 198022 +c 193956 +0 31e +f5 2bf99e +4b 26310 +3 19b868 +13 19c1c9 +79 1b39ef +30 1a0316 +34 3e517 +6f 1bb406 +ee f262c +6a 5b87a +4f 25071 +48 1e8608 +8c 102c5f +6 41d66 +23 3eb95 +f4 27df91 +4e 1b87dc +7f 1bbd73 +42 251a4 +d5 11fdad +2d 1967da +4a 1cd5f +5e 1b913d +52 25b05 +3d 19713b +5a 1d6c0 +6b 20f07 +d6 27881f +27 1d7eb0 +9c c9c50 +f6 2b60d8 +38 1d9e2f +a4 260a9f +ce e5e87 +f8 eaba3 +ef ea02f +ad ce999 +e2 ea156 +ee 27d78e +b5 d5f8d +84 d29f6 +c6 279180 +8c ca5b1 +b5 29aab1 +d2 121036 +16 192e97 +f7 f2b25 +c6 ef58e +a5 cd58c +ce e7149 +aa 2a10be +5a 60630 +fb f4211 +c6 e72a0 +a2 2a1215 +6e 1bc983 +62 2934b +c4 1209bd +f5 123f54 +e4 2b6d52 +6a 20f06 +25 196677 +42 1cbfc +4e 1b0234 +a4 cd33f +b4 cdca0 +3 42fe8 +e6 f2473 +d8 279c54 +47 5786a +ee ea02e +f6 f2dd4 +57 581cb +ac ce998 +84 10aeb8 +a1 107ce7 +fe ea98f +d8 eed4a +df eeb37 +9 172a +ea ea23b +6d 1bb70d +5f 1b919e +53 25b66 +c0 ef29a +47 1cbca +e6 ea185 +b3 110939 +d0 efbfb +57 1d52b +f6 eaae6 +62 2a60d +f5 125216 +6a 221c8 +d8 eed3c +cf ef498 +43 264c7 +bd 2a1cf6 +c0 280228 +cb 127865 +65 1bc56a +e 1ccdd1 +2 39799 +9 8762 +7f 22d99 +14 19c44e +2b 3dce6 +db 1281c6 +75 1bcecb +1e 1cd732 +12 3a0fa +42 1cb98 +4e 1b01d0 +44 1af0c0 +2c 55ff +21 1cfab8 +99 d3232 +c0 2804d6 +cb 127b13 +65 1bc818 +e 1cd07f +2 39a47 +46 1b0327 +2 3aa5b +55 1b0a44 +23 3ec03 +b7 10f6b6 +86 10c11f +8e 103cda +a7 107d2b +44 1aedc0 +66 22032 +d3 efc65 +2 3ad09 +bb 110da2 +86 103e31 +ba ce06f +7f 1ed449 +96 2953a0 +b 41c3f +29 4611 +8c 29cedf +4c 1b023b +40 1cc03 +b6 10f965 +94 10478d +be 107520 +43 1b7391 +f5 f2e30 +54 5f24f +c4 e5d2b +73 5b07f +7f 1ee6b7 +93 2652c2 +da 2b2375 +c 1cbd48 +9f 10b6c8 +b5 d623d +66 1ec680 +de efdfa +ae cd42f +9 1d4072 +1 19249d +7 3975b +a6 106d16 +11 192dfe +88 296130 +17 3a0bc +b6 107677 +5c 1b0b9a +50 1d562 +2a 3ed59 +52 1b8d05 +b2 cf1da +77 1ee5b4 +3 42daa +d4 e668a +ae 107e81 +d6 281e2d +8f 10c029 +79 1b4f5d +25 1966e5 +22 19e9fc +f1 286001 +ad 25f615 +50 1f2420 +67 1b327d +b0 cf1df +11 1c24 +46 1aee19 +0 70 +f5 2bf6f0 +c 1936a8 +52 1ea12d +7b 1bd05a +46 1b00e9 +25 196683 +42 1cc08 +4e 1b0240 +19 90c5 +6b 1b30e9 +22 19fa10 +93 2953e0 +67 1b4291 +7b 1bd308 +46 1b0397 +24 196436 +a1 268dbb +34 196d97 +f4 2bf99f +a9 268f12 +3c 196eee +66 5b764 +31 5dd9 +66 1b2fce +76 1bbecb +85 10abfd +6 1ccc6e +4f 1b72cd +24 1966e4 +40 1b8391 +31 6087 +66 1b327c +33 1d9a30 +47 1b7424 +1 83bf +d 19b9f7 +53 1f247c +70 1f65c9 +43 24f57 +4f 1b858f +83 29e33d +16 1cc319 +1c 1942b7 +10 c7f +d0 282105 +45 1b8431 +c2 e6f5f +36 1d8a5c +ab 2a263b +d1 280e44 +3e 1d0617 +cc 2bad40 +c0 127708 +69 22472 +26 1cfe0d +45 1b86df +c2 e720d +36 1d076e +3c 19845e +30 4e26 +26 1d0e21 +26 1d10cf +c 8794 +29 55c3 +1a 3a501 +ca e6106 +6f 22448 +da e6a67 +7f 22da9 +de e7d4a +8a 102c97 +2f 3efd9 +df 1294b9 +9a 1035f8 +3f 3f93a +6a 1b30e6 +18 90c2 +14 43982 +66 1ed9a6 +29 c909 +2d c93a +8e 263826 +7f 1f69f7 +73 633bf +39 d26a +3d d29b +21 1d93e8 +5a 25c5a +10 19c16f +7b 22aba +5e 25c8b +6b 294a1 +e1 123870 +ed 2b6ea8 +21 19f9b6 +6f 294d2 +7b 29e02 +f1 1241d1 +fd 2b7809 +31 1a0317 +7f 29e33 +d8 e7d20 +63 1f5f80 +88 d18ba +2d dbfc +82 d14b0 +51 1f140f +8e 264ae8 +73 64681 +98 d221b +3d e55d +ad d5a31 +f4 12c4ef +a9 d5a62 +bd d6392 +b9 d63c3 +8d 10ad52 +c7 2b9921 +ca ee452 +6f 2a794 +a3 106ce6 +af 29a31e +d5 278b27 +eb f25fa +fb f2f5b +2d c92e +8 9a23 +d8 2b3384 +df efded +4e 265ec +43 5f93b +88 d2b7c +f4 12d7b1 +76 1b38cf +a9 d6d24 +ce ef6e3 +c3 128a32 +5b 1b8ec1 +ca ef714 +a3 107fa8 +d5 279de9 +eb f38bc +1a 427eb +3b 3f64b +1e 4281c +bb 108742 +1 43045 +9e 10b913 +9a 10b944 +3f 47c86 +af 10f15a +5 1ccf14 +a4 267b1d +dd 280fcc +ab 10f18b +bf 10fabb +a 431a0 +15 1cd875 +b4 26847e +bb 10faec +f0 ebd70 +51 1e7b5 +e 4317d +8a 10c2a5 +95 29697a +dd 28228e +d1 eec56 +ab 11044d +88 d1858 +ea 27c1dd +98 d21b9 +94 10ca79 +e6 2b6a9d +a9 d5a00 +b9 d6361 +da eed51 +ac 2a2666 +a0 10f02e +d 1d50ab +1 41a73 +8 19a9b1 +18 19b312 +4a 250ad +2d 19eb28 +73 1f55ad +74 1f55e6 +29 19eb59 +5a 25a0e +3d 19f489 +63 63d30 +6b 1b4347 +4e 1b7518 +43 1f0867 +53 1f11c8 +5a 1b7eaa +86 26366b +6f 1bb6c0 +6b 1bb6f1 +88 10bf80 +7f 1bc021 +7b 1bc052 +a7 267813 +88 d2b1a +3c 4c9e +a9 d6cc2 +ca ef6b2 +a3 107f46 +d5 279d87 +7e 21836 +eb f385a +8 19bc73 +42 251a2 +4e 1b87da +43 1f1b29 +4a 1b880b +63 2934a +6f 1bc982 +dd eeb24 +ca 280640 +6b 1bc9b3 +9a 10b8e2 +bb 10fa8a +11 1cd844 +b0 26844d +f0 27cc9c +51 1af6e1 +2b 1d0ed8 +e 1d40a9 +d9 f0001 +a 1d539c +3b 3e389 +a 3adf2 +a5 ce850 +6e 1f4dd5 +3f 3e3ba +e 3ae23 +ee 2bdecc +bf 1074b1 +8e 103f1a +3f 3e3c8 +e 3ae31 +4d 1b8838 +fb ea8fd +41 25200 +ca e7366 +60 5ba3a +ff ea92e +ce e7397 +3b 3e3eb +a 3ae54 +bf 10776f +73 1ecfc1 +39 3e3f2 +8 3ae5b +6c 1f4e3e +bb 1074e2 +8a 103f4b +b9 1074e9 +88 103f52 +e 4316f +a8 29a2e7 +f0 ebd62 +af d6d50 +39 196e5e +ec 2bdf35 +d3 278841 +2c c93b +3c d29c +6e 294d3 +7a 29e03 +f0 1241d2 +fc 2b780a +7e 29e34 +4b 25040 +ea f25fb +4b 579f2 +54 1e90ef +f5 27ccd0 +fe f2f2b +22 3eea4 +54 1b0ce5 +5f 58322 +ff 2bf8b2 +f3 12c27a +d3 279b03 +2c dbfd +82 d2710 +4c 1f0c95 +72 64682 +d0 e78bb +21 46f4c +6a 2a764 +fd 12536d +75 1b4e39 +aa 29a2ee +f2 ebd69 +d0 278af7 +6e 2a795 +63 63ae4 +f6 ebd9a +ae 29a31f +a2 106ce7 +d4 278b28 +9 976a +a8 d6d25 +ee f388c +e3 12cbdb +4b 26302 +ea f38bd +3a 46994 +3e 469c5 +fd 27ddcb +f1 ea793 +ae 10f15b +b5 299861 +be 10fabc +14 1cd876 +1b 42532 +ba 10faed +71 2295e +54 25b2f +2e 47326 +b6 10892b +94 2956b9 +65 1b428a +fa f2efa +50 1b0cb4 +5b 582f1 +28 19eb5a +38 19f4bb +6e 1bb6c1 +d8 e67b0 +63 1f4a10 +6a 1bb6f2 +7e 1bc022 +73 1f5371 +25 1d0e1b +ba 10fa8b +10 1cd845 +2a 1d8283 +3e 1d8bb3 +85 10c17b +3a 1d8be4 +7b 21868 +4a 1e2d1 +3b 3e3f9 +a 3ae62 +fb ea95f +ca e73c8 +bb 1074f0 +8a 103f59 +42 1b7384 +8 87b5 +52 1b7ce5 +18 9116 +63 1bb52c +29 c95d +39 19811e +73 1bbe8d +39 d2be +cf 2803c4 +5a 25cae +c6 2804ac +8c d18dd +90 c9a66 +9c 25d09e +d6 280e0d +9c d223e +e7 284654 +ad d5a85 +80 ca677 +b1 cdc0e +bd 261246 +f7 284fb5 +bd d63e6 +6e 1edafd +1c 43ad9 +62 5a4c5 +80 264959 +ce ee475 +ed eb59a +4c 579b9 +2f 1d92c7 +23 45c8f +d2 e65fe +55 1b7ad0 +de 279c36 +90 2652ba +de eedd6 +a1 268b01 +0 1d4f20 +e8 2b5bb4 +ef f261d +81 d276c +c8 11f81f +6d 5bb61 +45 1b86e1 +c2 e720f +f3 ea7a6 +ff 27ddde +8c 25b46f +91 d30cd +d8 120180 +8c c92f1 +7d 5c4c2 +9c 25bdd0 +63 1bc7ee +29 dc1f +f7 284d05 +c6 28176e +8c d2b9f +e6 123599 +e7 285916 +ad d6d47 +ce ef737 +e0 12cbd3 +48 25346 +58 1b0b07 +79 29e4f +a 41ede +15 1cc5b3 +cf 2bad9c +c3 127764 +1a 1cd69f +1a 4283f +da e69f5 +5d 1b7ec7 +2b 46086 +3b 1d1847 +3b 469e7 +cc ee46e +21 45c88 +2d 1d92c0 +d0 e65f7 +aa 107dee +dc 279c2f +dc eedcf +83 10ab71 +8f 29e1a9 +ed f2616 +f1 ea79f +c0 e7208 +fd 27ddd7 +fd f2f77 +8e 10b006 +15 1d4661 +92 10318f +9e 2967c7 +af 10f1ae +82 103da0 +5 1d5272 +b3 107337 +bf 29a96f +cc ef730 +ed f38d8 +c 19a9d4 +aa cd6aa +2d 19eb7c +19 a377 +4e 1b756c +60 1f4a08 +6a 5a8d6 +23 d823 +5e 1b7ecd +70 1f5369 +7a 5b237 +33 e184 +0 865e +c 19bc96 +66 1ec690 +42 251f6 +4e 1b882e +60 1f5cca +63 2939e +6f 1bc9d6 +48 1e9bd8 +5c 1b7ec6 +a6 25f7d4 +78 5b230 +31 e17d +e 1d40fd +de 278c14 +2f 1d82a5 +98 10c8ef +3f 1d8c06 +ea eb4fd +61 29397 +6d 1bc9cf +2 41d87 +e 1d53bf +1a 911d +3b d2c5 +48 25354 +69 294fc +e9 eb2ab +cc ee47c +f9 ebc0c +dc eeddd +d7 efc32 +83 10ab7f +8f 29e1b7 +ed f2624 +fd f2f85 +48 26616 +69 2a7be +cc ef73e +ed f38e6 +73 1f55bd +39 469ee +e7 2bdd84 +ad 10f1b5 +f7 2be6e5 +bd 10fb16 +e7 2bf046 +ad 110477 +69 1b43a2 +4c 1b7573 +72 2af60 +21 d82a +79 1b4d03 +5c 1b7ed4 +31 e18b +40 251fd +4c 1b8835 +61 293a5 +ea eb50b +6d 1bc9dd +29 1d0f33 +c 1d4104 +39 1d1894 +1c 1d4a65 +c9 2b9d5e +eb 12cfd0 +dc 278c1b +aa 106dda +2d 1d82ac +ba 10773b +3d 1d8c0d +0 41d8e +c 1d53c6 +7a 29e57 +ac d5a86 +b0 cdc0f +bc 261247 +93 c9ac2 +9f 25d0fa +ee f261e +6c 5bb62 +fe f2f7f +6a 2a7b8 +81 263696 +75 1b4e8d +ac d6d48 +5a 1b7b9a +78 29e50 +5c 1b7ec8 +2a 46087 +3a 1d1848 +35 1d075c +3a 469e8 +ec f2617 +f0 ea7a0 +fc 27ddd8 +d3 e6653 +df 279c8b +4a 1b74d9 +fc f2f78 +ae 10f1af +68 2a7b1 +5c 1b918a +50 25b52 +2a 47349 +35 1d1a1e +ec f38d9 +3c 19f4de +b4 29a810 +14 1f10 +7e 1bc076 +1a 2093 +f6 2b73a8 +20 c807 +2c 19fe3f +9d 29580f +62 2939f +6e 1bc9d7 +a6 25f4c6 +58 56ddb +7c 1bc06f +18 208c +60 29398 +6c 1bc9d0 +bd cf308 +3b 3e639 +d1 efc5c +89 29e1e1 +39 3e640 +3f 3e66a +d2 128310 +de 2bb948 +d5 efc8d +81 10abda +8d 29e212 +f9 27e0aa +b 41e7f +73 1bd1b1 +5d 26f39 +9 41e86 +2d 1967cc +4a 1cd51 +71 1bd1b8 +fd 27e0db +f1 eaaa3 +f 41eb0 +98 c9ecf +77 1bd1e2 +50 1ea3e4 +d 41eb7 +f2 ebd17 +75 1bd1e9 +1f 42811 +fd ebe89 +a9 106dd6 +af 106e00 +ad 106e07 +8a 25c769 +bb 107730 +6 1d3c98 +b9 107737 +bf 107761 +bd 107768 +8b 10af76 +dd f0030 +89 10af7d +8f 10afa7 +70 2ad0d +8d 10afae +9b 10b8d7 +9f 10b908 +6b 21155 +6f 21186 +7b 21ab6 +7f 21ae7 +4b 252fc +6a 5bb36 +4f 2532d +5b 25c5d +2f 3dd17 +df 1281f7 +ff 2be58c +c2 1279bd +ce 2baff5 +3b 3e647 +3f 3e678 +2f 1967d3 +73 1bd1bf +77 1bd1f0 +1b 427ee +1f 4281f +eb ea24c +ef ea27d +fb eabad +38 5f2f +ed f362c +6f 1edaa8 +63 5a470 +4b 1b84ee +fd f3f8d +67 5a4a1 +af 106e0e +bb 10773e +0 3aca0 +a1 ce881 +6a 1f4e06 +df f0037 +8b 10af84 +c0 2804e2 +ad 1101bd +2c 432f +70 2ad1b +9b 10b8e5 +9f 10b916 +39 3e6a2 +d8 2bb980 +9 41ee8 +1b 42842 +19 42849 +94 265599 +ab 106e31 +fd ebeeb +a9 106e38 +6 1d3cfa +b9 107799 +8b 10afd8 +dd f0092 +89 10afdf +4d 2506a +ec f2625 +d3 e6661 +df 279c99 +4a 1b74e7 +5d 259cb +fc f2f86 +68 2a7bf +4d 2632c +ec f38e7 +38 469ef +93 1031f2 +9f 29682a +28 47350 +35 d146 +2e 19eb84 +a6 299eb6 +3e 19f4e5 +b6 29a817 +7c 1bc07d +18 209a +60 293a6 +6c 1bc9de +2c 1d82ad +3c 1d8c0e +20 45f37 +2c 1d956f +14 19c4b0 +d6 128091 +2b 3dd48 +db 128228 +f2 eba5b +75 1bcf2d +ac 10f1b6 +d 41bfb +19 9125 +53 1b7cf4 +3b 3e6a9 +da 2bb987 +1b 42850 +cb ee455 +8d 10acf2 +bb 1077a0 +9d 10b653 +42 1b7392 +8 87c3 +52 1b7cf3 +18 9124 +73 1bbe9b +39 d2cc +e6 2bdde5 +b1 110bf0 +47 1f082a +6b 29503 +2a 55c7 +8 192355 +de 2bb9a8 +d2 128370 +86 d14e1 +55 1f1440 +77 646b2 +d6 280e1b +b9 cf07b +9c d224c +e7 284662 +ad d5a93 +ff 2bfb50 +f3 12c518 +f7 284fc3 +bd d63f4 +6e 1edb0b +62 5a4d3 +eb eb2b2 +80 264967 +ce ee483 +fb ebc13 +90 2652c8 +de eede4 +ae ce6ef +8c 25b47d +be cf050 +9c 25bdde +6b 2a7c5 +8 193617 +f7 284d13 +c6 28177c +8c d2bad +d2 129632 +e7 285924 +ad d6d55 +d1 2ba568 +f3 12d7da +ce ef745 +49 1e98c9 +ef f38ed +80 c9107 +8c 25c73f +1a 4284d +5d 1b7ed5 +da e6a03 +2b 46094 +3b 469f5 +79 22b17 +bb 1087a4 +9e 10b975 +af 10f1bc +bf 10fb1d +5d 1b9197 +da e7cc5 +51 25b5f +2b 47356 +d1 e790e +8e 10c2d6 +d5 eec87 +af 11047e +29 197811 +c 19a9e2 +7b 21aa8 +32 e3cf +52 1f1467 +2d 19eb8a +73 1f560f +ce 2bb057 +ff 2be5ee +c2 127a1f +67 63d61 +19 a385 +6b 1b43a9 +4e 1b757a +7b 1b4d0a +5e 1b7edb +6f 1bb722 +0 866c +c 19bca4 +52 1f2729 +42 25204 +4e 1b883c +63 293ac +6f 1bc9e4 +2 41d95 +51 1b0a05 +e 1d53cd +2b 3ef9a +db 12947a +29 3efa1 +d9 129481 +b 43141 +9 43148 +9 1d5396 +ce 11f59d +f1 ebd65 +f 43172 +d 43179 +ab 108091 +a9 108098 +af 1080c2 +ad 1080c9 +1e 3a214 +8b 10c238 +1c 3a21b +89 10c23f +2b 19e8a6 +6b 22417 +4b 265be +4f 265ef +50 58192 +f1 ebd73 +f 43180 +eb eb50e +ef eb53f +5e 1d691 +cb ef6b5 +2b 3effc +f8 12d629 +29 3f003 +23 571f +d9 1294e3 +9 431aa +ab 1080f3 +a9 1080fa +8b 10c29a +89 10c2a1 +28 c96c +38 d2cd +d 84d9 +28 3ece2 +ac d5a94 +fe 2bfb51 +f2 12c519 +28 dc2e +6a 2a7c6 +81 2636a4 +75 1b4e9b +b4 26848c +15 19aed1 +30 1d16da +d 979b +ac d6d56 +f2 12d7db +4f 26333 +48 1e98ca +ee f38ee +b4 29a802 +3a 469f6 +fd 27de2d +f 41c02 +f1 ea7f5 +ae 10f1bd +5c 1b9198 +50 25b60 +2a 47357 +35 1d1a2c +f 42ec4 +d4 eec88 +f1 ebab7 +ae 11047f +2c 19eb8b +72 1f5610 +3c 19f4ec +39 e52e +6e 1bb723 +7e 1bc084 +20 c815 +2c 19fe4d +72 1f68d2 +62 293ad +6e 1bc9e5 +71 1b38ec +2e 1d82b4 +3e 1d8c15 +22 45f3e +54 1b7d7f +71 1b4bae +2e 1d9576 +4b 26620 +d6 129353 +2b 3f00a +14 3a372 +db 1294ea +ac 110478 +d 42ebd +cb ef717 +ab 108101 +8d 10bfb4 +8b 10c2a8 +f8 eac05 +59 1d64a +10 9f71 +97 26553d +fc eac36 +5d 1d67b +14 9fa2 +18 a0c8 +93 d205c +9f 265694 +1c a0f9 +31 e119 +39 e270 +f0 27dcb2 +2 41a87 +e 1d50bf +56 26b3a +5a 26c60 +2b 1d9236 +51 1b7a3f +73 2acb1 +59 1b7b96 +7b 2ae08 +dd e6772 +94 d3099 +4 1cbc51 +d9 e67a3 +90 d30ca +9c d31f0 +c 1cbda8 +98 d3221 +b5 d7241 +25 1cfdf9 +50 1af6ee +2a 1d0ee5 +bd d7398 +2d 1cff50 +ab 2a238f +d1 280b98 +f3 f3e0a +34 4de9 +d9 280cef +fb f3f61 +56 6026c +59 1d8f8 +10 a21f +5d 1d929 +14 a250 +31 e3c7 +c7 2814cd +e 1d536d +f0 27df60 +3f 1d8904 +2 41d35 +56 26de8 +2b 1d94e4 +51 1b7ced +73 2af5f +dd e6a20 +94 d3347 +d9 e6a51 +90 d3378 +bb 2a1a2c +8a 29e495 +d2 eff10 +ab 2a263d +3e 1d0619 +d1 280e46 +f3 f40b8 +eb 2857f0 +4a 1f1c0f +29 1d922f +71 2acaa +b7 260138 +16 1cc557 +79 2ae01 +a9 2a2388 +f1 f3e03 +df 11fe9b +96 10c7c2 +9e 10c919 +18 4283a +91 29d729 +b3 11099b +99 29d880 +bb 110af2 +69 1f5e22 +91 29d9d7 +b3 110c49 +98 d31bf +8 1cbd77 +b9 d7367 +29 1cff1f +fb f3eff +6b 1ecab7 +c4 e5f75 +f9 f2ee6 +59 1af89a +10 19c1c1 +10 8caf +1c 19c2e7 +18 19c318 +35 1a0338 +90 263ff6 +31 1a0369 +39 1a04c0 +52 1b8d59 +75 633f7 +7d 6354e +7b 1bd058 +d9 e69ef +90 d3316 +b1 d74be +41 2519e +4d 1b87d6 +3e 1d05b7 +f3 f4056 +5d 1afb17 +2b 3dcd6 +14 19c43e +59 1afb48 +10 19c46f +90 2642a4 +31 1a0617 +52 1b9007 +23 1d80cb +98 c9e6b +77 1bd17e +d2 280e3c +f1 27df61 +f 1d536e +57 26de9 +3 41d36 +50 1ea380 +f1 f3da1 +96 29564e +f9 f3ef8 +db 11fe6a +92 10c791 +d7 eff52 +9a 10c8e8 +67 1ec92f +df f00a9 +50 1b8d52 +79 1bd051 +5b 1e8fc3 +12 1d58ea +61 1bb283 +bf 2695f1 +12 423d8 +b3 d5fb9 +1e 1d5a10 +1a 1d5a41 +69 1bb3da +37 1d9a61 +92 29d71f +33 1d9a92 +33 46580 +2 42fe9 +3f 1d9bb8 +9a 29d876 +3b 1d9be9 +b2 cdc68 +be 2612a0 +d0 280e35 +b7 269748 +5f 1e9240 +16 1d5b67 +61 1bb531 +37 1d9d0f +92 29d9cd +33 1d9d40 +fa eac0c +5b 1d651 +12 9f78 +1a a0cf +11 19aeae +33 e120 +19 19b005 +3b e277 +54 26b41 +58 26c67 +d3 eebfb +df 282233 +5c 26c98 +71 2acb8 +85 263915 +16 1cc565 +79 2ae0f +8d 263a6c +db e67aa +92 d30d1 +9a d3228 +f1 f3e11 +5b 1d8ff +12 a226 +69 1bc700 +11 19b15c +33 e3ce +50 26dbe +b7 d74f8 +c 1cbafa +54 26def +71 2af66 +2d 1cfca2 +df e6a27 +96 d334e +db e6a58 +92 d337f +d4 efee6 +f1 f40bf +e9 2857f7 +48 1f1c16 +97 29ec6d +93 10b78c +9f 29edc4 +31 47849 +dd 11fea2 +94 10c7c9 +6b 1edd7b +d9 11fed3 +90 10c7fa +98 10c951 +7b 22d68 +10 19c41d +b5 110971 +b1 1109a2 +bd 110ac8 +b9 110af9 +f6 27ca1a +31 47af7 +f6 123f4c +d9 120181 +90 10caa8 +47 1f088a +b1 110c50 +69 1ecabe +c6 e5f7c +fb f2eed +12 8cb6 +1e 19c2ee +37 1a033f +b1 261122 +ff eac3e +17 9faa +10 1cd541 +92 263ffd +33 1a0370 +33 ce5e +2 98c7 +3f 1a0496 +42 1f0aa6 +8 41ed7 +b9 261279 +1f a101 +18 1cd698 +9a 264154 +3b 1a04c7 +6b 5a5c7 +54 1b8d2f +50 1b8d60 +58 1b8eb7 +79 1bd05f +db e69f6 +92 d331d +b3 d74c5 +43 251a5 +4f 1b87dd +d0 efeb5 +8c 294bf1 +f1 f405d +ad 298d99 +5f 1afb1e +16 19c445 +5b 1afb4f +12 19c476 +37 1a05ed +b1 2613d0 +17 a258 +10 1cd7ef +92 2642ab +33 1a061e +6b 5a875 +54 1b8fdd +50 1b900e +75 1bd185 +b 1d507f +bd 110b1e +d0 280e43 +b1 110940 +b5 2694a1 +5d 1e8f99 +14 1d58c0 +f9 12c616 +59 1e8fca +10 1d58f1 +bd 2695f8 +10 423df +b1 d5fc0 +1c 1d5a17 +18 1d5a48 +35 1d9a68 +ca 120ada +90 29d726 +31 1d9a99 +31 46587 +0 42ff0 +3d 1d9bbf +98 29d87d +39 1d9bf0 +74 2acdc +47 1f0828 +b1 110bee +b5 26974f +5d 1e9247 +8e c92e8 +14 1d5b6e +59 1e9278 +10 1d5b9f +90 29d9d4 +79 217f3 +30 e11a +3c e2a2 +a6 298c48 +76 2ace3 +3b 1d9bf7 +7a 2ae09 +7e 2ae3a +23 1d938b +fd ea91b +b4 d7242 +24 1cfdfa +f9 ea94c +b0 d7273 +53 1f1414 +bc d7399 +2c 1cff51 +f6 f3dda +bb 2a2cee +f2 f3e0b +fe f3f31 +fa f3f62 +79 21aa1 +30 e3c8 +8c d28ff +c6 2814ce +7d 21ad2 +34 e3f9 +76 2af91 +3b 1d9ea5 +f6 f4088 +4 42d71 +4d 5644a +a5 d6952 +bb 2a2f9c +f2 f40b9 +70 2acab +e6 12358b +21 1d9384 +3a 4799a +f4 f3dd3 +b9 2a2ce7 +fc f3f2a +f8 f3f5b +ba 110af3 +47 5e64a +74 2af8a +f4 f4081 +7a 5af27 +b9 2a2f95 +fb 124323 +b2 110c4a +b8 d7368 +28 1cff20 +fa f3f00 +6a 1ecab8 +79 1b3a43 +30 1a036a +3b 479a7 +30 ce58 +3c 1a0490 +38 1a04c1 +76 1bced1 +72 299f0 +7e 1bd028 +7a 1bd059 +f2 f4057 +0 42d40 +49 56419 +a1 d6921 +e9 27d499 +cc 28066a +3b 47c55 +76 1bd17f +25 19fa49 +f0 f3da2 +f8 f3ef9 +63 1b324e +8c 29cf4d +ba 110a91 +70 299e9 +7c 1bd021 +78 1bd052 +7b 1ed16c +32 1d9a93 +48 1af192 +3a 1d9bea +fb 1242c1 +b2 110be8 +a9 29a02a +8c 29d1fb +b4 cdc30 +5f 5f102 +74 1bd178 +da 128473 +7b 1ed41a +32 1d9d41 +4c 1af1b7 +4e 1af1be +4a 1af1ef +2a 3dcd7 +5c 1afb18 +58 1afb49 +f7 27dc79 +2e 58a6 +c 192634 +6a 22468 +48 1af1f6 +e7 27d326 +8e c9598 +9c c9ef2 +9e c9ef9 +cc e6122 +21 197666 +ce e6129 +f7 2be6d7 +bd 10fb08 +e 19268f +1c 192fe9 +84 102b5a +4c 1af219 +4e 1af220 +2a 3dd39 +5c 1afb7a +5e 1afb81 +c6 11f6f2 +3a e278 +91 cad8b +5a 1f1310 +74 2acea +b7 2a1b52 +7c 2ae41 +b1 1085f2 +e6 2b57e7 +5a 56d72 +fb ea953 +b2 d727a +98 103341 +77 1f6654 +f8 f3f69 +74 2af98 +fb eac01 +5a 57020 +b2 d7528 +98 1035ef +77 1f6902 +ac cd6d4 +51 26b05 +f0 f40c0 +77 21980 +2f 1cff05 +df 2ba3e5 +3c 479d2 +9e d3507 +b8 110afa +13 19c479 +7d 5b202 +34 47b29 +cc 120b12 +3a 1a04c8 +78 1bd060 +7b 1b3cf8 +32 1a061f +12 8fc6 +1e 19c5fe +38 1d9bf1 +8c c959f +de 2b365c +d2 120024 +83 10ab6f +8f 29e1a7 +77 5c366 +9c c9f00 +cc e6130 +f5 2be6de +c 192696 +52 1e911b +2a 3dd47 +5c 1afb88 +32 19f0af +c4 11f6f9 +59 1d658 +10 9f7f +5d 1d689 +14 9fb0 +18 a0d6 +1c a107 +31 e127 +39 e27e +52 26b17 +56 26b48 +32 3f565 +81 c93a6 +8d 25c9de +5a 26c6e +5e 26c9f +d9 e67b1 +90 d30d8 +98 d322f +b1 d7280 +b9 d73d7 +d6 efc3f +62 1b4251 +59 1d906 +10 a22d +5d 1d937 +66 1b4282 +14 a25e +56 26df6 +d9 e6a5f +90 d3386 +47 1b7168 +b1 d752e +a9 268c66 +8 1d5085 +d6 efeed +5b 56d81 +12 436a8 +5f 56db2 +16 436d9 +1a 437ff +1e 43830 +33 47850 +df 11fea9 +96 10c7d0 +db 11feda +92 10c801 +9e 10c927 +9a 10c958 +b3 1109a9 +bf 110acf +bb 110b00 +5b 5702f +12 43956 +5f 57060 +16 43987 +33 47afe +df 120157 +96 10ca7e +db 120188 +92 10caaf +b3 110c57 +d2 efc0e +42 1e87c6 +2b 3da36 +5d 1af877 +14 19c19e +f9 f2ef4 +59 1af8a8 +10 19c1cf +10 8cbd +1c 19c2f5 +18 19c326 +35 1a0346 +ca e73b8 +90 264004 +31 ce65 +0 98ce +3d 1a049d +c2 1289d1 +98 26415b +56 1b8d36 +52 1b8d67 +e2 27d348 +d9 e69fd +90 d3324 +47 1b7106 +b1 d74cc +d2 efebc +8e 294bf8 +5d 1afb25 +2b 3dce4 +14 19c44c +59 1afb56 +10 19c47d +90 2642b2 +c2 128c6f +f6 ea82c +31 1a0625 +56 1b8fe4 +52 1b9015 +db 11fe78 +92 10c79f +9a 10c8f6 +95 10b80a +b3 110947 +5f 1e8fa0 +16 1d58c7 +fb 12c61d +12 423e6 +1e 1d5a1e +1a 1d5a4f +33 1d9aa0 +db 120126 +92 10ca4d +5f 1e924e +16 1d5b75 +5b 1e927f +12 1d5ba6 +33 1d9d4e +bf cddf1 +8e ca85a +fd ea97b +cc e73e4 +0 312 +3d 196ee1 +c 19394a +40 1cea3 +7d 1b3a72 +4c 1b04db +eb 12ba70 +7d 21832 +34 e159 +54 1f11f1 +3c e2b0 +5c 1f1348 +a6 298c56 +72 2acc0 +76 2acf1 +ca e710c +a1 cd54f +ad 260b87 +7a 2ae17 +91 cb02b +7e 2ae48 +58 56d79 +f9 ea95a +b0 d7281 +d7 ee980 +f2 125189 +75 1f665b +b8 d73d8 +f6 f3de8 +31 3f7ab +66 1ec9a0 +ae cd42d +f2 f3e19 +3a 1d9e36 +fe f3f3f +fa f3f70 +7d 21ae0 +34 e407 +54 1f149f +76 2af9f +f9 eac08 +58 57027 +46 1b7169 +11 9f74 +b0 d752f +9f 29d854 +f6 f4096 +3a 479a8 +3e 479d9 +ff 124052 +b6 110979 +be 110ad0 +ba 110b01 +f2 f3db7 +62 1ec96f +fa f3f0e +6a 1ecac6 +43 1b7383 +f5 f2e22 +79 1b3a51 +30 1a0378 +38 1a04cf +76 1bcedf +7a 1bd067 +f2 f4065 +ae 298da1 +79 1b3cff +30 1a0626 +76 1bd18d +ba 110a9f +3 1d3f14 +b5 10f9b3 +32 4658f +3e 1d9bc7 +3a 1d9bf8 +fb 1242cf +b2 110bf6 +f7 27c9c7 +bd cddf8 +8c ca861 +d2 1212e6 +fd ea989 +cc e73f2 +f5 2bf9a0 +0 320 +3d 196eef +ce 11f539 +c 193958 +52 1ea3dd +59 1d6ac +10 9fd3 +97 26559f +fd 27cdc5 +b4 2696ec +52 1b8cf9 +18 a12a +93 d20be +9f 2656f6 +b0 d620b +bc 269843 +31 e17b +91 296949 +73 1bcea1 +39 e2d2 +99 296aa0 +a 1d50f0 +35 1a05e6 +52 26b6b +1d a0fa +f6 286284 +31 d105 +3d 1a073d +5a 26cc2 +f2 f2da3 +fe 2863db +d3 2b34e1 +59 1b7bf8 +7b 2ae6a +db 2b3638 +dd e67d4 +94 d30fb +d6 281e21 +9c d3252 +f7 285fc9 +bd d73fa +8a 10ad37 +b5 d622d +de efdea +8f 10c02b +d0 12103d +d8 121194 +50 1b0c60 +59 1d95a +10 a281 +31 e429 +c7 28152f +a 1d539e +3b 1d8935 +52 26e19 +5e 1d92f +10 193e13 +dd e6a82 +94 d33a9 +8 1d50e9 +50 26b64 +83 29d07d +d7 282130 +f4 28627d +d1 2b34da +16 1cc5b9 +79 2ae63 +d9 2b3631 +11 1d4632 +33 478a4 +19 1d4789 +3b 479fb +8b ca828 +d6 1212b5 +8c 29e211 +80 10abd9 +d4 efc8c +88 10ad30 +dc efde3 +95 29d75a +b7 1109cc +39 1d892e +8 1d5397 +50 26e12 +11 1d48e0 +33 47b52 +ad 2a2667 +a1 10f02f +f5 f40e2 +4f 1b757d +6a 1edd86 +5d 1af8cb +2b 3da8a +14 19c1f2 +c4 e5fd7 +f9 f2f48 +10 8d11 +1c 19c349 +b2 ceec8 +35 1a039a +9d 10c977 +8a 29e493 +2 1d3cd7 +56 1b8d8a +35 19f324 +52 258a9 +a 1d3e2e +5e 1b8ee1 +b 41c41 +58 1ea28b +5d 1afb79 +2b 3dd38 +14 19c4a0 +b2 cf176 +35 1a0648 +2 1d3f85 +56 1b9038 +50 258a2 +8 1d3e27 +5c 1b8eda +9d c9e9d +a6 2607e8 +5b 1b7e49 +37 1d9ac3 +33 465e2 +2 4304b +3f 1d9c1a +0 1d3f7e +6b 5a8c9 +54 1b9031 +37 1d9d71 +5b 1d6b3 +12 9fda +1a a131 +11 19af10 +33 e182 +64 1b3277 +93 296950 +19 19b067 +3b e2d9 +bf 2996af +d6 e7b93 +6c 1b33ce +9b 296aa7 +50 26b72 +d7 28213e +58 26cc9 +d3 eec5d +df 282295 +71 2ad1a +a0 29a180 +85 263977 +16 1cc5c7 +79 2ae71 +a8 29a2d7 +8d 263ace +df e67db +96 d3102 +9e d3259 +d4 efc9a +dc efdf1 +8a 29e1d7 +72 5c396 +f5 f3e42 +98 29db1d +fd f3f99 +5b 1d961 +12 a288 +11 19b1be +df 2ba3d7 +33 e430 +50 26e20 +71 2afc8 +df e6a89 +96 d33b0 +d4 eff48 +f5 f40f0 +97 29eccf +93 10b7ee +9f 29ee26 +31 478ab +dd 11ff04 +94 10c82b +b5 1109d3 +f7 2bf6f9 +bd 110b2a +fb eabff +90 2642b4 +52 26b7b +e 18f +35 1a05f6 +59 5708a +a7 25f775 +10 439b1 +31 47b59 +b5 110c81 +5f 1af8d2 +16 19c1f9 +c6 e5fde +5a 5f36e +fb f2f4f +79 5c493 +12 8d18 +1e 19c350 +50 258b0 +5c 1b8ee8 +71 29a58 +fa ebbbe +40 264c1 +7d 1bd090 +5f 1afb80 +16 19c4a7 +6b 5a8d7 +54 1b903f +5d 1e8ffb +14 1d5922 +f9 12c678 +10 42441 +1c 1d5a79 +b2 1085f8 +35 1d9aca +31 465e9 +0 43052 +ba 10874f +3d 1d9c21 +5d 1e92a9 +14 1d5bd0 +b2 1088a6 +35 1d9d78 +79 21855 +30 e17c +38 e2d3 +72 2ad14 +7a 2ae6b +57 1f1445 +f6 f3e3c +fe f3f93 +36 1d04c0 +a3 2a24e4 +72 2afc2 +f6 f40ea +3a 479fc +7b 5b22c +32 47b53 +7a 5af89 +72 29a52 +7e 1bd08a +70 29a4b +7c 1bd083 +ce 2baff3 +c2 1279bb +5a 1b7e4a +28 1d117e +d 19a975 +db 280fa2 +2a 1d1185 +50 1af98e +f 19a97c +9 19a9a6 +b 19a9ad +24 19fc86 +6d 1b335f +26 19fc8d +6f 1b3366 +20 19fcb7 +69 1b3390 +34 1a05e7 +7d 1b3cc0 +30 1a0618 +79 1b3cf1 +4d 1b7506 +49 1b7537 +4b 1b753e +59 1b7e98 +2 1cbbc5 +b4 107664 +a3 25f7a6 +24 19fc94 +6d 1b336d +12 3a39c +1e 1cd9d4 +20 19fcc5 +69 1b339e +6b 2a7b7 +49 1b7545 +7b 2185c +32 e183 +3a e2da +4a 1b84fb +fc f3f9a +7b 21b0a +de 2ba3d8 +32 e431 +2c 45dd +70 2afc9 +5f 1f12ee +38 47a03 +17 19c4aa +dd 121164 +1e 3a4c0 +bf ce0a1 +dd 120150 +94 10ca77 +e6 2b6a9b +8 19bc05 +39 19f19c +a8 1080e9 +8d d18e0 +b8 108a4a +9d d2241 +98 2957df +ba 108a51 +9f d2248 +c6 2bac42 +a4 d6bf1 +ed ea2ca +ef 2b5941 +a6 2a2268 +18 d74 +cd ee471 +c8 2b1a0f +ea 124c81 +cf ee478 +dd eedd2 +d8 2b2370 +fa 1255e2 +df eedd9 +2d 196830 +ab 10eecf +dd 280d10 +2f 196837 +98 cae81 +51 56c23 +5d 1ea25b +df 280d17 +fc 284e64 +2a 1d11e7 +50 1af9f0 +f 19a9de +87 295d10 +24 19fce8 +6d 1b33c1 +26 19fcef +6f 1b33c8 +34 1a0649 +7d 1b3d22 +36 1a0650 +7f 1b3d29 +e7 12389a +c7 2b28a1 +70 29a59 +7c 1bd091 +c 39b66 +ad cd747 +ff 2b7804 +f3 1241cc +1c 3a4c7 +bd ce0a8 +e4 2b6aa2 +2d 19683e +ab 10eedd +dd 280d1e +73 1ed2c3 +28 1d11ee +d 19a9e5 +53 1f146a +59 1d6ba +10 9fe1 +52 1b8d07 +18 a138 +31 e189 +73 1bceaf +39 e2e0 +d4 e7b9a +35 1a05f4 +52 26b79 +6a 291f4 +89 25ca0f +dd e67e2 +94 d3109 +b5 d72b1 +f7 285fd7 +bd d7408 +d6 efca1 +bf 26989f +12 42686 +b3 d6267 +1e 1d5cbe +b5 d623b +de efdf8 +59 1d968 +10 a28f +dd 2ba3de +31 e437 +52 26e27 +dd e6a90 +94 d33b7 +d6 eff4f +1a 43861 +33 478b2 +3b 47a09 +d6 1212c3 +9e 10c989 +b7 1109da +bf 110b31 +5b 57091 +12 439b8 +33 47b60 +b7 110c88 +5d 1af8d9 +2b 3da98 +14 19c200 +58 5f375 +f9 f2f56 +10 8d1f +1c 19c357 +c6 1289f2 +35 1a03a8 +a5 2a24ae +c2 128a33 +56 1b8d98 +35 19f332 +52 258b7 +5e 1b8eef +2b 3dd46 +5d 1afb87 +14 19c4ae +c6 128ca0 +35 1a0656 +56 1b9046 +fb 12c67f +12 42448 +1e 1d5a80 +4a 1e015 +21 4458 +2d 197a90 +ab 11012f +d1 ee938 +dd 281f70 +fb 27e0bd +88 25b74e +23 445f +d9 128223 +2f 197a97 +d3 ee93f +df 281f77 +8a 25b755 +29 197ac1 +d9 281fa1 +2b 197ac8 +14 192e30 +db 281fa8 +1 85ff +d 19bc37 +db 282264 +9 19bc68 +b 19bc6f +39 cfbe +61 20fe9 +6d 1b4621 +c8 2782df +8c 10acef +63 20ff0 +6f 1b4628 +ca 2782e6 +69 1b4652 +41 25190 +4d 1b87c8 +43 25197 +4f 1b87cf +49 1b87f9 +4b 1b8800 +79 29b4f +4a 1e023 +21 4466 +2d 197a9e +ab 11013d +d1 ee946 +dd 281f7e +aa ce9ce +88 25b75c +eb 1236b0 +d4 281e18 +29 197acf +34 60a9 +d9 281faf +1 860d +d 19bc45 +cb 127857 +9 19bc76 +61 20ff7 +6d 1b462f +ea eb55f +c8 2782ed +69 1b4660 +49 1b8807 +d9 12972f +5b 1af84d +e 3ae21 +af cea02 +4 1d4f53 +ec 2b5be7 +8d d2ba2 +88 296140 +d0 e7bbb +b9 2996d7 +8f d2ba9 +3b 5f29 +cc 2b9d8e +ed eb58c +18 2036 +cd ef733 +c8 2b2cd1 +f9 2b6268 +cf ef73a +79 21863 +30 e18a +38 e2e1 +2e 4336 +72 2ad22 +8 1ccfd7 +a9 260bb8 +7a 2ae79 +21 44ba +84 29cd88 +2d 197af2 +bc d7409 +23 44c1 +86 29cd8f +2f 197af9 +d3 ee9a1 +8b 29cf26 +df 281fd9 +f0 f2aee +fc 286126 +f6 f3e4a +32 4682f +3e 1d9e67 +fe f3fa1 +2e 45e4 +72 2afd0 +8a ca7c7 +bb cdd5e +1 8661 +d 19bc99 +50 1b0cb2 +3 8668 +f 19bca0 +3d cfef +61 2104b +6d 1b4683 +63 21052 +8c 10ad51 +c6 2b9920 +6f 1b468a +ca e7358 +fb ea8ef +41 251f2 +4d 1b882a +43 251f9 +4f 1b8831 +7d 29b80 +89 d184b +3a 47a0a +be 110b32 +a 19bc0c +3b 19f1a3 +7d 1b3a82 +49 1f1ec5 +34 1a03a9 +30 cec8 +3c 1a0500 +72 29a60 +7e 1bd098 +c 3ae28 +ad cea09 +f3 12548e +21 44c8 +ef 1236e1 +84 29cd96 +2d 197b00 +ab 11019f +d1 ee9a8 +dd 281fe0 +73 1ee585 +cf 127888 +1 866f +d 19bca7 +53 1f272c +18 a376 +1c a3a7 +a6 298e94 +11 436f6 +39 e51e +c3 edfec +cf 281624 +53 6028e +70 643db +59 1b7e44 +7b 2b0b6 +9c d349e +91 10c7ed +98 d34cf +da f0067 +d9 280f9d +fb f420f +56 6051a +88 10af7c +dc f002f +be 10776c +21 3ee9e +9e 10cbc7 +99 29db2e +bb 110da0 +8 97d7 +42 1b83a6 +98 d346d +b9 d7615 +fb f41ad +10 8f5d +1c 19c595 +11 1d58e4 +18 19c5c6 +38 3e3f1 +72 1ecfc0 +39 1a076e +5a 1b915e +2b 1d8222 +73 29c9d +7f 1bd2d5 +da 280f93 +f9 27e0b8 +5f 26f40 +b 41e8d +58 1ea4d7 +7b 1bd306 +9a 10cb96 +77 1ee552 +86 25b381 +29 1d821b +71 29c96 +7d 1bd2ce +d8 280f8c +1a 1d5cef +69 1bb688 +3b 1d9e97 +1a a37d +19 19b2b3 +3b e525 +58 26f15 +5c 26f46 +e6 2b5a33 +51 60295 +79 2b0bd +9a d34d6 +d8 f006e +f9 f4216 +e2 1235ba +ee 2b6bf2 +9c 10cbce +98 10cbff +4f 1f09e1 +b9 110da7 +f6 27ccc8 +9a d3474 +d8 f000c +f9 f41b4 +1a 19c5cd +33 d10c +3f 1a0744 +b9 261527 +1f a3af +18 1cd946 +9a 264402 +3b 1a0775 +50 25afc +5c 1b9134 +51 1f2483 +58 1b9165 +cd 277fff +84 264926 +71 29ca4 +7d 1bd2dc +d8 280f9a +79 1bd30d +a5 268ace +75 1ee559 +18 1d4718 +a6 ce5fa +84 25b388 +bd 2698a6 +10 4268d +62 1ec6b1 +b1 d626e +1c 1d5cc5 +18 1d5cf6 +98 29db2b +3c e550 +a6 298ef6 +31 4789f +7a 2b0b7 +29 d981 +7e 2b0e8 +73 64437 +bc d7647 +b1 110996 +fe f41df +c 42ec8 +ad d6aa9 +f3 12d52e +fa f4210 +7c 2b0e1 +b1 108892 +e6 2b5a87 +3a 47c48 +3e 47c79 +fc f41d8 +be 110d70 +80 10c0e5 +b4 cdca2 +c9 11f7be +ba 110da1 +47 5e8f8 +b8 d7616 +fa f41ae +8 42e97 +a9 d6a78 +30 d106 +3c 1a073e +31 1d9a8d +38 1a076f +72 29c9e +7e 1bd2d6 +21 c568 +2d 19fba0 +73 1f6625 +7a 1bd307 +ba 110d3f +70 29c97 +7c 1bd2cf +3a 1d9e98 +2e 3efc8 +54 1d7d1 +c 1cbd56 +3e 3f929 +1c 1cc6b7 +eb 124c22 +48 1e01a +1a 1cc6e1 +5e 1afb2d +fb 12c60f +c6 11f69e +5a 1afb5e +ce 2b2d07 +c2 11f6cf +86 25b31f +56 1d7d8 +e 1cbd5d +1e 1cc6be +1a 1cc6ef +e4 2b57e0 +3a e526 +91 cb039 +5a 1f15be +7c 2b0ef +47 1e84da +b1 1088a0 +e6 2b5a95 +2d 3da52 +71 6443e +59 26c5c +f8 f4217 +3c 47c80 +32 d10d +3e 1a0745 +33 1d9a94 +3a 1a0776 +78 1bd30e +38 1d9e9f +8e 102cba +8c 102cc1 +9e 10361b +9c 103622 +1e 1cc712 +1c 1cc719 +b1 2a18d0 +eb 124c84 +ce e6137 +9b 10c8eb +de e6a98 +8e 102cc8 +9e 103629 +5e 1afb8f +fb 12c671 +c6 11f700 +e 1cbdbf +1e 1cc720 +6a 1b43a8 +18 a384 +5a 26f1c +5e 26f4d +53 6029c +98 d34dd +4f 1b72bf +b9 d7685 +8d 10c014 +c7 2babe3 +1a 43aad +1e 43ade +9e 10cbd5 +9a 10cc06 +bb 110dae +ea 27d49f +98 d347b +4f 1b725d +b9 d7623 +da f0013 +a0 1102f0 +e9 1239c9 +1 42d35 +18 19c5d4 +31 d113 +5a 26cd0 +3d 1a074b +f3 12c216 +ff 2bf84e +c2 128c7f +98 264409 +52 25b03 +5e 1b913b +53 1f248a +5a 1b916c +cf 278006 +86 26492d +9a 10cba4 +12 42694 +1e 1d5ccc +1a 1d5cfd +bf 107513 +8e 103f7c +f7 2b60e9 +bd 10751a +8c 103f83 +3d 1d0611 +0 39a42 +c 1cd07a +3c e55e +92 d3071 +5c 1f15f6 +7 1cb949 +a6 298f04 +31 478ad +7a 2b0c5 +63 21050 +6f 1b4688 +c6 2b991e +7e 2b0f6 +2f 3da59 +73 64445 +19 a0cb +4e 1b72c0 +b8 d7686 +fe f41ed +af 106b50 +f3 12d53c +3a 47c56 +3e 47c87 +be 110d7e +fa f41bc +38 1a077d +72 29cac +7e 1bd2e4 +2f 1cfc47 +73 1f6633 +7a 1bd315 +ba 110d4d +c4 12779b +32 4683d +3e 1d9e75 +3a 1d9ea6 +ff ea990 +ce e73f9 +bf 107521 +8e 103f8a +3f 1d0618 +2 39a49 +e 1cd081 +52 1b8fa7 +18 a3d8 +73 1bd14f +39 e580 +c3 ee04e +cf 281686 +5a 26f70 +d6 2820cf +9c d3500 +f6 123efa +f7 286277 +bd d76a8 +62 5b787 +8a 10afe5 +de f0098 +ac 106b48 +f0 12d534 +d8 121442 +79 2b111 +88 10afde +dc f0091 +10 8fbf +1c 19c5f7 +a9 110446 +76 1ecff1 +52 25b57 +a 1d40dc +5e 1b918f +2c 1cfc3f +70 1f662b +50 25b50 +8 1d40d5 +5c 1b9188 +a6 260a96 +33 46890 +3f 1d9ec8 +1a a3df +19 19b315 +3b e587 +dc f009f +fd f4247 +d6 2bb7ff +9c 10cc30 +f7 2bf9a7 +bd 110dd8 +50 25b5e +5c 1b9196 +fa ebe6c +71 29d06 +7d 1bd33e +10 426ef +1c 1d5d27 +ba 1089fd +31 46897 +3d 1d9ecf +38 e581 +a5 267aca +c2 ee04f +ce 281687 +7a 2b119 +fe f4241 +78 2b112 +3a 47caa +6e 1edd55 +62 5a71d +be 110dd2 +80 10c147 +c9 11f820 +30 d168 +3c 1a07a0 +72 29d00 +7e 1bd338 +70 29cf9 +7c 1bd331 +69 221c2 +3b 1d0889 +85 263921 +cf 128b5a +39 1d0890 +2f 47317 +55 25b20 +d 1d40a5 +da e7cb7 +51 25b51 +5d 1b9189 +2b 47348 +9 1d40d6 +3f 47c78 +1d 1d4a06 +49 26369 +1b 1d4a30 +3b 47ca9 +19 1d4a37 +6f 1b3374 +88 103c34 +7f 1b3cd5 +e7 123846 +4b 1b754c +7a 1ee685 +5f 1b7e7c +c7 1279ed +5b 1b7ead +87 26366e +73 219b1 +d6 2ba27f +7f 1b4fe9 +2b 1cff36 +36 3e510 +db 2ba416 +3f 1d0866 +3b 1d0897 +1b 1d4a3e +79 22d63 +ad 106e69 +af 2a260c +f7 f4087 +a3 10efd4 +d5 280e15 +bf 1077c3 +8f 10b009 +8d 10b010 +9f 10b96a +9d 10b971 +2f 1cff59 +98 1045a3 +df 2ba439 +bf d73a1 +fc 2be586 +2d 1cff60 +27 19667c +dd 2ba440 +bd d73a8 +3f 1d08ba +3d 1d08c1 +f 1d4100 +8a 102c35 +de e7ce8 +d 1d4107 +1f 1d4a61 +9a 103596 +1d 1d4a68 +3a e588 +38 47cb1 +70 29d07 +7c 1bd33f +30 46898 +3c 1d9ed0 +bf 1077d1 +d0 120029 +dc 2b3661 +8f 10b017 +9f 10b978 +2f 1cff67 +df 2ba447 +3f 1d08c8 +52 1b8fb5 +18 a3e6 +73 1bd15d +39 e58e +d6 2820dd +9c d350e +f7 286285 +bd d76b6 +62 5b795 +de f00a6 +1a 43b0f +3b 47cb7 +9e 10cc37 +bf 110ddf +10 8fcd +1c 19c605 +52 25b65 +5e 1b919d +12 426f6 +1e 1d5d2e +2b 1d11ea +71 22c6c +29 1d11f1 +55 26de2 +1 41d2f +d 1d5367 +b 1d5391 +39 466e0 +94 25cf39 +51 26e13 +9 1d5398 +8c 10acfd +63 20ffe +6f 1b4636 +ca 2782f4 +4b 1b880e +ad 10812b +8f 10c2cb +8d 10c2d2 +23 3dbe3 +2f 1d121b +d3 1280c3 +df 2bb6fb +f0 12c210 +fc 2bf848 +21 3dbea +2d 1d1222 +d1 1280ca +27 19793e +dd 2bb702 +3 41d8a +f 1d53c2 +3d 46711 +1 41d91 +8a 103ef7 +bb 10748e +d 1d53c9 +38 e58f +84 29cde8 +1d a0fc +bc d76b7 +3a 47cb8 +30 d176 +3c 1a07ae +72 29d0e +7e 1bd346 +32 4689f +3e 1d9ed7 +d0 1212eb +8f 10c2d9 +23 3dbf1 +2f 1d1229 +d3 1280d1 +df 2bb709 +0 1921fc +3b 3f8fb +20 1963a3 +1 1921fd +21 1963a4 +8 192353 +28 1964fa +9 192354 +29 1964fb +e5 284401 +30 196d04 +ed 284558 +38 196e5b +cc 2b9d8c +19 192cb5 +8 1938c5 +39 196e5c +0 19a54a +6 3acd8 +3b 47c49 +40 1b70db +8 19a6a1 +48 1b7232 +9 19a6a2 +4a 1e25f +10 19aeab +58 1b7b93 +e7 1235ee +19 19b003 +a1 26784d +0 1d3c6c +b1 2681ae +eb eb562 +4a 57981 +10 1d45cd +b9 268305 +18 1d4724 +0 1934be +90 1046f8 +fa 2be85e +20 197665 +1 1934bf +8 193615 +28 1977bc +9 193616 +29 1977bd +49 1aeee5 +0 19b80c +40 1b839d +8 19b963 +48 1b84f4 +9 19b964 +a1 268b0f +49 1e8607 +ef f262b +0 1d4f2e +e7 284406 +14 1c56 +ef 28455d +1c 1dad +1e 1db4 +50 1e7b6 +52 1e7bd +58 1e90d +5a 1e914 +5e 1e945 +90 cad1c +eb f3602 +4a 5fa21 +f4 284cff +f6 284d06 +94 cad4d +96 cad54 +98 cae73 +fc 284e56 +fe 284e5d +0 19a85a +9c caea4 +21 46f3e +d0 e78ad +17 1d58ca +d4 e78de +99 2967f2 +13 423e9 +1f 1d5a21 +50 1e7c4 +54 1e7f5 +58 1e91b +97 29565d +5c 1e94c +94 cad5b +9c caeb2 +d4 e78ec +50 1e818 +35 19829a +52 1e81f +93 295680 +58 1e96f +3d 1983f1 +31 4db9 +5a 1e976 +90 cad7e +ce 128e07 +ff 12c39e +f4 284d61 +e2 12bb64 +ee 2bf19c +92 cad85 +29 19781f +74 1ee2ac +57 1ea15f +f6 284d68 +98 caed5 +fc 284eb8 +9a caedc +70 5adcb +7c 1ee403 +53 56c7e +5f 1ea2b6 +94 cad59 +fe 284ebf +d0 e790f +17 1d592c +b5 261391 +d2 e7916 +d8 e7a66 +13 4244b +1f 1d5a83 +bd 2614e8 +b1 cdeb0 +da e7a6d +85 29e367 +ef ea031 +4e 56450 +81 10ae86 +8d 29e4be +ff 27de42 +5e 1ea261 +f3 ea80a +52 56c29 +91 29ec97 +ea 27d74d +95 29ecc8 +ff ea992 +cb 128dd5 +5e 56db1 +91 10b7e7 +9d 29ee1f +7f 1ee409 +42 5783a +73 5add1 +28 3f000 +62 1edbcf +46 5786b +77 5ae02 +66 1edc00 +ce 11f547 +d6 11fd51 +de 11fea8 +ef 1236ef +e4 27c0b2 +d4 281e88 +eb 123720 +c6 120962 +f7 123ef9 +25 1d1059 +42 575de +d 3ab6d +ac 108128 +e6 2b6cf7 +ce 120ab9 +ff 124050 +21 3db78 +2d 1d11b0 +4a 57735 +f4 27ca13 +ea 2b6e7f +2d 3da54 +d9 27899f +90 2652c6 +f7 eaae9 +56 56f08 +6f 1edd56 +63 5a71e +f8 27cb39 +67 5a74f +b1 26946e +77 5b0b0 +d6 11ffff +1a 42531 +77 5c372 +cb 128b7b +4 39a03 +96 25bc80 +f7 1241a7 +42 5788c +25 3dbab +b7 25fe28 +e3 eb16b +42 5758a +ef eb2f3 +4e 57712 +63 5b732 +db f0016 +52 1d509 +5e 1b0b41 +a5 29a15e +c2 1206e3 +31 196fb5 +5a 1b0b72 +ce 120809 +e7 12485a +73 216b1 +7f 1b4ce9 +48 5672a +da 2789a7 +ef 1249b1 +e4 27d374 +eb 1249e2 +e3 eb419 +42 57838 +e7 eb44a +7b 647da +46 57869 +63 5b9e0 +52 1d7b7 +5e 1b0def +c2 120991 +5a 1b0e20 +86 25c5e1 +8a 10acc7 +e7 124b08 +9c 10b65e +73 2195f +7f 1b4f97 +da 278c55 +e3 124b39 +98 10b68f +7b 1b4fc8 +59 5809e +a7 260789 +6e 5a5f7 +a1 10f02d +ad 2a2665 +db 12822a +7e 1ee408 +72 5add0 +b1 2a2e3e +b5 2a2e6f +eb 12cf7c +7e 5af58 +b1 10f98e +bd 2a2fc6 +4f 1f1c4f +43 5e617 +47 5e648 +7b 1b3c96 +32 1a05bd +5f 1f25b0 +53 5ef78 +57 5efa9 +77 1f533e +46 1f1da7 +7e 1b3a78 +eb 285a9c +ea 12371f +28 461c +62 1b31eb +f6 123ef8 +cf 127896 +c4 280259 +cb 1278c7 +e2 eb0fa +65 1bc5cc +54 5f251 +9 87c4 +43 1b7393 +66 5a74e +df 2bb9b9 +d3 128381 +76 5b0af +4f 1f1efd +43 5e8c5 +d8 280ce0 +47 5e8f6 +57 5f257 +f6 1241a6 +d7 12834e +df 2bb9b7 +d3 12837f +5 41d52 +97 263fcf +4f 2507f +6a 5b888 +c6 279482 +f7 27ca19 +6e 5b8b9 +91 2642b3 +db 1294ec +43 5f8d9 +47 5f90a +ee 1249b0 +4 39a11 +cb 128b89 +54 60513 +9 9a86 +43 1b8655 +47 251d6 +62 5b9df +66 5ba10 +8f 102c57 +d3 129643 +43 5fb87 +d8 281fa2 +47 5fbb8 +c7 128caf +42 1e8518 +53 25b06 +5f 1b913e +c3 128ce0 +5b 1b916f +ce 2b19e3 +87 264930 +d8 129730 +5a 1af84e +ef 2b6b9f +e3 123567 +52 1e8e79 +d7 129610 +5a 1e8fd0 +df 129767 +a2 1102e9 +eb 1239c2 +d4 28212a +6f 1ec7e6 +6b 1ec817 +8d 10afa0 +ef 2b6e4d +e3 123815 +7b 1b3ca4 +7b 1f5706 +e7 27c376 +46 1e8795 +f7 27ccd7 +56 1e90f6 +52 1e9127 +77 1ed29e +41 5e672 +4d 1f1caa +ca 1207d8 +e3 124829 +6b 294f7 +42 1e97da +6b 1edad9 +45 1f1e01 +c2 12092f +e3 124ad7 +7b 1f69c8 +e7 27d638 +46 1e9a57 +42 1e9a88 +6d 1f4b8f +ea 1236bd +cf 2bad46 +c3 12770e +5b 1b7b9d +66 1ec68e +de 280f72 +62 1ec6bf +e7 12ce56 +da 280fa3 +6e 1ec7e5 +76 1ecfef +7e 1ed146 +8c 10af9f +ff 12d90e +f4 2862d1 +47 1f0836 +4b 1f09be +57 1f1197 +5b 1f131f +7a 1b3ca3 +cf 2baff4 +c3 1279bc +5b 1b7e4b +df 2bb955 +d3 12831d +66 1ec93c +62 1ec96d +76 1ed29d +72 1ed2ce +43 1f0b15 +53 1f1476 +c3 1289d0 +66 1ed950 +d2 eebfc +de 282234 +47 1f1af8 +4b 1f1c80 +65 1f5fa8 +c7 ee2cd +e2 124ad6 +7a 1b4f65 +f7 ea83b +56 56c5a +c3 128c7e +66 1edbfe +47 1f1da6 +43 1f1dd7 +50 1e826 +93 29568e +58 1e97d +90 cad8c +55 1ea166 +21 45ed4 +2d 1d950c +4a 5fa91 +f4 284d6f +98 caee3 +51 56c85 +5d 1ea2bd +fc 284ec6 +d0 e791d +d8 e7a74 +e7 2846b4 +14 1f04 +16 1f0b +f8 27cde5 +90 cafca +eb f38b0 +4a 5fccf +f4 284fad +57 1ea3ab +f6 284fb4 +94 caffb +96 cb002 +17 1d5b78 +50 1ea72 +54 1eaa3 +94 cb009 +ca 281902 +dd efde6 +57 56c5b +eb 27d74e +77 1ecff0 +46 1e9a59 +66 5a4a2 +6a 5a5c8 +6e 5a5f9 +e3 12ce27 +76 5ae03 +7a 5af29 +eb 12cf7e +7e 5af5a +c7 11f3f1 +86 10be6f +cf 11f548 +c4 277f0b +ee 2b6c02 +e2 1235ca +ea 123721 +fe 124051 +11 19c472 +fa 124082 +4f 1e9baf +43 56577 +dd f0094 +5f 1ea510 +53 56ed8 +57 56f09 +6e 1edd57 +62 5a71f +66 5a750 +c7 11f69f +e6 123847 +ee 2b6eb0 +e2 123878 +f6 1241a8 +6a 5b88a +97 295403 +20 196651 +56 1e8e3a +f7 27ca1b +c7 1206b3 +cf 12080a +c4 2791cd +cb 12083b +e6 12485b +bc 25ffe5 +e2 12488c +ee 1249b2 +a4 25f779 +c7 120961 +c3 120992 +e6 124b09 +1d 192cd8 +bc 260293 +e2 124b3a +4a 5e76f +4e 5e7a0 +5a 5f0d0 +ce 127897 +d6 1280a1 +f9 1252da +77 64414 +de 1281f8 +4e 1f1efe +7f 1f5495 +42 5e8c6 +e3 12481d +c6 1279ee +e9 124c27 +f3 12517e +d6 12834f +e7 2856ca +46 1f1ae9 +f9 125588 +77 646c2 +4a 5fa31 +0 19a7f8 +cf 2b29f8 +c3 11f3c0 +47 1e84e8 +2 1d4f97 +4b 1e8670 +57 1e8e49 +12 1d58f8 +5b 1e8fd1 +62 1ec6c1 +bd 2685e2 +e7 12ce58 +6e 1ec7e7 +6a 1ec818 +43 1b70d5 +f5 f2b74 +ef 12cfaf +72 1ed022 +f7 12d7b9 +7e 1ed148 +7a 1ed179 +ff 12d910 +cf 2b2ca6 +c3 11f66e +71 216fe +7d 1b4d36 +53 1e9128 +66 1ec93e +76 1ed29f +72 1ed2d0 +cb 1207d9 +c0 27919c +b8 25ffb4 +47 1e97aa +e6 2843b3 +43 562c9 +4f 1e9901 +ee 28450a +4b 1e9932 +ea 28453b +66 1ed952 +62 5a471 +6e 1edaa9 +c3 120930 +19 192ca7 +b8 260262 +43 1e9a89 +ca 127866 +7b 22dda +de 2bb6a8 +d2 128070 +da 1281c7 +42 1f0868 +4e 1f098e +75 1bbeb5 +4a 1f09bf +56 1f1198 +ca 2818f6 +fb 284e8d +79 1ee3d1 +52 1f11c9 +5e 1f12ef +5a 1f1320 +de 2bb956 +d2 12831e +18 202a +eb 2847da +69 1edd1e +42 1f0b16 +fb 28513b +aa ce720 +88 25b4ae +79 1ee67f +52 1f1477 +ca 128b28 +46 1f1af9 +24 196676 +eb 2857ee +42 5e618 +4e 1f1c50 +21 1d80c4 +75 1bd177 +4a 1f1c81 +39 1d05e2 +50 1eac6 +3b 1d05e9 +52 1eacd +90 cb02c +ff 12c64c +4a 5fd31 +f4 28500f +92 cb033 +29 197acd +74 1ee55a +57 1ea40d +f6 285016 +1f 42561 +b9 2996d9 +d0 e7bbd +17 1d5bda +bb 2996e0 +d2 e7bc4 +50 1ead4 +f4 eaa6f +ac 298ff4 +8f 294ea7 +52 1e8e0b +18 3a23c +bc 299955 +9f 295808 +73 1ecfb3 +8 3ae4d +39 3e3e4 +28 1d11e2 +58 1ea4d9 +5f 26f42 +51 60297 +d 398ab +ac 106e66 +e6 2b5a35 +71 1f68da +2d 1cfeee +4a 56473 +59 603ee +ee 2b5b8c +1d 3a20c +bc 1077c7 +f6 2b6396 +3d 1d084f +5a 56dd4 +fe 2b64ed +0 1d3cd0 +54 1b8d83 +6b 5a61b +6a 1edd7a +83 25b34f +94 10320d +e6 2b6d4b +5d 26c9b +c7 2b1641 +4e 1e9c02 +42 565ca +6f 1eddaa +63 5a772 +7f 1ee70b +73 5b0d3 +c0 ee2f4 +cc 28192c +83 25b5fd +94 1034bb +d6 120053 +f7 1241fb +51 1b8cf1 +0 1d4f92 +6b 5b8dd +84 103b6e +10 9c5 +1c 193ffd +c6 120706 +35 196fd8 +52 1d55d +5e 1b0b95 +e7 1248ae +73 21705 +7f 1b4d3d +21 3ee9c +b3 261119 +63 5ba34 +84 103e1c +10 c73 +1c 1942ab +bb 10f840 +a5 107fc4 +94 29d6e9 +31 4e1b +3d 198453 +c6 1209b4 +52 1d80b +5e 1b0e43 +e7 124b5c +d6 2ba281 +8b 2637f4 +73 219b3 +9c 10b6b2 +7f 1b4feb +3c 1d18c4 +9e c9be9 +30 3e28c +b7 299858 +72 1ecfb2 +38 3e3e3 +bf 2999af +6a 5a61a +7e 1ee45c +72 5ae24 +4b 5e7c2 +68 6290f +5b 5f123 +78 63270 +a3 25f4f6 +b4 1073b4 +95 10b55c +d7 1280f4 +a9 d57b4 +f4 12c241 +f7 2be489 +c6 2baef2 +7e 1ee70a +72 5b0d2 +4f 1f1f51 +43 5e919 +6c 1f609e +60 62a66 +53 1ea0da +5f 1f28b2 +53 5f27a +7c 1f69ff +70 633c7 +a3 25f7a4 +b4 107662 +f6 1241fa +62 1ed913 +28 3ed44 +d5 278b19 +a3 106cd8 +af 29a310 +50 1b8cf0 +4b 5fa84 +68 63bd1 +85 10bebd +11 8d14 +9a cae7a +1d 19c34c +c7 128a55 +53 258ac +5f 1b8ee4 +e4 12cba2 +8e ca7f8 +bf cdd8f +20 3ee9b +b2 261118 +1 43043 +93 2652c0 +40 1e87c1 +62 5ba33 +43 5fbdb +60 63d28 +7 192289 +85 10c16b +4 1cb9a3 +81 29e328 +14 1cc304 +89 29e47f +1c 1cc45b +25 1cfb4b +b8 29ac38 +9d 26442f +4 1ccf15 +35 1d04ac +14 1cc5b2 +35 1d075a +ef 2bef43 +e3 12b90b +3a 1d1846 +4 1ccc65 +0 39784 +c 1ccdbc +21 3d92c +2d 1d0f64 +bd 1077ca +b 1cbd2b +4 1ccf13 +52 26e1b +3b 1d8937 +24 1cfb4a +9c 26442e +2c 1cfca1 +a1 2a24cf +34 1d04ab +a9 2a2626 +3c 1d0602 +82 102820 +8e 295e58 +5 1d3cf2 +8a 102977 +d 1d3e49 +9a 1032d8 +1d 1d47aa +14 426be +31 3f4ed +b5 d629f +66 1ec6e2 +de 280fc6 +e3 2bf067 +76 1ed043 +a7 d6bf7 +1 1ccbd3 +a0 29a18e +85 263985 +24 1cfdf8 +34 1d0759 +bc 110b2b +a 1d508c +1a 1d59ed +31 3f79b +66 1ec990 +76 1ed2f1 +86 c9131 +24 1d0e0c +b4 107672 +2 1cbbd3 +90 d20b8 +9c 2656f0 +82 103ae2 +5 1d4fb4 +1 41ad3 +8a 103c39 +d 1d510b +d2 eec50 +de 282288 +86 c93df +24 1d10ba +82 103d90 +5 1d5262 +66 1edc52 +5d 26f49 +c7 2b18ef +48 5647a +69 5a622 +68 1edd81 +40 575e5 +c7 2b2bb1 +48 5773c +c3 11f6d0 +cf 2b2d08 +69 5b8e4 +c4 12070d +50 1d564 +2a 3ed5b +5c 1b0b9c +e5 1248b5 +71 2170c +7d 1b4d44 +7b 1b3a4a +32 1a0371 +c4 1209bb +2a 3f009 +50 1d812 +5c 1b0e4a +88 10ad22 +e5 124b63 +bc 2685e3 +1d 19b028 +38 1d1831 +68 5a621 +7 1d4f59 +ef 2b5bed +f7 2b63f7 +78 5af82 +17 1d58ba +ff 2b654e +49 5e7c9 +d5 1280fb +f5 2be490 +5f 1d93e +c4 2baef9 +68 5b8e3 +e3 123877 +ef 2b6eaf +49 5fa8b +c5 128a5c +44 1e9804 +40 56323 +4c 1e995b +44 1e9ab2 +64 1ec6e9 +f9 27ddfc +b 41bd1 +aa 10f18c +dc 280fcd +6c 1ec840 +64 1ec997 +74 1ed2f8 +c6 e5cd0 +64 1ed9ab +b 42e93 +d0 eec57 +aa 11044e +dc 28228f +c6 e5f7e +64 1edc59 +8e 294ba4 +ca 11f57a +f5 eaa70 +ad 298ff5 +39 1d05d2 +8 1cd03b +da 11fedb +bd 299956 +2c 1d0f65 +20 3d92d +a7 298ef9 +e5 eb193 +b7 29985a +38 3e3e5 +ed eb2ea +bf 2999b1 +6a 5a61c +85 1028ad +8d 102a04 +d3 278afd +a4 106a55 +a3 25f4f8 +2 1cb917 +b4 1073b6 +21 1d93e6 +e6 1235ed +21 47198 +29 1d953d +ee 123744 +29 472ef +31 1d9d47 +f6 123f4e +66 1f4cee +31 47af9 +39 1d9e9e +fe 1240a5 +6e 1f4e45 +39 47c50 +a4 106d03 +e6 12389b +f6 1241fc +20 3ebef +a7 29a1bb +28 3ed46 +50 1b8cf2 +a3 106cda +af 29a312 +3a 1d05d8 +85 103b6f +8d 103cc6 +d3 279dbf +e6 1248af +bf cdd91 +20 3ee9d +b2 26111a +3a 1d0886 +85 103e1d +a4 107fc5 +e6 124b5d +73 64443 +87 29d0a0 +8 41c2b +7b 6459a +8f 29d1f7 +c5 ef33a +97 29da01 +18 4258c +cd ef491 +9f 29db58 +84 10abfc +f7 12d56b +8c 10ad53 +ff 12d6c2 +d2 280e4c +77 1bd18e +c6 127794 +61 1b4499 +ce 1278eb +69 1b45f0 +d6 1280f5 +de 12824c +84 10aeaa +f7 12d819 +c6 127a42 +d6 1283a3 +0 42d96 +b5 10f6b1 +87 29e362 +8 42eed +bd 10f808 +83 10ae81 +8f 29e4b9 +25 1d93a9 +42 5f92e +f7 12c249 +84 10bebe +c6 128a56 +92 2652c1 +f7 12c4f7 +5 1cb9a4 +1d 1cc45c +24 1cfb4c +a1 2a24d1 +34 1d04ad +a9 2a2628 +3c 1d0604 +e3 2bf069 +76 1ed045 +eb 2bf1c0 +7e 1ed19c +34 1d075b +76 1ed2f3 +1 39785 +d 1ccdbd +ac 2679c6 +24 1d0e0e +56 1b0c88 +2 1cbbd5 +24 1d10bc +66 1edc54 +4 1d3cf3 +77 1f6662 +c 1d3e4a +14 1d4654 +1c 1d47ab +11 43696 +f9 12432a +b0 110c51 +46 1f088b +56 1f11ec +5e 1f1343 +be 29969e +4 1d3fa1 +77 1f6910 +14 1d4902 +56 1f149a +4 1d4fb5 +0 41ad4 +c 1d510c +77 1f5392 +46 1f1dfb +63 1bb28c +29 c6bd +74 6314a +0 42da2 +49 5647b +a7 25f4c7 +10 43703 +59 56ddc +79 1ed171 +48 1e9bda +6c 1edb04 +60 5a4cc +68 5a623 +78 5af84 +84 10beca +cd 11f5a3 +b8 261518 +e4 1235f4 +ec 12374b +4d 1e9c0a +41 565d2 +d3 27884f +f2 27c9f7 +e4 1238a2 +60 5b78e +68 5b8e5 +7a 1ed177 +c5 12070e +ac 2a1334 +cd 120865 +e4 1248b6 +b9 26126b +9c 26443c +18 1cd68a +48 5e7ca +cc 1278f2 +7d 1f54f0 +4c 1f1f59 +fa 12401e +40 5e921 +d2 280b9e +77 1bcee0 +5c 1f28ba +50 5f282 +c4 127a49 +40 5f935 +48 5fa8c +c4 128a5d +8e 25b474 +d2 281e60 +64 1ec6eb +6c 1ec842 +e1 2bf070 +74 1ed04c +e9 2bf1c7 +7c 1ed1a3 +74 1ed2fa +64 1ed9ad +64 1edc5b +d6 e6621 +8e 294ba6 +44 1f0892 +3a 1d9e44 +4c 1f09e9 +54 1f11f3 +5c 1f134a +a6 298c58 +e1 28467c +54 1f14a1 +44 1f1b54 +40 5e673 +4c 1f1cab +1c 90f5 +6e 1b3119 +39 5f24 +ae cd6db +f2 f40c7 +53 26b0c +d6 2bb551 +9c 10c982 +73 22c83 +50 1ea0d4 +f6 f40f8 +57 26b3d +fa f421e +5b 26c63 +88 264d5c +58 1ea22b +fe f424f +5f 26c94 +80 d1755 +8c 264d8d +ac 106bb8 +e6 2b5787 +e6 27c0b9 +94 d2095 +b1 ceec4 +21 19ea02 +bd cf04c +4a 1b74db +d8 12011e +c6 280260 +91 d306b +da 120125 +5d 1f15f7 +93 d3072 +52 1e7bb +a 1ccd40 +9b d31c9 +9d d31f3 +d6 eec2d +f3 eba5c +84 264996 +cd 27806f +f5 eba86 +97 29d761 +f7 eba8d +61 1bb593 +fd ebbdd +9f 29d8b8 +63 1bb59a +ff ebbe4 +d3 efc03 +d5 efc2d +d7 efc34 +db efd5a +dd efd84 +df efd8b +46 1b7177 +11 9f82 +15 9fb3 +4e 1b72ce +19 a0d9 +83 294a7f +1d a10a +87 294ab0 +90 29ec88 +71 2296c +79 22ac3 +ac cd6e2 +51 26b13 +c6 27816a +55 26b44 +59 26c6a +ce 2782c1 +c3 2b1610 +c6 28026e +91 d3079 +95 d30aa +ce 2803c5 +99 d31d0 +9d d3201 +f1 eba63 +f5 eba94 +f9 ebbba +fd ebbeb +d1 efc0a +d5 efc3b +d9 efd61 +5f 26f50 +54 1b8d91 +6b 5a629 +30 6086 +e5 f3783 +6a 1edd88 +43 1b8645 +f5 f40e4 +d6 11fdb3 +c6 1209c4 +f7 123f5b +e6 2b6d59 +d6 120061 +f7 124209 +25 1d1067 +42 575ec +2d 1d11be +21 3db86 +4a 57743 +f4 27ca21 +6b 5b8eb +35 196fe6 +52 1d56b +5e 1b0ba3 +e7 1248bc +73 21713 +7f 1b4d4b +fb 12d933 +c6 1209c2 +52 1d819 +5e 1b0e51 +8a 10ad29 +e7 124b6a +1f 19b02f +3a 1d1838 +4b 5e7d0 +5b 5f131 +f6 123f5a +d7 128102 +f7 2be497 +c6 2baf00 +39 5f86 +58 57089 +a6 25f774 +11 9fd6 +86 25b62d +57 56c4d +f6 124208 +d7 1283b0 +6a 5b8ea +4b 5fa92 +c7 128a63 +53 258ba +5f 1b8ef2 +73 229c7 +7b 22b1e +e6 27c305 +ac cd736 +51 26b67 +91 cafc9 +c6 2781be +c7 128d11 +53 25b68 +5f 1b91a0 +bb 2695c0 +1a 1d59df +ae cd73d +53 26b6e +5b 26cc5 +e2 2b57b8 +aa 106e2e +dc 278c6f +d6 eec8f +52 57edd +f3 ebabe +19 3b4ab +4e 1e86a0 +56 1e9158 +25 1cfda5 +42 5632a +4e 1e9962 +31 3f4fb +66 1ec6f0 +39 3f652 +6e 1ec847 +47 1f0898 +4f 1f09ef +57 1f11f9 +5f 1f1350 +31 3f7a9 +66 1ec99e +76 1ed2ff +47 1f0b46 +57 1f14a7 +66 1ed9b2 +d2 eec5e +de 282296 +47 1f1b5a +43 5e679 +4f 1f1cb1 +66 1edc60 +47 1f1e08 +39 5f94 +11 9fe4 +19 a13b +71 229ce +79 22b25 +ac cd744 +d 189 +e6 27c313 +51 26b75 +fb 28513d +91 cafd7 +5a 1f155c +c6 2781cc +ee 27c46a +59 26ccc +99 cb12e +ce 278323 +6b 63c39 +b 39881 +aa 106e3c +dc 278c7d +18 3b4aa +b9 cf08b +f 42ed2 +50 57ee4 +f1 ebac5 +58 5803b +f9 ebc1c +66 1b3270 +31 607b +15 a253 +bc 299709 +53 26dba +fa 2b6270 +55 26de4 +1e 3b4d4 +bf cf0b5 +fc 2b629a +50 1ea382 +57 26deb +fe 2b62a1 +e6 27c367 +b1 cf172 +49 1f0c05 +6b 63e77 +b5 cf1a3 +ca 127b66 +c6 28050e +91 d3319 +93 d3320 +f3 ebd0a +d3 efeb1 +67 1b4285 +15 a261 +51 26dc1 +55 26df2 +f1 ebd11 +3e 3f68b +d1 efeb8 +11 a284 +89 264aaf +71 22c6e +9c 10c974 +d6 2bb543 +8b 264ab6 +73 22c75 +51 26e15 +53 26e1c +52 5818b +f3 ebd6c +4a 1e98c3 +12 4370a +5b 56de3 +d5 eff3d +7b 1ed178 +4a 1e9be1 +6a 5a62a +80 d16f3 +8c 264d2b +7a 5af8b +c7 11f453 +b2 2613c8 +86 10bed1 +cf 11f5aa +ba 26151f +d7 11fdb4 +96 10c832 +df 11ff0b +e6 1235fb +ee 123752 +f6 123f5c +fe 1240b3 +82 102884 +8e 295ebc +d7 120062 +e6 1238a9 +f6 12420a +c7 120715 +ae 2a133b +cf 12086c +e6 1248bd +62 5ba43 +c7 1209c3 +bb 261272 +1a 1cd691 +ce 1278f9 +69 1b45fe +d6 128103 +f9 12533c +c 19a982 +29 1977b1 +e3 12487f +c6 127a50 +e9 124c89 +a1 cd2a1 +ad 2608d9 +f3 1251e0 +d6 1283b1 +46 1f1b4b +f9 1255ea +25 1d93b7 +42 5f93c +c6 128a64 +47 1e854a +57 1e8eab +16 1d5929 +5f 1e9002 +97 2642df +b2 29aae8 +31 3f4fd +66 1ec6f2 +e3 2bf077 +76 1ed053 +eb 2bf1ce +84 263912 +7e 1ed1aa +57 1e9159 +76 1ed301 +47 1e980c +b1 d7220 +e6 284415 +43 5632b +4f 1e9963 +b9 d7377 +ee 28456c +66 1ed9b4 +47 1e9aba +47 1b7108 +62 1ed911 +b1 d74ce +e6 2846c3 +66 1edc62 +56 1f11fa +79 1ee433 +69 1edd80 +46 1f1b5b +77 1f53a0 +46 1f1e09 +df 1294ab +11 a292 +d 437 +51 26e23 +b9 ce069 +18 3a488 +29 3dccf +eb ea2a0 +a2 d6bc7 +4a 566bf +a6 d6bf8 +0 1ccbd4 +ef ea2d1 +4e 566f0 +98 26540f +0 1d3f1c +54 1b8fcf +6b 5a867 +31 1d16dd +14 1d48ae +7f 5b1f9 +c1 127769 +cd 2bada1 +6 41d56 +a7 d5937 +a0 298ece +f4 27df81 +88 102c80 +2d 3efc2 +5 19bb42 +82 ca670 +b3 cdc07 +bf 26123f +9c 1035b0 +98 1035e1 +3d 3f923 +a9 106e28 +96 2953b0 +bd 107758 +b9 107789 +62 22001 +40 1aed8f +3d 19719d +ce 11f7e7 +ca 11f818 +35 1d87a6 +4 1d520f +6f 5bb5a +da 120179 +8e c92ea +14 1d5b70 +7f 5c4bb +84 29d044 +ef 12398f +94 29d9a5 +ff 1242f0 +90 29d9d6 +fb 124321 +a9 ce9ca +8 3ade9 +ad ce9fb +c 3ae1a +15 19af31 +92 c9a5f +9e 25d097 +b5 2681df +14 1d45fe +ef eb593 +4e 579b2 +73 1b4bc3 +ad 1080b9 +28 3dcce +38 3e62f +ce 2b1735 +4b 5ea0e +c7 e5fdf +5b 5f36f +39 1d05d4 +a8 106e27 +bc 107757 +b8 107788 +53 1b7aa8 +8d 10af9e +a1 2a121d +f5 2862d0 +89 10afcf +99 10b930 +cb 127b67 +e8 12bcb4 +db 1284c8 +f8 12c615 +d 8787 +28 3ef90 +2c 3efc1 +35 19f0d8 +4 19bb41 +b2 cdc06 +be 26123e +99 10cbf4 +9 43138 +d 43169 +93 d1dae +9f 2653e6 +4b 5fcd0 +e9 2b6e79 +4f 5fd01 +72 1b4bc2 +ac 1080b8 +1c 1d4a67 +39 1d1896 +cb 128e29 +88 102c1e +af 268f3e +e 1d535d +2 41d25 +a3 d5906 +f0 27df50 +98 10357f +a9 106dc6 +b9 107727 +39 19716c +ca 11f7b6 +d4 2820c6 +eb 12395e +fb 1242bf +18 1cc6d8 +7b 1ed418 +88 103ee0 +8 1cd039 +da 11fed9 +bd 299954 +42 56568 +ef 27d781 +e3 ea149 +4e 1e9ba0 +e2 2bdaf8 +6b 1edd79 +a8 106dc5 +b8 107726 +89 10af6d +b6 29a87b +f1 28629f +ea 12395d +b6 d5fe9 +cb 127b05 +db 128466 +28 1cff1e +38 1d087f +cc 128b50 +9 1d40c6 +19 1d4a27 +6e 1eca85 +6a 1ecab6 +7a 1ed417 +4b 1f0c5e +2b dbc6 +68 1f4dab +5b 1f15bf +67 22033 +78 1f570c +8d d187e +a8 108087 +b 19234d +1c 3a20b +bd cddec +89 10c22f +ff ea984 +5e 56da3 +b6 d72ab +cb 128dc7 +d 19a9d7 +8a c9505 +28 1d11e0 +9 1d5388 +62 5a70f +6e 1edd47 +4f 1b756f +6a 1edd78 +4b 1f1f20 +68 1f606d +5 9644 +a4 d6bff +ed ea2d8 +4c 566f7 +2f 1d8005 +de 278974 +fd eac39 +4b 1af19a +b4 d7560 +15 9fa5 +5c 57058 +dc e7d43 +88 102c90 +67 1f5fa3 +69 5a86e +95 d309c +dc 12014f +1 42d33 +e9 1239c7 +fd 1242f7 +da 279c59 +bd 25ff78 +ed 124c58 +21 d7ba +68 5a86d +25 d7eb +6c 5a89e +61 1b424d +44 1b741e +fe 27cb1b +d9 1284d1 +66 1bb310 +31 e11b +78 5b1ce +49 5ea15 +4d 5ea46 +30 1d9a9a +79 1ed173 +c9 127b6e +8d 2637be +d9 1284cf +9d 26411f +4d 25326 +68 5bb2f +f5 27ccc0 +6c 5bb60 +f2 ea7a5 +75 1bbc77 +44 1b86e0 +fe 27dddd +d9 129793 +49 5fcd7 +4d 5fd08 +d3 ee94d +df 281f85 +ec 124c57 +79 1ee435 +4b 1aef4e +c9 128e30 +81 d1448 +8d 264a80 +e9 123965 +4b 56412 +2 42d39 +ea 1239cd +a3 d691a +99 d346e +ce 280663 +4a 1cfad +e0 2bdaff +b4 d5ff0 +c9 127b0c +d9 12846d +4d 1b7576 +ca e60a4 +68 1edd7f +28 3dcd0 +38 3e631 +6 43018 +a0 29a190 +a7 d6bf9 +4f 566f1 +99 265410 +b3 d7529 +12 43948 +5b 57021 +6a 5a868 +63 2a350 +41 1b70de +d1 12963c +8d 102c50 +a1 298ecf +7 41d57 +f5 27df82 +89 102c81 +9d 1035b1 +99 1035e2 +ac 106df8 +a8 106e29 +bc 107759 +b8 10778a +86 10c10f +cf 11f7e8 +82 10c140 +cb 11f819 +e8 123966 +96 10ca70 +df 120149 +92 10caa1 +db 12017a +f8 1242c7 +ee 123990 +e7 f3478 +c5 280206 +ea 1239c1 +e3 f34a9 +c1 280237 +28 3ef92 +2c 3efc3 +5b 58035 +df eede7 +fa 1255f0 +35 19f0da +b2 cdc08 +be 261240 +30 1d8776 +6a 5bb2a +41 1b83a0 +8d 103f12 +89 103f43 +ac 1080ba +95 29d6f6 +cf 120aaa +91 29d727 +cb 120adb +e8 124c28 +b2 107338 +be 29a970 +35 1d880a +c5 2814c8 +4a 5ea0f +4e 5ea40 +5e 5f3a1 +88 10afd0 +fb 12d93f +2d 47312 +9c 10b900 +98 10b931 +3d 47c73 +3d 19f4ed +ce 127b37 +ca 127b68 +6f 63eaa +3d 46701 +c 4316a +da 129797 +92 d1daf +9e 2653e7 +5f 1b7c30 +7a 1ee439 +89 102c1f +f 1d535e +3 41d26 +f1 27df51 +99 103580 +a8 106dc7 +b8 107728 +82 10c0de +b6 cdc9b +cb 11f7b7 +92 10ca3f +db 120118 +ea 12395f +19 1cc6d9 +12 1d5b98 +5b 1e9271 +3b 61d9 +6e 1eca87 +7a 1ed419 +c4 2804b1 +89 103ee1 +a8 108089 +b6 cef5d +cb 120a79 +ea 124c21 +37 1d8803 +9 1cd03a +a8 267c43 +88 10af6e +fb 12d8dd +f0 2862a0 +7b 1f5458 +46 1e84e7 +e7 27c0c8 +98 10b8cf +56 1e8e48 +f7 27ca29 +39 19f4bc +ca 127b06 +8 1d40c7 +7b 1f6a36 +a7 2a21f7 +18 1d4a28 +b7 2a2b58 +4e 1f0c2e +4a 1f0c5f +5a 1f15c0 +7b 1f671a +46 1e97a9 +e7 27d38a +40 1e9a81 +df 278975 +47 1b716a +62 1ed973 +b1 d7530 +10 4394f +59 57028 +b5 d7561 +14 43980 +66 1ed9a4 +5d 57059 +68 5a86f +2 43049 +4b 56722 +6c 5a8a0 +fe 27cb1d +78 5b1d0 +12 439aa +5b 57083 +3b 19f1a5 +84 10c116 +cd 11f7ef +ec 123997 +86 10c171 +cf 11f84a +0 42d34 +e8 1239c8 +fc 1242f8 +96 10cad2 +df 1201ab +c9 120ae2 +ec 124c59 +95 29d758 +cf 120b0c +48 5ea16 +4c 5ea47 +de 280cc4 +c8 127b6f +6d 63eb1 +d8 1284d0 +7d 64812 +79 6326f +48 5fcd8 +af 2a2660 +a3 10f028 +d5 280e69 +49 1e9bd9 +c8 127b0d +d8 12846e +12 3b34e +2b 19e8a4 +76 1f5331 +16 3b37f +1a 3b4a5 +b5 cef03 +7e 1f5488 +1e 3b4d6 +ab 26799b +f6 2be428 +96 104476 +94 10447d +6b 1bc943 +fe 2be57f +2 1d3f83 +9e 1045cd +9c 1045d4 +52 1e7cb +56 1e7fc +5a 1e922 +5e 1e953 +12 3b35c +76 1f533f +1a 3b4b3 +91 295679 +7e 1f5496 +d6 e78f3 +15 1d5925 +92 104453 +f6 2be436 +11 42444 +1d 1d5a7c +9a 1045aa +fe 2be58d +10 3b3b7 +74 1f539a +18 3b50e +7c 1f54f1 +90 1044ae +5e 1d93f +f4 2be491 +98 104605 +fc 2be5e8 +eb ea2ae +3 961a +4a 566cd +ef ea2df +7 964b +0 1ccbe2 +4e 566fe +98 26541d +13 9f7b +fb eac0f +5a 5702e +10 1cd543 +ff eac40 +17 9fac +5e 5705f +87 d2742 +ce 11f7f5 +97 d30a3 +de 120156 +62 63ad5 +ef 12399d +e4 27c360 +ff 1242fe +10 1d462f +4a 579e3 +f4 27ccc1 +13 4369b +fb 12432f +20 1d108b +bf 25ff7f +2d 3dd02 +ef eb5a1 +4e 579c0 +eb 124c90 +a3 cd2a8 +af 2608e0 +23 d7c1 +6a 5a874 +27 d7f2 +20 1d0d89 +6e 5a8a5 +db 1284d8 +33 e122 +7a 5b1d5 +30 1d16ea +37 e153 +7e 5b206 +a7 d68e9 +ee 12399c +32 1d9aa1 +7b 1ed17a +b7 d724a +fe 1242fd +cf 127b44 +c4 280507 +cb 127b75 +e2 eb3a8 +65 1bc87a +df 1284a5 +a2 10f027 +ae 2a265f +d4 280e68 +db 1284d6 +f2 ebd09 +75 1bd1db +d 41ea9 +6e 5bb67 +db 12979a +cb 128e37 +21 1d912a +83 d144f +8f 264a87 +33 3f504 +6f 1eca94 +7f 1ed3f5 +6d 1f4e3d +a3 d68b8 +ea 12396b +b3 d7219 +7d 1f579e +fa 1242cc +27 19f9e0 +6e 1eca93 +37 1a0341 +7e 1ed3f4 +4b 1f0c6c +4b 1f1f2e +35 1982a8 +52 1e82d +12 3b3be +76 1f53a1 +1a 3b515 +7e 1f54f8 +b5 26139f +d2 e7924 +bd 2614f6 +b1 cdebe +da e7a7b +92 1044b5 +f6 2be498 +9a 10460c +fe 2be5ef +12 3b5fc +2b 19eb52 +76 1f55df +16 3b62d +ab 267c49 +f6 2be6d6 +96 104724 +a4 298bdf +52 1ea79 +56 1eaaa +12 3b60a +76 1f55ed +d6 e7ba1 +15 1d5bd3 +92 104701 +1d 4255a +f6 2be6e4 +96 104732 +10 3b665 +74 1f5648 +6a 5a876 +6e 5a8a7 +b8 2695c6 +7a 5b1d7 +7e 5b208 +86 10c11d +cf 11f7f6 +c4 2781b9 +ee 12399e +2 42d3b +ea 1239cf +fe 1242ff +12 4369c +fa 124330 +6e 5bb69 +cb 120ae9 +4a 5ea1d +6b 5b87d +4e 5ea4e +5a 5f37e +7b 5c1de +5e 5f3af +eb 124974 +ce 127b45 +ca 127b76 +6f 63eb8 +da 1284d7 +7f 64819 +7f 632a7 +4e 5fd10 +6e 1eca95 +7e 1ed3f6 +4b 1e9be0 +ca 127b14 +75 29a1b +45 1e9805 +da 128475 +4a 1f0c6d +7b 1ee3cc +5e 1f159d +12 3b66c +76 1f564f +d2 e7bd2 +92 104763 +52 1e90b9 +18 3a4ea +73 1ed261 +39 3e692 +86 29e0bf +cf 2b1798 +4a 56721 +5a 57082 +10 1d48df +7b 5b22a +c6 2b1880 +8c 102cb1 +80 295d2d +ce 11f849 +90 29668e +de 1201aa +63 1edbc2 +29 3eff3 +16 1cd57b +51 1b8f9f +bb 261270 +31 1d87d7 +0 1d5240 +6b 5bb8b +a3 110286 +36 3e262 +d5 2820c7 +72 1ed260 +38 3e691 +99 1045a2 +ce 2b1797 +53 1f1408 +19 42839 +4b 5ea70 +68 62bbd +5b 1ea231 +5b 5f3d1 +78 6351e +e6 2b5a27 +ac 106e58 +d4 280e04 +ab 25f8fb +f6 2b6388 +bc 1077b9 +91 103189 +9d 2967c1 +a0 299ed4 +ee 1239f0 +c5 280266 +9b 265417 +4b 5fd32 +68 63e7f +1c 1cc709 +3d 1d08b1 +0 39a32 +c 1cd06a +21 3dbda +2d 1d1212 +2c 1cff4f +3c 1d08b0 +8a 102c25 +d 1d40f7 +9a 103586 +1d 1d4a58 +39 3f8f2 +6e 1ecae7 +7e 1ed448 +20 3dbd9 +8e c9536 +2c 1d1211 +3d 1d0851 +5a 56dd6 +8a 103ee7 +1 41d81 +d 1d53b9 +62 5a771 +6e 1edda9 +85 d279d +3a 19f206 +cc 11f850 +5 42d64 +ed 1239f8 +15 436c5 +fd 124359 +ed 124cba +eb 27c49a +b5 d72a5 +14 436c4 +3 19b806 +fc 124358 +40 565d1 +4c 1e9c09 +a2 ce569 +25 19fa3b +6c 1ecaee +b2 ceeca +35 1a039c +7c 1ed44f +ce e60d5 +60 5a778 +6c 1eddb0 +19 3a4eb +38 3e693 +8d 102cb2 +9d 103613 +ee 1239f2 +e7 f34da +e0 2b6a71 +c5 280268 +fe 124353 +af 2a23c0 +f0 2b73d2 +a3 10ed88 +f7 f3e3b +d5 280bc9 +28 3eff4 +50 1b8fa0 +ba 261271 +8d 103f74 +4a 5ea71 +5a 5f3d2 +8c 10b001 +ff 12d970 +ce 127b99 +23 1d90dd +de 1284fa +33 1d9a3e +9a 265418 +bd 10fab6 +ff 12c64e +1d 1cc70a +3c 1d08b2 +7e 1ed44a +1 39a33 +d 1cd06b +ac 267c74 +20 3dbdb +2c 1d1213 +62 5a773 +6e 1eddab +c 1d40f8 +1c 1d4a59 +5e 1f15f1 +7f 1f54e9 +42 5e91a +4e 1f1f52 +6b 1bb3e3 +7c 632a1 +0 43050 +49 56729 +db 2789a6 +68 5a8d1 +fa 27cb4e +84 10c178 +3b 19f207 +cd 11f851 +94 10cad9 +dd 1201b2 +4 42d65 +ec 1239f9 +68 5bb93 +fa 27de10 +3b 1a04c9 +cd 120b13 +48 5ea78 +e7 12cba8 +da 280cf5 +cc 127ba0 +da 281fb7 +7c 1ed451 +60 5a77a +6c 1eddb2 +4c 1f0c97 +5c 1f15f8 +a6 298f06 +1e 4281e +3b 3f64d +6e 1ec849 +39 3f654 +1a 4252f +bb d6110 +fb 124331 +b2 110c58 +13 4369d +46 1f0899 +11 436a4 +e9 f35a5 +86 294cfb +ba 110daf +1b 437f4 +4e 1f09f0 +19 437fb +8e 294e52 +be 110de0 +1f 43825 +1d 4382c +96 10b7be +b3 1085ed +8d 294c00 +de 2b2336 +b7 10861e +b5 108625 +8 41ee5 +42 1f0ab4 +9e 10b915 +bb 108744 +ee 2b5940 +b9 10874b +23 1d812b +bf 108775 +bd 10877c +4a 1f0c0b +c6 2b9990 +91 10c79b +97 10c7c5 +9f 10c91c +9d 10c923 +73 22973 +1 1934b1 +a0 260a6c +7b 22aca +9 193608 +a8 260bc3 +53 26b1a +57 26b4b +cd 27831d +84 264c44 +5b 26c71 +88 264d6a +3b 3f65b +13 436ab +17 436dc +c8 2bb01f +f9 2be5b6 +1b 43802 +1f 43833 +f3 eba6a +f7 eba9b +fb ebbc1 +ff ebbf2 +d3 efc11 +d7 efc42 +db efd68 +b3 1085fb +59 5f378 +62 1f5cc3 +97 265291 +de 2b2344 +b7 10862c +5d 5f3a9 +66 1f5cf4 +bb 108752 +6a 1f5e1a +bf 108783 +93 10c7a2 +97 10c7d3 +9b 10c8f9 +9f 10c92a +39 3f6b6 +1b 43856 +f6 eaa76 +ae 298ffb +19 4385d +8e 294eb4 +9e 10b977 +bb 1087a6 +b9 1087ad +87 d27a4 +80 295d3b +ce 11f857 +a1 299ee3 +7 42d6b +ef 1239ff +b1 29a844 +17 436cc +ff 124360 +ef 124cc1 +a7 d694b +4f 56443 +6 42d6a +a0 299ee2 +ee 1239fe +81 29e08a +cf 127ba6 +91 29e9eb +df 128507 +cf 128e68 +33 3f566 +42 565d8 +4e 1e9c10 +39 3f900 +27 19fa42 +6e 1ecaf5 +4f 1f0c9d +62 5a77f +6e 1eddb7 +43 5e927 +4f 1f1f5f +73 229d5 +7b 22b2c +f 190 +53 26b7c +5b 26cd3 +3b 3f6bd +13 4370d +1b 43864 +b3 10865d +97 2652f3 +de 2b23a6 +bb 1087b4 +51 604e3 +13 4394b +63 1ed976 +46 1f0b47 +11 43952 +11 1d5ba0 +d6 11fda7 +17 4397c +b3 10889b +8d 294eae +b7 1088cc +93 10ca42 +c5 11f6fa +33 19f0b0 +9c 10c920 +73 22c21 +57 26df9 +33 3f7b2 +13 43959 +17 4398a +f9 2be864 +f3 ebd18 +d3 efebf +b3 1088a9 +62 1f5f71 +7f 1f67ab +42 5fbdc +73 63173 +b7 1088da +66 1f5fa2 +93 10ca50 +13 439ad +11 439b4 +b3 1088fd +2 43057 +4b 56730 +6a 5a8d8 +86 10c17f +cf 11f858 +96 10cae0 +df 1201b9 +6 42d6c +ee 123a00 +16 436cd +fe 124361 +6a 5bb9a +cf 120b1a +4a 5ea7f +eb 1249d6 +ce 127ba7 +fb 125337 +de 128508 +16 1d5bd7 +5f 1e92b0 +7e 1ed458 +43 565d9 +4f 1e9c11 +62 5a781 +6e 1eddb9 +7f 1f54f7 +42 5e928 +4e 1f1f60 +33 3f814 +13 439bb +b3 10890b +95 10c7be +94 d20f7 +b1 cef26 +10 3b345 +97 296911 +b5 cef57 +3 1934b8 +14 3b376 +9c d224e +b9 cf07d +18 3b49c +93 103430 +9f 296a68 +bd cf0ae +b 19360f +1c 3b4cd +f7 ebaef +56 57f0e +fb ebc15 +de eede6 +5a 58034 +ff ebc46 +5e 58065 +94 10446d +90 10449e +9c 1045c4 +98 1045f5 +10 1940c1 +b5 108615 +e3 284685 +42 1f0aa4 +bd 10876c +eb 2847dc +81 ca676 +4a 1f0bfb +d6 121005 +10 1f43 +de 12115c +18 208a +52 1b0c59 +f7 1251ad +31 60eb +94 29e9b9 +ff 125304 +b1 cf1d4 +10 3b5f3 +b5 cf205 +3 193766 +14 3b624 +94 10471b +90 10474c +b5 1088c3 +b1 1088f4 +f7 12545b +23 19765f +34 3f51d +1d 8e3a +bc d63f5 +38 3f643 +b3 1075d7 +bf 29ac0f +2b 1977b6 +3c 3f674 +46 1f0889 +f9 124328 +11 43694 +4e 1f09e0 +85 ca45b +19 437eb +9 1923b6 +54 1e8e43 +76 5c0b5 +25 1cfae9 +9d d3263 +c7 e6ff3 +5b 60383 +b4 108614 +95 d1e3c +b0 108645 +bc 10876b +99 10c944 +23 19790d +d9 2bb6d1 +34 3f7cb +46 1f0b37 +11 43942 +15 43973 +25 1cfd97 +42 5631c +4e 1e9954 +9d d3511 +f1 2b7683 +57 6050b +b4 1088c2 +95 d20ea +b0 1088f3 +13 192bb9 +91 10ca9b +90 10443c +98 104593 +94 10b7b5 +e6 2b57d9 +b1 1085e4 +ea 2b6bc1 +98 10cb9d +cf 2803b8 +da 12112b +b5 261145 +14 1cd564 +10 1cd595 +bd 26129c +10 3a083 +b1 cdc64 +1c 1cd6bb +18 1cd6ec +f7 27dcdd +56 1ea0fc +ff 27de34 +52 56c1b +f3 ea7fc +5e 1ea253 +31 1d06c7 +5a 1ea284 +90 1046ea +b5 2613f3 +14 1cd812 +10 1cd843 +31 1d19eb +95 d1dda +b0 1085e3 +99 d31c2 +ce 2803b7 +9d d1f31 +b8 10873a +df eeac9 +fa 1252d2 +6f 21184 +21 197668 +34 1d170b +9e 25d099 +15 19af33 +92 c9a61 +30 1d173c +30 3e22a +3c 1d1862 +76 1ee2a3 +72 5adc2 +7e 1ee3fa +85 264be5 +5b 1f25d3 +78 1f6720 +c6 2b9c2e +13 192b57 +91 10ca39 +15 1d5b61 +11 1d5b92 +f5 1251b4 +fd 12530b +98 10b621 +f5 125462 +6b 211a7 +0 19a85c +ce 2b9a75 +55 25882 +2f 47079 +70 5c08b +f7 2b7657 +84 294ce8 +5d 259d9 +78 5c1e2 +f3 124176 +ff 2b77ae +51 60233 +70 1b4e59 +2f 19fe47 +23 c80f +f4 1251b3 +2f 47327 +55 25b30 +70 5c339 +f4 125461 +b4 2610e2 +53 1af758 +d1 12963a +f1 125183 +79 29e51 +f1 125431 +f5 27df92 +7 41d67 +54 1ea3b1 +d6 280e6d +71 1ee58a +2a 19fb05 +dd eead0 +f8 1252d9 +53 1b0c58 +59 1f25da +a3 299ee8 +d5 eec27 +af 11041e +f0 125430 +30 3f4ee +38 3f645 +2b 562a +76 5c0b7 +95 10446e +9d 1045c5 +99 1045f6 +b4 108616 +b0 108647 +bc 10876d +b8 10879e +d7 121006 +df 12115d +db 12118e +f8 1252db +ab ce721 +f6 1251ae +30 3f79c +d9 2bb6d3 +23 19790f +34 3f7cd +95 10471c +b4 1088c4 +b0 1088f5 +d7 1212b4 +f8 124329 +10 43695 +84 ca45c +18 437ec +b 97d1 +56 6025e +c6 e6ff4 +5a 60384 +10 19b14b +90 10c7ee +98 10c945 +8b d28c8 +d6 129355 +de 1294ac +fb 2bfb2f +94 264273 +10 43943 +3 19bab6 +14 43974 +b 9a7f +56 6050c +16 192b89 +94 10ca6b +12 192bba +90 10ca9c +c6 2b1632 +91 10443d +ce 2b1789 +99 104594 +b0 1085e5 +b8 10873c +db 12112c +fa 1252d4 +bc 2682c5 +19 1cd6ed +27 45f0c +b8 2682f6 +34 1d170d +30 1d173e +30 3e22c +3c 1d1864 +38 1d1895 +5b 1ea285 +90 cad28 +fa 284e8e +7f 2ae3b +78 1ee3d2 +2b 197818 +76 1ee2a5 +72 5adc4 +7e 1ee3fc +c6 2b18e0 +91 1046eb +b0 108893 +34 1d19bb +30 1d19ec +90 10c78c +98 10c8e3 +da 12947b +10 1d58e5 +18 1d5a3c +b 19b9bf +56 1f244c +52 5ef6b +5e 1f25a3 +12 192b58 +90 10ca3a +14 1d5b62 +10 1d5b93 +d9 121195 +a9 ce728 +f4 1251b5 +60 222b6 +d7 121068 +fc 12530c +68 2240d +df 1211bf +f8 12533d +a9 ce9d6 +f4 125463 +d7 121316 +f0 125494 +ad cd429 +50 60234 +89 d28cf +d4 12935c +8c 1029a1 +d0 12938d +dc 1294b3 +22 5720 +d8 1294e4 +52 1af759 +8c 102c4f +d0 12963b +59 1ea28c +f8 284e95 +78 1ee434 +70 1ee58b +2f 46057 +d0 12932b +dc e6a81 +67 1f4ce1 +d8 129482 +5 3acd2 +6f 1f4e38 +9 19b9c6 +54 1f2453 +f9 286148 +50 5ef72 +5c 1f25aa +a6 299eb8 +58 1f25db +a2 299ee9 +ce e5e7b +9 19bc74 +54 1f2701 +b2 10739a +be 29a9d2 +f9 2863f6 +14 193e44 +50 581f4 +16 193e4b +52 581fb +22 3eb94 +54 1b09d5 +52 1b0a0d +8d 29ceec +75 5b0ab +2a 3eceb +50 1d4f4 +5c 1b0b2c +58 1b0b5d +14 193e52 +10 193e83 +22 3eba2 +54 1b09e3 +94 cadaf +96 cadb6 +9c caf06 +9e caf0d +d4 e7940 +d6 e7947 +16 193ead +56 1b0a3e +50 1d556 +2a 3ed4d +5c 1b0b8e +f3 ebacc +52 57eeb +fb ebc23 +5a 58042 +d6 121013 +de 12116a +fb 125343 +88 1029d4 +40 1af03b +2d 3ed16 +f3 ebd7a +52 58199 +f7 ebdab +56 581ca +73 5c341 +c 1cd00a +3d 1d05a1 +0 399d2 +d6 1212c1 +d2 1212f2 +35 3e25c +4 3acc5 +9a 10b628 +f7 125469 +84 102afa +f3 12549a +8c 296163 +bd 2996fa +80 102b2b +25 3ee6d +57 25889 +72 5c092 +76 5c0c3 +5f 259e0 +7a 5c1e9 +8e 294e46 +7e 5c21a +53 6023a +f6 1251ba +fe 125311 +57 25b37 +72 5c340 +76 5c371 +53 604e8 +57 60519 +f6 125468 +d3 129641 +5 43014 +51 5efd3 +5d 1f260b +da 121139 +f7 27dceb +56 1ea10a +52 1ea13b +31 1d06d5 +5a 1ea292 +7b 1ee43a +8 1cbacb +8d 10c262 +55 1f2762 +d2 121290 +f3 125438 +8c 296101 +bd 299698 +80 102ac9 +f7 27df99 +56 1ea3b8 +52 1ea3e9 +1a 1d471f +77 1ee560 +d2 2b221e +cb 2bad69 +4 1cbbf1 +76 1ee2b1 +5b 1f25e1 +d7 eec2e +75 1f6909 +f2 125437 +d3 1295df +76 1ee55f +57 1b7d87 +72 1ee590 +57 1f2707 +53 1f2738 +94 cadbd +9c caf14 +d4 e794e +14 193eb4 +22 3ec04 +54 1b0a45 +14 1940f2 +d6 2b31f5 +8b 25c768 +9c 104626 +bf 2a2cbf +b3 10f687 +22 3ee42 +54 1b0c83 +ff 2bf850 +f3 12c218 +56 1b0c8a +b5 107601 +de 1211be +52 1b0cbb +22 3ee50 +54 1b0c91 +cb 279307 +dc 1211c5 +94 cb05d +96 cb064 +bd 29970a +d4 e7bee +bf 299711 +d6 e7bf5 +56 1b0cec +72 5c094 +76 5c0c5 +7a 5c1eb +e5 2846af +30 196fb2 +d7 121014 +df 12116b +db 12119c +f6 1251bc +f2 1251ed +fe 125313 +b4 2600da +fa 125344 +11 192b50 +b0 26010b +72 5c342 +d7 1212c2 +d3 1212f3 +f6 12546a +f2 12549b +af cd430 +52 6023b +5a 60392 +10 19b159 +d6 129363 +b5 2a2e0f +8e 1029a8 +d2 129394 +de 1294ba +94 264281 +af cd6de +52 604e9 +d6 129611 +8e 102c56 +d2 129642 +db 12113a +57 1ea10b +64 62a97 +f6 284d14 +53 56c2a +5f 1ea262 +6c 62bee +fe 284e6b +5b 1ea293 +68 62c1f +fa 284e9c +76 1ee2b3 +72 1ee2e4 +72 5add2 +7e 1ee40a +7a 1ee43b +d3 121291 +53 1ea3ea +ac 10eefa +19 8e69 +53 1b7a38 +f2 284ff3 +76 1ee561 +72 1ee592 +d2 129332 +da 129489 +56 1f245a +34 196fd7 +fb 28614f +e 1cba9f +af 25f680 +52 1f248b +52 5ef79 +5e 1f25b1 +31 1d8a25 +5a 1f25e2 +d2 1295e0 +56 1f2708 +fb 2863fd +e 1cbd4d +af 25f92e +52 1f2739 +94 cb06b +d4 e7bfc +d6 11fd43 +14 194162 +22 3eeb2 +54 1b0cf3 +10 3b3a7 +b4 29aac0 +97 296973 +52 1ea0cd +18 3b4fe +b0 1075df +bc 29ac17 +93 103492 +9f 296aca +73 1ee275 +56 1f1446 +39 3f6a6 +35 1d19ba +52 57f3f +1d 3b4ce +bc 108a89 +f6 2b7658 +3d 1d1b11 +31 3e4d9 +5a 58096 +10 1d58f3 +7b 5c23e +83 25c611 +94 1044cf +d6 121067 +f7 12520f +94 29ea1b +ff 125366 +94 10b4f9 +77 1b4e32 +d2 278af0 +10 3b655 +31 3f7fd +c7 2b2903 +52 581ed +73 5c395 +83 25c8bf +94 10477d +b5 108925 +d6 121315 +f7 1254bd +9e caeab +30 3f54e +b7 29ab1a +72 1ee274 +38 3f6a5 +b3 107639 +bf 29ac71 +5b 603e5 +78 64532 +95 10c81e +d7 2bb544 +9d 10c975 +15 19c441 +d4 2b1f9c +f6 12520e +d7 1293b6 +a9 d6a76 +f4 12d503 +df 12950d +57 1b8fd9 +fc 12d65a +fb 2bf873 +c6 2b2902 +11 439a4 +53 6053c +70 64689 +a3 260a66 +b4 108924 +17 192bea +95 10cacc +d4 2b224a +f6 1254bc +14 1cd5c6 +10 3a0e5 +1c 1cd71d +35 1d176e +13 1cc535 +0 3acf6 +31 3e28d +3d 1d18c5 +1b 1cc68c +56 1ea15e +35 1d06f8 +52 56c7d +5e 1ea2b5 +14 1cd874 +35 1d1a1c +56 1ea40c +96 c9a92 +34 1d176d +c7 2b9c31 +12 1cc534 +92 104443 +15 1d5915 +9a 10459a +11 42434 +1d 1d5a6c +76 1ee305 +96 c9d40 +34 1d1a1b +92 1046f1 +15 1d5bc3 +76 1ee5b3 +50 57f46 +d7 2b3512 +a6 260788 +58 5809d +d3 120031 +df 2b3669 +79 5c245 +de e7a4a +70 5c0ed +9a 29d878 +f7 2b76b9 +78 5c244 +f3 1241d8 +ff 2b7810 +d5 1293bd +27 5750 +dd 129514 +51 60543 +e3 27d605 +f4 1254c3 +54 1ea165 +9 1925f4 +50 56c84 +5c 1ea2bc +54 1ea413 +d6 e6631 +74 1ee30c +29 19679b +de e6788 +70 5ae2b +7c 1ee463 +d6 e68df +74 1ee5ba +9e 295505 +30 3f550 +38 3f6a7 +72 5c0e8 +f6 125210 +fe 125367 +5f 1f1600 +b4 26012e +30 3f7fe +95 10477e +f6 1254be +10 436f7 +18 4384e +35 1d9d0a +52 6028f +d6 1293b7 +94 2642d5 +10 439a5 +52 6053d +d6 129665 +11 3a0e6 +1d 1cd71e +bc 268327 +34 1d176f +12 1cc536 +30 3e28e +3c 1d18c6 +1a 1cc68d +76 1ee307 +72 5ae26 +7e 1ee45e +34 1d1a1d +7c 5c213 +6b 1b4355 +82 29cdbe +76 1ee5b5 +14 1d5916 +10 42435 +1c 1d5a6d +56 1f24ae +14 1d5bc4 +56 1f275c +70 5c0ef +78 5c246 +c 398aa +ad cd48b +50 60296 +c 39b58 +ad cd739 +50 60544 +74 1ee30e +29 19679d +70 5ae2d +7c 1ee465 +80 29cdc5 +74 1ee5bc +9e 295507 +54 1f24b5 +9 19a944 +50 5efd4 +5c 1f260c +a6 299f1a +74 1b391a +e1 28593e +54 1f2763 +37 197ff3 +73 5c3a3 +92 25bcb1 +ff 27ce2e +5e 1e924d +17 19c19a +53 6054a +77 1b4b84 +d2 278842 +71 2169c +2e 46064 +7d 1b4cd4 +47 565a8 +d8 278992 +f6 2862e6 +57 1b8d2b +35 197ffa +4a 1f09bd +b2 cef2a +90 25bcb8 +8a 10acc5 +7e 5c4bc +16 3b37d +b7 cef5e +f4 2b6143 +90 29669c +de 1201b8 +97 d3105 +d4 2ba2ea +98 2967f3 +9f d325c +26 19667d +dc 2ba441 +43 1b0049 +e2 27d604 +f5 ebae8 +4b 1b01a0 +ea 27d75b +fd ebc3f +c2 2817ab +d5 efc8f +37 198055 +5c 1e92a8 +92 cad23 +15 19c1f5 +5e 1e92af +17 19c1fc +77 1b4be6 +23 46f45 +d2 e78b4 +55 1b8d86 +57 1b8d8d +6 1ccc0a +a7 2607eb +35 1d19c8 +52 57f4d +3d 1d1b1f +31 3e4e7 +5a 580a4 +7b 5c24c +8 398dd +b5 10760f +de 1211cc +56 1b0c98 +f7 12521d +84 1028ae +ff 125374 +94 10b507 +77 1b4e40 +8c 102a05 +d2 278afe +d6 121323 +9a 10b68a +f7 1254cb +84 102b5c +72 5c0f4 +7a 5c24b +8e 294ea8 +5b 603f3 +d7 1293c4 +df 12951b +57 1b8fe7 +57 57f0f +f6 1254ca +d7 129672 +56 1ea16c +bd ce09a +1c 3a4b9 +b 1925fb +35 1d0706 +52 56c8b +5e 1ea2c3 +77 1ee314 +4 1cb9a5 +56 1ea41a +76 1ee313 +3c 3e660 +2b 1967a2 +72 5ae32 +7e 1ee46a +57 1f24bb +53 5efda +5f 1f2612 +76 1ee5c1 +57 1f2769 +95 d310c +54 57f15 +43 1b0057 +f5 ebaf6 +5c 5806c +4b 1b01ae +fd ebc4d +d5 efc9d +dd efdf4 +15 19c203 +31 1982cb +33 1982d2 +71 1b4e5c +51 1b9003 +53 1b900a +f8 12d68b +df 2bb699 +d3 128061 +11 19c480 +71 1b4e6a +d 192625 +51 1b9011 +90 29694a +97 d33b3 +d4 2ba598 +43 1b02f7 +f5 ebd96 +56 581bc +f7 ebd9d +42 562bc +4e 1e98f4 +d0 2b34db +d7 eff44 +37 198303 +92 cafd1 +15 19c4a3 +57 1b903b +eb 2857fe +4a 1f1c1d +fc 12d6bc +d7 121076 +be 2a1c9c +df 1211cd +fe 125375 +15 192b81 +b4 26013c +d7 121324 +e 398b1 +35 1d9d18 +af cd492 +52 6029d +b5 10f95f +de 12951c +e 39b5f +af cd740 +52 6054b +57 1ea16d +f6 284d76 +53 56c8c +5f 1ea2c4 +fe 284ecd +c5 278156 +76 1ee315 +3c 3e662 +2b 1967a4 +57 1ea41b +57 1b7a69 +72 1ee272 +f6 285024 +82 29cdcc +76 1ee5c3 +56 1f24bc +1c 42809 +b 19a94b +76 1b3921 +e3 285945 +56 1f276a +46 1e97fd +95 d33ba +d5 eff4b +90 10c78a +f7 123eeb +c6 120954 +35 19830a +d7 128092 +15 19c4b1 +23 47201 +d2 e7b70 +55 1b9042 +b1 269462 +f8 2b6515 +ff f2f7e +10 1d5881 +9c 104872 +98 1048a3 +bd 108a1a +da 12143b +94 29ec67 +ff 1255b2 +1d 90e8 +38 3f8f1 +45 1f0891 +c2 11f3bf +ce 2b29f7 +4e 1f0c8e +85 ca709 +19 43a99 +1d 43aca +c7 e72a1 +5b 60631 +f9 2b77da +5f 60662 +bc 108a19 +1b 192d10 +99 10cbf2 +db 12978a +7a 1b39f5 +f8 12d8d7 +98 104841 +18 1cd99a +5a 1ea532 +7b 1ee6da +9d d21df +b8 1089e8 +ce 2b9d85 +1b 192cae +99 10cb90 +db 129728 +19 1d5ce9 +5b 1f2881 +78 1f69ce +fd 1255b9 +66 1bc5d2 +5d 25c87 +78 5c490 +fc 1255b8 +dd eede0 +f8 1255e9 +b0 cdc01 +bc 261239 +5b 1af8af +d9 129791 +fd 27e0e9 +50 56ed0 +f1 eaab1 +f 41ebe +5c 1ea508 +de 280fc4 +5b 56d73 +fa 12432e +12 4369a +b3 d727b +5a 1d90e +f0 2be460 +38 3f8f3 +9d 104873 +99 1048a4 +bc 108a1b +b8 108a4c +df 12140b +db 12143c +f8 125589 +84 ca70a +b5 cdca1 +18 43a9a +77 1b4bf4 +5e 60663 +1e 192ce0 +9c 10cbc2 +1a 192d11 +98 10cbf3 +ce 2b1a37 +99 104842 +b8 1089ea +db 1213da +19 1cd99b +b8 2685a4 +38 1d1b43 +53 56eca +5f 1ea502 +11 8fc2 +1d 19c5fa +9a cb128 +fe 28510b +5b 1ea533 +90 cafd6 +fa 28513c +7f 2b0e9 +78 1ee680 +1a 192caf +98 10cb91 +18 1d5cea +d9 121443 +fc 1255ba +df 12146d +f8 1255eb +5a 1af8b0 +d8 129792 +70 5b079 +7c 1ee6b1 +53 56f2c +5f 1ea564 +9a cb18a +94 cb007 +fe 28516d +16 1cd56d +14 1cd574 +96 264030 +9c 26443a +b9 261269 +12 1cd59e +61 1b2f37 +10 1cd5a5 +7e 1ed456 +37 1a03a3 +12 3a08c +1e 1cd6c4 +10 3a093 +1c 1cd6cb +9e 264187 +1a 1cd6f5 +20 19f9b5 +69 1b308e +8c 103f11 +52 1b0a1b +12 1cd5ac +12 3a09a +1e 1cd6d2 +59 1b90f6 +1a 1cd703 +9c 104636 +14 1cd5d6 +fb ebed1 +5a 582f0 +ff ebf02 +5e 58321 +de 121418 +da 121449 +3d 3e3b3 +c 3ae1c +ff 1255c0 +6 41d58 +f4 27df83 +8c 102c51 +fb 1255f1 +88 102c82 +2d 3efc4 +5f 25c8e +7a 5c497 +7e 5c4c8 +fe 1255bf +db 129798 +d 4316b +51 5f281 +5d 1f28b9 +da 1213e7 +fb 12558f +e 1d535f +3f 1d88f6 +2 41d27 +f0 27df52 +88 102c20 +ff 27e0f0 +f3 eaab8 +52 56ed7 +5e 1ea50f +f2 2be467 +72 5b07e +7e 1ee6b6 +d6 e7955 +12 3a33a +1e 1cd972 +b5 cdeef +de e7aac +56 1b0a4c +14 1cd822 +96 2642de +b9 261517 +10 1cd853 +37 1a0651 +52 1b0cc9 +16 1cd829 +bb 26151e +12 1cd85a +14 1cd884 +7a 5c499 +8a 10acd3 +7e 5c4ca +df 121419 +db 12144a +fe 1255c1 +fa 1255f2 +5a 60640 +5e 60671 +de 129768 +da 129799 +db 1213e8 +72 5b080 +7e 1ee6b8 +da 129737 +52 5f227 +5e 1f285f +d6 e7c03 +56 1b0cfa +16 1cd88b +52 1ea37b +18 3b7ac +73 1ee523 +39 3f954 +c3 11f422 +cf 2b2a5a +5a 58344 +10 1d5ba1 +7b 5c4ec +d6 2b34a3 +8b 25ca16 +9c 1048d4 +bf 2a2f6d +b3 10f935 +de 12146c +94 29ecc9 +ff 125614 +53 1f26ca +19 43afb +5b 60693 +78 647e0 +d7 2bb7f2 +1f 192d41 +9d 10cc23 +10 3a393 +1c 1cd9cb +31 3e53b +3d 1d1b73 +52 56f2b +5e 1ea563 +9a 104848 +11 426e2 +1d 1d5d1a +fd 12561b +eb 27d75c +fc 12561a +50 56f32 +5c 1ea56a +de e6a36 +70 5b0d9 +7c 1ee711 +38 3f955 +9d 1048d5 +fe 125615 +18 43afc +5a 60694 +de 1297bc +11 3a394 +1d 1cd9cc +bc 2685d5 +30 3e53c +3c 1d1b74 +72 5b0d4 +8a 29cf15 +7e 1ee70c +10 426e3 +1c 1d5d1b +52 5f27b +5e 1f28b3 +dd 121474 +51 56f33 +5d 1ea56b +98 cb191 +2b 45d78 +5d 1b7bb9 +fc 285174 +88 29cf1c +70 5b0db +7c 1ee713 +37 1d1715 +92 2953d3 +33 3e234 +3f 1d186c +9a 29552a +31 3e23b +3d 1d1873 +9f d1f9a +ba 1087a3 +98 295531 +ff 2b6550 +b6 2a2e77 +17 1d58bc +77 1b4b92 +7d 1ed1a2 +4c 1e9c0b +40 565d3 +d2 278850 +57 1b8d39 +37 1d1723 +92 2953e1 +33 3e242 +3f 1d187a +9a 295538 +b7 108680 +d2 278afc +bf 1087d7 +69 2114e +20 da75 +97 10c827 +28 dbcc +9f 10c97e +9d 10c985 +37 1d1777 +33 3e296 +3f 1d18ce +31 3e29d +3d 1d18d5 +17 1d591e +13 4243d +1f 1d5a75 +de 12147a +ff 125622 +8c 102cb3 +5f 58066 +fe 125621 +df 1297c9 +52 56f39 +5e 1ea571 +72 5b0e0 +7e 1ee718 +56 57f1c +f7 ebafd +33 3e4e2 +3f 1d1b1a +c8 e7111 +9a 2957d8 +5e 58073 +ff ebc54 +d7 efca4 +13 42689 +1f 1d5cc1 +67 1ec681 +df efdfb +b7 10868e +bf 1087e5 +55 60514 +97 10c835 +9f 10c98c +57 1b8d9b +37 1d1785 +33 3e2a4 +3f 1d18dc +37 1d19c3 +c0 e6fba +92 295681 +33 1d19f4 +91 d1e17 +9d 26544f +31 1d19fb +17 1d5b6a +13 1d5b9b +11 1d5ba2 +f 19262c +53 1b9018 +33 1d1a02 +13 1d5ba9 +b7 10892e +3 1cce96 +b5 108935 +8e 29cf48 +97 10cad5 +37 1d1a25 +17 1d5bcc +df 12147b +fe 125623 +de 1297ca +53 56f3a +5f 1ea572 +8a 29cf23 +72 5b0e2 +7e 1ee71a +52 5f289 +5e 1f28c1 +b7 10893c +97 10cae3 +94 10b569 +77 1b4ea2 +17 19aed8 +32 1d16e1 +57 1b9049 +b3 269469 +fa 2b651c +5b 1e8f61 +12 1d5888 +37 1d1a33 +25 45c49 +29 45d6f +2d 45da0 +73 1bbe99 +20 1d9377 +4 43013 +35 465aa +24 1d93a8 +ef 285821 +e3 f21e9 +4e 1f1c40 +42 5e608 +6f 1f5de8 +63 627b0 +67 627e1 +46 5fbab +77 63142 +66 1f5f40 +e7 e9ed8 +4a 5fcd1 +7b 63268 +6a 1f6066 +4e 5fd02 +7f 63299 +35 4786c +a5 10ed40 +79 6357f +3d 19f1cf +4 19228f +39 19f200 +ad 10ee97 +a9 10eec8 +6 192228 +84 10c10a +b5 10f6a1 +a4 2a249f +2 192259 +bd 2a2d0a +80 10c13b +b1 10f6d2 +3b 4f15 +a0 2a24d0 +e 19237f +8c 10c261 +bd 10f7f8 +a 1923b0 +88 10c292 +b9 10f829 +a8 2a2627 +3d 19f23d +0 866e +c 19bca6 +ce 127887 +25 19fcf7 +e7 12b8d8 +7f 1bbd67 +7a 5c1db +5f 259d2 +fe f2f8d +58 1e8f69 +ef 2bef41 +e3 12b909 +11 1c32 +c4 128d09 +46 1aee27 +7b 1bbd98 +d9 129731 +eb 12ba60 +c6 128ca2 +35 1a0658 +f7 12c239 +2e 196586 +e6 2bf037 +ca 128e2a +fb 12c3c1 +ea 2bf1bf +96 263fcc +bd 269898 +b1 d6260 +1c 1d5cb7 +10 4267f +b5 d6291 +3 19a7f2 +14 426b0 +2d 1d94fe +4a 5fa83 +21 45ec6 +25 45ef7 +86 264bdd +b7 268174 +c7 2b992d +35 46858 +f0 2b63c0 +f7 f2e29 +56 5f248 +6f 1f6096 +63 62a5e +67 62a8f +77 633f0 +94 10b7a7 +35 47b1a +ce 128bab +a5 10efee +5a 25a02 +3d 19f47d +ad 2a2657 +a1 10f01f +4 19253d +39 19f4ae +de 12950c +6 1924d6 +b5 10f94f +2 192507 +bd 2a2fb8 +b1 10f980 +ca 11f7c6 +4d 1f0c98 +e7 12bb86 +7f 1bc015 +ef 2bf1ef +e3 12bbb7 +11 1ee0 +46 1af0d5 +7b 1bc046 +f7 12c4e7 +21 46eda +29 47031 +e3 f34ab +42 5f8ca +a1 110033 +4 193551 +39 1a04c2 +ad 110159 +a9 11018a +31 cec7 +0 9930 +3d 1a04ff +ce 128b49 +a5 d6bf2 +4 43011 +df 278967 +96 26528e +21 47188 +e3 f3759 +42 5fb78 +23 1963ff +a1 1102e1 +4 1937ff +39 1a0770 +a9 10ee66 +6f 63bfa +4d 1f0988 +a 19234e +88 10c230 +b9 10f7c7 +39 19f20c +8 19bc75 +ca 127856 +39 1a077e +ca 128dc8 +fb 12c35f +0 1d3cc0 +c7 e5f7d +8 1d3e17 +10 1d4621 +d7 e68de +18 1d4778 +25 1d7e37 +8f 2637c5 +21 1d7e68 +2d 1d7f8e +29 1d7fbf +4 1d5201 +35 1d8798 +0 1d5232 +9f 264126 +31 1d87c9 +c 1d5358 +0 41d20 +3d 1d88ef +e4 2856d0 +8 1d5389 +39 1d8920 +e7 284408 +f9 1242c6 +46 1f0827 +f7 284d69 +56 1f1188 +ff 284ec0 +95 cad5a +5e 1f12df +6b 1f4b57 +10 1cc271 +b1 25fe52 +4a 1f1f21 +7b 1f54b8 +18 19b2a4 +9c 29edae +90 10b776 +6f 63ea8 +4d 1f0c36 +a5 267b1e +4 1d3f3d +0 1d3f6e +b5 26847f +14 1d489e +10 1d48cf +42 5e66a +4e 1f1ca2 +25 1d80e5 +8f 263a73 +21 1d8116 +5e 1f2603 +52 5efcb +35 1d8a46 +9f 2643d4 +31 1d8a77 +e7 2846b6 +46 1f0ad5 +63 1f4cae +a1 10ffd1 +a9 110128 +31 1a0377 +c2 1289c1 +5a 1b8e50 +39 1a04ce +ca 128b18 +0 1d4f82 +84 29e306 +ef 124c51 +b5 2a189d +c7 e723f +8 1d50d9 +80 10ae25 +8c 29e45d +d4 efed8 +bd 2a19f4 +25 1d90f9 +80 29cdb7 +21 45c18 +2d 1d9250 +88 29cf0e +8b d15a6 +29 1d9281 +63 1f5cc2 +6b 1f5e19 +b4 cdc94 +2 1921f5 +80 10c0d7 +18 19c566 +34 3e25b +23 19639d +a1 11027f +a5 268de0 +4 1d51ff +9f 264124 +0 1d5230 +d2 1280d0 +de 2bb708 +b5 2a1b4b +e7 285978 +46 1f1d97 +6a 22158 +48 1aeee6 +e2 f21e8 +ee 285820 +6b 20e97 +0 19a54c +8 19a6a3 +4a 1e261 +7b 217f8 +10 19aead +ed 285828 +e1 f21f0 +4c 1f1c47 +40 5e60f +c7 2b9bdb +cf 2b9d32 +fd 286189 +f1 f2b51 +5c 1f25a8 +50 5ef70 +d7 2ba53c +6d 1f5def +61 627b7 +65 627e8 +69 6290e +6d 6293f +cc 12788e +e5 12b8df +7d 1bbd6e +79 1bbd9f +7b 21ab4 +e0 2bf06f +4a 1aef4f +c8 128e31 +f9 12c3c8 +e8 2bf1c6 +6d 1f609d +61 62a65 +7d 1f69fe +8c 26382d +71 633c6 +79 1bc04d +c7 2bae9d +61 63a79 +a7 298f07 +69 63bd0 +e1 12cbd2 +79 1bd061 +ed 12ccf8 +e9 12cd29 +61 63d27 +63 1b2f9e +e1 12ce80 +79 1bd30f +c8 12785d +e9 12ba05 +12 1d5b38 +5e 1e8ff3 +cb 2bb017 +69 1f4b5e +48 1f1f28 +79 1f54bf +cc 2bafec +c0 1279b4 +58 1b7e43 +dc 2bb94d +d0 128315 +ed 2bf194 +e1 12bb5c +8 475 +fd 2bfaf5 +42 1af044 +f1 12c4bd +cf 280612 +61 1f4cb5 +df 280f73 +71 1f5616 +c0 1289c8 +58 1b8e57 +c8 128b1f +4 1cbbef +a5 25f7d0 +e1 12cb70 +e9 12ccc7 +48 1f1c78 +cf 281626 +c3 edfee +61 1f5cc9 +cb ee145 +69 1f5e20 +58 1b9105 +e5 28597f +44 1f1d9e +df 280cc3 +40 1f1dcf +cf 2818d4 +c3 ee29c +61 1f5f77 +22 3eb92 +54 1b09d3 +7 8389 +0 1cb920 +27 19e71e +32 3f4f3 +17 8cea +10 1cc281 +7f 1f6a67 +73 6342f +37 19f07f +a2 10ed79 +d4 280bba +ae 2a23b1 +24 45c4a +28 45d70 +2c 45da1 +72 1bbe9a +23 19e6ed +34 465ab +a4 cd341 +38 466d1 +2b 19e844 +3c 46702 +6e 1f5de9 +62 627b1 +66 627e2 +6a 62908 +6e 62939 +2b c6b6 +76 63143 +e6 e9ed9 +7a 63269 +7e 6329a +a4 10ed41 +78 63580 +ac 10ee98 +53 1ea388 +a8 10eec9 +b4 10f6a2 +bc 10f7f9 +b8 10f82a +e6 12b8d9 +ee 12ba30 +ea 12ba61 +ab d57ad +f6 12c23a +fe 12c391 +fa 12c3c2 +2c 1d94ff +20 45ec7 +3c 1d9e60 +30 46828 +8c 10ad5f +63 21060 +6f 1b4698 +c6 2b992e +6e 1f6097 +62 62a5f +66 62a90 +2b c964 +76 633f1 +a4 10efef +97 296663 +64 1b2fbb +93 296694 +92 c9d0d +15 19b1df +9e 25d345 +e6 12bb87 +ab d5a5b +f6 12c4e8 +a5 10edb2 +20 46edb +24 46f0c +28 47032 +2c 47063 +72 1bd15c +62 63a73 +66 63aa4 +6a 63bca +6e 63bfb +a4 110003 +78 64842 +a0 110034 +ac 11015a +a8 11018b +3d 1a0491 +31 ce59 +e6 12cb9b +e2 12cbcc +ee 12ccf2 +ea 12cd23 +7f 1bd029 +73 299f1 +20 47189 +24 471ba +62 63d21 +66 63d52 +26 1963cf +a4 1102b1 +22 196400 +a0 1102e2 +e6 12ce49 +e2 12ce7a +ac 2a2348 +a0 10ed10 +a8 10ee67 +b8 10f7c8 +ea 12b9ff +fa 12c360 +24 1d7e38 +20 1d7e69 +2c 1d7f8f +e7 ea126 +28 1d7fc0 +34 1d8799 +30 1d87ca +3c 1d88f0 +f7 eaa87 +38 1d8921 +10 3b353 +b1 cef34 +7a 1f54b9 +ac 2a25f6 +a0 10efbe +ee 2bf18e +e2 12bb56 +24 1d80e6 +20 1d8117 +34 1d8a47 +30 1d8a78 +a0 10ffd2 +a8 110129 +e2 12cb6a +ea 12ccc1 +24 1d90fa +20 1d912b +9c 2957ac +20 45c19 +2c 1d9251 +e7 eb3e8 +28 1d9282 +66 1f5c92 +22 19639e +a0 110280 +4d 5e79a +2a 4367 +e2 12ce18 +bf 2682cd +20 1d93d9 +ba 108741 +9f d1f38 +3d 1d9c13 +0 43044 +31 465db +6b 22159 +4e 2532a +0 19b80e +8 19b965 +64 627e9 +6c 62940 +63 1bb53a +29 c96b +74 633f8 +23 45f3d +2f 1d9575 +d2 e68ac +55 1b7d7e +de 279ee4 +d3 2b3233 +64 63aab +6c 63c02 +64 63d59 +f6 285fd6 +62 1b2f9f +e0 12ce81 +68 1f4b5f +2b d97a +78 1f54c0 +3b e2db +60 1f4cb6 +70 1f5617 +68 1f5e21 +ff 284e6c +60 1f5f78 +df eead7 +7d 1f67b2 +40 5fbe3 +fa 1252e0 +71 6317a +ef 28582f +e3 f21f7 +4e 1f1c4e +42 5e616 +ff 286190 +f3 f2b58 +5e 1f25af +52 5ef77 +d1 2bb82a +8d 294e3e +fb f2caf +5a 5f0ce +67 627ef +7f 1f6757 +42 5fb88 +73 6311f +62 1f5f1d +46 5fbb9 +77 63150 +66 1f5f4e +ce 127895 +e7 12b8e6 +7f 1bbd75 +c6 128cb0 +f7 12c247 +25 1d93a7 +42 5f92c +d 42ebb +e6 2bf045 +73 5c085 +f7 f2e37 +56 5f256 +6f 1f60a4 +63 62a6c +7f 1f6a05 +73 633cd +a2 10ed17 +d4 280b58 +ae 2a234f +77 633fe +f3 12517c +d6 12834d +e7 12bb94 +88 10bf82 +7f 1bc023 +7b 1bc054 +f7 12c4f5 +42 5fbda +25 45ef9 +e3 12cbd9 +7b 1bd068 +ef 12ccff +e4 2856c2 +24 3dbb8 +eb 12cd30 +63 63d2e +e3 12ce87 +7b 1bd316 +6b 22477 +ce 2bad45 +c2 12770d +5a 1b7b9c +e7 284416 +46 1f0835 +42 1f0866 +f7 284d77 +56 1f1196 +52 1f11c7 +ff 284ece +5e 1f12ed +5a 1f131e +63 1f4a0e +6b 1f4b65 +10 1cc27f +17 8ce8 +b1 25fe60 +42 1f1dd8 +73 1f536f +4a 1f1f2f +7b 1f54c6 +de 2bb954 +d2 12831c +ef 2bf19b +e3 12bb63 +e7 2846c4 +63 1ed912 +46 1f0ae3 +f7 285025 +73 1ee273 +56 1f1444 +52 1f1475 +ff 125306 +63 1f4cbc +73 1f561d +e3 12cb77 +eb 12ccce +e0 285691 +e7 2856d8 +46 1f1af7 +42 1f1b28 +21 1d80c2 +4a 1f1c7f +67 1f5c9f +c2 2b995d +63 1f5cd0 +63 627be +6f 1f5df6 +4 41d4f +a5 d5930 +ca 2b9ab4 +6b 1f5e27 +f6 ea83a +c2 128c7d +5a 1b910c +76 5ae01 +e3 12ce25 +e7 285986 +46 1f1da5 +42 1f1dd6 +67 1f5f4d +c2 2b9c0b +63 1f5f7e +ef 28480b +1c 205b +77 1f5394 +58 1ebbb +5a 1ebc2 +ea e9f9d +6d 1bb46f +5c 1ebec +5e 1ebf3 +98 cb121 +fc 285104 +9c cb152 +30 3f7fc +9e cb159 +13 42697 +1f 1d5ccf +2b 4734a +51 25b53 +5d 1b918b +da e7cb9 +5c 1ebfa +58 1ec1d +5a 1ec24 +98 cb183 +fc 285166 +d8 e7d14 +13 426f9 +1f 1d5d31 +da e7d1b +85 29600b +66 627f0 +75 1ecfeb +8d 296162 +81 102b2a +6e 62947 +95 29696c +76 63151 +91 10348b +9d 296ac3 +7e 632a8 +e6 12b8e7 +ee 12ba3e +ea 12ba6f +f6 12c248 +fe 12c39f +fa 12c3d0 +6e 1f60a5 +62 62a6d +66 62a9e +76 633ff +e6 12bb95 +f6 12c4f6 +62 63a81 +66 63ab2 +6a 63bd8 +d7 ee9d4 +f2 1251dd +d0 2b1f6b +75 1ee2ad +81 103dec +6e 63c09 +e6 12cba9 +e2 12cbda +ee 12cd00 +ea 12cd31 +7f 1bd037 +73 299ff +62 63d2f +66 63d60 +e6 12ce57 +43 5f8cd +e2 12ce88 +fa 12c36e +62 1f4a0f +6a 1f4b66 +72 1f5370 +62 1f4cbd +72 1f561e +66 1f5ca0 +62 1f5cd1 +81 295fda +62 627bf +6e 1f5df7 +6a 1f5e28 +62 1f5f7f +7f 1f67b9 +42 5fbea +73 63181 +d8 e7d22 +87 29d09e +a4 2a11eb +42 1f07f8 +8 41c29 +8f 29d1f5 +ac 2a1342 +97 29d9ff +b4 2a1b4c +52 1f1159 +18 4258a +9f 29db56 +bc 2a1ca3 +63 1f49a0 +d8 e6740 +29 45dd1 +73 1f5301 +8 4319b +39 46732 +28 1d9530 +3d 1d8b9d +5a 5f122 +11 426f2 +1d 1d5d2a +9a 104858 +fe 2be83b +4a 5fd33 +7b 632ca +6a 1f60c8 +bb 108a00 +83 26369d +77 1b4e94 +94 10b55b +a5 10eda2 +ba cdd5f +3d 19f231 +e7 2bdac8 +ad 10eef9 +42 24f4a +4e 1b8582 +25 19e9c5 +f3 284ff2 +6 19228a +84 10c16c +b5 10f703 +3f 4f46 +a4 2a2501 +f7 2be429 +8c 10c2c3 +e 1923e1 +bd 10f85a +52 258ab +5e 1b8ee3 +35 19f326 +a0 10f020 +ac 2a2658 +d6 1280f3 +71 1b4df8 +e7 12b93a +15 1c63 +7f 1bbdc9 +7a 5c23d +58 1e8fcb +dd 129762 +ef 12ba91 +67 1bb55d +62 5b9d1 +47 251c8 +40 1e875f +c6 128d04 +f7 12c29b +ce 128e5b +ff 12c3f2 +77 1bbebe +3b 198119 +e2 12bbb8 +ee 2bf1f0 +57 25b29 +72 5c332 +50 1e90c0 +31 1d16e9 +82 264c0e +b3 2681a5 +9f d21e6 +ba 1089ef +3d 1d9ec1 +31 46889 +c7 2b998f +4e 1f1f50 +42 5e918 +f3 27dcba +52 1ea0d9 +5e 1f28b1 +52 5f279 +83 26394b +94 10b809 +a5 10f050 +b5 29a811 +ba ce00d +3d 19f4df +6 192538 +b5 10f9b1 +d6 1283a1 +e7 12bbe8 +15 1f11 +7f 1bc077 +f7 2b73a9 +84 294a3a +7a 5c4eb +58 1e9279 +f7 12c549 +94 29539b +d0 e78ab +8f d2899 +21 46f3c +63 1f5c62 +d8 e7a02 +29 47093 +41 1f0862 +63 63ad4 +a2 106cd7 +ae 29a30f +d4 278b18 +c5 2b998a +e7 12cbfc +58 1ea28d +8f d2b47 +d0 e7b59 +21 471ea +b3 269467 +27 196430 +a5 110312 +c5 2b9c38 +e7 12ceaa +84 295cfc +58 1ea53b +4 1d3cf1 +c 1d3e48 +14 1d4652 +1c 1d47a9 +a2 1069c7 +ae 299fff +d4 278808 +25 1d7e99 +aa 106b1e +dc 27895f +2d 1d7ff0 +ba 10747f +0 41d82 +c 1d53ba +3d 1d8951 +4 1d3f9f +14 1d4900 +4 1d4fb3 +0 41ad2 +c 1d510a +4 1d5261 +c7 2b9c3d +48 5e7c8 +cf 2b9d94 +d7 2ba59e +6d 1f5e51 +cf ee176 +ea 12497f +61 62819 +69 62970 +48 5fd3a +79 632d1 +68 1f60cf +e5 12b941 +fa ea8fe +7d 1bbdd0 +46 1aee29 +c4 128d0b +f5 12c2a2 +4e 1aef80 +cc 128e62 +fd 12c3f9 +75 1bbec5 +39 198120 +e0 12bbbf +ec 2bf1f7 +ea 124c2d +6d 1f60ff +cf ee424 +61 62ac7 +25 19e717 +c2 2817ad +f3 284d44 +df eed85 +7d 1f6a60 +fa 12558e +71 63428 +35 19f078 +a0 10ed72 +ac 2a23aa +e5 12bbef +fa eabac +7d 1bc07e +40 5f933 +c7 2baeff +48 5fa8a +c3 127a1e +cf 2bb056 +cf ef438 +61 63adb +69 63c32 +e5 12cc03 +67 1b2fcf +e5 12ceb1 +44 1f1b52 +40 5e671 +4c 1f1ca9 +44 1f1e00 +28 45dd2 +38 46733 +6e 1f5e4b +62 62813 +6a 6296a +7e 1f67ac +72 63174 +7a 632cb +a4 10eda3 +a3 267846 +2 1d3c65 +b4 10f704 +ab 26799d +a 1d3dbc +bc 10f85b +e6 12b93b +ee 12ba92 +f6 12c29c +fe 12c3f3 +38 3f6b3 +72 1ee282 +7e 1f6a5a +72 63422 +a4 10f051 +b4 29a812 +33 4b12 +3f 19814a +e6 12bbe9 +bc 1087db +f6 2b73aa +f6 12c54a +28 47094 +6a 63c2c +e6 12cbfd +ee 12cd54 +70 1ecfb9 +77 29a22 +bf d60df +20 471eb +62 63d83 +26 196431 +a4 110313 +f3 284d46 +52 1f1165 +33 5dd4 +e6 12ceab +24 1d7e9a +2c 1d7ff1 +34 1d87fb +3c 1d8952 +24 1d8148 +34 1d8aa9 +6b 1bb3e1 +24 1d915c +56 1b8fd6 +2 1d3f23 +20 45c7b +2c 1d92b3 +52 25af5 +5e 1b912d +a 1d407a +24 1d940a +13 1940c7 +68 62971 +78 632d2 +e4 12b942 +3d 198151 +31 4b19 +e4 12bbf0 +d7 2b3264 +60 63adc +68 63c33 +e4 12cc04 +31 5ddb +66 1b2fd0 +e4 12ceb2 +69 1bb3cc +64 1f5cfb +60 6281a +6c 1f5e52 +64 1f5fa9 +d6 ee96f +8e 29cef4 +89 294e6f +3d 1d8bab +5a 5f130 +4a 5fd41 +7b 632d8 +6a 1f60d6 +d6 128101 +71 1b4e06 +e7 12b948 +7f 1bbdd7 +ef 12ba9f +67 1bb56b +c6 128d12 +f7 12c2a9 +47 1f1aec +e6 2bf0a7 +ce 128e69 +ff 12c400 +80 10be2b +77 1bbecc +f3 1251de +d6 1283af +80 ca669 +b1 cdc00 +bd 261238 +e7 12bbf6 +88 10bfe4 +7f 1bc085 +a6 107cba +84 294a48 +f7 12c557 +b6 10861b +94 2953a9 +25 1d93b5 +42 5f93a +63 63ae2 +e7 12cc0a +ef 12cd61 +c2 2804eb +67 1bc82d +e7 12ceb8 +84 295d0a +11 436a2 +46 1f0897 +19 437f9 +4e 1f09ee +b6 cef5b +94 25bce9 +56 1f11f8 +11 43950 +63 1ed974 +46 1f0b45 +73 1ee2d5 +56 1f14a6 +46 1f1b59 +25 1d80f3 +42 5e678 +4e 1f1cb0 +67 1f5d01 +63 62820 +6f 1f5e58 +46 1f1e07 +67 1f5faf +6e 1b33c7 +39 61d2 +c5 2b163a +1b a380 +47 1de8c +e6 eb447 +58 1f1565 +5b 26f11 +bb cf2d0 +bd cf2fa +bf cf301 +fc 2b64e6 +96 29ecc0 +df 2b2399 +79 22d71 +46 1b83c9 +f9 ebe68 +fd ebe99 +79 22dc5 +de 2bb69a +d2 128062 +7b 22dcc +5b 26f73 +9 398dc +e2 2b5a66 +18 3b74a +b9 cf32b +1a 3b751 +bb cf332 +6a 62978 +75 1ed04d +7a 632d9 +e6 12b949 +ee 12baa0 +f6 12c2aa +fe 12c401 +62 63ae3 +6a 63c3a +75 1ee30f +e6 12cc0b +ee 12cd62 +77 29a30 +62 63d91 +47 5f8fe +e6 12ceb9 +6b 1bb3d3 +66 1f5d02 +62 62821 +6e 1f5e59 +66 1f5fb0 +85 294a9b +19 a3e9 +18 3b758 +6 19b89a +b9 cf339 +58 582e9 +46 1b842b +f9 ebeca +9e 264123 +29 4601d +2d 4604e +82 d16fc +8e 264d34 +bf 2682cb +e7 ea186 +7b 63516 +31 1d9a2b +7f 63547 +52 1b7aa7 +8c 10af9d +19 1d477b +88 10afce +2d 47310 +b3 d5f55 +82 d29be +bf 26958d +9c 10b8fe +98 10b92f +3d 47c71 +a9 10f176 +96 29d6fe +e 19262d +bd 10faa6 +3d 19f4eb +ce 127b35 +5b 1f1313 +ef 12bcdd +eb 12bd0e +ff 12c63e +fb 12c66f +92 d1dad +9e 2653e5 +29 472df +52 1b8d69 +e 19237d +8c 10c25f +19 1d5a3d +76 1ecfe3 +2b 196556 +a9 110438 +96 29e9c0 +31 d175 +3d 1a07ad +ce 128df7 +5b 1f25d5 +88 10af6c +98 10b8cd +c6 279490 +5a 1f2820 +f7 27ca27 +a9 10f114 +39 19f4ba +ca 127b04 +eb 12bcac +fb 12c60d +8 1d40c5 +18 1d4a26 +29 1d826d +39 1d8bce +ef 28480d +85 ca6a7 +4e 1f0c2c +f4 2862df +55 1b8d24 +23 46ee3 +94 25bf27 +6b 1f4e05 +10 1cc51f +b1 260100 +7b 1f5766 +a 19234c +bc cddeb +88 10c22e +2b 1964f4 +3c 3e3b2 +a9 1103d6 +fe ea983 +39 1a077c +ca 128dc6 +8 1d5387 +da 128227 +bd 2a1ca2 +8b d1854 +29 1d952f +6b 1f60c7 +e9 f25f5 +48 5ea14 +69 62bbc +79 6351d +e9 12bd15 +4a 1af1fd +f9 12c676 +a3 10f026 +af 2a265e +d5 280e67 +69 63e7e +6b 1b30f5 +e9 12cfd7 +c8 127b0b +d8 12846c +e9 12bcb3 +4a 1af19b +f9 12c614 +69 1f4e0c +79 1f576d +53 26dc8 +a5 2a0f2e +7c 5af51 +6b 1b3093 +e9 12cf75 +48 1f1f26 +cb ee3f3 +69 1f60ce +28 4601e +ba 29ac4d +1b 1cd692 +a4 cd5ef +38 4697f +6b 211b7 +ce 2b9a85 +2b 19eaf2 +3c 469b0 +6a 62bb6 +6e 62be7 +e6 ea187 +7a 63517 +84 25c63a +d8 e6750 +29 45de1 +7e 63548 +ac 10f146 +93 103182 +9f 2967ba +a8 10f177 +6c 1b3112 +9b 2967eb +b8 10fad8 +ee 12bcde +ea 12bd0f +fe 12c63f +ad 10ef09 +28 472e0 +2c 47311 +b2 d5f56 +be 26958e +6a 63e78 +6e 63ea9 +2e 196526 +ac 110408 +2a 196557 +a8 110439 +5a 26cc4 +3d 1a073f +31 d107 +8d d163e +c7 28020d +ee 12cfa0 +ea 12cfd1 +7f 1bd2d7 +73 29c9f +a8 10f115 +b8 10fa76 +ea 12bcad +fa 12c60e +fd f3f8b +a9 10eed8 +28 1d826e +38 1d8bcf +b5 cf1b1 +7e 1f5736 +1a 3b753 +10 3b601 +b1 cf1e2 +7a 1f5767 +2a 1964f5 +a8 1103d7 +ea 12cf6f +6b 1bb691 +7c 6354f +e8 12bd16 +db 2b338a +f8 12c677 +6c 63eb0 +f2 f2af5 +fe 28612d +6a 1b30f6 +e8 12cfd8 +7d 1bd2de +71 29ca6 +68 1f4e0d +2b dc28 +78 1f576e +a4 2a0f2f +3b e589 +eb 124972 +ce 127b43 +b0 25fe4f +5b 1f1321 +fb 1252d3 +de 1284a4 +eb 12bd1c +fb 12c67d +2d 46050 +eb 12cfde +eb 12bcba +e0 28467d +fb 12c61b +f0 284fde +ef 28481b +6b 1eda69 +4e 1f0c3a +55 1b8d32 +23 46ef1 +b6 cf1a7 +94 25bf35 +33 47852 +5a 1f15cc +23 44bf +2f 197af7 +86 29cd8d +6b 1f4e13 +10 1cc52d +17 8f96 +b1 26010e +7b 1f5774 +a7 2a0f35 +4a 1f1f2d +6b 1f60d5 +1e 3b784 +fe 2be82d +9e 10487b +1a 3b761 +7e 1f5744 +1e 3b792 +5d 1b9199 +2b 47358 +51 25b61 +da e7cc7 +70 5c39b +de e7cf8 +9e 104889 +18 3b7bc +7c 1f579f +98 1048b3 +fc 2be896 +6a 62bc4 +a6 cd33a +75 1ed299 +6e 62bf5 +7a 63525 +b9 2695b9 +18 1d59d8 +84 25c648 +7e 63556 +ee 12bcec +fe 12c64d +5b 5f0c3 +fa 12c67e +6e 63eb7 +4b 5fa24 +ea 12cfdf +7f 1bd2e5 +73 29cad +65 1ed9ac +fa 12c61c +6a 1f4e14 +a6 2a0f36 +1a 3b7c3 +7e 1f57a6 +da e7d29 +9a 1048ba +52 1f1407 +18 42838 +73 1f55af +39 469e0 +cf 2b9ae6 +5a 5f3d0 +c6 2b9bce +8c 10afff +90 103188 +9c 2967c0 +e7 2bdd76 +ad 10f1a7 +80 103d99 +b1 107330 +bd 29a968 +80 29e07b +1b ac0 +ce 127b97 +55 1f11f2 +d2 11fd20 +de 2b3358 +a1 2a2223 +ef 12bd3f +8c 294b91 +b1 2a2b84 +ff 12c6a0 +9c 2954f2 +e7 2bf038 +2f 196587 +ad 110469 +c 1d40f6 +1c 1d4a57 +aa 106dcc +dc 278c0d +2d 1d829e +ba 10772d +3d 1d8bff +0 41d80 +c 1d53b8 +5b 5f125 +69 62c1e +2d 19e86e +ca 281904 +fb 284e9b +19 ac7 +cc 127b9e +ed 12bd46 +4e 1af22e +fd 12c6a7 +6f 1b3126 +ed 12d008 +40 5e91f +4c 1f1f57 +38 469e1 +99 10c8f2 +ce 2b9ae7 +6a 62c18 +7a 1ee3d9 +7a 63579 +ac 10f1a8 +b0 107331 +bc 29a969 +3b 4c69 +ee 12bd40 +f2 123ec9 +75 1f539b +fe 2b7501 +6a 63eda +2e 196588 +ac 11046a +35 d138 +fb 284e9d +5a 1f12bc +3b 5f2b +ee 12d002 +70 1ed267 +77 29cd0 +5c 1ea50a +50 56ed2 +2c 1d829f +3c 1d8c00 +20 45f29 +2c 1d9561 +62 62ac1 +6e 1f60f9 +bf 108a31 +39 4c70 +ec 12bd47 +d3 11fd83 +df 2b33bb +eb 2847ea +4a 1f0c09 +fc 12c6a8 +68 63ee1 +fa 28615e +6e 1b3127 +39 5f32 +ec 12d009 +75 29cd7 +60 62ac8 +6c 1f6100 +80 29e089 +eb 1249d4 +ce 127ba5 +90 29e9ea +fb 125335 +de 128506 +a1 2a2231 +ef 12bd4d +ae 107e11 +d4 e661a +8c 294b9f +b1 2a2b92 +ff 12c6ae +be 108772 +9c 295500 +ef 12d00f +80 102829 +d4 e78dc +8c 295e61 +19 43aa7 +6b 1edacb +4e 1f0c9c +d2 e78c2 +55 1b8d94 +23 46f53 +b6 cf209 +94 25bf97 +7b 1ee42c +5e 1f15fd +33 478b4 +42 5e926 +4e 1f1f5e +63 62ace +6f 1f6106 +6e 1ecaf7 +39 3f902 +1a 427dd +bb d63be +1b 43aa2 +1f 43ad3 +bb 1089f2 +bf 108a23 +84 25b324 +bd 108a2a +9b 10cb99 +3b 3f909 +1b 43ab0 +1f 43ae1 +fb ebe6f +ff ebea0 +39 3f964 +1b 43b04 +19 43b0b +6 1d4fbc +b9 108a5b +6a 62c26 +75 1ed2fb +6a 63ee8 +81 29cdc6 +75 1ee5bd +4f 5fa55 +ee 12d010 +77 29cde +62 62acf +6e 1f6107 +3b 3f96b +a 1d533a +bc 110dd9 +1d 4381e +1b 43b12 +bb 108a62 +9d 10c915 +97 29ec5f +93 10b77e +9f 29edb6 +31 4783b +a5 ce602 +39 47992 +c6 e6ff2 +fb f3f63 +5a 60382 +e7 eb19a +59 1f12b8 +7b 6452a +90 10c7ec +98 10c943 +10 19c40f +6 1934ea +b5 110963 +2 19351b +b1 110994 +e 193641 +2 9 +bd 110aba +a 193672 +b9 110aeb +d9 2ba411 +34 3e50b +23 19664d +fb 12d683 +b5 d7553 +3 19bab4 +14 43972 +31 47ae9 +c7 2babef +16 192b87 +94 10ca69 +2 1937c9 +33 196d60 +b1 110c42 +ca 120a88 +41 5e922 +fb 12401f +4d 1f1f5a +98 10c8e1 +dd f00a2 +fb 12d621 +10 1d58e3 +d7 e7ba0 +18 1d5a3a +35 1d9a5a +90 29d718 +9f 2653e8 +93 d1db0 +31 1d9a8b +0 42fe2 +31 46579 +3d 1d9bb1 +98 29d86f +f7 28602b +56 1f244a +ff 286182 +52 5ef69 +f3 f2b4a +5e 1f25a1 +33 196cfe +2 193767 +b1 110be0 +41 5e8c0 +4d 1f1ef8 +b5 269741 +14 1d5b60 +71 643da +85 29d037 +b7 299868 +79 64531 +42 1b00ba +f1 12d533 +4a 1b0211 +21 196654 +f9 12d68a +8c 264aef +80 d14b7 +71 64688 +52 1af757 +d0 129639 +ab 106b21 +94 265289 +42 1b0368 +73 1b38ff +f1 12d7e1 +42 1b0058 +8 1489 +f1 12d4d1 +58 1f25d9 +a2 299ee7 +df 281f87 +d3 ee94f +71 1f662a +db eeaa6 +79 1f6781 +52 1af6f5 +d0 1295d7 +f5 2862e0 +ce e5e79 +54 1f26ff +b2 107398 +be 29a9d0 +df 282235 +d3 eebfd +71 1f68d8 +30 4783c +23 19f9af +6a 1eca62 +34 4786d +a4 ce603 +38 47993 +2b 19fb06 +3c 479c4 +9 19a706 +2b d978 +76 64405 +e6 eb19b +7a 6452b +7e 6455c +b4 110964 +b0 110995 +bc 110abb +b8 110aec +45 5e643 +ab d6a6f +89 2637fd +f6 12d4fc +fe 12d653 +fa 12d684 +23 19fc5d +34 47b1b +2b dc26 +9 19a9b4 +76 646b3 +36 196d30 +b4 110c12 +ab d6d1d +89 263aab +f6 12d7aa +a5 110074 +b0 110933 +b8 110a8a +fa 12d622 +34 1d9a5b +30 1d9a8c +30 4657a +3c 1d9bb2 +f7 ebd49 +38 1d9be3 +2b 19fb66 +76 1f65f3 +72 63112 +7e 1f674a +34 1d9d09 +2b 19fe14 +76 1f68a1 +d4 279ada +a2 107c99 +25 1d916b +63 1bc54e +29 d97f +74 6440c +6b 1bc6a5 +7c 64563 +63 1bc7fc +29 dc2d +74 646ba +72 1b3900 +ac 106df6 +f0 12d7e2 +78 1f6782 +2c 1cfeed +70 1f68d9 +81 102ac8 +8d 296100 +f7 f40f9 +56 60518 +d6 12960f +f7 286039 +56 1f2458 +af 25f67e +52 1f2489 +d2 1295de +f7 2862e7 +56 1f2706 +af 25f92c +52 1f2737 +2a 3ef99 +50 1d7a2 +5c 1b0dda +fb 12c36f +58 1b0e0b +9c cb1b4 +9e cb1bb +2a 3effb +50 1d804 +5c 1b0e3c +fb 12c3d1 +72 643e2 +76 64413 +7a 64539 +91 10474d +7e 6456a +f6 12d50a +ae 106b4f +f2 12d53b +fe 12d661 +fa 12d692 +72 64690 +76 646c1 +f6 12d7b8 +ae 106dfd +53 6022e +f2 12d7e9 +f2 12d4d9 +fa 12d630 +76 1f6601 +2e 1cfc46 +72 1f6632 +7a 1f6789 +f2 12d787 +76 1f68af +2e 1cfef4 +72 1f68e0 +9c cb1c2 +10 436f5 +97 29ecc1 +fd 2b64e7 +b4 2a2e0e +52 1f241b +18 4384c +93 10b7e0 +9f 29ee18 +b0 10f92d +bc 2a2f65 +9f d31fa +31 4789d +73 1f65c3 +39 479f4 +35 1d9d08 +52 6028d +1d 4381c +f6 2bf9a6 +3d 1d9e5f +31 46827 +5a 603e4 +f2 12c4c5 +fe 2bfafd +51 1f11c3 +73 64435 +59 1f131a +7b 6458c +83 26495f +94 10c81d +d6 1293b5 +d5 2ba2eb +f7 12d55d +10 439a3 +9f d34a8 +31 47b4b +c7 2bac51 +52 6053b +51 1f1471 +73 646e3 +b6 d7559 +ff eac32 +5e 57051 +10 1cd535 +83 264c0d +16 192be9 +94 10cacb +37 196d91 +6 1937fa +b5 110c73 +d6 129663 +d5 2ba599 +f7 12d80b +14 1d5914 +10 42433 +1c 1d5a6b +b2 1085ea +97 d1de1 +35 1d9abc +13 1d4883 +14 1d5bc2 +b2 108898 +97 d208f +35 1d9d6a +d7 2bb860 +df efd99 +71 6443c +79 64593 +46 1b00eb +f5 12d564 +46 1b0399 +77 1b3930 +f5 12d812 +54 1f24b3 +9 19a942 +50 5efd2 +5c 1f260a +a6 299f18 +54 1f2761 +30 4789e +38 479f5 +72 64436 +7a 6458d +f6 12d55e +fe 12d6b5 +30 47b4c +c6 2bac52 +72 646e4 +f6 12d80c +34 1d9abd +12 1d4884 +30 465dc +3c 1d9c14 +1a 1d49db +34 1d9d6b +6b 1bc6a3 +2c 3da51 +70 6443d +78 64594 +a9 d59f4 +48 1b8796 +79 1bbd2d +74 1f665c +70 6317b +7c 1f67b3 +74 1f690a +9e 29d855 +af cd490 +35 1d9d16 +52 6029b +6a 62916 +89 296131 +31 46835 +3d 1d9e6d +5a 603f2 +d6 129671 +56 1f24ba +35 1d8a54 +52 5efd9 +5e 1f2611 +56 1f2768 +71 2194a +7d 1b4f82 +d8 278c40 +79 1b4fb3 +59 1b915a +2 1cce87 +b4 108926 +a3 260a68 +c9 ee3dc +7a 6459b +f6 12d56c +ab d59fb +fe 12d6c3 +2e 3dd06 +72 646f2 +4a 1b879d +7b 1bbd34 +c5 2804a4 +76 1f6663 +76 1f6911 +1e 3b782 +bf cf363 +14 1d58b4 +fc 2b6548 +98 296aa1 +9f d350a +dc 2ba6ef +5e 58313 +ff ebef4 +d8 2b3632 +df f009b +89 2637ed +71 219ac +7d 1b4fe4 +1c 3b789 +bd cf36a +1e 192cde +9c 10cbc0 +d9 2ba6bf +fb 12d931 +3b 196e55 +a 1938be +b9 110d37 +4 1cb941 +a5 25f522 +fb 12d8cf +b0 10889f +46 1e84d9 +95 d2096 +e7 27c0ba +5a 1f2880 +5a 1af8ae +d8 129790 +90 d1da8 +9c 2653e0 +4a 1b04bf +7b 1b3a56 +f9 12d938 +5a 1af84c +d8 12972e +e6 eb18b +c4 277f19 +44 1e84e0 +e5 27c0c1 +fd 286437 +f1 f2dff +50 5f21e +5c 1f2856 +a6 29a164 +58 1f2887 +3 1ccbda +b5 108679 +a2 29a195 +21 577a +84 29e048 +db eed54 +79 1f6a2f +a5 2a21f0 +2b 19fdb4 +3c 47c72 +e6 eb449 +7a 647d9 +d8 e7a12 +29 470a3 +7e 6480a +3e 196e87 +bc 110d69 +fe 12d901 +ad 1101cb +fa 12d932 +fa 12d8d0 +a9 11019a +72 633c0 +7e 1f69f8 +dc 279c31 +d0 e65f9 +aa 107df0 +21 45c8a +2d 1d92c2 +6b 1bc953 +7c 64811 +7a 1b3a57 +f8 12d939 +78 1f6a30 +a4 2a21f1 +de 129766 +5a 1f288e +cf 2b1728 +23 5781 +86 29e04f +1a 1cd9a3 +97 29ea15 +20 19fc63 +69 1b333c +12 3a348 +1e 1cd980 +1a 1cd9b1 +7a 647e7 +7e 64818 +fe 12d90f +5b 60385 +fa 12d940 +fa 12d8de +72 633ce +7e 1f6a06 +7a 1f6a37 +9c 1048e4 +10 3a3a3 +1c 1cd9db +de e7d5a +9e 1048eb +12 3a3aa +1e 1cd9e2 +52 1f26c9 +18 43afa +73 1f6871 +39 47ca2 +c3 127770 +cf 2bada8 +5a 60692 +59 1f15c8 +7b 6483a +be d76b0 +18 1cd68c +8b 264d64 +1e 192d40 +d6 2bb7f1 +9c 10cc22 +dd 2ba6f0 +ff 12d962 +10 426e1 +1c 1d5d19 +4e 1b04f0 +42 1ceb8 +7f 1b3a87 +fd 12d969 +50 5f280 +5c 1f28b8 +7 1ccc0b +a6 29a1c6 +7a 6483b +ab 268f0d +a 1d532c +3e 196ee9 +bc 110dcb +fe 12d963 +30 4688a +3c 1d9ec2 +eb 285aac +4a 1f1ecb +7e 1b3a88 +fc 12d96a +70 63429 +7c 1f6a61 +de 1297c8 +52 5f287 +5e 1f28bf +3b 1d1b4b +85 264be3 +39 1d1b52 +1b 1d5cf2 +19 1d5cf9 +5f 603b6 +fe 12d971 +72 63430 +7e 1f6a68 +bf 108a85 +b9 2682f7 +18 1d4716 +84 25b386 +b 1ccfed +bd 108a8c +9f 10cc2c +9d 10cc33 +33 3e544 +3f 1d1b7c +31 3e54b +3d 1d1b83 +13 426eb +1f 1d5d23 +bf 108a93 +9f 10cc3a +33 3e552 +3f 1d1b8a +f4 ea833 +c0 128c76 +42 1aed94 +fc ea98a +c8 128dcd +4a 1aeeeb +74 5adfa +e1 12ce1e +63 1b2f3c +8 1737 +42 1b0306 +f1 12d77f +73 1b389d +cf 2803c6 +ea 2b6bcf +4a 1b045d +f9 12d8d6 +7b 1b39f4 +8c 25b47b +9c 25bddc +a5 25f4cc +a1 25f4fd +ad 25f623 +f4 2b60e1 +a9 25f654 +80 25c8c7 +b1 25fe5e +88 25ca1e +b9 25ffb5 +e3 12482b +e7 27c064 +f4 123f53 +e3 27c095 +ef 27c1bb +fc 1240aa +eb 27c1ec +0 1924aa +4a 576e3 +5a 58044 +4a 1b020f +21 196652 +6b 5b88b +5a 1b0b70 +31 196fb3 +9 1ccd2a +af d6d4e +8d 263adc +a8 29a2e5 +7b 5c1ec +74 5b0a8 +63 1b31ea +84 25b5d2 +ce 12080b +ce 279337 +c2 e5cff +a5 25f77a +ef 1249b3 +a1 25f7ab +84 c9128 +eb 1249e4 +b1 26010c +94 c9a89 +fb 125345 +27 1d815c +d6 278acb +f4 ebaf5 +42 1b0056 +74 5c0bc +63 1b41fe +84 25c5e6 +80 25c617 +80 c9105 +8c 25c73d +88 25c76e +a5 26078e +a1 2607bf +a1 cd2ad +ad 2608e5 +c2 2791af +ca 279306 +f4 125215 +e3 27d357 +0 19376c +f4 ebda3 +42 1b0304 +74 5c36a +63 1b44ac +84 25c894 +80 25c8c5 +c2 e6fc1 +a5 260a3c +a1 260a6d +c6 27942c +62 1b2f3b +6a 1b3092 +72 1b389c +c5 e6f8a +7a 1b39f3 +5b 1b7b9b +c 43168 +ad d6d49 +a4 25f4cb +a0 25f4fc +ac 25f622 +a8 25f653 +b4 25fe2c +73 29cff +7f 1bd337 +2b 1d8284 +b0 25fe5d +bc 25ff83 +75 21989 +8d 2637ca +23 1cfd6f +d4 2ba288 +71 219ba +7d 1b4ff2 +89 2637fb +9d 26412b +61 22009 +33 1d06d0 +99 26415c +c3 1289d2 +e6 27c063 +cd 2bb051 +c1 127a19 +e2 27c094 +ee 27c1ba +c9 127b70 +ea 27c1eb +d4 1280fa +c3 28023c +26 448d +dc 128251 +cb 280393 +1 19a7f9 +a0 ce560 +4b 5fa32 +11 19b15a +88 29e48c +b0 ceec1 +5b 60393 +75 216cd +62 1b31e9 +89 10ad23 +81 263952 +cb 128b8b +95 264282 +df 1294bb +51 26b73 +e6 27c311 +71 2acba +f5 ea826 +43 1aed87 +e2 27c342 +c4 2801f5 +f6 27cc72 +d4 1283a8 +c3 2804ea +62 1b41fd +a4 26078d +a0 2607be +a0 cd2ac +ac 2608e4 +a8 260915 +75 22c4b +81 d1454 +8d 264a8c +23 1d1031 +e6 27d325 +c1 128cdb +e2 27d356 +65 1bb316 +e2 e9e44 +ee 27d47c +c9 128e32 +ea 27d4ad +d4 1293bc +c3 2814fe +4 1924dd +dc 129513 +26 574f +cb 281655 +97 2966c5 +20 197913 +c6 e5cc2 +1 19babb +91 cad1b +c6 277f10 +75 2298f +62 1b44ab +89 10bfe5 +54 60511 +f5 f40f2 +43 1b8653 +e6 27d5d3 +56 1af788 +d4 12966a +c3 2817ac +b4 1073c4 +2 1cb925 +90 d1e0a +9c 265442 +c7 2b9983 +12 1cc286 +cf 2b9ada +1a 1cc3dd +ab 106dcf +94 265537 +23 1cfacd +42 1e108 +2b 1cfc24 +2 1cce97 +33 1d042e +52 1ea69 +a 1ccfee +3b 1d0585 +86 294a4d +96 2953ae +31 1d8777 +6b 5bb2b +c6 e7230 +af 298d4c +82 295ff0 +b3 299587 +33 1d06dc +f9 f3f06 +96 29565c +b4 108686 +2 1ccbe7 +23 1d0d8f +ce 11f84b +51 1af6ef +2b 1d0ee6 +82 295d40 +5 1d3d00 +82 10282e +8e 295e66 +8a 295e97 +d5 278817 +a3 1069d6 +af 29a00e +b4 108934 +2 1cce95 +86 295fbd +82 295fee +22 1cfacc +b0 d5fb1 +bc 2695e9 +e7 2bdb2a +32 1d042d +ef 2bdc81 +85 103b1b +3a 1d0584 +13 1d45d5 +1b 1d472c +ae 298d4b +b6 299555 +b2 299586 +21 1d0dde +4 1d3faf +be 2996ac +87 29cd9c +97 29d6fd +b0 d625f +bc 269897 +e7 2bddd8 +32 1d06db +b6 299803 +13 1cc279 +b2 299834 +31 4e19 +3d 198451 +94 29d6e7 +22 1d0d8e +bd 110b2c +b 1d508d +25 1d7ea7 +a2 1069d5 +d4 278816 +ae 29a00d +d0 278847 +aa 29a03e +e1 28594e +74 1b392a +40 1f1d6d +87 29e05e +83 29e08f +c1 128c77 +f5 ea834 +54 56c53 +43 1aed95 +c9 128dce +fd ea98b +5c 56daa +b4 d72b2 +4b 1aeeec +14 1cc5b4 +28 436e +e0 12ce1f +62 1b2f3d +e8 12cf76 +6a 1b3094 +38 4ccf +f0 12d780 +72 1b389e +85 25b325 +81 25b356 +8d 25b47c +95 25bc86 +91 25bcb7 +9d 25bddd +99 25be0e +a4 25f4cd +87 25b380 +6 8386 +a0 25f4fe +ac 25f624 +8f 25b4d7 +e 84dd +a8 25f655 +16 8ce7 +b0 25fe5f +bc 25ff85 +9f 25be38 +1e 8e3e +b8 25ffb6 +d4 11fdac +c3 277eee +dc 11ff03 +cb 278045 +ac cd496 +e6 27c065 +e2 27c096 +ee 27c1bc +ea 27c1ed +1 1924ab +11 192e0c +8f d2ba7 +88 29613e +a3 d5904 +af 268f3c +85 25b5d3 +81 25b604 +95 25bf34 +91 25bf65 +6 8634 +a0 25f7ac +f5 285fce +16 8f95 +b0 26010d +d4 12005a +c3 27819c +28 5630 +62 1b41ff +85 25c5e7 +81 25c618 +81 c9106 +8d 25c73e +d4 2b31fc +89 25c76f +a4 26078f +87 25c642 +6 9648 +ee ea2dc +4f 1cd21 +a0 2607c0 +a0 cd2ae +ac 2608e6 +83 c9161 +8f 25c799 +e 979f +a8 260917 +d4 12106e +c3 2791b0 +ac ce758 +e6 27d327 +e2 e9e46 +65 1bb318 +ee 27d47e +ea 27d4af +1 19376d +54 581c3 +f5 ebda4 +43 1b0305 +85 25c895 +81 25c8c6 +d4 12131c +c3 27945e +4a 1b723b +ef e9fbf +84 263674 +25 19f9e7 +8c 2637cb +ce e7389 +ff ea920 +94 263fd5 +ca e73ba +fb ea951 +90 264006 +35 1a0348 +9c 26412c +c2 28023d +67 1bc57f +ca 280394 +ef ea26d +84 263922 +ff eabce +94 264283 +a9 ce71a +8c d18eb +c6 2804ba +ef eb281 +84 264936 +ca 281656 +b5 1073c5 +3 1cb926 +bd 10751c +b 1cba7d +85 264bd7 +13 1cc287 +1b 1cc3de +76 1b4b81 +22 1cface +7e 1b4cd8 +72 216a0 +2a 1cfc25 +b5 d5f81 +32 1d042f +3a 1d0586 +87 294a4e +f6 ea7c8 +ae 298d4d +b6 299557 +b2 299588 +be 2996ae +76 1b4e2f +22 1cfd7c +32 1d06dd +b5 108687 +3 1ccbe8 +bd 1087de +b 1ccd3f +22 1d0d90 +72 22962 +50 1af6f0 +2a 1d0ee7 +fc 1242f6 +b5 d7243 +25 1cfdfb +83 295d41 +83 10282f +8f 295e67 +56 1b8d28 +2 1d3c75 +5e 1b8e7f +52 25847 +a 1d3dcc +12 1d45d6 +1a 1d472d +86 29cd9d +86 29d04b +96 29d9ac +2 1d4f37 +52 26b09 +a 1d508e +82 102ad0 +8e 296108 +bf 29969f +5 1d3fa2 +86 29e05f +82 29e090 +d6 efc31 +82 10ab7e +8e 29e1b6 +84 25b378 +8c 25b4cf +94 25bcd9 +82 102adc +5 1d3fae +8e 296114 +9c 25be30 +35 19805c +c6 1206a6 +a5 25f520 +50 1f2482 +ad 25f677 +80 c9409 +8c 25ca41 +bd 25ffd8 +e7 12484e +99 cae72 +ce 278067 +ce e5e19 +9 19bc12 +d6 278871 +d6 e6623 +11 19c41c +de 2789c8 +de e677a +19 19c573 +95 d2094 +e7 27c0b8 +9d d21eb +ef 27c20f +c2 e5fa1 +ce 2795d9 +ff 27cb70 +84 25b626 +a5 25f7ce +ef 124a07 +d6 278b1f +80 c9159 +8c 25c791 +a5 2607e2 +a1 cd301 +ad 260939 +c6 2791d2 +6b 62979 +c2 e5cf1 +a5 25f76c +ce 279329 +95 d3356 +e7 27d37a +9d d34ad +e3 e9e99 +ef 27d4d1 +84 25c8e8 +d8 e69fe +29 4608f +a5 260a90 +4f 1f09f1 +a4 25f51f +ac 25f676 +25 1d8155 +a2 106c83 +d4 278ac4 +ae 29a2bb +85 2636c7 +8d 26381e +9d 26417f +c7 1289f5 +94 d2093 +b1 ceec2 +e6 27c0b7 +e6 e9e69 +21 19fc62 +b9 cf019 +9c d21ea +ee 27c20e +ee e9fc0 +29 19fdb9 +f6 27ca18 +f6 ea7ca +31 1a05c3 +fe 27cb6f +fe ea921 +39 1a071a +85 263975 +1a 3b4b1 +cf 128bae +b1 cf170 +e6 27c365 +71 2ad0e +f6 27ccc6 +43 5e67b +4f 1f1cb3 +a4 2607e1 +85 264989 +81 d14a8 +8d 264ae0 +dd e6a2e +94 d3355 +e6 27d379 +9c d34ac +e2 e9e98 +ee 27d4d0 +c7 281521 +e4 28566e +c3 ee040 +cf 281678 +e0 f218d +ec 2857c5 +e6 27d627 +c4 277f09 +cc 278060 +a2 106a29 +ae 29a061 +d4 27886a +c2 11f66d +45 1f0b3f +ce 2b2ca5 +aa 106b80 +dc 2789c1 +e5 27c0b1 +ed 27c208 +c0 e5f9a +cc 2795d2 +fd 27cb69 +96 295402 +9e 295559 +86 296013 +b7 2995aa +c4 2781b7 +e5 27c35f +f9 f3f5a +96 2956b0 +c4 2791cb +69 62972 +c0 e5cea +cc 279322 +e5 27d373 +e1 e9e92 +ed 27d4ca +86 295d63 +c4 279479 +69 62c20 +e5 27d621 +e4 27c0b0 +ec 27c207 +f4 27ca11 +65 1f4ce6 +e2 123814 +ee 2b6e4c +fc 27cb68 +c5 280258 +cd 2803af +ae 298d9f +b6 2995a9 +be 299700 +87 29cdf0 +a4 2a0f3d +8f 29cf47 +ac 2a1094 +97 29d751 +ee 124c52 +b4 2a189e +9f 29d8a8 +bc 2a19f5 +e4 27c35e +f4 27ccbf +c5 280506 +b6 299857 +e4 27d372 +e0 e9e91 +ec 27d4c9 +c5 28151a +c1 ee039 +cd 281671 +9d 1035bf +a6 299f0a +87 29e0b2 +83 10abd1 +8f 29e209 +a0 10ed1e +ac 2a2356 +e4 27d620 +c5 2817c8 +a6 29a1b8 +2d 19fb30 +21 c4f8 +29 c64f +4 3f +39 cfb0 +3b cfb7 +3d cfe1 +5b 1b8e51 +fa 28640c +6d 1bc6c1 +61 29089 +6f 1bc6c8 +63 29090 +65 290ba +e7 27c374 +7b 1f5704 +60 1ec658 +67 290c1 +4c 1e98fb +40 562c3 +69 291e0 +6b 291e7 +6d 29211 +68 1ec7af +6f 29218 +48 5641a +7d 1bd022 +71 299ea +44 1cbd0 +79 29b41 +46 1cbd7 +7b 29b48 +7d 29b72 +78 1ed110 +7f 29b79 +58 56d7b +ad 268c27 +a1 d55ef +af 268c2e +a3 d55f6 +a5 d5620 +79 29e5f +a9 d5746 +ab d574d +ad d5777 +bd 269588 +b1 d5f50 +bf 26958f +b3 d5f57 +84 c9136 +b9 d60a7 +bb d60ae +bd d60d8 +ed 2857b8 +e1 f2180 +ef 2857bf +e3 f2187 +e5 f21b1 +e7 f21b8 +e9 f22d7 +eb f22de +ed f2308 +ef f230f +c8 11f511 +fd 286119 +f1 f2ae1 +ff 286120 +f3 f2ae8 +f5 f2b12 +6a 1ec7b6 +f7 f2b19 +dc 2b3353 +d0 11fd1b +c4 e5cc7 +f9 f2c38 +c6 e5cce +fb f2c3f +fd f2c69 +ff f2c70 +d8 11fe72 +2d 19fb3e +21 c506 +25 c537 +3b 1d8b81 +29 c65d +2d c68e +7f 1f674b +73 63113 +65 290c8 +7b 1f5712 +6d 2921f +ad 268c35 +a1 d55fd +a5 d562e +a9 d5754 +ad d5785 +ff 2bf842 +a 1c2 +f3 12c20a +bd 269596 +b1 d5f5e +16 192beb +94 10cacd +83 264c0f +b9 d60b5 +1e 192d42 +9c 10cc24 +8b 264d66 +fd 286127 +f1 f2aef +56 1af77c +c3 2817a0 +f9 f2c46 +5e 1af8d3 +cb 2818f7 +86 25b37f +8e 25b4d6 +96 25bce0 +9e 25be37 +f9 284e88 +c4 277f17 +ff 125616 +a2 106a37 +ae 29a06f +d4 278878 +aa 106b8e +dc 2789cf +e5 27c0bf +ed 27c216 +c4 279489 +f5 27ca20 +cc 2795e0 +c0 e5fa8 +fd 27cb77 +96 25bf8e +f9 285136 +c4 2781c5 +e5 27c36d +86 25c641 +5d 1b7c29 +da e6757 +78 1ee432 +2b 45de8 +82 c9160 +51 1e90bf +8e 25c798 +f9 28614a +c4 2791d9 +c0 e5cf8 +cc 279330 +e5 27d381 +e1 e9ea0 +ed 27d4d8 +f9 2863f8 +c4 279487 +88 2637ee +e5 27d62f +b6 25fe87 +e4 27c0be +ec 27c215 +f4 27ca1f +fc 27cb76 +cd 2803bd +f3 f3daa +63 1ec962 +b6 260135 +45 1aedb1 +e4 27c36c +c5 280514 +87 264990 +83 d14af +8f 264ae7 +a0 d55fc +ac 268c34 +e4 27d380 +5a 1e8f62 +e0 e9e9f +ec 27d4d7 +45 1b0073 +e4 27d62e +c5 2817d6 +b9 2a1a19 +84 294aa8 +ce 280673 +4a 1e98c1 +99 d347e +eb 27d4a2 +94 295409 +9c 295560 +c4 e728b +ad 298da7 +fe 2b64dd +84 29601a +b5 2995b1 +d4 e7bec +8c 296171 +80 102b39 +bd 299708 +b9 2a1cc7 +84 294d56 +94 2956b7 +a5 298efe +b5 29985f +b9 2a2cdb +84 295d6a +a1 106a31 +ad 29a069 +f2 124167 +fe 2b779f +b9 2a2f89 +84 296018 +a4 298c4f +b4 2995b0 +85 29cdf7 +d6 2ba52d +5 1cb942 +a4 298efd +77 5ae04 +15 1cc2a3 +b4 29985e +85 29d0a5 +95 29da06 +ea 27c48b +a4 299f11 +5 1ccc04 +a4 29a1bf +de 2b33bc +b5 2997ff +d2 11fd84 +77 5c0c6 +70 1b4bab +2f 19fb99 +23 c561 +29 c6b1 +78 1b4d02 +2b c6b8 +ba cf021 +3d 1a04f3 +31 cebb +5d 1b9137 +51 25aff +2b 472f6 +3f 1a04fa +33 cec2 +39 d012 +3b d019 +69 29242 +6b 29249 +fa ebbb2 +7d 1bd084 +71 29a4c +6b 63e87 +7f 1bd08b +73 29a53 +79 29ba3 +7b 29baa +8 41bc7 +a9 d57a8 +18 42528 +84 c9198 +b9 d6109 +e9 f2339 +4a 5e75f +eb f2340 +69 5b884 +c4 e5d29 +f9 f2c9a +c6 e5d30 +5a 5f0c0 +fb f2ca1 +79 5c1e5 +3d 1a0501 +ce 128b4b +31 cec9 +39 d020 +69 29250 +fa ebbc0 +7d 1bd092 +71 29a5a +79 29bb1 +18 42536 +b9 d6117 +48 5e766 +e9 f2347 +58 5f0c7 +f9 f2ca8 +85 25b379 +8d 25b4d0 +9d 25be31 +ac 25f678 +bc 25ffd9 +9c d21ec +b9 cf01b +ee 27c210 +85 25b627 +85 25c63b +e7 ea188 +46 565a7 +7b 63518 +81 c915a +8d 25c792 +94 d3357 +e6 27d37b +9c d34ae +e2 e9e9a +ee 27d4d2 +66 290ce +85 25c8e9 +8c 26381f +c2 ef5bf +f3 f2b56 +ff 28618e +9c 264180 +d6 280bc1 +9 193678 +54 1ea105 +de 280d18 +50 56c24 +5c 1ea25c +d6 280e6f +b 41e7d +f9 27e0a8 +9 193926 +54 1ea3b3 +d8 1213e0 +c6 281522 +a5 267abc +c2 ee041 +ce 281679 +c5 277f0a +ec 27c209 +fc 27cb6a +a6 298c4a +b6 2995ab +be 299702 +c5 2781b8 +a6 298ef8 +e4 eb192 +b6 299859 +c5 2791cc +c1 e5ceb +cd 279323 +e0 e9e93 +ec 27d4cb +a6 299f0c +c5 27947a +cc 2803b0 +aa 10eed0 +dc 280d11 +96 29d752 +9e 29d8a9 +86 29d09f +a9 29a2d8 +c4 ef339 +96 29da00 +b9 29ac39 +c0 ee03a +cc 281672 +86 29e0b3 +6f 1bc976 +63 2933e +65 29368 +60 1ec906 +67 2936f +4c 1e9ba9 +7d 1ed140 +40 56571 +75 29cc9 +ad 268ed5 +ca ef45a +a1 d589d +af 268edc +a3 d58a4 +ce ef48b +a5 d58ce +bd 269836 +da efdbb +b1 d61fe +bf 26983d +b3 d6205 +de efdec +b5 d622f +7e 1b4f86 +72 2194e +2a 1cfed3 +ed 285a66 +e1 f242e +ef 285a6d +e3 f2435 +e5 f245f +e7 f2466 +cc 2b2ca0 +fd 2b6237 +c0 11f668 +fd 2863c7 +f1 f2d8f +ff 2863ce +f3 f2d96 +f5 f2dc0 +6a 1eca64 +f7 f2dc7 +dc 2b3601 +d0 11ffc9 +25 c7e5 +65 29376 +ad 268ee3 +ca ef468 +a1 d58ab +ce ef499 +a5 d58dc +bd 269844 +da efdc9 +62 1ec64f +b1 d620c +fd 2863d5 +f1 f2d9d +b6 25fe89 +c5 277f18 +e4 27c0c0 +ec 27c217 +fc 27cb78 +b6 260137 +c5 2781c6 +e4 27c36e +c5 2791da +c1 e5cf9 +cd 279331 +e0 e9ea1 +ec 27d4d9 +c5 279488 +c4 280267 +cc 2803be +aa 10eede +dc 280d1f +f1 27dca5 +a2 10f035 +f 1d50b2 +3 41a7a +ae 2a266d +d4 280e76 +98 10484f +86 264991 +a4 298c51 +b4 2995b2 +85 294d57 +a4 298eff +b4 299860 +85 295d6b +d6 2b34a1 +81 10288a +8d 295ec2 +d2 11ffc0 +de 2b35f8 +a4 299f13 +85 296019 +ef 123743 +84 29cdf8 +d4 ee9ca +8c 29cf4f +9c 29d8b0 +a1 299ed5 +ef 1239f1 +84 29d0a6 +b1 29a836 +ff 124352 +94 29da07 +7f 21ae5 +e4 2bf0a0 +31 197fc9 +ba cf2cf +3d 1a07a1 +31 d169 +e2 2b6a78 +c7 28026f +3f 1a07a8 +33 d170 +6f 1bc9d8 +63 293a0 +7f 1bd339 +73 29d01 +ad 268f37 +c 1d5356 +0 41d1e +a1 d58ff +6b 1f5dc5 +ef 285acf +42 5e8b6 +4e 1f1eee +e3 f2497 +61 5b9db +fd 286429 +f1 f2df1 +3d 1a07af +ce 128df9 +ff 12c390 +31 d177 +fa ebe6e +7d 1bd340 +71 29d08 +ad 268f45 +c 1d5364 +0 41d2c +a1 d590d +ed 285ad6 +40 5e8bd +4c 1f1ef5 +e1 f249e +29 d911 +61 2a34b +63 2a352 +6 1cbbf8 +69 2a4a2 +e8 123964 +a1 d68b1 +cc 2805fa +ec 123995 +a5 d68e2 +79 2b121 +a9 d6a08 +ad d6a39 +e1 f3442 +e5 f3473 +e7 f347a +c0 12067c +86 294cef +e9 f3599 +ed f35ca +ef f35d1 +c8 1207d3 +61 2a359 +6 1cbc06 +69 2a4b0 +a5 d68f0 +c5 2b9988 +ad d6a47 +cd 2b9adf +a 1484 +f3 12d4cc +e1 f3450 +e5 f3481 +86 294cfd +e9 f35a7 +ed f35d8 +9c 25be3e +c6 1206b4 +18 1d5c88 +84 25c8f8 +b5 25fe8f +80 c9417 +8c 25ca4f +bd 25ffe6 +e7 12485c +d6 27887f +de 2789d6 +ef 27c21d +c2 e5faf +ce 2795e7 +ff 27cb7e +b9 2685a5 +18 1d49c4 +84 25b634 +a5 106cb0 +ce 12086d +39 1d8b6c +a5 25f7dc +ef 124a15 +b5 26013d +ff 125376 +d6 278b2d +80 c9167 +8c 25c79f +d2 2b3224 +39 1d9b80 +a5 2607f0 +f6 27df26 +a1 cd30f +ad 260947 +75 1bbf17 +f2 eaa45 +fe 27e07d +e3 e9ea7 +ef 27d4df +b9 269867 +18 1d5c86 +84 25c8f6 +39 1d9e2e +a5 260a9e +fb 2863ff +5a 1f281e +c6 27948e +a4 25f52d +b4 25fe8e +b1 ceed0 +e6 27c0c5 +b9 cf027 +ee 27c21c +f6 27ca26 +fe 27cb7d +d7 280bce +df 280d25 +85 263983 +cf 128bbc +95 2642e4 +df 12951d +47 1aedb8 +b1 cf17e +e6 27c373 +2d 4330 +71 2ad1c +57 1af719 +f6 27ccd4 +a0 cd30e +ac 260946 +d7 280bc2 +f2 2b73cb +e6 27d387 +e2 e9ea6 +ee 27d4de +47 1b007a +e6 27d635 +8e 294c06 +96 295410 +31 1d87d9 +6b 5bb8d +9e 295567 +86 296021 +b7 2995b8 +f9 f3f68 +96 2956be +bb 2a2ce2 +86 295d71 +82 102890 +8e 295ec8 +bb 2a2f90 +86 29601f +b6 2995b7 +17 1cc2aa +b6 299865 +87 29e0c0 +83 10abdf +8f 29e217 +29 d973 +6 1cbc5a +69 2a504 +6b 2a50b +0 42d32 +e8 1239c6 +a1 d6913 +cc 28065c +8 42e89 +a9 d6a6a +a 42e90 +ab d6a71 +e8 2bdc56 +e1 f34a4 +86 294d51 +e9 f35fb +61 2a3bb +6 1cbc68 +69 2a512 +40 5f8d1 +e1 f34b2 +21 da68 +61 2a5f9 +63 2a600 +34 4b3b +a1 d6b5f +74 216cc +e1 f36f0 +61 2a607 +74 216da +e1 f36fe +95 25bce8 +9d 25be3f +38 1d88bf +a4 25f52f +bc 25ffe7 +d7 278880 +df 2789d7 +b9 cf029 +ee 27c21e +f6 27ca28 +fe 27cb7f +19 1d49c5 +85 25b635 +95 25bf96 +38 1d8b6d +a4 25f7dd +f6 27ccd6 +19 1d59d9 +85 25c649 +27 1d9410 +d6 279d7f +7b 63526 +81 c9168 +8d 25c7a0 +23 45f2f +d2 e689e +55 1b7d70 +2f 1d9567 +de 279ed6 +2c 4731f +d3 2b3225 +a0 cd310 +ac 260948 +f2 2b73cd +e2 e9ea8 +ee 27d4e0 +19 1d5c87 +85 25c8f7 +75 1f68a7 +ef ea021 +84 2636d6 +ff ea982 +ce e73eb +94 264037 +9c 26418e +d6 280bcf +b 41bdd +f9 27de08 +de 280d26 +b1 261114 +10 1cd533 +ff eac30 +94 2642e5 +e9 27d755 +f3 27dcac +52 1ea0cb +d6 280e7d +b 41e8b +f9 27e0b6 +8f 294c07 +98 29eddf +b6 2995b9 +f1 284fdd +b6 299867 +87 295d72 +83 102891 +8f 295ec9 +86 29e0c1 +82 10abe0 +d6 efc93 +8e 29e218 +21 daca +63 2a662 +0 42fe0 +a1 d6bc1 +2 42fe7 +4b 566c0 +a3 d6bc8 +e0 2bddad +e1 f3752 +61 2a669 +0 42fee +49 566c7 +a1 d6bcf +40 5fb7f +e1 f3760 +8 1925f3 +18 192f54 +39 1970fc +fc eac2a +4a 1af18b +6b 1b3333 +7b 1b3c94 +da e67a9 +bd 260224 +eb 27c48c +46 1e8797 +fb 27cded +56 1e90f8 +8 1938b5 +fc ebeec +4a 1b044d +6b 1b45f5 +88 25ca0e +a1 cd54d +ca e710a +ad 260b85 +28 19679a +38 1970fb +19 19b2a3 +5b 1b7e3b +32 465f1 +3e 1d9c29 +8d 263a6a +89 263a9b +9d 2643cb +8 398db +42 1e84aa +99 2643fc +fa 27cdec +cb 280633 +34 19805b +49 1e9b77 +db 280f94 +8a ca579 +59 1ea4d8 +28 197a5c +9 19bc04 +fd f423b +4b 1b879c +48 1af184 +58 1afae5 +69 1b332c +79 1b3c8d +ed 27c454 +e9 27c485 +66 5ba02 +44 1e8790 +fd 27cdb5 +f9 27cde6 +9 192664 +76 5c363 +54 1e90f1 +d2 2bb830 +8e 294e44 +b8 110da8 +4e 1f09e2 +19 437ed +9e 2957a5 +ab 29901d +48 1b0446 +c0 e5f36 +cc 27956e +c8 27959f +82 102ace +5 1d3fa0 +8e 296106 +d1 278ae8 +ab 29a2df +68 1b332b +78 1b3c8c +ec 27c453 +fc 27cdb4 +72 63182 +7e 1f67ba +cd 2805fb +c9 28062c +d6 e6683 +8e 294c08 +d9 280f8d +9e 295569 +4 3aa23 +a5 ce604 +6e 1f4b89 +39 47994 +aa 29901c +be 29994c +8b 29d1c4 +68 1b45ed +e0 ea0dd +ec 27d715 +2e 3da58 +c1 ee285 +72 64444 +cd 2818bd +2a 3da89 +5c 1af8ca +c9 2818ee +d6 e7945 +82 102892 +8e 295eca +a2 106c75 +d4 278ab6 +25 1d8147 +ae 29a2ad +bc ce099 +a 1925fa +1a 192f5b +3b 197103 +58 1afaf3 +79 1b3c9b +ed 27c462 +92 103491 +9e 296ac9 +1 19b7ff +e9 27c493 +bc cf35b +a 1938bc +3c 3f922 +2b 197a64 +48 1b0454 +c8 2795ad +e1 ea0ec +ed 27d724 +2a 1967a1 +85 ca699 +3a 197102 +1b 19b2aa +df 2b20eb +96 29ea12 +68 1b3339 +78 1b3c9a +49 1b74e1 +59 1b7e42 +aa 25f8fa +ba 26025b +49 5fa8d +d6 2ba52f +9c 10b960 +8b 263aa2 +9 1ccfe6 +9b 264403 +19 1cd947 +49 1aeed7 +0 19b7fe +e8 27c492 +cd 280609 +c9 28063a +d9 280f9b +2a 197a63 +bd d76aa +1c 43ac9 +b 19bc0b +49 1b87a3 +49 1b0199 +e8 27d754 +2e 3da66 +c1 ee293 +cd 2818cb +2a 3da97 +5c 1af8d8 +c9 2818fc +29 1cfecb +39 1d082c +8c 294e4b +ca 11f578 +ad 298ff3 +80 102ad5 +8c 29610d +a1 106c7d +ca 12083a +ad 29a2b5 +2f c933 +28 1cfeca +3f d294 +38 1d082b +51 1f2423 +ac 298ff2 +af d5a8c +9 1cba68 +a8 299023 +32 47851 +7b 5af2a +bc 299953 +bf d63ed +19 1cc3c9 +b8 299984 +8d 29d19a +9d 29dafb +2f dbf5 +d 19a983 +28 1d118c +a0 106c7c +ac 29a2b4 +81 10ae24 +8d 29e45c +19 192f55 +28 19679c +9d 25c07d +99 25c0ae +ac 25f8c4 +a1 298c13 +a8 25f8f5 +c0 ef548 +f1 f2adf +fd 286117 +bc 260225 +b1 299574 +b8 260256 +59 26cbe +ee 27c45c +9d 29edbf +91 10b787 +e3 2b57ab +ea 27c48d +b7 110c7a +95 29da08 +fe 27cdbd +f3 2b610c +c 1cbaa6 +ad 25f687 +fa 27cdee +9 1938b6 +fd ebeed +4b 1b044e +8 19a943 +7b 1bd2b2 +8c 263a6b +f3 f2da2 +ff 2863da +81 29cdba +88 263a9c +4a 26363 +21 c7a6 +2d 19fdde +fb 28640b +ce 280603 +c3 2b9952 +68 1b332d +78 1b3c8e +80 264c05 +c9 2782de +ec 27c455 +e8 27c486 +fc 27cdb6 +f8 27cde7 +8b 294e76 +9b 2957d7 +aa 29901e +ec eb287 +be 29994e +e8 eb2b8 +ba 29997f +49 1b0447 +c1 e5f37 +72 5c0f6 +cd 27956f +c9 2795a0 +48 1b74d4 +58 1b7e35 +cc 2805fc +c8 28062d +61 29337 +6d 1bc96f +aa 10f11c +dc 280f5d +d8 280f8e +71 29c98 +7d 1bd2d0 +8e 29d194 +8a 29d1c5 +46 5f909 +e7 f34ea +cc ef42e +9e 29daf5 +c8 ef45f +9a 29db26 +56 6026a +f7 f3e4b +1b 192f5c +2a 1967a3 +d6 2b21e1 +9c 103612 +8b 25b754 +9b 25c0b5 +19 a12d +ae 25f8cb +a3 298c1a +aa 25f8fc +f3 f2ae6 +c2 ef54f +ff 28611e +be 26022c +b3 29957b +ba 26025d +84 264be2 +cd 2782bb +80 264c13 +c9 2782ec +b2 d6266 +be 26989e +61 1bc59b +4e 2531c +0 19b800 +e8 27c494 +1c 3b77b +bd cf35c +b 1938bd +49 1b0455 +c1 e5f45 +cd 27957d +c9 2795ae +a 19a94a +9d 295553 +48 1b74e2 +8e 263a72 +83 29cdc1 +cc 28060a +e3 12cbcb +c1 2b9959 +c8 28063b +61 29345 +6d 1bc97d +9 1cbd24 +19 1cc685 +c0 128c78 +42 1aed96 +8 1c7 +f1 12c20f +fd 2bf847 +1e 4280e +bf d63ef +b8 299986 +50 25aee +5c 1b9126 +8 1d4073 +7 12fb +18 1d49d4 +d4 eec16 +8c 29d19b +9c 29dafc +87 ca454 +98 29db2d +8 192601 +29 1967a9 +39 19710a +35 e14c +7c 5b1ff +6b 1b3341 +7b 1b3ca2 +ca e5e56 +ad 25f8d1 +f4 2b638f +a9 25f902 +b9 260263 +e3 124ad9 +8 1938c3 +fc ebefa +4a 1b045b +7c 5c4c1 +6b 1b4603 +88 25ca1c +a1 cd55b +ca e7118 +ad 260b93 +c2 e5f4b +45 1b741d +ce 279583 +c3 2b28d2 +9f 29555a +28 1967a8 +5e 1e8f91 +b6 269499 +ff 27cb72 +ed 284806 +38 197109 +9 19a950 +19 19b2b1 +7d 21824 +34 e14b +6a 1b3340 +c5 e7238 +7a 1b3ca1 +8d 29e450 +81 10ae18 +8d 263a78 +d4 2ba536 +89 263aa9 +9d 2643d9 +99 26440a +56 56c5c +c3 128c80 +59 26cca +ee 27c468 +e3 2b57b7 +16 1cc567 +79 2ae11 +4b 1aeede +fd ea97d +2 19b805 +b4 d72a4 +ea 27c499 +cc 28034c +61 1b44fb +fe 27cdc9 +c2 2b2b81 +f3 2b6118 +25 1cfaeb +dc 1284ff +cb 280641 +93 1031e4 +9f 29681c +28 197a6a +7d 22ae6 +6a 1b4602 +81 d1702 +8d 264d3a +65 1bb5c4 +e2 ea0f2 +ee 27d72a +e3 2b6a79 +5e 1af8df +dc 1297c1 +cb 281903 +2b 1cfed2 +53 1f242a +19 4385b +ae 298ff9 +6e 1f4b97 +39 479a2 +29 460f +63 1b31de +8c 29cedd +21 1d108c +be 29995a +d0 278af5 +b 1ccd31 +bd 1087d0 +aa 29a2ec +29 58d1 +63 1b44a0 +80 10ab67 +8c 29e19f +9 192602 +19 192f63 +6a 1b3342 +8d 25b72a +d4 2b21e8 +f6 12545a +89 25b75b +9d 25c08b +68 1ecabd +21 19fa0a +99 25c0bc +e 878b +a8 25f903 +f1 f2aed +c0 ef556 +fd 286125 +1e 90ec +b8 260264 +95 d30fe +dc 1201b1 +cb 2782f3 +db 278c54 +9 1938c4 +5c 5831a +fd ebefb +4b 1b045c +81 c93b4 +8d 25c9ec +d4 2b34aa +89 25ca1d +dc 121473 +cb 2795b5 +8 19a951 +8c 263a79 +a3 11003a +d5 281e7b +81 29cdc8 +88 263aaa +4a 26371 +21 c7b4 +2d 19fdec +ce 280611 +ca 280642 +3a 1d0834 +d6 eec1d +8e 29d1a2 +9e 29db03 +8c 25b77d +50 1f2730 +ad 25f925 +bd 260286 +e7 124afc +80 c9407 +8c 25ca3f +a1 cd5af +ad 260be7 +8d 263acc +9d 26442d +42 1e850c +c7 128ca3 +b9 cf2c7 +ee 27c4bc +16 1cc5bb +79 2ae65 +fe 27ce1d +e2 ea146 +ee 27d77e +ed 27c4b6 +fd 27ce17 +8e 294ea6 +ae 298fed +19 4384f +9e 295807 +c0 e5f98 +cc 2795d0 +e1 ea140 +ed 27d778 +ec 27c4b5 +fc 27ce16 +cd 28065d +53 1f247e +ae 29904d +39 479f6 +be 2999ae +e0 ea13f +ec 27d777 +c1 ee2e7 +cd 28191f +3f 1d9bba +33 46582 +37 465b3 +6 39768 +3b 466d9 +3f 4670a +d2 279db0 +a7 10ed49 +82 294d2e +d6 279de1 +7b 63588 +a5 10ed50 +a2 107fa7 +3 3a9ec +d4 279de8 +da 279f07 +fd f3f29 +a9 10ee76 +d8 279f0e +af 10eea0 +8a 294e85 +d2 e6900 +de 279f38 +ad 10eea7 +d0 e6907 +aa 1080fe +b 3ab43 +dc 279f3f +bf 2a2cb1 +b3 10f679 +b7 10f6aa +86 10285f +bb 10f7d0 +b9 10f7d7 +1e 1cc464 +8b 29e488 +bf 10f801 +6f 1bc6d6 +63 2909e +67 290cf +6b 291f5 +6f 29226 +7f 29b87 +3f 1d9bc8 +33 46590 +37 465c1 +3f 46718 +e7 f21c6 +eb f22ec +ef f231d +f4 2b73a1 +a7 10ed57 +d6 279def +d2 e690e +de 279f46 +5 19b830 +ed 27c4c4 +15 19c191 +fd 27ce25 +c0 e5fa6 +cc 2795de +e1 ea14e +ed 27d786 +53 1b8d5c +ae 25f92b +39 e2d4 +be 26028c +4 19b82f +4d 1aef08 +ec 27c4c3 +5d 1af869 +2b 3da28 +14 19c190 +fc 27ce24 +cd 28066b +f3 f4058 +a2 cd5b5 +ae 260bed +4d 1b01ca +e0 ea14d +41 1cb92 +ec 27d785 +c1 ee2f5 +cd 28192d +9c 29580e +ad 299055 +bd 2999b6 +a1 106cdf +ad 29a317 +d 1cba99 +51 1f2485 +ac 299054 +36 47882 +7f 5af5b +1d 1cc3fa +bc 2999b5 +8d 29d1fc +b3 110be9 +9d 29db5d +5d 1b7c1b +da e6749 +2b 45dda +3b 4673b +39 46742 +94 25cf9b +86 1028c1 +bb 10f832 +b9 10f839 +6b 29257 +7f 1bd099 +73 29a61 +7b 29bb8 +de 279c38 +70 1ee2db +55 1b7ad2 +d2 e6600 +2f 1d92c9 +23 45c91 +3f 1d9c2a +33 465f2 +3b 46749 +96 25cfa2 +4a 5e76d +eb f234e +8d 25b77e +9d 25c0df +ac 25f926 +bc 260287 +b9 cf2c9 +ee 27c4be +fe 27ce1f +6e 29225 +81 c9408 +8d 25ca40 +8c 263acd +f3 f2e04 +ff 28643c +84 264c36 +cd 27830f +ec 27c4b7 +fc 27ce18 +ae 29904f +ec eb2e9 +be 2999b0 +c1 e5f99 +cd 2795d1 +cc 28065e +aa 10f17e +dc 280fbf +8e 29d1f6 +cc ef490 +9e 29db57 +55 1b7d10 +2f 1d9507 +23 45ecf +4a 5fa93 +75 2af89 +2d 1d950e +21 45ed6 +90 c9aba +9c 25d0f2 +27 45f00 +3f 1d9e68 +33 46830 +5a 603f4 +3d 1d9e6f +31 46837 +37 46861 +ad 2a2605 +ca 128b8a +f5 f4080 +a1 10efcd +a7 10eff7 +bf 2a2f5f +b3 10f927 +bd 2a2f66 +da 1294eb +b1 10f92e +b7 10f958 +6f 1bc984 +63 2934c +67 2937d +92 c9ac1 +9e 25d0f9 +74 1ee558 +27 45f0e +3f 1d9e76 +33 4683e +37 4686f +e7 f2474 +ae 25f92d +d0 11fd19 +8f 10ad07 +dc 2b3351 +be 26028e +4 19b831 +ec 27c4c5 +14 19c192 +2b 3da2a +fc 27ce26 +c1 e5fa7 +cd 2795df +f4 eaad1 +ac 299056 +bc 2999b7 +81 102b38 +8d 296170 +b9 29a98d +9c 29db5e +bf 2a2fc1 +b3 10f989 +bd 2a2fc8 +b1 10f990 +7f 1bd347 +73 29d0f +de 279ee6 +70 1ee589 +d2 e68ae +2f 1d9577 +55 1b7d80 +23 45f3f +3f 1d9ed8 +33 468a0 +ef 285add +4e 1f1efc +42 5e8c4 +e3 f24a5 +ff 28643e +52 5f225 +5e 1f285d +f3 f2e06 +4a 1f095d +bf 2a2fcf +b3 10f997 +9c 25c07e +29 47041 +d5 281e1b +a3 10ffda +ce 2b9d23 +a7 11000b +14 8f8e +7b 6484a +dd 281f72 +d1 ee93a +ab 110131 +a9 110138 +d5 ee96b +af 110162 +ad 110169 +63 2a360 +6b 2a4b7 +51 25851 +5d 1b8e89 +2b 47048 +be cf2fe +9c 25c08c +e3 f3457 +e7 f3488 +eb f35ae +ef f35df +a7 110019 +d5 ee979 +f0 125182 +af 110170 +bd 260294 +e7 124b0a +de 278c84 +80 c9415 +8c 25ca4d +d2 2b34d2 +a1 cd5bd +ad 260bf5 +f3 2b767a +c2 e5fad +ce 2795e5 +9d 26443b +c7 128cb1 +42 1e851a +6 19b836 +b9 cf2d5 +4f 1aef0f +ee 27c4ca +16 1cc5c9 +79 2ae73 +5f 1af870 +16 19c197 +fe 27ce2b +25 1cfb4d +e2 ea154 +43 1cb99 +4f 1b01d1 +ee 27d78c +9e 295815 +82 102b3e +8e 296176 +53 1f248c +f 1cbaa0 +ae 29905b +39 47a04 +1f 1cc401 +be 2999bc +a2 106ce5 +f 1ccd62 +3 3972a +d4 278b26 +ae 29a31d +51 258a5 +da e7a0b +5d 1b8edd +2b 4709c +9c 25c0e0 +dd 281fd4 +d1 ee99c +ab 110193 +63 2a3c2 +6b 2a519 +51 258b3 +5d 1b8eeb +da e7a19 +2b 470aa +be cf360 +9c 25c0ee +42 5f8d8 +e3 f34b9 +4a 5fa2f +eb f3610 +d5 281e8b +a3 11004a +99 10cb9e +eb 2b6bc2 +ce 2b9d93 +dd 281fe2 +d1 ee9aa +ab 1101a1 +55 1b8fd2 +23 47191 +d5 2820c9 +36 3e264 +a3 110288 +63 2a60e +55 1b8fe0 +23 4719f +76 216e1 +e3 f3705 +9d 25c0ed +bc 260295 +b9 cf2d7 +6 19b838 +ee 27c4cc +16 19c199 +fe 27ce2d +81 c9416 +8d 25ca4e +d3 2b34d3 +f6 eaad8 +ae 29905d +be 2999be +d2 e7b62 +55 1b9034 +23 471f3 +d0 e7b69 +21 471fa +d5 28212b +a3 1102ea +a1 1102f1 +63 2a670 +42 5fb86 +e3 f3767 +d5 282139 +a3 1102f8 +10 193e11 +14 19b18a +31 197fb9 +52 1b09a9 +5a 1b0b00 +56 1b7d22 +73 1b4b51 +5e 1b7e79 +7b 1b4ca8 +90 25cf6a +90 c9a58 +9c 25d090 +98 25d0c1 +b5 2610e1 +94 2642e3 +b1 261112 +27 1d9162 +d6 279ad1 +7b 63278 +2f 1d92b9 +23 45c81 +d2 e65f0 +55 1b7ac2 +de 279c28 +45 1b86d3 +f3 ea798 +c2 e7201 +ff 27ddd0 +56 1ea10c +10 1940bf +73 1b4dff +94 25d1e7 +39 4698e +90 25d218 +b1 2613c0 +30 197fb8 +f9 27cdf4 +11 19c160 +19 19c2b7 +72 1b4b50 +7a 1b4ca7 +5b 1b8e4f +53 5ef7a +5f 1f25b2 +b4 2610e0 +51 1b7d41 +2b 1d9538 +5b 1f25e3 +b0 261111 +b0 cdbff +5b 5f0d1 +bc 261237 +95 265288 +91 d1da7 +9d 2653df +f6 27dc78 +75 1bbc69 +f2 ea797 +44 1b86d2 +fe 27ddcf +fa 27de00 +30 198266 +11 19c40e +95 265536 +d3 2820ff +f 19b990 +3 8358 +50 1b09a2 +b 84af +58 1b0af9 +54 1b7d1b +71 1b4b4a +b3 26111b +12 1cd53a +16 1d48b3 +33 1d16e2 +1e 1d4a0a +3b 1d1839 +25 1d915b +a2 107c89 +87 d1480 +d4 279aca +4b 1f1f22 +79 63271 +d0 e65e9 +aa 107de0 +8f d15d7 +21 45c7a +2d 1d92b2 +dc 279c21 +8b d1608 +d8 279c52 +f5 27dc72 +c0 e71fa +f1 ea791 +fd 27ddc9 +96 296662 +92 296693 +92 103181 +15 1d4653 +9e 2967b9 +9a 2967ea +b7 29a80a +18 1cc6da +96 29da0c +b3 29a83b +35 19f386 +b2 cdeb4 +be 2614ec +9e 29db63 +bb 29a992 +3 8606 +f 19bc3e +50 1b0c50 +b3 2613c9 +12 1cd7e8 +33 1d1990 +25 1d9409 +a2 107f37 +87 d172e +d4 279d78 +79 6351f +8f 264d97 +83 d175f +d0 279da9 +ad 2a2349 +a1 10ed11 +96 296910 +92 296941 +b3 29aae9 +2f 19fb37 +23 c4ff +70 1b4b49 +2b c656 +78 1b4ca0 +59 1b8e48 +a7 d5627 +f4 27dc71 +6b 1f60c9 +f0 ea790 +af d577e +fc 27ddc8 +ab d57af +a 41bce +f8 27ddf9 +b6 29a809 +97 264031 +b2 29a83a +b2 107328 +4 1d5263 +35 1d87fa +be 29a960 +97 29e9b1 +64 1bb309 +93 29e9e2 +92 d205b +9e 265693 +93 10b4d0 +9f 29eb08 +6c 1bb460 +9b 29eb39 +2f 19fde5 +23 c7ad +70 1b4df7 +17 19b186 +32 1d198f +13 1d5b37 +a7 d58d5 +f4 27df1f +3e 1d18cb +32 3e293 +d1 2820f8 +b6 29aab7 +64 1bb5b7 +93 29ec90 +12 193e18 +33 197fc0 +3b 198117 +d8 279c60 +12 1940c6 +33 19826e +50 1b0c5e +96 25d1ee +3b 46995 +92 25d21f +b3 2613c7 +25 1d9417 +a2 107f45 +d4 279d86 +f5 27df2e +32 197fbf +fb 27cdfb +13 19c167 +1b 19c2be +70 1b4b57 +78 1b4cae +59 1b8e56 +f4 27dc7f +4 19a81b +6b 1f60d7 +f0 ea79e +fc 27ddd6 +a 41bdc +f8 27de07 +32 19826d +13 19c415 +70 1b4e05 +b6 261395 +93 26556e +f4 27df2d +3e 1d18d9 +32 3e2a1 +d1 282106 +97 d3103 +90 29669a +b7 269498 +9f d325a +98 2967f1 +b3 d5fb7 +82 d2a20 +bf 2695ef +b1 29a842 +b0 cdebb +bc 2614f3 +31 1d1997 +b3 268453 +94 296917 +97 d33b1 +90 296948 +b7 269746 +b1 29aaf0 +15 19aedf +37 e151 +30 1d16e8 +1d 19b036 +3f e2a8 +38 1d183f +11 1d5890 +95 264038 +b7 d72aa +b0 29a841 +b0 10732f +bc 29a967 +9d 26418f +bf d7401 +b8 29a998 +95 29e9b8 +91 29e9e9 +90 d2062 +e2 27c086 +9c 26569a +37 e3ff +15 19b18d +30 1d1996 +b4 29aabe +95 29ec66 +7e 21ad6 +30 197fba +38 5f83 +72 1b4b52 +91 25cf6b +91 c9a59 +9d 25d091 +99 25d0c2 +27 3ee80 +b8 26126a +bc cf0ab +f6 27dc7a +fa 27de02 +11 1940c0 +72 299fe +7e 1bd036 +91 25d219 +b4 261390 +d3 279db1 +bc cf359 +f6 27df28 +39 1d9b82 +4 1ccc11 +a5 2607f2 +5e 25c7d +f8 27cdf5 +10 19c161 +7 43027 +98 265411 +4e 566f2 +2 41d33 +e 1d536b +f0 27df5e +51 1b09a3 +70 1b4b4b +78 1b4ca2 +32 1d16e3 +3a 1d183a +e7 124b6c +8e 10afb2 +d1 e65ea +ab 107de1 +dd 279c22 +8a 10afe3 +d9 279c53 +f4 27dc73 +f0 ea792 +fc 27ddca +a 41bd0 +67 5ba11 +f8 27ddfb +b6 29a80b +b2 29a83c +ba 29a993 +51 1b0c51 +70 1b4df9 +13 1cd7e9 +32 1d1991 +90 d1db4 +9c 2653ec +d1 279daa +f4 27df21 +64 1b3269 +93 296942 +b2 29aaea +58 1b8e49 +1a 1d59e1 +c7 128d13 +33 60e4 +96 29e9b2 +92 29e9e3 +92 10b4d1 +3b 623b +9e 29eb09 +9a 29eb3a +92 29ec91 +32 197fc1 +d7 2bb7f0 +d1 279b0a +d9 279c61 +a 41bde +5e 26c91 +f8 27de09 +51 1b0c5f +93 25d220 +b6 261397 +d1 279db8 +fa 27cdfc +12 19c168 +96 26553e +92 26556f +91 29669b +90 c9d14 +9c 25d34c +5f 56da4 +16 436cb +b7 d72ac +fe 12435f +b0 29a843 +92 c9d6f +9e 25d3a7 +1e 43822 +bf d7403 +b8 29a99a +b7 d755a +16 43979 +5f 57052 +b0 29aaf1 +18 1d59e8 +90 10b4d8 +39 6242 +9c 29eb10 +98 29eb41 +8a c92b9 +7b 5c48a +10 1d5b3f +fb 1255e3 +90 29ec98 +90 25cf78 +98 25d0cf +b1 261120 +b9 261277 +c5 2817ca +10 1940cd +94 25d1f5 +90 25d226 +d2 e7922 +b5 26139d +b1 2613ce +27 1d941e +d6 279d8d +e5 2856c3 +30 197fc6 +ed 28581a +e1 f21e2 +38 19811d +19 19c2c5 +72 1b4b5e +5b 1b8e5d +b4 2610ee +51 1b7d4f +73 2afc1 +2b 1d9546 +b0 26111f +b0 cdc0d +bc 261245 +b8 261276 +91 d1db5 +9d 2653ed +e5 285971 +30 198274 +b4 26139c +11 193e12 +b0 2613cd +95 265544 +f6 27df34 +92 2966a1 +9a 2967f8 +b3 29a849 +c7 2baef3 +12 1cd7f6 +33 1d199e +96 29691e +92 29694f +b3 29aaf7 +95 29e9aa +e7 2bedec +32 1d16ef +13 1d5897 +1b 1d59ee +b2 29a848 +4 1d5271 +b2 107336 +35 1d8808 +be 29a96e +ba 29a99f +f1 2862af +ca e5e48 +ad 25f8c3 +50 1f26ce +97 29e9bf +64 1bb317 +93 29e9f0 +93 10b4de +9f 29eb16 +e7 2bf09a +32 1d199d +13 1d5b45 +b6 29aac5 +13 1cd53b +b2 29aaf6 +31 60db +94 29e9a9 +64 1bb5c5 +93 29ec9e +91 25cf79 +99 25d0d0 +fe eac3d +5f 1d682 +16 9fa9 +b0 261121 +1e a100 +b8 261278 +11 1940ce +95 25d1f6 +91 25d227 +5f 1d930 +16 a257 +b0 2613cf +90 d1db6 +9c 2653ee +98 26541f +4e 56700 +ff ebe90 +ab 106ddd +94 265545 +fb ebec1 +90 265576 +b2 29a84a +ba 29a9a1 +ca e5e4a +ad 25f8c5 +50 1f26d0 +13 1cd7f7 +32 1d199f +b2 29aaf8 +31 60dd +94 29e9ab +12 1d5898 +1a 1d59ef +9e 296a69 +92 103431 +15 1d4903 +92 29e9f1 +92 10b4df +9e 29eb17 +9a 29eb48 +12 1d5b46 +92 29ec9f +b5 261143 +b1 cdc62 +80 ca6cb +bd 26129a +82 294a80 +d6 279b33 +7b 632da +8a 294bd7 +d2 e6652 +b5 2600cd +de 279c8a +94 25d249 +39 469f0 +b5 2613f1 +b0 cdc61 +5b 5f133 +bc 261299 +95 2652ea +91 d1e09 +9d 265441 +f2 ea7f9 +aa 298d7e +fe 27de31 +80 294a79 +a2 107ceb +87 d14e2 +d4 279b2c +79 632d3 +88 294bd0 +d0 e664b +aa 107e42 +8f d1639 +dc 279c83 +6d 1f609f +61 62a67 +b7 29a86c +80 294d27 +a2 107f99 +87 d1790 +d4 279dda +a5 10ed42 +79 63581 +af d57e0 +e 41bff +a8 298d77 +f0 ea7f2 +fc 27de2a +b6 29a86b +b2 10738a +be 29a9c2 +69 1b333a +97 29ea13 +93 10b532 +9f 29eb6a +b0 10f67f +bc 2a2cb7 +b6 29ab19 +21 19e748 +29 19e89f +31 19f0a9 +65 1bb2a8 +67 1bb2af +e6 f24d5 +62 5b723 +47 24f1a +40 1e84b1 +61 1bb2d9 +63 1bb2e0 +9 1c8 +54 56c55 +c1 128c79 +6d 1bb3ff +69 1bb430 +1 12d1 +6b 1bb437 +5c 56dac +c9 128dd0 +75 1bbc09 +77 1bbc10 +e2 12b90a +ee 2bef42 +f6 f2e36 +57 2587b +72 5c084 +50 1e8e12 +71 1bbc3a +73 1bbc41 +19 b29 +d1 1295da +7d 1bbd60 +44 1aee20 +79 1bbd91 +25 19e725 +21 19e756 +72 1bbe8c +2d 19e87c +29 19e8ad +c5 ef57a +7a 1bbfe3 +c2 127701 +ce 2bad39 +6b 2246b +31 19f0b7 +61 1bb2e7 +75 1bbc17 +e0 12b911 +ec 2bef49 +71 1bbc48 +b7 26114a +a2 107cf9 +d4 279b3a +d0 e6659 +aa 107e50 +dc 279c91 +f 41c0e +f1 ea801 +c0 e726a +fd 27de39 +96 25d250 +3b 469f7 +b6 261149 +97 2652f1 +fd 27cb17 +b4 26943e +93 d1e10 +9f 265448 +b0 d5f5d +bc 269595 +f0 ea800 +e 41c0d +fc 27de38 +b6 2613f7 +a3 1102f6 +d5 282137 +94 2966cb +90 1031ea +9c 296822 +b5 29a873 +b1 107392 +80 103dfb +bd 29a9ca +94 296979 +b5 29ab21 +b4 29a872 +b0 107391 +bc 29a9c9 +15 1cd565 +b4 29ab20 +4 41aa1 +a5 d5682 +bb 2a1ccc +c 41bf8 +ad d57d9 +1c 42559 +b 19a69b +bd d613a +30 4b16 +3c 19814e +e5 f2213 +fb 2be85d +e0 2b57b1 +46 5e639 +e7 f221a +cc 2b2a54 +c0 11f41c +65 5b75e +38 4c6d +ed f236a +4e 5e790 +e8 2b5908 +ef f2371 +c8 11f573 +6d 5b8b5 +f0 2b6112 +56 5ef9a +f7 f2b7b +dc 2b33b5 +d0 11fd7d +75 5c0bf +4b 1b722c +fd f2ccb +f8 2b6269 +5e 5f0f1 +ff f2cd2 +d8 11fed4 +7d 5c216 +a2 cd2a7 +ae 2608df +25 19e779 +aa cd3fe +2d 19e8d0 +ee 27d470 +e2 e9e38 +65 1bb30a +79 5b1cf +67 1bb311 +62 5b785 +40 1e8513 +ea e9f8f +6d 1bb461 +5 1302 +6f 1bb468 +6a 5b8dc +48 1e866a +cd 128e01 +f2 ea799 +fe 27ddd1 +75 1bbc6b +ca 2b9d64 +63 62a6e +6f 1f60a6 +77 1bbc72 +72 5c0e6 +50 1e8e74 +d5 12960b +fa ea8f0 +7d 1bbdc2 +14 42410 +b5 d5ff1 +1c 42567 +bd d6148 +44 5e640 +e5 f2221 +fb 2be86b +4a 5e7c3 +2d 1d823e +4c 5e797 +ed f2378 +54 5efa1 +43 1b70e3 +f5 f2b82 +5a 5f124 +3d 1d8b9f +5c 5f0f8 +4b 1b723a +fd f2cd9 +f2 ea7a7 +fe 27dddf +75 1bbc79 +91 c9abb +9d 25d0f3 +b0 cdc63 +bc 26129b +f2 ea7fb +fe 27de33 +d6 281e83 +b5 26841d +d2 ee9a2 +de 281fda +d6 282131 +d1 e664c +ab 107e43 +8e 10b014 +dd 279c84 +f0 ea7f4 +e 41c01 +fc 27de2c +b6 29a86d +b2 10738c +be 29a9c4 +d0 ee99b +aa 110192 +dc 281fd3 +68 1b333b +96 29ea14 +92 10b533 +9e 29eb6b +21 19e9f6 +23 19e9fd +51 1f2421 +31 19f357 +33 19f35e +65 1bb556 +61 1bb587 +63 1bb58e +75 1bbeb7 +71 1bbee8 +73 1bbeef +ef 2bdc1d +21 19ea04 +c2 1279af +ce 2bafe7 +ff 2be57e +31 19f365 +61 1bb595 +71 1bbef6 +b6 26114b +d1 e665a +ab 107e51 +dd 279c92 +f0 ea802 +e 41c0f +fc 27de3a +b6 2613f9 +96 2652f2 +92 d1e11 +9e 265449 +aa 1101a0 +d0 ee9a9 +dc 281fe1 +96 2655a0 +a2 1102f7 +3 42d3c +eb 1239d0 +d4 282138 +91 1031eb +9d 296823 +b4 29a874 +b0 107393 +bc 29a9cb +b4 29ab22 +e0 2b5a5f +46 5e8e7 +e7 f24c8 +65 5ba0c +a2 cd555 +ae 260b8d +25 19ea27 +ee 27d71e +e2 ea0e6 +65 1bb5b8 +6a 1bc6a4 +7a 1bd005 +77 1bbf20 +72 5c394 +50 1e9122 +4 41d5d +21 3eb8c +a5 d593e +44 5e8ee +e5 f24cf +38 19f19d +25 19ea35 +c6 1279e0 +35 19f396 +ee 27d72c +e2 ea0f4 +65 1bb5c6 +fe 27e08d +f2 eaa55 +75 1bbf27 +29 19fb61 +2b 19fb68 +69 1bc6f2 +21 19fa18 +72 1bd14e +29 19fb6f +7a 1bd2a5 +61 1bc5a9 +94 25cfa9 +90 c9ac8 +9c 25d100 +b5 261151 +b1 cdc70 +80 ca6d9 +bd 2612a8 +d6 279b41 +d2 e6660 +b5 2600db +de 279c98 +c2 e7271 +f3 ea808 +ff 27de40 +8c 25b4d1 +94 25d257 +b5 2613ff +b4 261150 +b0 cdc6f +bc 2612a7 +f2 ea807 +fe 27de3f +d7 281e90 +d3 ee9af +df 281fe7 +15 193e43 +b4 2613fe +96 2966d2 +92 1031f1 +9e 296829 +b7 29a87a +96 296980 +b6 29a879 +97 29ea21 +93 10b540 +9f 29eb78 +17 1cd56c +b6 29ab27 +4 42d63 +ec 1239f7 +a5 d6944 +bb 2a2f8e +c 42eba +ad d6a9b +e0 2b6a73 +46 5f8fb +e7 f34dc +c0 1206de +e8 2b6bca +4e 5fa52 +ef f3633 +c8 120835 +aa ce6c0 +21 c55a +2d 19fb92 +ea eb251 +61 290eb +6d 1bc723 +5c 5f3a8 +4b 1b74ea +63 290f2 +6f 1bc72a +48 1e992c +44 5f902 +e5 f34e3 +88 29d1be +fb 2bfb2d +4c 5fa59 +ed f363a +e2 eb108 +65 1bc5da +ea eb25f +61 290f9 +6d 1bc731 +21 19fcb8 +61 1bc849 +61 1bc857 +95 25cfaa +91 c9ac9 +9d 25d101 +b4 261152 +b0 cdc71 +bc 2612a9 +f2 ea809 +fe 27de41 +95 25d258 +b4 261400 +90 d1e18 +9c 265450 +d6 281e91 +b5 26842b +d2 ee9b0 +de 281fe8 +ff ebef2 +ab 106e3f +94 2655a7 +d6 28213f +96 29ea22 +92 10b541 +9e 29eb79 +e0 2b6d21 +46 5fba9 +e7 f378a +fd 2b755b +f1 123f23 +c0 12098c +4 4301f +4d 566f8 +a5 d6c00 +44 5fbb0 +e5 f3791 +38 1a045f +e2 eb3b6 +65 1bc888 +18 194216 +7b 1b4f56 +90 c9d06 +9c 25d33e +91 29668d +98 25d36f +da e7a6b +b1 cdeae +bd 2614e6 +fb 27e0af +56 1ea3ba +38 1983bd +19 19c565 +5b 1b90fd +91 d2055 +9d 26568d +8 3ab9d +42 1e976c +fa 27e0ae +db 282256 +b 875d +58 1b0da7 +aa 10808e +8f d1885 +d0 e6897 +21 45f28 +2d 1d9560 +dc 279ecf +8b d18b6 +d8 279f00 +a9 10ee68 +92 10342f +15 1d4901 +9e 296a67 +f0 eaa3e +af d5a2c +fc 27e076 +a 41e7c +ab d5a5d +f8 27e0a7 +3a 3e3ea +d9 28224f +b2 1075d6 +35 1d8aa8 +be 29ac0e +1a 19421d +58 1b0db5 +9a 25d376 +21 45f36 +aa 10809c +d0 e68a5 +2d 1d956e +dc 279edd +d1 2b322c +f1 eaa4d +fd 27e085 +1b 19c56c +78 1b4f5c +59 1b9104 +9b 2656c5 +f0 eaa4c +fc 27e084 +f1 2b73d3 +a 41e8a +59 1b0afa +f8 27e0b5 +3a 3e3f8 +d9 28225d +90 103436 +9c 296a6e +9f d3508 +98 296a9f +b3 d6265 +bf 26989d +da 12119b +b1 1075de +bd 29ac16 +3f e556 +1d 19b2e4 +38 1d1aed +b0 1075dd +bc 29ac15 +e3 2b57a9 +91 10b785 +9d 29edbd +19 194217 +5b 1b0daf +91 c9d07 +9d 25d33f +7a 29b55 +99 25d370 +b0 cdeaf +bc 2614e7 +db 279f08 +f2 eaa47 +75 1bbf19 +fe 27e07f +0 39730 +c 1ccd68 +a1 cd311 +ad 260949 +fa 27e0b0 +59 1b0da8 +78 1b4f50 +ab 10808f +d1 e6898 +dd 279ed0 +d9 279f01 +f0 eaa40 +fc 27e078 +a 41e7e +f8 27e0a9 +6c 1b33c0 +9b 296a99 +ba 29ac41 +9a 29ede8 +1b 19421e +59 1b0db6 +9b 25d377 +b2 cdeb6 +35 19f388 +be 2614ee +ab 10809d +d1 e68a6 +dd 279ede +d9 279f0f +a 41e8c +5e 26f3f +f8 27e0b7 +1e 43ad0 +bf d76b1 +b8 29ac48 +18 1d5c96 +90 10b786 +e2 2b57aa +9c 29edbe +98 29edef +5a 1b0dbc +7b 1b4f64 +8 1925f5 +98 25d37d +da e7a79 +b1 cdebc +bd 2614f4 +b9 261525 +ed 285ac8 +e1 f2490 +38 1983cb +7a 1b4f63 +14 3a0b6 +81 10c0da +5b 1b910b +19 193f69 +b8 261524 +91 d2063 +e3 27c087 +9d 26569b +75 1bbf25 +f2 eaa53 +fe 27e08b +f3 2b73da +25 1d0dad +80 294a6b +92 10343d +15 1d490f +9e 296a75 +35 1d8ab6 +b2 1075e4 +be 29ac1c +19 194225 +5b 1b0dbd +91 c9d15 +9d 25d34d +99 25d37e +1e a3ae +b8 261526 +db 279f16 +1b 1cd94e +3a 1d1af6 +44 1e8544 +ba 29ac4f +90 10b4ca +39 6234 +9c 29eb02 +1a 1d5c9d +9a 29edf6 +90 c9d68 +9c 25d3a0 +b1 cdf10 +bd 261548 +5b 5f3e1 +b0 cdf0f +bc 261547 +f2 eaaa7 +aa 29902c +fe 27e0df +88 294e7e +aa 1080f0 +d0 e68f9 +8f d18e7 +dc 279f31 +ad 10ee99 +e 41ead +af d5a8e +f0 eaaa0 +a8 299025 +fc 27e0d8 +b2 107638 +be 29ac70 +9c c9be0 +27 1d7e40 +25 1d7e47 +44 26482 +75 29a19 +2d 1d7f9e +2b 1d7fc8 +71 29a4a +40 264b3 +fa ebbb0 +7d 1bd082 +29 1d7fcf +b1 cf180 +e6 27c375 +7a 1f5705 +37 1d87a1 +3f 1d88f8 +54 26de3 +3d 1d88ff +6 1cb9b8 +3b 1d8929 +50 26e14 +39 1d8930 +67 1bb2bd +63 1bb2ee +6f 1bb414 +6b 1bb445 +77 1bbc1e +e2 12b918 +ee 2bef50 +ad 10f145 +73 1bbc4f +77 1bcf32 +98 c9c1f +23 1d7e7f +73 29a51 +42 264ba +7f 1bd089 +2b 1d7fd6 +33 1d87e0 +a7 10edab +af 10ef02 +b7 10f70c +3 1d3c74 +b5 10f713 +bf 10f863 +b 1d3dcb +bd 10f86a +d6 278811 +9c c9c42 +27 1d7ea2 +3f 1d895a +ba 10748f +3d 1d8961 +f 41ebc +f1 eaaaf +fd 27e0e7 +b2 cdf16 +be 26154e +5d 1b0b2b +51 1d4f3 +2b 3ecea +f0 eaaae +e 41ebb +fc 27e0e6 +90 103498 +9c 296ad0 +b1 107640 +bd 29ac78 +b0 10763f +11 3a084 +1d 1cd6bc +bc 29ac77 +46 5e647 +e7 f2228 +2f 1d8245 +4e 5e79e +ef f237f +56 5efa8 +f7 f2b89 +6d 2a4df +3f 1d8ba6 +5e 5f0ff +ff f2ce0 +b7 10f71a +bf 10f871 +67 1bb31f +6f 1bb476 +77 1bbc80 +7e 29b86 +91 c9d69 +9d 25d3a1 +b0 cdf11 +bc 261549 +f2 eaaa9 +fe 27e0e1 +ab 1080f1 +d1 e68fa +dd 279f32 +e 41eaf +f0 eaaa2 +fc 27e0da +aa 110440 +d0 eec49 +dc 282281 +9c c9e8e +27 1d80ee +42 5e67a +4e 1f1cb2 +25 1d80f5 +98 c9ebf +23 1d811f +f2 ebd07 +75 1bd1d9 +21 1d8126 +65 2a388 +37 1d8a4f +52 5efdb +5e 1f2613 +35 1d8a56 +61 2a3b9 +33 1d8a80 +31 1d8a87 +63 1bb59c +73 1bbefd +98 c9ecd +77 1bd1e0 +23 1d812d +33 1d8a8e +b7 10f9ba +61 1b41f9 +3 1d3f22 +b5 10f9c1 +d4 278ac6 +ae 29a2bd +f6 ebd38 +a2 106c85 +25 1d8157 +37 1d8ab1 +b2 1075e6 +be 29ac1e +35 1d8ab8 +b2 cdf18 +be 261550 +ab 1080ff +d1 e6908 +dd 279f40 +e 41ebd +f0 eaab0 +2b 3ecec +fc 27e0e8 +91 103499 +9d 296ad1 +b0 107641 +bc 29ac79 +63 5b724 +46 5e8f5 +e7 f24d6 +85 d273b +cc 11f7ee +3a 19f1a4 +b7 10f9c8 +67 1bb5cd +80 10be8d +77 1bbf2e +51 1b7a93 +2b 1d928a +71 2ad0c +29 1d9291 +e6 27d637 +7a 1f69c7 +63 1bc5b0 +6b 1bc707 +98 caee1 +23 1d9141 +51 1b7aa1 +73 2ad13 +2b 1d9298 +a7 11006d +c2 2804e9 +d5 ee9cd +af 1101c4 +de 279c2a +23 45c83 +55 1b7ac4 +d2 e65f2 +2f 1d92bb +90 c9d76 +9c 25d3ae +b1 cdf1e +bd 261556 +1d 193f9a +b0 cdf1d +11 962 +bc 261555 +5f 1b0b32 +f2 eaab5 +53 1d4fa +fe 27e0ed +25 1d0e0f +92 10349f +9e 296ad7 +1f 1cd6c3 +b2 107646 +13 3a08b +be 29ac7e +4e 5fa60 +ef f3641 +a7 11007b +d5 ee9db +f0 1251e4 +af 1101d2 +67 1bc5e1 +63 29100 +6f 1bc738 +63 1bc85e +98 cb18f +23 1d93ef +a7 11031b +a5 110322 +d4 279d88 +a2 107f47 +25 1d9419 +91 c9d77 +9d 25d3af +b0 cdf1f +bc 261557 +f2 eaab7 +fe 27e0ef +46 5fbb7 +e7 f3798 +cc 120ab0 +3a 1a0466 +a7 110329 +67 1bc88f +21 19e6e4 +0 19baae +31 19f045 +63 1bb27c +42 1b8646 +8 9a77 +73 1bbbdd +74 2197a +8c 2637bb +fb 2bf881 +94 263fc5 +9c 26411c +a1 26783d +ad 267963 +84 264bd6 +b5 26816d +80 264c07 +b1 26819e +80 d16f5 +8c 264d2d +bd 2682c4 +c6 2801fc +c2 28179f +f3 284d36 +71 1ee27a +4a 1b854f +21 19e992 +6b 63bcb +5a 1b8eb0 +31 19f2f3 +e7 eb19c +46 575bb +7b 6452c +63 1bb52a +73 1bbe8b +ef 12ccf3 +d2 ee9a0 +8a 29cf25 +de 281fd8 +b5 26841b +ff 12d654 +b1 26844c +c6 120714 +fb 12d685 +c6 2804aa +f3 284fe4 +80 25b357 +a2 ce5c9 +71 1ee528 +21 19f9a6 +29 19fafd +63 1bc53e +6b 1bc695 +c9 278030 +80 264957 +74 22c3c +80 d1445 +8c 264a7d +70 22c6d +88 264aae +a1 268aff +a1 d55ed +ad 268c25 +e3 285697 +e3 f2185 +ef 2857bd +49 1af185 +0 19baac +21 19fc54 +f4 f40e3 +42 1b8644 +63 1bc7ec +72 5ae34 +7e 1ee46c +cd 2782ad +84 264bd4 +e7 285914 +61 1bb275 +40 1b863f +71 1bbbd6 +23 1d7e0d +32 60e3 +de 2b208a +2b 1d7f64 +2 1d51d7 +33 1d876e +a 1d532e +3b 1d88c5 +e2 2856a6 +e1 2843ce +c0 281798 +f1 284d2f +33 4e20 +3f 198458 +96 29d6ee +3b 4f77 +9e 29d845 +a3 2a0f66 +af 2a108c +ab 2a10bd +86 29e2ff +b7 2a1896 +82 29e330 +b3 2a18c7 +82 10ae1e +8e 29e456 +bf 2a19ed +8a 29e487 +bb 2a1a1e +a3 267af4 +b4 10f9b2 +2 1d3f13 +b3 268455 +12 1d4874 +23 1d80bb +33 1d8a1c +c4 2804a3 +86 29d03b +96 29d99c +6c 2a4d2 +a7 2a11e3 +68 2a503 +a3 2a1214 +7c 2ae33 +b7 2a1b44 +78 2ae64 +b3 2a1b75 +61 1bc537 +69 1bc68e +23 1d90cf +51 1b7a2f +2b 1d9226 +e5 28565f +e1 285690 +e1 f217e +ed 2857b6 +cb 2b1759 +82 29e080 +5a 5f132 +3d 1d8bad +82 10ab6e +2b 58d8 +76 5c365 +8e 29e1a6 +a3 2a2228 +a3 10ed16 +d5 280b57 +af 2a234e +d1 280b88 +ab 2a237f +23 1d937d +e5 28590d +cb 2b1a07 +82 29e32e +a7 2a24a5 +36 1d04b2 +a3 2a24d6 +9c 296816 +90 1031de +22 1d103c +35 3f520 +18 3a23e +98 103335 +50 1af99c +2a 1d1193 +3d 3f677 +23 19e6eb +2f 1d7ff9 +de 278968 +2 19bab5 +33 19f04c +40 1b864d +71 1bbbe4 +48 1b87a4 +79 1bbd3b +af 26796a +e1 2843dc +e9 284533 +c0 2817a6 +f1 284d3d +c8 2818fd +f9 284e94 +b4 d6290 +2 19a7f1 +12 19b152 +23 19e999 +2f 1d82a7 +de 278c16 +33 19f2fa +2d 47064 +40 1b7389 +71 1bbe92 +86 263919 +96 26427a +a7 267ac1 +b7 268422 +e1 28468a +23 19f9ad +2b 19fb04 +61 1bc545 +69 1bc69c +76 22c43 +82 d144c +8e 264a84 +a3 268b06 +a3 d55f4 +af 268c2c +e5 28566d +e1 28569e +e1 f218c +ed 2857c4 +e9 2857f5 +b4 d7552 +fd eac2b +4b 1af18c +2 19bab3 +23 19fc5b +40 1b864b +61 1bc7f3 +cf 2782b4 +86 264bdb +cb 2782e5 +82 264c0c +a7 268d83 +e5 28591b +74 1b3928 +e1 28594c +21 1d7e14 +30 60ea +fe 125303 +dc 2b2091 +40 2644f +29 1d7f6b +0 1d51de +6b 5bb29 +31 1d8775 +50 26db0 +8 1d5335 +39 1d88cc +21 44c6 +2d 197afe +84 29cd94 +29 461d +74 5b0aa +63 1b31ec +8c 29ceeb +3d 19845f +31 4e27 +ce 120aa9 +94 29d6f5 +39 4f7e +73 1b3b4d +9c 29d84c +a5 2a0f3c +a1 2a0f6d +c4 ef577 +ad 2a1093 +eb 124c82 +80 29e337 +b1 2a18ce +a1 267afb +0 1d3f1a +b1 26845c +10 1d487b +5a 1f25e0 +31 1d8a23 +84 29d042 +94 29d9a3 +ce 2bada7 +c2 12776f +a5 2a11ea +b1 2a1b7c +21 1d90d6 +29 1d922d +21 5788 +cd 2b172f +84 29e056 +74 5c36c +80 10ab75 +29 58df +63 1b44ae +8c 29e1ad +a5 2a21fe +a1 10ed1d +ad 2a2355 +a1 268dbd +34 196d99 +49 1e88b5 +0 1d51dc +cd 2b19dd +84 29e304 +c9 2b1a0e +80 29e335 +ae 106e0b +53 6023c +c2 128a31 +a5 2a24ac +df 2bb6a9 +d3 128071 +10 1cc2d5 +95 10ca6c +18 1cc42c +9d 10cbc3 +20 19e6e5 +28 19e83c +30 19f046 +62 1bb27d +6a 1bb3d4 +72 1bbbde +c5 ef2cc +7a 1bbd35 +20 19e993 +30 19f2f4 +62 1bb52b +28 19fafe +62 1bc53f +6a 1bc696 +a8 268c57 +e6 285667 +d9 eed4d +e2 285698 +e2 f2186 +ee 2857be +ea 2857ef +60 1bb276 +68 1bb3cd +70 1bbbd7 +78 1bbd2e +e8 284526 +67 62a9d +f8 284e87 +ae 2a108d +b6 2a1897 +be 2a19ee +60 1bb524 +70 1bbe85 +a6 2a11e4 +b6 2a1b45 +60 1bc538 +68 1bc68f +50 1b7a30 +2a 1d9227 +e4 285660 +e0 f217f +ec 2857b7 +e8 2857e8 +d0 280b89 +aa 2a2380 +77 1b3be0 +80 103b3f +7f 1b3d37 +88 103c96 +8 1ccd8d +d4 e7b8c +bd 2996a8 +22 19e6ec +c4 11f697 +32 19f04d +22 19e99a +84 29600c +b5 2995a3 +32 19f2fb +aa 268c5e +e0 28569f +8 1d5327 +a5 25f52e +39 1d88be +e8 2857f6 +a8 2a10c5 +b8 2a1a26 +4f 5e791 +e9 2b5909 +a0 2a2230 +82 d175c +8e 264d94 +a8 2a2387 +21 19e6f2 +aa 106b2e +fe ebbe1 +2d 1d8000 +dc 27896f +29 19e849 +0 19babc +6b 22407 +31 19f053 +8 19bc13 +39 19f1aa +f4 f2b81 +42 1b70e2 +fc f2cd8 +4a 1b7239 +63 1bb28a +42 1b8654 +8 9a85 +73 1bbbeb +4a 1b87ab +7b 1bbd42 +ce e7387 +94 263fd3 +69 2a750 +a5 26781a +a1 26784b +ad 267971 +eb eb560 +80 264c15 +b1 2681ac +c6 28020a +ce 280361 +e7 2843b2 +10 1c33 +e3 2843e3 +ef 284509 +18 1d8a +eb 28453a +4a 1b855d +21 19e9a0 +6b 63bd9 +5a 1b8ebe +31 19f301 +7b 6453a +63 1bb538 +ef 12cd01 +a1 267af9 +84 d1476 +eb 12cd32 +b1 26845a +94 d1dd7 +fb 12d693 +c6 2804b8 +e7 284660 +10 1ee1 +e3 284691 +29 19fb0b +f4 f3e43 +42 1b83a4 +63 1bc54c +a5 268adc +a1 268b0d +a1 d55fb +ad 268c33 +f4 2bf6f1 +a9 268c64 +c6 2814cc +c2 2814fd +c2 edfeb +ce 281623 +ca 281654 +e3 2856a5 +e3 f2193 +ef 2857cb +24 196684 +eb 2857fc +49 1af193 +0 19baba +f4 f40f1 +55 26b36 +42 1b8652 +63 1bc7fa +c6 28177a +e7 285922 +76 1b392f +e3 285953 +b4 10f712 +2 1d3c73 +bc 10f869 +a 1d3dca +12 1d45d4 +1a 1d472b +23 1d7e1b +42 26456 +2b 1d7f72 +2 1d51e5 +33 1d877c +52 26db7 +a 1d533c +3b 1d88d3 +86 29cd9b +76 5b0b1 +8e 29cef2 +96 29d6fc +6b 63e79 +1 1d4f85 +9e 29d853 +a7 2a0f43 +a3 2a0f74 +c6 ef57e +af 2a109a +82 29e33e +b3 2a18d5 +b4 10f9c0 +15 42405 +2 1d3f21 +12 1d4882 +33 1d8a2a +86 29d049 +2e 1d9514 +54 1b7d1d +22 45edc +71 1b4b4c +96 29d9aa +6c 2a4e0 +a7 2a11f1 +78 2ae72 +b3 2a1b83 +51 1b7a3d +2b 1d9234 +82 10ab7c +76 5c373 +8e 29e1b4 +72 5c3a4 +8a 29e1e5 +a3 2a2236 +a3 10ed24 +d5 280b65 +af 2a235c +cf 2b19e4 +86 29e30b +a7 2a24b3 +62 1bb28b +6a 1bb3e2 +72 1bbbec +c5 ef2da +7a 1bbd43 +62 1bb539 +c4 2b2bab +f5 2b6142 +62 1bc54d +48 1e8606 +e9 27c1e7 +4f 2506f +a0 268b0e +a8 268c65 +e6 285675 +e2 f2194 +ee 2857cc +ea 2857fd +22 1d7e1c +2a 1d7f73 +32 1d877d +85 10be6b +3a 1d88d4 +a6 2a0f44 +ae 2a109b +b6 2a18a5 +be 2a19fc +ba 2a1a2d +b 1483 +56 57f10 +22 1d80ca +32 1d8a2b +50 1b7a3e +2a 1d9235 +d4 278ab8 +ae 29a2af +a2 106c77 +25 1d8149 +a2 10ed25 +d4 280b66 +ae 2a235d +84 2636c6 +8c 26381d +94 264027 +82 10ae2a +8e 29e462 +a5 26786e +ad 2679c5 +84 264c38 +b5 2681cf +a3 10efd2 +d5 280e13 +af 2a260a +80 d1757 +8c 264d8f +bd 268326 +e7 12cb9c +a5 267b1c +3a 3f658 +ef 12cd55 +b5 26847d +ff 12d6b6 +cd 278061 +84 264988 +80 d14a7 +8c 264adf +a5 268b30 +a1 d564f +ad 268c87 +c6 281520 +c2 ee03f +a5 267aba +ce 281677 +c6 2817ce +c4 280257 +cc 2803ae +86 29cdef +a7 2a0f97 +af 2a10ee +86 29e361 +b7 2a18f8 +8e 29e4b8 +82 10ae80 +bf 2a1a4f +c4 280505 +d4 280e66 +86 29d09d +a7 2a1245 +b7 2a1ba6 +c4 281519 +c0 ee038 +cc 281670 +e5 2856c1 +e1 f21e0 +ed 285818 +cf 2b178a +86 29e0b1 +a7 2a2259 +c4 2817c7 +e5 28596f +cf 2b1a38 +86 29e35f +a7 2a2507 +c4 280265 +ff 12d964 +e1 27d342 +c4 280513 +f 1d50b0 +f1 27dca3 +3 41a78 +d4 280e74 +cf 278068 +86 26498f +e9 124c29 +82 d14ae +51 1f140d +8e 264ae6 +e5 2856cf +e1 f21ee +ed 285826 +cf 278316 +86 264c3d +c4 2817d5 +e5 28597d +84 29cdf6 +ce 120b0b +94 29d757 +73 1b3baf +9c 29d8ae +84 29e368 +ef 124cb3 +b5 2a18ff +8c 29e4bf +80 10ae87 +d4 eff3a +bd 2a1a56 +a1 299ed3 +84 29d0a4 +57 5efab +b1 29a834 +94 29da05 +a5 2a124c +b5 2a1bad +a1 10ed7f +ad 2a23b7 +cd 2b1a3f +84 29e366 +57 6026d +dd eed7e +f8 125587 +e6 2856c9 +a6 2a0f98 +ae 2a10ef +a6 2a1246 +e0 f21e1 +ec 285819 +ef 2b5933 +9d 10b90f +a6 2a225a +29 c8fd +78 1b4f4e +2b c904 +4 2ed +39 d25e +61 1f4a07 +cf 280364 +3b d265 +3d d28f +69 2948e +6b 29495 +6d 294bf +68 1eca5d +6f 294c6 +48 566c8 +44 1ce7e +79 29def +46 1ce85 +7b 29df6 +7d 29e20 +78 1ed3be +7f 29e27 +58 57029 +ad d5a25 +84 c93e4 +b9 d6355 +bb d635c +bd d6386 +ed f25b6 +ef f25bd +c8 11f7bf +fd f2f17 +ff f2f1e +d8 120120 +29 c90b +2d c93c +39 d26c +6d 294cd +79 29dfd +a9 d5a02 +ad d5a33 +ff 2bfaf0 +a 470 +f3 12c4b8 +6a 1ec7a6 +b9 d6363 +8 41e75 +a9 d5a56 +18 427d6 +84 c9446 +b9 d63b7 +e0 f21ef +ec 285827 +5 1d3c92 +a4 2a124d +15 1d45f3 +b4 2a1bae +29 c96d +39 d2ce +69 294fe +8 41e83 +a9 d5a64 +6a 1ec808 +18 427e4 +b9 d63c5 +84 2636d4 +bf 110dd3 +ce e73e9 +94 264035 +69 2a7b2 +ef eb591 +84 264c46 +b5 2681dd +8c 264d9d +80 d1765 +bd 268334 +e7 12cbaa +91 d3077 +c6 28026c +99 d31ce +ce 2803c3 +d6 280bcd +de 280d24 +14 1c64 +e7 284414 +1c 1dbb +ef 28456b +c6 2817de +f7 284d75 +c2 ee2fd +ce 281935 +ff 284ecc +a5 267b2a +ef 12cd63 +b5 26848b +ff 12d6c4 +91 d3325 +e3 27d349 +c6 28051a +f3 27dcaa +d6 280e7b +14 1f12 +e7 2846c2 +f7 285023 +a5 268b3e +a1 d565d +ad 268c95 +f3 2bf71a +c6 28152e +c2 ee04d +a5 267ac8 +ce 281685 +e7 2856d6 +e3 f21f5 +ef 28582d +c6 2817dc +e7 285984 +a7 2a0fa5 +c6 ef5e0 +af 2a10fc +86 29e36f +b7 2a1906 +82 10ae8e +8e 29e4c6 +d6 eff41 +bf 2a1a5d +a7 2a1253 +b7 2a1bb4 +82 10abde +8e 29e216 +a7 2a2267 +a3 10ed86 +d5 280bc7 +af 2a23be +cf 2b1a46 +86 29e36d +a7 2a2515 +29 dbbf +3c 4c92 +a9 d6cb6 +3e 4c99 +ab d6cbd +cb 2b9d55 +7c 21823 +e9 f3847 +69 2a75e +3c 4ca0 +a9 d6cc4 +c9 2b9d5c +7c 21831 +e9 f3855 +29 dc21 +6b 2a7b9 +8 43137 +a9 d6d18 +a 4313e +ab d6d1f +e8 2bdf04 +e9 f38a9 +a0 d565e +ac 268c96 +f2 2bf71b +e2 f21f6 +ee 28582e +a6 2a0fa6 +ae 2a10fd +b6 2a1907 +be 2a1a5e +7 1d3c99 +a6 2a1254 +17 1d45fa +b6 2a1bb5 +a2 10ed87 +d4 280bc8 +ae 2a23bf +29 dc2f +69 2a7c0 +8 43145 +a9 d6d26 +48 5fcd6 +e9 f38b7 +8 19a941 +18 19b2a2 +29 19eae9 +39 19f44a +7b 1bbfe2 +ca ee196 +ad 267c11 +8 19bc03 +29 19fdab +fc f423a +4a 1b879b +e3 f2433 +ef 285a6b +48 1b74d2 +58 1b7e33 +dc 280f5b +e6 284407 +f8 1242c5 +b1 d7212 +21 1cfdca +8e 29d192 +9e 29daf3 +af 2a133a +ab 2a136b +bf 2a1c9b +48 1b8794 +e1 f242c +ed 285a64 +82 10ae1c +8e 29e454 +a3 10efc4 +d5 280e05 +af 2a25fc +3e 1d0609 +d1 280e36 +ab 2a262d +bc d63e7 +a 19a948 +1a 19b2a9 +2b 19eaf0 +3b 19f451 +48 1b74e0 +58 1b7e41 +e7 12389c +79 1bbfe9 +af 267c18 +bf 268579 +e9 2847e1 +f9 285142 +bc d76a9 +a 19bc0a +2b 19fdb2 +48 1b87a2 +69 1bc94a +8a 264d63 +a3 d58a2 +af 268eda +e1 f243a +ed 285a72 +7c 1b3a7f +e9 285aa3 +a9 267c52 +8 1d4071 +b9 2685b3 +18 1d49d2 +29 1d8219 +39 1d8b7a +8c 29d199 +9c 29dafa +ca 1278c6 +ad 2a1341 +a9 268f14 +3c 196ef0 +8 1d5333 +ee 1236e2 +29 1d94db +9c 10b90c +b9 10873b +ee 2b5930 +80 10ae23 +8c 29e45b +a1 10efcb +ca 128b88 +ad 2a2603 +db 1281c8 +6a 1bb682 +68 1bb67b +78 1bbfdc +e8 2847d4 +f8 285135 +8 19a94f +fa 2b7522 +df 280d19 +29 19eaf7 +39 19f458 +5a 1b7e48 +6b 1bb68f +7b 1bbff0 +ca ee1a4 +ad 267c1f +ce 28060f +c3 2b995e +ef 2847b7 +18 2038 +eb 2847e8 +fb 285149 +4a 250a1 +2d 19eb1c +8 19bc11 +5d 26c8d +fc f4248 +4a 1b87a9 +6b 1bc951 +c2 ee299 +ce 2818d1 +c3 2bac20 +e3 f2441 +ef 285a79 +7e 1b3a86 +eb 285aaa +bc 10fb17 +1d 4255c +a 1d4078 +2b 1d8220 +8e 29d1a0 +2a 46033 +5c 1b7e74 +79 1b4ca3 +1 1d5233 +9e 29db01 +af 2a1348 +6a 1bb690 +cc 2b2d02 +c0 11f6ca +fd 2b6299 +c5 ef588 +7a 1bbff1 +2a 1d8221 +85 10c119 +3a 1d8b82 +1b 1d4720 +ba 2a1cdb +b 1731 +56 581be +ad 267c73 +c2 ee2ed +ce 281925 +7b 21806 +4a 1e26f +e0 2bedc1 +dc 280fbd +f8 124327 +10 43693 +b1 d7274 +8e 29d1f4 +af 2a139c +bf 2a1cfd +c0 ee2e6 +cc 28191e +e1 f248e +ed 285ac6 +b 41bcf +f9 27ddfa +dc 280fcb +59 56d7a +10 436a1 +b1 d7282 +e1 f249c +ed 285ad4 +b9 29a98b +9c 29db5c +ad 2a13a3 +bd 2a1d04 +ae 2a139d +5d 1b7e67 +2b 46026 +6 39a16 +3b 46987 +3f 469b8 +dd 280f5e +ab 10f11d +fd f41d7 +a9 10f124 +af 10f14e +f7 f40eb +56 6050a +f0 2b7682 +86 102b0d +bb 10fa7e +b9 10fa85 +bf 10faaf +6b 294a3 +6f 294d4 +7b 29e04 +7f 29e35 +7c 1ee6af +70 5b077 +2f 46065 +3f 469c6 +eb f259a +ef f25cb +fb f2efb +bb 10fa8c +3b 469e9 +dd 280fc0 +ab 10f17f +fd f4239 +a9 10f186 +b9 10fae7 +d 1d3de9 +ac 2a13a4 +1d 1d474a +bc 2a1d05 +4a 5ea1b +eb f25fc +5a 5f37c +fb f2f5d +bb 10faee +ad 267c81 +f3 2be706 +eb 27d4a0 +99 d347c +ce 280671 +fb 27de01 +de 280fd2 +82 102822 +8e 295e5a +5 1d3cf4 +1c 2069 +ef 284819 +ff 28517a +c2 ee2fb +ce 281933 +e3 f24a3 +ef 285adb +af 2a13aa +bf 2a1d0b +82 10ae8c +d1 279afc +8e 29e4c4 +a3 10f034 +d5 280e75 +af 2a266c +51 25af1 +5d 1b9129 +2b 472e8 +dd 282220 +3e 3e3bb +d1 eebe8 +ab 1103df +3c 3e3c2 +a9 1103e6 +6b 2a765 +7e 21838 +eb f385c +d8 e7cc0 +29 47351 +dd 282282 +d1 eec4a +ab 110441 +a9 110448 +1f 1d4751 +be 2a1d0c +6b 2a7c7 +b5 26848d +14 1d48ac +31 1d16db +4a 5fcdd +eb f38be +dd 282290 +d1 eec58 +ab 11044f +59 1af838 +f8 27cdf3 +10 19c15f +31 1a0307 +39 1a045e +5a 1b8e4e +73 1bce9f +7b 1bcff6 +ab 106b1f +dd 278960 +94 265287 +90 d1da6 +9c 2653de +b5 26942f +b1 d5f4e +80 d29b7 +bd 269586 +59 1afae6 +10 19c40d +31 1a05b5 +73 1bd14d +ab 106dcd +dd 278c0e +94 265535 +d9 278c3f +90 265566 +d2 efc62 +8a 29e1e7 +b5 2696dd +f7 286275 +58 1b8e47 +71 1bce98 +79 1bcfef +33 60e2 +df 2b2089 +96 29e9b0 +db 2b20ba +92 29e9e1 +3b 6239 +92 10b4cf +9e 29eb07 +9a 29eb38 +b3 2a2b89 +b3 10f677 +82 10c0e0 +bf 2a2caf +bb 2a2ce0 +33 1d9cde +ca 2bad78 +f5 28626e +db 2b2368 +92 29ec8f +b7 2a2e06 +b3 2a2e37 +5b 1af83f +fa 27cdfa +12 19c166 +33 1a030e +3b 1a0465 +58 1b8e55 +e7 1248b0 +71 1bcea6 +79 1bcffd +f9 286156 +5b 1afaed +12 19c414 +33 1a05bc +71 1bd154 +df 278c15 +96 26553c +db 278c46 +92 26556d +b7 2696e4 +b3 269715 +f5 28627c +b1 269470 +59 1e8f68 +ff f2f8c +10 1d588f +b9 2695c7 +18 1d59e6 +31 1d9a37 +39 1d9b8e +31 60e9 +dd 2b2090 +94 29e9b7 +d9 2b20c1 +90 29e9e8 +73 1b4e0f +90 10b4d6 +39 6240 +9c 29eb0e +98 29eb3f +b5 2a2b5f +b1 2a2b90 +b1 10f67e +80 10c0e7 +bd 2a2cb6 +ab cd45f +f6 123eec +31 1d9ce5 +f6 2b613a +dd 2b233e +94 29ec65 +d9 2b236f +90 29ec96 +d2 129392 +b5 2a2e0d +72 1bcea0 +7a 1bcff7 +b0 d5f4f +bc 269587 +f6 285fc8 +f2 f2ae7 +fe 28611f +fa 286150 +f6 286276 +70 1bce99 +78 1bcff0 +3a 1d9b88 +e7 12ceba +f0 f2ae0 +fc 286118 +67 63d5f +f8 286149 +b2 10f678 +be 2a2cb0 +ba 2a2ce1 +47 1f0838 +7b 1ed3b8 +32 1d9cdf +f4 28626f +c5 1206ac +7a 1ed115 +fb 2b6511 +b2 2a2e38 +b4 25fe80 +5f 1f1352 +c4 120959 +7b 1b39e8 +32 1a030f +f8 286157 +ff 27cdbe +b6 2696e5 +f9 2b626a +5f 5f0f2 +b0 2a2b91 +92 d20bd +9e 2656f5 +b8 2a2ce8 +11 1d5884 +f9 2b6518 +5f 5f3a0 +b0 2a2e3f +59 1af846 +10 19c16d +39 1a046c +ab 106b2d +dd 27896e +94 265295 +b5 26943d +b1 d5f5c +80 d29c5 +bd 269594 +59 1afaf4 +10 19c41b +ab 106ddb +dd 278c1c +94 265543 +d9 278c4d +90 265574 +d2 efc70 +b5 2696eb +3b 1d9b95 +df 2b2097 +96 29e9be +db 2b20c8 +92 29e9ef +92 10b4dd +9e 29eb15 +9a 29eb46 +b3 2a2b97 +b3 10f685 +82 10c0ee +bf 2a2cbd +5b 1e921d +12 1d5b44 +33 1d9cec +df 2b2345 +96 29ec6c +db 2b2376 +92 29ec9d +b7 2a2e14 +b3 2a2e45 +72 1bceae +7b 1ed118 +32 1d9a3f +3a 1d9b96 +b2 1075d8 +be 29ac10 +35 1d8aaa +ff 2b6240 +b6 2a2b67 +fb 2b6271 +b2 2a2b98 +b2 10f686 +be 2a2cbe +ba 2a2cef +7b 1ed3c6 +32 1d9ced +fb 2b651f +13 1d588b +b2 2a2e46 +ab 106b81 +dd 2789c2 +94 2652e9 +90 d1e08 +9c 265440 +b5 269491 +b1 d5fb0 +80 d2a19 +bd 2695e8 +ab 2a10cd +c2 ef5b1 +f3 f2b48 +ff 286180 +df efdeb +8b 10ad38 +d8 2b3382 +ab 106e2f +dd 278c70 +94 265597 +82 29d07c +d6 28212f +a9 2a10c6 +c0 ef5aa +f1 f2b41 +fd 286179 +b7 2a2bba +b3 10f6d9 +82 10c142 +bf 2a2d11 +80 29d075 +eb 1239c0 +d4 282128 +b7 2a2e68 +df 2789c9 +96 2652f0 +46 1f1aeb +f9 12558a +92 d1e0f +9e 265447 +c0 ef5b8 +f1 f2b4f +fd 286187 +df 278c77 +96 26559e +3 42d3a +eb 1239ce +d4 282136 +dd 2b20f2 +94 29ea19 +90 10b538 +73 1b4e71 +9c 29eb70 +b5 2a2bc1 +b1 10f6e0 +80 10c149 +bd 2a2d18 +dd 2b23a0 +94 29ecc7 +f2 f2b49 +fe 286181 +f0 f2b42 +fc 28617a +62 1ec65d +70 1b3b35 +2f 19eb23 +f 878e +2a 3ef97 +5c 1b0dd8 +50 1d7a0 +8 1cbd25 +29 19eb4d +2b 19eb54 +59 1f2578 +6d 1bb6ad +6f 1bb6b4 +69 1bb6de +1 157f +6b 1bb6e5 +7d 1bc00e +44 1af0ce +79 1bc03f +4a 250af +2d 19eb2a +29 19eb5b +69 1bb6ec +c 41ea6 +ad d5a87 +1c 42807 +b 19a949 +bd d63e8 +38 4f1b +ed f2618 +4e 5ea3e +e8 2b5bb6 +0 1d4f22 +ef f261f +c8 11f821 +6d 5bb63 +4b 1b74da +fd f2f79 +d8 120182 +89 10accd +7d 5c4c4 +fa eab9e +7d 1bc070 +f5 2b73a2 +f0 f2b50 +fc 286188 +fd 2b6549 +15 1d58b5 +b4 2a2e70 +c 41eb4 +29 3ece3 +ad d5a95 +ff 2bfb52 +f3 12c51a +1c 42815 +39 3f644 +6e 1ec839 +bd d63f6 +4c 5ea45 +ed f2626 +4b 1b74e8 +5c 5f3a6 +fd f2f87 +2d 19eb8c +73 1f5611 +ea ea24b +6d 1bb71d +ab 106b8f +dd 2789d0 +94 2652f7 +b5 26949f +b1 d5fbe +80 d2a27 +bd 2695f6 +d6 281e8f +d2 ee9ae +b5 268429 +de 281fe6 +ab 106e3d +dd 278c7e +94 2655a5 +d6 28213d +df 2b20f9 +96 29ea20 +92 10b53f +9e 29eb77 +b7 2a2bc8 +b3 10f6e7 +82 10c150 +bf 2a2d1f +df 2b23a7 +96 29ecce +b7 2a2e76 +29 19fe0f +2b 19fe16 +69 1bc9a0 +eb 12b9fe +29 19fe1d +69 1bc9ae +38 61dd +ed f38da +e8 2b6e78 +4e 5fd00 +ef f38e1 +f9 12407a +c8 120ae3 +aa ce96e +21 c808 +2d 19fe40 +ea eb4ff +61 29399 +6d 1bc9d1 +b0 d5fbf +bc 2695f7 +f2 f2b57 +fe 28618f +ff 2b62a2 +b6 2a2bc9 +b2 10f6e8 +be 2a2d20 +c 43176 +ad d6d57 +f3 12d7dc +4c 5fd07 +ed f38e8 +ef 12ba2f +21 c816 +2d 19fe4e +73 1f68d3 +ea eb50d +61 293a7 +6d 1bc9df +18 19c564 +39 1a070c +7b 1bd2a4 +90 d2054 +9c 26568c +91 29e9db +98 2656bd +f3 f2d94 +ff 2863cc +f1 f2d8d +fd 2863c5 +92 10b77d +9e 29edb5 +b3 10f925 +bf 2a2f5d +1a 19c56b +58 1b9103 +e7 124b5e +79 1bd2ab +9a 2656c4 +b3 d6203 +bf 26983b +f1 f2d9b +fd 2863d3 +f9 286404 +b9 269875 +18 1d5c94 +fe 124043 +39 1d9e3c +fe 2b6291 +90 10b784 +e2 2b57a8 +9c 29edbc +98 29eded +b1 10f92c +da 1294e9 +bd 2a2f64 +b0 d61fd +bc 269835 +f2 f2d95 +fe 2863cd +fa 2863fe +f0 f2d8e +fc 2863c6 +f8 2863f7 +4e 1e863e +ef 27c21f +ba 2a2f8f +47 1f0ae6 +b2 d6204 +be 26983c +19 1d59db +b8 2a2f96 +5a 1b910a +98 2656cb +b1 d620a +da efdc7 +bd 269842 +28 3ed52 +62 1ed921 +92 10b78b +9e 29edc3 +b3 10f933 +bf 2a2f6b +7a 1bd2b3 +19 19c2b9 +b8 269874 +1b 1d59e2 +ba 2a2f9d +90 d20b6 +9c 2656ee +bc 107519 +f6 2b60e8 +d2 eec4e +8a 29d1d3 +de 282286 +ac 298d36 +5a 1ebd0 +f0 2bf722 +ab 2a137b +f3 f2df6 +ff 28642e +df f0099 +8b 10afe6 +d8 2b3630 +d0 eec47 +88 29d1cc +dc 28227f +a9 2a1374 +f1 f2def +fd 286427 +b3 10f987 +bf 2a2fbf +d0 eec55 +b 42e91 +dc 28228d +f1 f2dfd +fd 286435 +90 10b7e6 +9c 29ee1e +f2 f2df7 +fe 28642f +f0 f2df0 +fc 286428 +62 1ec90b +75 29cc7 +4a 5e7d1 +2d 1d824c +71 29cf8 +fa ebe5e +7d 1bd330 +29 1d827d +69 2a510 +6 1cbc66 +3b 1d8bd7 +39 1d8bde +6f 1bb6c2 +6b 1bb6f3 +3b 1d8be5 +f0 f2dfe +51 25843 +5d 1b8e7b +2b 4703a +fc 286436 +af 10f1b0 +ad 10f1b7 +bf 10fb11 +69 1b4350 +b 1d4079 +bd 10fb18 +fe ebe8f +aa 106ddc +dc 278c1d +2d 1d82ae +3f 1d8c08 +ba 10773d +3d 1d8c0f +10 1d5891 +7b 5c1dc +5e 5f3ad +ff f2f8e +bf 10fb1f +6f 1bb724 +90 d20c4 +9c 2656fc +d2 eec5c +de 282294 +82 103ae4 +5 1d4fb6 +92 10b7ed +9e 29ee25 +b3 10f995 +bf 2a2fcd +71 2afba +29 1d953f +6b 1bc9b5 +b0 d626d +11 8cb2 +1d 19c2ea +bc 2698a5 +53 2584a +f2 f2e05 +5f 1b8e82 +fe 28643d +d5 eec7b +af 110472 +ad 110479 +de 279ed8 +55 1b7d72 +d2 e68a0 +23 45f31 +2f 1d9569 +dc 279edf +aa 10809e +d0 e68a7 +21 45f38 +2d 1d9570 +4e 5fd0e +ef f38ef +b5 29a803 +d5 eec89 +f0 125492 +af 110480 +63 293ae +6f 1bc9e6 +63 1ec65e +6b 1ec7b5 +42 1e9a28 +8 3ae59 +73 1ecfbf +4a 1e9b7f +7b 1ed116 +d6 2b1f3f +de 2b2096 +ef 2b58dd +eb 2b590e +ca 2b2cd8 +fb 2b626f +2d 1cfc42 +63 1ec90c +73 1ed26d +c6 2b188c +ac 106e04 +51 60235 +d6 2b21ed +e3 2b5a65 +79 2b0bf +f3 2b63c6 +63 1ed920 +c2 2b28d1 +63 1edbce +c6 2b2b4e +e3 2b6d27 +72 1ecfbe +53 1f1166 +27 196432 +a5 110314 +5b 1f12bd +2f 196589 +ad 11046b +ee 2b58dc +44 1f0b40 +fe 2b623d +26 3dbaf +cb 2b9ab5 +72 1ed26c +f6 2b6394 +c3 2b9c0c +d3 2ba56d +5 1d3f40 +47 1b7116 +62 1ed91f +65 1f4a38 +e2 123566 +ee 2b6b9e +4 1cbbff +26 3ee71 +cb 2bad77 +47 1b73c4 +62 1edbcd +e6 2b6cf5 +56 1e8eaa +c3 2baece +26 3d903 +c5 281768 +53 1e8e18 +a5 107fc6 +12 1d5896 +5b 1e8f6f +ad 10811d +7a 1ed117 +82 29e08e +cb 2b1767 +ee 2b58de +f2 2b6119 +fe 2b623f +28 3dd3e +62 1ec90d +38 3e69f +72 1ed26e +19 3a23d +f2 2b63c7 +6a 1eda78 +f5 f3dd4 +ac 107e7a +e6 2b6a49 +e2 123568 +65 1f4a3a +ee 2b6ba0 +ea 2b6bd1 +8 1739 +39 4cd0 +f1 12d781 +73 1b389f +8 41c37 +42 1f0806 +18 42598 +52 1f1167 +5a 1f12be +c2 2b995f +dc e7a41 +67 1f5ca1 +f5 284fac +ca 2b9ab6 +9c 10b6c0 +73 219c1 +7f 1b4ff9 +d6 2ba28f +d2 2ba2c0 +77 1f6602 +7b 21b18 +de 2ba3e6 +da 2ba417 +18 42846 +52 1f1415 +a9 107e3c +8c 10b00d +c6 2b9bdc +c2 2b9c0d +dc e7cef +67 1f5f4f +b9 10879d +9c 10b96e +d6 2ba53d +d2 2ba56e +77 1f68b0 +8 42ef9 +42 1f1ac8 +4a 1f1c1f +af cd6dc +52 604e7 +f3 f40c8 +94 2953fb +9c 295552 +a5 298c42 +c6 2b2ba4 +f7 2b613b +84 294d48 +94 2956a9 +a5 298ef0 +b5 299851 +f7 2b63e9 +c6 2b28f4 +52 1eadb +a4 298c41 +5a 1ec32 +ac 298d98 +b4 2995a2 +85 29cde9 +39 e590 +95 29d74a +a4 298eef +77 5adf6 +b4 299850 +85 29d097 +95 29d9f8 +f6 2b63e8 +a4 299f03 +85 29e0ab +c7 2bac43 +e4 2bed90 +c3 127762 +cf 2bad9a +e0 12b8af +ec 2beee7 +3b e527 +e5 2b57e1 +ed 2b5938 +e5 2b5a8f +e4 2b6d50 +a4 298c43 +b4 2995a4 +f6 2b613c +fe 2b6293 +85 294d49 +a4 298ef1 +b4 299852 +f6 2b63ea +84 29cdea +f7 2bf759 +8c 29cf41 +f3 12c278 +c2 128ce1 +ff 2bf8b0 +94 29d74b +9c 29d8a2 +91 10c78d +c6 2b9982 +99 10c8e4 +ce 2b9ad9 +43 1e9a27 +f5 1254c6 +d6 2ba2e3 +de 2ba43a +84 29d098 +f7 2bfa07 +94 29d9f9 +d6 2ba591 +1f af1 +84 29e0ac +c6 2bac44 +ee 28455e +84 ca3f8 +b9 d7369 +29 1cff21 +bd d739a +2d 1cff52 +f1 f3da3 +f7 f3ddb +67 1ec993 +d0 120fdd +fd f3f2b +ff f3f32 +26 1d9411 +6f 1ecaea +d8 121134 +f1 f3db1 +61 1ec969 +f5 f3de2 +65 1ec99a +96 29565e +f9 f3f08 +20 1d93e7 +69 1ecac0 +fd f3f39 +24 1d9418 +6d 1ecaf1 +d6 2b1fa1 +de 2b20f8 +c6 2b2bb2 +f7 2b6149 +d6 2b224f +fb 2bfb21 +c6 2b2bb0 +f6 2b6148 +57 1e8e3b +f6 2b63f6 +e6 2b6aa9 +47 1e979c +e6 2b6d57 +73 2ad15 +7b 2ae6c +84 ca45a +18 437ea +b9 d73cb +1a 437f1 +bb d73d2 +f8 2be5b7 +f1 f3e05 +52 6022b +f3 f3e0c +39 e2e2 +18 437f8 +b9 d73d9 +50 60232 +ad cd427 +f1 f3e13 +66 1bb5be +31 e3c9 +8d d2900 +c7 2814cf +71 2af5a +e6 2846b5 +b1 d74c0 +f1 f4051 +71 2af68 +2d 1cfca4 +f1 f405f +b1 1085f4 +e6 2b57e9 +f6 2b614a +b1 1088a2 +e6 2b5a97 +f6 2b63f8 +e6 2b6aab +d6 2ba2f1 +f9 2b752a +de 2ba448 +e3 2b6a6d +91 10ca49 +c6 2b9c3e +e9 2b6e77 +f3 2b73ce +d6 2ba59f +f9 2b77d8 +31 e42b +c7 281531 +73 2afc3 +f1 f40b3 +2d 45de +71 2afca +6b 1eca55 +d0 2bb829 +8c 294e3d +9c 29579e +f1 2bf9d1 +ca 11f56a +ad 298fe5 +a9 299016 +b9 299977 +ef 2b5b7d +eb 2b5bae +ff 2b64de +eb 27d750 +4a 1e9b6f +6b 1edd17 +80 102ac7 +8c 2960ff +a1 106c6f +ca 12082c +ad 29a2a7 +c2 11f65f +31 197015 +ce 2b2c97 +e3 123807 +ef 2b6e3f +eb 2b6e70 +6a 1eca54 +36 19f0e0 +4b 1f0bfc +a8 299015 +32 47843 +7b 5af1c +b8 299976 +8d 29d18c +89 29d1bd +99 29db1e +ee 2b5b7c +ea 2b5bad +4f 1b750d +6a 1edd16 +7f 1b3a7b +36 1a03a2 +4b 1f1ebe +a0 106c6e +ac 29a2a6 +da 11fe6b +81 10ae16 +8d 29e44e +e2 123806 +ee 2b6e3e +69 1eca5c +aa cd45e +79 1ed3bd +ed 2b5b84 +1 1d4f21 +e9 2b5bb5 +fd 2b64e5 +11 1d5882 +f9 2b6516 +c0 11f666 +cc 2b2c9e +e1 12380e +ed 2b6e46 +d9 2ba6bd +6a 1eda68 +4f 1b725f +ee 28481a +b9 d7625 +6f 2a786 +4d 1b7514 +68 1edd1d +e0 12380d +ec 2b6e45 +5c 1e8ffa +c9 2bb01e +a3 268db6 +b4 110c74 +36 196d92 +2 1d51d5 +4b 1e88ae +b3 269717 +12 1d5b36 +5b 1e920f +6a 1eca56 +9d 29579f +99 2957d0 +ac 298fe6 +a8 299017 +b8 299978 +86 29e2fd +cf 2b19d6 +96 29ec5e +df 2b2337 +ee 2b5b7e +ea 2b5baf +fe 2b64df +c4 e728d +f5 ea824 +ad 298da9 +fa 2b6510 +36 198054 +4b 1e9b70 +c3 11f660 +cf 2b2c98 +4a 1f0bfd +21 1d8124 +bd 10876e +8c 29d18d +f3 12c4c4 +ff 2bfafc +9c 29daee +98 29db1f +54 60263 +43 1b83a5 +f5 f3e44 +ce 2b9d25 +ca 2b9d56 +de 2ba686 +af 1101c2 +d5 ee9cb +8d 29cf50 +da 2ba6b7 +73 633c1 +7f 1f69f9 +b1 26971e +8a c92b7 +10 1d5b3d +59 1e9216 +35 4786e +6b 1eca63 +3 1d4f28 +b5 1109c7 +eb 2b5bbc +eb 2b6e7e +a7 268ad7 +59 603ec +ee 2b5b8a +79 64533 +a3 268b08 +2 1d4f27 +b4 1109c6 +4b 1e8600 +ea 2b5bbb +b7 269438 +fe 2b64eb +cb 2b9d63 +5e 1e9001 +cb 2bb025 +7a 1ed3c5 +2 1d4f29 +ea 2b5bbd +12 1d588a +fa 2b651e +5a 1f156c +ce 2b9d33 +de 2ba694 +da 2ba6c5 +8c 294e9f +9c 295800 +ad 299047 +ef 2b5bdf +ff 2b6540 +e3 123869 +ef 2b6ea1 +ac 299046 +36 47874 +7f 5af4d +8d 29d1ee +9d 29db4f +b9 1089e9 +ee 2b5bde +fe 2b653f +5 1d4f52 +ed 2b5be6 +15 1d58b3 +fd 2b6547 +8d 294ea0 +9d 295801 +ac 299048 +fe 2b6541 +8c 29d1ef +f3 12c526 +ff 2bfb5e +9c 29db50 +de 2ba6e8 +6 3aa2a +3b 4799b +b7 11096c +bf 110ac3 +73 2acc1 +7b 2ae18 +3b 479a9 +f3 f3db8 +63 1ec970 +f7 f3de9 +67 1ec9a1 +fb f3f0f +22 1d93ee +6b 1ecac7 +ff f3f40 +26 1d941f +6f 1ecaf8 +b3 110949 +de 2ba692 +b7 11097a +bb 110aa0 +bf 110ad1 +a7 268b39 +4f 1e8631 +6 1d4f58 +b9 1089f7 +ee 2b5bec +79 64595 +b7 26949a +5f 1e8f92 +16 1d58b9 +fe 2b654d +3b 479fd +86 103b83 +bb 110af4 +2f 4337 +73 2ad23 +7b 2ae7a +3b 47a0b +af cd42e +52 60239 +f3 f3e1a +5a 60390 +fb f3f71 +b3 1109ab +bb 110b02 +33 47b00 +48 1e8668 +f3 f4066 +af 298da2 +b3 110bf7 +6 1d4f5a +b9 1089f9 +ee 2b5bee +16 1d58bb +fe 2b654f +fb 2b7525 +de 2ba6f6 +33 47b54 +31 47b5b +b3 110c4b +2f 45e5 +73 2afd1 +b3 110c59 +fb 27de03 +de 280fd4 +5a 1ea222 +ff 28517c +5e 1f159b +7b 1ee3ca +90 29668c +90 10317a +9c 2967b2 +98 2967e3 +d6 2b31f3 +d2 11fd12 +10 194131 +de 2b334a +da 2b337b +f7 2b739b +c2 120923 +f3 123eba +31 1982d9 +ff 2b74f2 +de 2ba6f4 +fb 2b7523 +b1 2613c2 +10 1cd7e1 +31 1d1989 +94 296909 +90 29693a +b1 29aae2 +f7 2b7649 +5f 1b7bc0 +fe 28517b +7a 1ee3c9 +95 26402a +b0 29a833 +b0 107321 +bc 29a959 +f6 2b739a +30 1982d8 +f2 123eb9 +fe 2b74f1 +11 1d5b30 +95 29ec58 +f1 123ec1 +c0 12092a +fd 2b74f9 +87 10ae5e +d4 2b34a8 +f5 2b7650 +f1 2b7681 +55 1b7a70 +23 45c2f +77 2ace2 +2f 1d9267 +70 1ee279 +2b 45d86 +5d 1b7bc7 +7f 2ae39 +78 1ee3d0 +f0 123ec0 +af 10eeae +fc 2b74f8 +77 2af90 +2f 1d9515 +55 1b7d1e +23 45edd +70 1ee527 +a7 10f005 +f4 2b764f +d1 2bb828 +9d 26443d +bf d76af +b8 29ac46 +19 1cd68b +7e 5b1f8 +30 1d16dc +38 1d1833 +e5 124b65 +df 280fd5 +fa 2b77de +5b 1ea223 +7a 1ee3cb +91 10317b +9d 2967b3 +99 2967e4 +b4 29a804 +fe 124351 +b0 29a835 +b0 107323 +bc 29a95b +b8 29a98c +d7 2b31f4 +d3 11fd13 +df 2b334b +db 2b337c +bc 1087cd +ab 26090f +f6 2b739c +f2 123ebb +fe 2b74f3 +11 1cd7e2 +30 1d198a +72 63120 +7e 1f6758 +91 29693b +b4 29aab2 +b0 29aae3 +d7 2b34a2 +ab 260bbd +bc 108a7b +f6 2b764a +a5 299f14 +f8 2b6517 +5e 5f39f +ff f2f80 +10 1d5883 +7a 1f5775 +c5 128d0c +c6 2791e2 +5a 1f2572 +de 1284f8 +90 29e9dc +98 29eb33 +10 1d5b31 +94 29ec59 +90 29ec8a +d9 2b3383 +a9 260916 +f4 2b73a3 +60 1b44a4 +d7 2b3256 +f0 123ec2 +fc 2b74fa +d3 11fd75 +68 1b45fb +df 2b33ad +a9 260bc4 +f4 2b7651 +d7 2b3504 +ad 25f617 +50 1f2422 +58 1f2579 +fb 2b7531 +21 3d8cc +2d 1d0f04 +88 294bc2 +f3 27df68 +52 1ea387 +73 1ee52f +0 1cbbc0 +d6 2b34af +f3 2b7688 +42 575e0 +25 1d105b +80 294d19 +57 1b7a77 +72 1ee280 +53 1f2428 +75 1f5399 +f2 123ec7 +44 1f1e02 +fe 2b74ff +57 1b7d25 +72 1ee52e +53 1f26d6 +f6 2b7656 +d3 2bb82f +5 1d5202 +f2 2b73db +fa 2b7532 +38 3f961 +72 1ee530 +19 3b4ff +f2 2b7689 +22 1cfd7a +35 3e25e +18 4385a +af 25f61e +52 1f2429 +5a 1f2580 +af 25f8cc +18 43b08 +52 1f26d7 +d6 2b3255 +b5 2997ef +d2 11fd74 +de 2b33ac +f7 2b73fd +d6 2b3503 +f7 2b76ab +b4 29a864 +95 29ea0c +f6 2b73fc +a7 10edb9 +f4 2b7403 +f0 123f22 +af 10ef10 +fc 2b755a +a7 10f067 +f4 2b76b1 +f6 2b73fe +f2 123f1d +fe 2b7555 +f6 2b76ac +94 29ea0d +90 10b52c +9c 29eb64 +d6 2bb5a5 +b5 2a1b3f +d2 1280c4 +de 2bb6fc +94 29ecbb +d6 2bb853 +d4 2bb5ac +d0 1280cb +26 19793f +dc 2bb703 +d4 2bb85a +78 1ed41e +31 1a036b +7a 1ed425 +c5 1209bc +33 1a0372 +71 1bcefc +a8 10f185 +9 41bca +44 1b00e2 +79 1bd053 +c2 1289c3 +31 1a0379 +d6 2b3263 +d2 11fd82 +b5 2997fd +de 2b33ba +f7 2b740b +d6 2b3511 +f6 2b740a +f2 123f29 +fe 2b7561 +57 1ea0fd +f6 2b76b8 +56 6025c +f0 2b73d4 +f7 f3e3d +d0 12103f +5e 603b3 +f8 2b752b +ff f3f94 +d8 121196 +5d 56dab +14 436d2 +b5 d72b3 +1c 43829 +bd d740a +5c 603ba +4b 1b84fc +fd f3f9b +c6 1289f4 +35 1a03aa +31 1a0619 +33 1a0620 +71 1bd1aa +9 41e78 +f3 12c208 +a 1c0 +c2 128c71 +ff 2bf840 +31 1a0627 +f6 2b740c +f2 123f2b +fe 2b7563 +f6 2b76ba +d6 2bb5b3 +b5 2a1b4d +d2 1280d2 +de 2bb70a +d6 2bb861 +90 103428 +9c 296a60 +98 296a91 +b1 1075d0 +da 12118d +bd 29ac08 +f3 124168 +ff 2b77a0 +b0 1075cf +bc 29ac07 +d0 11ffc7 +8f 10afb5 +dc 2b35ff +f1 12416f +fd 2b77a7 +7f 2b0e7 +5d 1b7e75 +2b 46034 +78 1ee67e +f0 12416e +af 10f15c +fc 2b77a6 +dd 280fce +ff f4240 +ab 10f18d +f8 2b77d7 +d9 2bb97f +5b 1ea4d1 +91 103429 +9d 296a61 +7a 63277 +99 296a92 +b0 1075d1 +bc 29ac09 +b8 29ac3a +d3 11ffc1 +df 2b35f9 +db 2b362a +f2 124169 +fe 2b77a1 +f5 ebae6 +a1 106a33 +ad 29a06b +90 10b778 +9c 29edb0 +98 29ede1 +f0 124170 +fc 2b77a8 +d3 120023 +df 2b365b +55 1f14a0 +d2 11ffce +de 2b3606 +75 1f5647 +f2 124175 +fe 2b77ad +5b 1ea4df +7a 1ee687 +fa 2b77e0 +2a 1cfed1 +3d 3e3b5 +5a 1f282e +d2 120022 +de 2b365a +f3 1241ca +ff 2b7802 +f0 1241d0 +af 10f1be +fc 2b7808 +f2 1241cb +fe 2b7803 +90 10b7da +9c 29ee12 +d2 128372 +de 2bb9aa +d0 128379 +dc 2bb9b1 +6 1ccc7a +3b 1d9beb +39 1d9bf2 +33 1d9aa2 +3b 1d9bf9 +d2 120030 +de 2b3668 +53 56c1c +f2 1241d7 +5f 1ea254 +fe 2b780f +3 1d4f36 +b5 1109d5 +d2 280e4a +bf 110b25 +b2 1085fa +35 1d9acc +33 465e4 +3f 1d9c1c +ba 108751 +31 465eb +3d 1d9c23 +5e 603c1 +ff f3fa2 +b7 1109dc +bf 110b33 +33 1d9d42 +a 398e2 +31 1d9d49 +33 1d9d50 +f2 1241d9 +fe 2b7811 +d2 128380 +de 2bb9b8 +b7 110c7c +3 1d51e4 +b5 110c83 +37 1d9d73 +b2 1088a8 +35 1d9d7a +b7 110c8a +21 1d7e06 +29 1d7f5d +0 1d51d0 +31 1d8767 +e3 2843d7 +42 1f07f6 +63 1f499e +6b 1f4af5 +4a 1f1ebf +e7 27c0c6 +7b 1f5456 +b 40d +a1 2a0f5f +5b 26f1f +ad 2a1085 +a9 2a10b6 +84 29e2f8 +b5 2a188f +80 29e329 +1b d6e +b1 2a18c0 +88 29e480 +b9 2a1a17 +ca 2bb018 +fb 2be5af +a1 267aed +0 1d3f0c +b1 26844e +10 1d486d +4a 1f1c71 +21 1d80b4 +5a 1f25d2 +31 1d8a15 +63 1f4c4c +84 29d034 +94 29d995 +a1 2a120d +b1 2a1b6e +c6 2b9bcc +21 1d90c8 +29 1d921f +63 1f5c60 +6b 1f5db7 +80 29e079 +a1 10ed0f +ad 2a2347 +a9 2a2378 +e7 2bed88 +e3 2bedb9 +e3 12b8a7 +21 19fcc6 +ef 2beedf +24 1cfd98 +eb 2bef10 +a1 268daf +34 196d8b +0 1d51ce +e3 285947 +76 1b3923 +42 1f1d66 +84 29e2f6 +61 1f49a5 +69 1f4afc +40 1f1d6f +71 1f5306 +48 1f1ec6 +79 1f545d +61 1f4c53 +71 1f55b4 +e1 2bddac +61 1f5c67 +69 1f5dbe +4b 1e26e +e1 2bedc0 +e1 12b8ae +ed 2beee6 +e9 2bef17 +61 1f5f15 +74 1ed04a +e1 2bf06e +62 1f499f +a1 ce571 +6a 1f4af6 +b1 ceed2 +e6 27c0c7 +7a 1f5457 +ac 2a1086 +b4 2a1890 +80 103ae9 +ea 2bdc4f +90 10444a +fa 2be5b0 +62 1f4c4d +e2 2bdda6 +20 1d90c9 +28 1d9220 +62 1f5c61 +6a 1f5db8 +a 16d0 +3b 4c67 +a0 2a2222 +a8 2a2379 +e6 2bed89 +e2 12b8a8 +ee 2beee0 +ea 2bef11 +e8 2bef18 +e3 2843e5 +f4 12c2a3 +42 1f0804 +63 1f49ac +6b 1f4b03 +8 431a7 +42 1f1d76 +73 1f530d +4a 1f1ecd +7b 1f5464 +63 2105e +6f 1b4696 +c6 2b992c +6b 211b5 +ce 2b9a83 +10 3b355 +e3 2bdb05 +18 3b4ac +eb 2bdc5c +c2 2baecf +f3 2be466 +25 1d7e39 +ca 2bb026 +fb 2be5bd +2d 1d7f90 +f3 284ff4 +52 1f1413 +63 1f4c5a +73 1f55bb +c6 2b9bda +d6 2ba53b +10 3b603 +e3 2bddb3 +f3 2be714 +42 5e66c +4e 1f1ca4 +25 1d80e7 +e3 2856a7 +f4 12d565 +42 1f1ac6 +63 1f5c6e +e3 2bedc7 +e3 12b8b5 +ef 2beeed +24 1cfda6 +eb 2bef1e +e3 285955 +76 1b3931 +f4 12d813 +42 1f1d74 +63 1f5f1c +e7 2bf044 +76 1ed051 +e3 2bf075 +62 1f49ad +6a 1f4b04 +72 1f530e +fa 2be5be +62 1f4c5b +72 1f55bc +53 1f115a +19 4258b +f2 2be715 +62 1f5c6f +6a 1f5dc6 +e6 2bed97 +e2 12b8b6 +ee 2beeee +63 1b3240 +8c 29cf3f +94 29d749 +73 1b3ba1 +9c 29d8a0 +f 43e +53 26e2a +a5 2a0f90 +5b 26f81 +ad 2a10e7 +1f d9f +84 29e35a +b5 2a18f1 +84 29d096 +f6 12c558 +57 5ef9d +94 29d9f7 +a5 2a123e +b5 2a1b9f +84 29e0aa +a1 10ed71 +ad 2a23a9 +e7 2bedea +84 29e358 +f6 12d81a +57 6025f +13 193e19 +c6 2baef0 +4f 1e29f +e5 2bedf1 +e1 12b910 +ed 2bef48 +11 193e20 +c4 2baef7 +e5 2bf09f +e 43f +a4 2a0f91 +ac 2a10e8 +1e da0 +b4 2a18f2 +a4 2a123f +b4 2a1ba0 +3f 4c98 +e 1701 +a4 2a2253 +7f 21837 +4e 1e2a0 +e4 2bedf2 +91 10c799 +c6 2b998e +99 10c8f0 +ce 2b9ae5 +91 10ca47 +e3 2b6a6b +c6 2b9c3c +f3 2b73cc +d6 2ba59d +c6 2bac50 +e7 2bedf8 +e3 12b917 +ef 2bef4f +c6 2baefe +e7 2bf0a6 +ee 28480c +84 ca6a6 +b9 d7617 +c4 e7237 +f9 f41a8 +f9 f41b6 +e6 2bedf9 +39 e582 +c3 ee050 +cf 281688 +79 2b113 +18 43a98 +84 ca708 +b9 d7679 +1a 43a9f +bb d7680 +e7 eb18c +f8 2be865 +c4 e7299 +f9 f420a +4f 1b72c1 +18 43aa6 +6a 1edaca +b9 d7687 +58 60637 +f9 f4218 +6b 1f4da3 +8c 29d18b +ca 1278b8 +ad 2a1333 +a9 2a1364 +b9 2a1cc5 +eb 285a9e +7e 1b3a7a +4a 1f1ebd +6b 1f6065 +80 10ae15 +8c 29e44d +ca 128b7a +a1 10efbd +ad 2a25f5 +db 1281ba +e3 12bb55 +ef 2bf18d +7e 1ed19a +eb 2bf1be +69 1f4daa +79 1f570b +e9 285aa5 +7c 1b3a81 +48 1f1ec4 +69 1f606c +7c 1ed1a1 +e9 2bf1c5 +a1 ce81f +6a 1f4da4 +80 103d97 +ea 2bdefd +6b 1f4db1 +18 3b75a +eb 2bdf0a +7e 1ed1a8 +eb 2bf1cc +6a 1f4db2 +c5 128caa +7a 1f5713 +5b 1f12b1 +fa 2be86c +8c 29d1ed +fe 12c6af +5f 5f0f4 +9c 29db4e +ad 2a1395 +e1 12bbbe +ed 2bf1f6 +ac 2a1396 +e3 12bbc5 +ef 2bf1fd +86 103dcf +bb 110d40 +3b 47c57 +fb f41bd +bb 110d4e +c5 12779c +39 47cb2 +3b 47cb9 +5a 6063e +fb f421f +bb 110db0 +e7 27d388 +7b 1f6718 +90 29e9da +98 29eb31 +b5 2a2b51 +1b 2030 +b1 2a2b82 +2 1921f7 +80 10c0d9 +b1 10f670 +bd 2a2ca8 +94 29ec57 +71 1f65c8 +79 1f671f +5b 1ebcf +f1 2bf721 +f9 2bf878 +eb e9ff0 +80 2636a5 +71 1f6876 +f1 2bf9cf +30 1d9a2a +a4 2607f1 +38 1d9b81 +67 1b2fd1 +e5 12ceb3 +e6 27d389 +7a 1f6719 +b4 2a2b52 +1a 2031 +b0 2a2b83 +b0 10f671 +bc 2a2ca9 +b8 2a2cda +67 63aa3 +45 1f0831 +ab 268c5d +f6 2bf6ea +f2 12c209 +fe 2bf841 +b4 2a2e00 +b0 2a2e31 +da 11fe79 +5d 1f134b +a7 298c59 +3e 196ee7 +ab 268f0b +f6 2bf998 +f8 2bf879 +ac 298fe4 +f0 2bf9d0 +f3 286008 +af 25f61c +52 1f2427 +f3 2862b6 +af 25f8ca +52 1f26d5 +72 1f65d0 +7a 1f6727 +f6 2bf6f8 +ae 298d3d +f2 2bf729 +f2 12c217 +fe 2bf84f +fa 2bf880 +72 1f687e +53 1f241c +19 4384d +ae 298feb +f2 2bf9d7 +35 465ac +94 29ea0b +73 1b4e63 +90 10b52a +9c 29eb62 +1f 2061 +b5 2a2bb3 +f7 2bf74b +94 29ecb9 +5f 1ec00 +f5 2bf752 +42 1aedf8 +c0 128cda +f1 12c271 +fd 2bf8a9 +d4 2bb858 +f5 2bfa00 +1e 2062 +b4 2a2bb4 +b0 10f6d3 +bc 2a2d0b +b4 2a2e62 +5e 1ec01 +f4 2bf753 +f0 12c272 +fc 2bf8aa +f4 2bfa01 +d6 2bb85f +f6 2bf75a +f2 12c279 +fe 2bf8b1 +44 1b0390 +79 1bd301 +5e 60661 +f8 2b77d9 +ff f4242 +d8 121444 +fa ebe60 +71 29cfa +7d 1bd332 +62 5a4c3 +6e 1edafb +1c 43ad7 +bd d76b8 +5c 60668 +4b 1b87aa +fd f4249 +da 1294db +2 1924a5 +b1 10f91e +bd 2a2f56 +a 46e +f3 12c4b6 +ff 2bfaee +f9 286406 +58 1f2825 +88 2637fc +79 1f69cd +f9 2bfb26 +b0 10f91f +bc 2a2f57 +b8 2a2f88 +67 63d51 +45 1f0adf +c6 e7294 +f7 ea82b +af 298db0 +f2 12c4b7 +fe 2bfaef +f8 2bfb27 +7a 1f69d5 +5b 1f2573 +fa 2bfb2e +3d 46703 +d0 128377 +dc 2bb9af +42 1af0a6 +f1 12c51f +fd 2bfb57 +90 10b7d8 +9c 29ee10 +b0 10f981 +bc 2a2fb9 +f0 12c520 +fc 2bfb58 +d2 12837e +de 2bb9b6 +5f 1f25a4 +53 5ef6c +f2 12c527 +fe 2bfb5f +6 1ccf28 +3b 1d9e99 +39 1d9ea0 +3b 1d9ea7 +b 1d533b +bd 110dda +33 46892 +3f 1d9eca +ba 1089ff +31 46899 +3d 1d9ed1 +5e 6066f +ff f4250 +bf 110de1 +a5 1069f4 +30 47b5a +79 5b233 +29 3da23 +a9 106b1a +ad 106b4b +bd 29a95c +b1 107324 +b5 107355 +39 3e384 +b9 10747b +bd 1074ac +46 562ed +1 1d51dd +35 196d9a +c6 11f3e4 +c4 11f3eb +4a 56413 +39 196ec0 +ca 11f50a +4e 56444 +3d 196ef1 +9 1d5334 +ce 11f53b +4c 5644b +cc 11f542 +b 1c1 +c3 128c72 +56 56c4e +67 5a495 +e7 12358c +65 5a49c +e5 123593 +54 1b8d23 +22 46ee2 +6b 5a5bb +d4 281e1a +a2 10ffd9 +eb 1236b2 +20 46ee9 +69 5a5c2 +a0 10ffe0 +e9 1236b9 +26 46f13 +6f 5a5ec +a6 11000a +ef 1236e3 +24 46f1a +6d 5a5f3 +a4 110011 +ed 1236ea +f7 123eed +75 5adfd +f5 123ef4 +b2 11093a +fb 124013 +30 4784a +79 5af23 +b0 110941 +f9 12401a +b6 11096b +ff 124044 +34 4787b +7d 5af54 +b4 110972 +fd 12404b +a9 106b7c +29 1cfc73 +3d 1d05a3 +b9 1074dd +4a 1e8663 +cf 128dfa +b 1923af +89 10c291 +56 1e8e3c +5e 1e8f93 +7e 5c20c +5c 1e8f9a +5a 1e8fc4 +df 12975b +67 1ec683 +65 1ec68a +26 1d9101 +6f 1ec7da +24 1d9108 +6d 1ec7e1 +a2 11003b +d4 281e7c +eb 123714 +22 1d9132 +19 427e7 +6b 1ec80b +a0 110042 +e9 12371b +77 1ecfe4 +36 1d9a62 +7f 1ed13b +34 1d9a69 +7d 1ed142 +1c 1cd96b +10 3a333 +b2 11099c +fb 124075 +db 1294dc +14 3a364 +b0 1109a3 +f9 12407c +9c 296a62 +90 10342a +94 10345b +2d 1d11b2 +4a 57737 +21 3db7a +ad 29a2a9 +ca 12082e +a1 106c71 +ce 12085f +a5 106ca2 +5a 58098 +3d 1d1b13 +31 3e4db +bd 29ac0a +da 12118f +b1 1075d2 +35 3e50c +de 1211c0 +b5 107603 +4e 1e9ba2 +7f 1ed139 +42 5656a +b 46f +56 56efc +ef 2b6e41 +e3 123809 +6d 1edd51 +61 5a719 +ed 2b6e48 +e1 123810 +67 5a743 +e7 12383a +e5 123841 +c2 2791a3 +7d 1ee6b2 +ae ce753 +71 5b07a +fd 2b77a9 +f1 124171 +8f 29cee5 +77 5b0a4 +f7 12419b +f5 1241a2 +db 2bb6ca +14 1cc552 +9c 296ac4 +90 10348c +35 3f7ce +10 1cc583 +bd 29ac6c +b1 107634 +31 1d072b +7a 1f5465 +c5 1289fc +46 1e8789 +b 19265d +56 1e90ea +67 1ec931 +65 1ec938 +ef 2b6ea3 +e3 12386b +ed 2b6eaa +e1 123872 +77 1ed292 +fd 2b780b +f1 1241d3 +6f 1f4b28 +71 1ed2ca +84 10ae56 +a1 107c85 +a5 107cb6 +79 5c4f5 +c 41eb6 +29 3ece5 +8c 10afad +a9 107ddc +ad 107e0d +c4 1206ad +39 198182 +ca 1207cc +cc 120804 +e5 124855 +e9 12497b +ed 1249ac +8c 10b00f +a9 107e3e +c 1d4106 +29 1d0f35 +67 1ed945 +c2 2b1603 +65 1ed94c +e2 12487c +c7 ee073 +c0 2b160a +63 5a464 +6f 1eda9c +ca 2b175a +61 5a46b +6d 1edaa3 +ea 1249d3 +cf ee1ca +c8 2b1761 +19 43aa9 +4e 1f0c9e +6b 1edacd +e9 1249dd +31 3e22b +3d 1d1863 +0 3ac94 +b1 107322 +bd 29a95a +80 103d8b +b5 107353 +84 103dbc +21 3ee3c +de 128258 +a1 107f33 +a5 107f64 +e1 124ad2 +e5 124b03 +bd 29a9bc +b1 107384 +80 103ded +67 1edbf3 +c2 2b18b1 +e1 124b34 +89 10acc1 +b0 10f98f +1d 1d5a0c +bc 2a2fc7 +11 423d4 +9d 29eb03 +91 10b4cb +b8 10fae6 +19 4252b +99 10b622 +4f 1f1c43 +e2 12bbc6 +ee 2bf1fe +43 5e60b +4d 1f1c4a +41 5e612 +e6 12bbf7 +47 5e63c +c7 127733 +c5 12773a +ea 12bd1d +4b 5e762 +cb 127859 +49 5e769 +c9 127860 +ee 12bd4e +4f 5e793 +cf 12788a +cd 127891 +5d 1f25ab +51 5ef73 +dd 2bb6a2 +d1 12806a +d7 128094 +55 5efa4 +d5 12809b +d9 1281c1 +df 1281eb +dd 1281f2 +9 1d3e1a +ee 2bdf3c +b9 110d47 +4f 1f0981 +cb 1278bb +4b 1f09b2 +c9 1278c2 +6b 63c2b +49 1f09b9 +f6 2be746 +57 1f118b +fe 2be89d +5f 1f12e2 +d 1d5359 +1 41d21 +1d 1d5cba +11 42682 +9d 29edb1 +91 10b779 +15 426b3 +95 10b7aa +cf 2bafe8 +c3 1279b0 +cd 2bafef +c1 1279b7 +47 5e8ea +c7 1279e1 +c5 1279e8 +dd 2bb950 +d1 128318 +57 5f24b +d7 128342 +d5 128349 +1 1d3f71 +15 1d48a1 +11 1d48d2 +47 1f0ad8 +cf 2bb04a +1a 1cd94d +c3 127a12 +43 1f0b09 +63 63d82 +41 1f0b10 +57 1f1439 +a8 110447 +9 42e8c +41 5f8d4 +c1 1289cb +cb 128b1b +49 5fa2b +c9 128b22 +cd 128b53 +39 1d0882 +9 1d50dc +45 1f1af3 +4b 1f1c74 +c9 128b84 +49 1f1c7b +1 42fe3 +47 1f1d9a +45 1f1da1 +f7 ebaed +a3 106a3a +af 29a072 +c3 128cd4 +61 1b2f29 +29 4361 +63 1b2f30 +20 19f9a7 +69 1b3080 +21 196398 +22 19f9ae +6b 1b3087 +2e 1d8252 +71 1b388a +29 1964ef +30 1a0308 +79 1b39e1 +23 1cfac1 +2b 1cfc18 +7d 1b4cd2 +71 2169a +40 1e103 +29 1cfc1f +33 1d0422 +3b 1d0579 +50 1ea64 +39 1d0580 +c4 277ea9 +cc 278000 +ce 278007 +e5 27c051 +ad cd489 +50 60294 +e7 27c058 +e1 27c082 +9d 26569d +42 1e84a8 +91 d2065 +e3 27c089 +a4 268acf +ed 27c1a8 +0 1cb910 +a1 25f4f1 +58 603eb +a6 268ad6 +ef 27c1af +a0 268b00 +e9 27c1d9 +99 d21bc +4a 1e85ff +a2 268b07 +eb 27c1e0 +bd cddea +8c ca853 +f7 27c9b9 +8 1cba67 +a9 25f648 +f 1d3df0 +ae 2a13ab +f1 27c9e3 +a 1cba6e +bc 10750d +ab 25f64f +41 1b737e +52 1e8e09 +f3 27c9ea +b4 269430 +fd 27cb09 +b6 269437 +ff 27cb10 +b0 269461 +f9 27cb3a +5a 1e8f60 +b2 269468 +fb 27cb41 +86 294a41 +5a 1e9280 +18 1cc3c8 +b9 25ffa9 +1a 1cc3cf +bb 25ffb0 +2b 1d94d6 +51 1b7cdf +29 3dd33 +8e 294b98 +e6 eb129 +c4 277eb7 +ee eb280 +cc 27800e +96 2953a2 +a2 1069d7 +f6 eba8a +25 1d7ea9 +ae 29a00f +d4 278818 +9e 2954f9 +a7 298be9 +32 1d9d4f +7b 1ed428 +e5 27c05f +af 298d40 +c4 e722b +f5 ea7c2 +ad 298d47 +ab 298d71 +40 1e84af +47 24f18 +e1 27c090 +c0 e725c +f1 ea7f3 +f 41c00 +fd 27de2b +a9 298d78 +b7 29954a +a4 268add +ed 27c1b6 +bf 2996a1 +bb 2996d2 +50 1e8e10 +f 1d3dfe +57 25879 +f1 27c9f1 +58 1e8f67 +5f 259d0 +b0 26946f +f9 27cb48 +40 1af02f +2a 1d1187 +50 1af990 +61 1b31d7 +71 1b3b38 +2 1cbbc7 +40 1de61 +12 1cc528 +75 1b4e29 +4a 1e9933 +21 1cfd76 +96 25bf2e +e5 27c2ff +e1 27c330 +42 1e8756 +e3 27c337 +0 1cbbbe +a1 25f79f +f 1d409e +f1 27cc91 +52 1e90b7 +f3 27cc98 +12 1cc526 +b3 260107 +c4 e6f89 +f9 f3efa +96 295650 +a3 298ec8 +40 1e875d +47 251c6 +e1 27c33e +e1 eb162 +b3 299829 +f 1d40ac +50 1e90be +57 25b27 +f1 27cc9f +44 1b73bc +61 1b41eb +46 1b73c3 +29 5623 +63 1b41f2 +6f 21176 +4 19a82b +21 19765a +34 3f51f +23 197661 +4c 1b7513 +69 1b4342 +3c 3f676 +2b 1977b8 +54 25b2d +71 2295c +29 1d0ee1 +c4 27916b +8c ca5a3 +c6 279172 +c0 e5c8a +cc 2792c2 +88 25c762 +45 1b7163 +c2 e5c91 +ce 2792c9 +c8 2792f3 +ca 2792fa +a4 1102bf +ed 123998 +e5 27d313 +ad ce74b +e7 27d31a +ef ea2cf +0 1ccbd2 +84 263984 +a1 2607b3 +c4 280515 +e1 27d344 +42 1e976a +91 d3327 +c6 28051c +e3 27d34b +e1 e9e32 +ed 27d46a +e3 e9e39 +ef 27d471 +8 1ccd29 +8c 263adb +a9 26090a +cc 28066c +e9 27d49b +bc 1087cf +a 1ccd30 +ab 260911 +41 1b8640 +86 295d03 +5a 1ea542 +c4 279179 +c0 e5c98 +cc 2792d0 +c8 279301 +8a 295e8b +8f d28fb +d0 e790d +88 295e92 +e5 27d321 +40 1e9771 +47 261da +e1 27d352 +e1 e9e40 +ed 27d478 +f5 eba84 +a1 1069d1 +ad 29a009 +d6 eec7f +8e 29d204 +ab 29a033 +f1 ebab5 +d4 eec86 +f 42ec2 +a9 29a03a +21 1d1038 +c2 e6fb5 +a5 260a30 +0 1cce80 +a1 260a61 +e1 27d5f2 +42 1e9a18 +e3 27d5f9 +40 1e9a1f +47 26488 +e1 27d600 +a0 267afa +1 19a53f +14 42404 +3 19a546 +e0 28468b +41 1b70d0 +9 8508 +e2 284692 +43 1b70d7 +a8 267c51 +9 19a696 +1c 4255b +b 19a69d +e8 2847e2 +49 1b7227 +ea 2847e9 +4b 1b722e +2b 1d9228 +f0 284fec +51 1b7a31 +29 3da85 +f8 285143 +59 1b7b88 +a2 2a1223 +3 1d3c68 +23 46ee1 +55 1b8d22 +1 1d3c6f +aa 2a137a +b 1d3dbf +2b 47038 +5d 1b8e79 +51 25841 +9 1d3dc6 +b2 2a1b84 +13 1d45c9 +33 47842 +11 1d45d0 +6b 2a4a7 +49 1b7235 +3b 47999 +19 1d4727 +c5 2801f8 +f4 124203 +42 1e8764 +e3 27c345 +8d d1630 +c7 2801ff +c1 280229 +c3 280230 +cd 28034f +cf 280356 +c9 280380 +cb 280387 +af 2a2350 +a3 10ed18 +d5 280b59 +ad 106bad +52 1e90c5 +f3 27cca6 +81 263698 +ab 10ee6f +dd 280cb0 +94 10b55d +83 26369f +df 280cb7 +89 2637ef +9c 10b6b4 +8b 2637f6 +87 29cd90 +b0 2600fd +5b 1f15cf +a7 110009 +85 29cd97 +8f 29cee7 +af 110160 +d5 ee969 +8d 29ceee +8b 29cf18 +ab 110191 +d1 ee99a +dd 281fd2 +89 29cf1f +97 29d6f1 +b7 11096a +95 29d6f8 +9f 29d848 +bf 110ac1 +9d 29d84f +ef f35cf +cd 28035d +eb f3600 +24 4488 +c9 28038e +ab 10ee7d +ff f3f30 +dd 280cbe +3 1d3f16 +23 4718f +55 1b8fd0 +1 1d3f1d +41 261b0 +13 1d4877 +33 47af0 +11 1d487e +c5 2804a6 +81 263946 +2e 1cfcaa +c1 2804d7 +94 10b80b +83 26394d +c3 2804de +af 2a25fe +a3 10efc6 +d5 280e07 +ad 106e5b +95 264276 +a7 1102b7 +85 29d045 +83 29d06f +e7 f3726 +c5 2804b4 +a3 1102e8 +d5 282129 +81 29d076 +e3 f3757 +2e 1cfcb8 +c1 2804e5 +b7 110c18 +95 29d9a6 +e0 28594d +41 1b8392 +e2 285954 +9 97ca +43 1b8399 +14 436c6 +4a 1e88bb +fc 12435a +eb 27c49c +3 19b808 +e8 285aa4 +49 1b84e9 +ea 285aab +4b 1b84f0 +a8 268f13 +9 19b958 +1c 4381d +b 19b95f +4f 5ea4d +1 1d4f31 +49 1b84f7 +c5 2814ba +f4 1254c5 +42 1e9a26 +e3 27d607 +8d d28f2 +c7 2814c1 +cf ee476 +c8 2b1a0d +81 26495a +c1 2814eb +c3 2814f2 +ca 2b1a14 +94 10c81f +83 264961 +c1 edfd9 +cd 281611 +4a 1e9b7d +fc 12561c +eb 27d75e +c3 edfe0 +cf 281618 +c9 281642 +cb 281649 +89 264ab1 +9c 10c976 +8b 264ab8 +85 29e059 +d5 efc2b +81 10ab78 +8d 29e1b0 +8b 29e1da +3 1d51d8 +1 1d51df +54 1af775 +2e 1d0f6c +22 3d934 +c1 281799 +26 3d911 +c5 281776 +2e 1d0f7a +54 1af783 +22 3d942 +c1 2817a7 +21 1cfaba +29 1cfc11 +31 1d041b +39 1d0572 +29 3da83 +63 1ec652 +22 1d90d0 +6b 1ec7a9 +20 1d90d7 +69 1ec7b0 +32 1d9a31 +7b 1ed10a +30 1d9a38 +79 1ed111 +98 295523 +31 3e22d +3d 1d1865 +a5 298be2 +30 1d9d48 +79 1ed421 +ad 298d39 +a9 298d6a +bd 29969a +b9 2996cb +e6 12484b +c4 2b15d9 +22 5782 +ce 2b1729 +ee 1249a2 +20 5789 +cc 2b1730 +9c 103364 +8b 25b4a6 +d6 2b1f33 +f6 1251ac +89 25b4ad +d4 2b1f3a +d2 2b1f64 +77 1ee2a6 +ae ce751 +7d 1ee6b0 +71 5b078 +8c 25b4df +da 2b20bb +73 5adc5 +7f 1ee3fd +fa 125334 +df eeb2b +d8 2b20c2 +71 5adcc +7d 1ee404 +ad 106bab +e7 2b577a +a6 2a21f8 +ef 2b58d1 +a4 2a21ff +ed 2b58d8 +99 10b8de +a2 2a2229 +eb 2b5902 +b6 2a2b59 +ff 2b6232 +b4 2a2b60 +fd 2b6239 +b2 2a2b8a +fb 2b6263 +10 1cc521 +4a 1e9925 +21 1cfd68 +5a 1ea286 +31 1d06c9 +8 39b89 +42 1e8758 +61 1ec907 +a2 cd309 +ae 260941 +71 1ed268 +94 295649 +ce 2b2a4d +c2 11f415 +a5 298e90 +a1 298ec1 +b1 299822 +d2 2b2212 +77 1ee554 +ae ce9ff +8c 25b78d +ad 106e59 +e7 2b5a28 +e3 2b5a59 +bd 1077ba +f7 2b6389 +f3 2b63ba +c 1cbd54 +ad 25f935 +4 1d3f4d +6f 5a898 +21 1d0d7c +54 25b1f +c 1d40a4 +29 1d0ed3 +46 1f0ae5 +29 3ed45 +63 1ed914 +4e 1f0c3c +6b 1eda6b +69 1eda72 +88 295e84 +a5 299ea4 +79 1ee6e3 +a1 1069c3 +ad 299ffb +d4 eec78 +8c 29d1fd +a9 29a02c +c4 2b289b +31 196d69 +c2 11f3b3 +ce 2b29eb +c0 11f3ba +cc 2b29f2 +ca 2b2a1c +cf ef48c +c8 2b2a23 +ad 107e6d +e7 2b6a3c +e3 12355b +ef 2b6b93 +e1 123562 +ed 2b6b9a +99 10cba0 +ce 2b9d95 +eb 2b6bc4 +5e 1f134f +21 1d102a +61 1edbc9 +c2 1206d7 +a5 29a152 +ad 10811b +e7 2b6cea +a0 2a121c +1 1d3c61 +a8 2a1373 +9 1d3db8 +b0 2a1b7d +11 1d45c2 +a7 ce5fb +b8 2a1cd4 +19 1d4719 +e2 2bddb4 +9 41c2a +43 1f07f9 +63 63a72 +41 1f0800 +ea 2bdf0b +4b 1f0950 +6b 63bc9 +49 1f0957 +8d 29cee0 +f3 eaaa8 +ff 27e0e0 +ab 29902d +89 29cf11 +9d 29d841 +bb 29998e +23 dad1 +cf 2b9a78 +21 dad8 +ef 12ccf1 +cd 2b9a7f +cb 2b9aa9 +eb 12cd22 +24 3dbaa +c9 2b9ab0 +9d 10b6b3 +d7 2ba282 +33 e432 +df 2ba3d9 +1 1d3f0f +11 1d4870 +9 41ed8 +43 1f0aa7 +63 63d20 +41 1f0aae +f3 ea7fa +c2 e7263 +ff 27de32 +ab 298d7f +81 29d068 +95 29d998 +8d 10b000 +c7 2b9bcf +e7 12ce48 +c5 2b9bd6 +c3 2b9c00 +e3 12ce79 +c1 2b9c07 +9d 10b961 +d7 2ba530 +e9 2b5bb7 +a0 2a24de +4f 5ea3f +1 1d4f23 +e2 2bf076 +9 42eec +43 1f1abb +41 1f1ac2 +81 10ab6a +8d 29e1a2 +f3 ebd6a +ab 29a2ef +89 29e1d3 +c3 127702 +cf 2bad3a +c1 127709 +cd 2bad41 +cb 2bad6b +24 3ee6c +c9 2bad72 +32 47af1 +7b 5b1ca +1 1d51d1 +9 4319a +43 1f1d69 +41 1f1d70 +f3 ebabc +d6 eec8d +ab 29a041 +85 29e2f9 +f 1923e0 +8d 10c2c2 +c7 2bae91 +d 1923e7 +c5 2bae98 +39 3e3e6 +9 41c2c +19 4258d +29 3ed47 +f0 2862ae +ac 25f8c2 +51 1b8cf3 +9 42eee +4a 56421 +d1 eff0a +1c 1cc46b +89 29e48f +4e 56452 +81 10ae88 +d5 eff3b +8d 29e4c0 +c6 11f3f2 +4d 1f09ea +ca 11f518 +ce 11f549 +4c 1e995d +40 56325 +2d 1cfef0 +4a 56475 +48 5647c +5c 1ea2be +50 56c86 +58 56ddd +2d 1cfefe +75 21979 +4a 56483 +4e 1e9bb0 +7f 1ed147 +42 56578 +4e 1e9c04 +7f 1ed19b +42 565cc +4e 1e9c12 +7f 1ed1a9 +42 565da +de 2b366a +d2 120032 +83 10ab7d +8f 29e1b5 +77 5c374 +41 5e674 +4d 1f1cac +ca 1207da +40 575e7 +48 5773e +7f 1ee45d +73 5ae25 +42 5788e +f7 1241a9 +71 5ae2c +7d 1ee464 +40 57895 +73 5ae33 +7f 1ee46b +42 5789c +67 5a4a3 +22 46ef0 +54 1b8d31 +6b 5a5c9 +26 46f21 +6f 5a5fa +a4 26077f +4f 1f1c51 +43 5e619 +a0 cd29e +ac 2608d6 +4b 5e770 +a2 10ffe7 +d4 281e28 +eb 1236c0 +a6 110018 +ef 1236f1 +b2 110948 +fb 124021 +cf 2bad48 +c3 127710 +c7 127741 +cb 127867 +cf 127898 +d7 1280a2 +df 1281f9 +22 46f44 +54 1b8d85 +6b 5a61d +20 46f4b +69 5a624 +30 478ac +79 5af85 +4b 5e7c4 +49 5e7cb +54 1b8d93 +22 46f52 +6b 5a62b +81 d16f4 +32 478b3 +8d 264d2c +7b 5af8c +a0 cd300 +ac 260938 +4b 5e7d2 +a2 110049 +d4 281e8a +eb 123722 +b2 1109aa +fb 124083 +cb 1278c9 +6f 1edd58 +63 5a720 +67 5a751 +7f 1ee6b9 +73 5b081 +8f 29cef3 +77 5b0b2 +4f 1f1eff +a4 260a2d +43 5e8c7 +5f 1f2860 +b4 26138e +53 5f228 +57 5f259 +ef 2b6e4f +e3 123817 +e7 123848 +ff 2b77b0 +f3 124178 +cf 2baff6 +c3 1279be +c7 1279ef +df 2bb957 +d3 12831f +d7 128350 +52 1e8e17 +f3 27c9f8 +5f 1f28b4 +53 5f27c +a4 260a8f +4f 1f1f61 +43 5e929 +5f 1f28c2 +b4 2613f0 +53 5f28a +ef 2b6eb1 +e3 123879 +ff 2b7812 +f3 1241da +cf 2bb058 +c3 127a20 +eb 124982 +cb 128b29 +69 5b8e6 +61 5ba3d +43 5fbdd +63 5ba44 +43 5fbeb +e3 124b3b +c3 128ce2 +94 10320f +9c 103366 +81 29e32a +14 1cc306 +89 29e481 +1c 1cc45d +84 103b70 +8c 103cc7 +0 39786 +c 1ccdbe +b5 1073b5 +84 103e1e +a5 106a56 +a2 298ed3 +3 1cb918 +b5 1073b7 +b 1cba6f +aa 29902a +bd 10750e +82 29d07a +95 10b55e +3d 1d0605 +8a 102979 +d 1d3e4b +9e 2967bb +92 103183 +15 1d4655 +9a 1032da +1d 1d47ac +a5 107d18 +c0 278194 +ad 107e6f +21 3d92e +2d 1d0f66 +8a 103c3b +1 41ad5 +d 1d510d +25 1d10bd +b3 107329 +82 103d92 +bf 29a961 +5 1d5264 +52 1e8e7b +d7 129612 +5e 1e8fa1 +5a 1e8fd2 +df 129769 +cc 11f5a4 +d4 11fdae +b0 108903 +46 1e853d +b8 108a5a +4e 1e8694 +c3 2baec2 +56 1e8e9e +c1 2baec9 +54 1e8ea5 +cb 2bb019 +5e 1e8ff5 +c6 11f454 +ce 11f5ab +d6 11fdb5 +46 1e854b +c3 2baed0 +56 1e8eac +52 1e9129 +c6 11f6f4 +c4 11f6fb +d6 120055 +d4 12005c +56 1e914c +54 1e9153 +c6 11f702 +d6 120063 +56 1e915a +c6 120708 +c4 12070f +cc 120866 +46 1e97ff +44 1e9806 +42 5631e +25 1cfd99 +4e 1e9956 +c6 120716 +46 1e980d +f7 123f4d +c6 1209b6 +75 1ed04b +44 1e9ab4 +77 1ed052 +46 1e9abb +26 1d910f +6f 1ec7e8 +22 1d9140 +6b 1ec819 +36 1d9a70 +7f 1ed149 +a0 25f4ee +4b 1f09c0 +57 1f1199 +b4 25fe1e +5f 1f12f0 +e5 1235f5 +a6 11006c +ef 123745 +a4 110073 +ed 12374c +43 1e84b7 +f5 123f56 +2 1d4f35 +b4 1109d4 +4b 1e860e +fd 1240ad +12 3a098 +1e 1cd6d0 +c7 127795 +1a 3a1ef +cf 1278ec +cd 1278f3 +d5 1280fd +27 4490 +dd 128254 +15 426c1 +67 1ec6e5 +65 1ec6ec +26 1d9163 +1d 42818 +6f 1ec83c +24 1d916a +6d 1ec843 +77 1ed046 +36 1d9ac4 +7f 1ed19d +34 1d9acb +7d 1ed1a4 +b1 110c52 +47 1f088c +ce 2b29f9 +c2 11f3c1 +45 1f0893 +b9 110da9 +4f 1f09e3 +57 1f11ed +de 2b335a +d2 11fd22 +55 1f11f4 +5f 1f1344 +e7 1235fc +a6 11007a +ef 123753 +f7 123f5d +b6 1109db +ff 1240b4 +c7 1277a3 +cf 1278fa +d7 128104 +df 12825b +67 1ec6f3 +26 1d9171 +6f 1ec84a +77 1ed054 +85 263913 +36 1d9ad2 +7f 1ed1ab +47 1f089a +57 1f11fb +67 1ec93f +77 1ed2a0 +73 1ed2d1 +43 1f0b17 +57 1f1447 +53 1f1478 +f7 1241fd +43 1e8765 +f5 124204 +1e 1cd97e +12 3a346 +c7 127a43 +d7 1283a4 +d5 1283ab +77 1ed2f4 +47 1f0b3a +ce 2b2ca7 +c2 11f66f +ff 2b623e +45 1f0b41 +57 1f149b +de 2b3608 +d2 11ffd0 +55 1f14a2 +f7 12420b +d7 1283b2 +77 1ed302 +47 1f0b48 +57 1f14a9 +67 1ed953 +63 5a472 +6f 1edaaa +47 1f1afa +e5 1248b7 +ed 124a0e +12 3b35a +c7 128a57 +7a 1f54c7 +c5 128a5e +cd 128bb5 +65 1ed9ae +63 5a4c6 +1d 43ada +6f 1edafe +61 5a4cd +6d 1edb05 +47 1f1b4e +c2 120683 +45 1f1b55 +43 5e66d +4f 1f1ca5 +e7 1248be +c7 128a65 +67 1ed9b5 +63 5a4d4 +6f 1edb0c +47 1f1b5c +47 1f1da8 +12 3b608 +c7 128d05 +67 1edc55 +65 1edc5c +47 1f1dfc +75 6314b +c2 120931 +f3 123ec8 +ff 2b7500 +45 1f1e03 +67 1edc63 +47 1f1e0a +18 3a48a +1c 3a4bb +98 103581 +9c 1035b2 +98 1035e3 +3d 3f925 +39 3e382 +8 3adeb +b9 107479 +88 103ee2 +bd 1074aa +8c 103f13 +b9 1074db +88 103f44 +29 3dcd1 +39 3e632 +3d 3e663 +19 427d9 +1d 4280a +a9 106dc8 +ad 106df9 +b9 107729 +bd 10775a +89 10af6f +99 10b8d0 +9d 10b901 +39 3e694 +a9 106e2a +b9 10778b +29 3ef93 +9 4313a +a9 10808a +ad 1080bb +1c 3a20d +89 10c231 +29 3eff5 +a9 1080ec +4a 566c1 +4c 566f9 +70 1ed019 +2f 1d8007 +de 278976 +5e 57053 +5c 5705a +39 19716e +ca 11f7b8 +3d 19719f +ce 11f7e9 +cc 11f7f0 +da 120119 +4a 566cf +5a 57030 +5e 57061 +ce 11f7f7 +5d 1f15f9 +da 120127 +4a 56723 +5a 57084 +ca 11f81a +35 1d87a8 +6f 5bb5c +4a 56731 +ca 11f828 +6f 5bb6a +da 120189 +8b 10acd4 +7f 5c4cb +7b 5af1a +10 1d45cf +4a 57983 +7f 5af4b +14 1d4600 +4e 579b4 +ff 124042 +3d 198461 +94 29d6f7 +31 4e29 +ce 120aab +7f 5af59 +4e 579c2 +10 1d4631 +7b 5af7c +4a 579e5 +b6 110c27 +ff 124300 +79 5af83 +48 579ec +fb 124073 +90 29d728 +ca 120adc +7b 5af8a +4a 579f3 +fb 124081 +ca 120aea +54 1b8fd1 +22 47190 +6b 5a869 +26 471c1 +6f 5a89a +36 47b22 +7f 5b1fb +4b 5ea10 +49 5ea17 +4f 5ea41 +4d 5ea48 +40 1f1dd1 +71 1f5368 +df 280cc5 +5f 5f3a2 +a6 1102b8 +ef 123991 +b6 110c19 +ff 1242f2 +b4 110c20 +fd 1242f9 +cb 127b07 +c9 127b0e +cf 127b38 +cd 127b3f +db 128468 +e2 2bedba +d9 12846f +df 128499 +e6 2bedeb +dd 1284a0 +54 1b8fdf +22 4719e +6b 5a877 +26 471cf +6f 5a8a8 +32 47aff +7b 5b1d8 +36 47b30 +7f 5b209 +ac 260b84 +a0 cd54c +4b 5ea1e +a4 cd57d +4f 5ea4f +b0 cdead +bc 2614e5 +5b 5f37f +b4 cdede +5f 5f3b0 +a6 1102c6 +ef 12399f +cb 127b15 +cf 127b46 +db 128476 +df 1284a7 +22 471f2 +54 1b9033 +6b 5a8cb +20 471f9 +69 5a8d2 +5a 1e8f6e +fb 27cb4f +5b 5f3d3 +cb 127b69 +22 47200 +54 1b9041 +6b 5a8d9 +cb 127b77 +4b 5fcd2 +49 5fcd9 +4f 5fd03 +b5 2a189f +ef 124c53 +ed 124c5a +5e 56da5 +cb 128dc9 +a4 ce83f +4f 5fd11 +69 5bb94 +e9 124c8b +cb 128e2b +4 2df +6b 5bb9b +84 c93d6 +eb 124c92 +cb 128e39 +9c 103614 +1c 1cc70b +bd 10750c +f7 2b60db +8c 103f75 +3d 1d0603 +0 39a34 +c 1cd06c +9 1d40c8 +19 1d4a29 +b 1cbd1d +bd 1077bc +8d 10b002 +9d 10b963 +3d 1d08b3 +8a 102c27 +d 1d40f9 +9a 103588 +1d 1d4a5a +29 1d11e3 +9 1d538a +8d 10c2c4 +21 3dbdc +2d 1d1214 +bb 107480 +1 41d83 +8a 103ee9 +d 1d53bb +4e 1e88e0 +5e 1e9241 +7e 5c4ba +5c 1e9248 +5a 1e9272 +5e 1e924f +cc 11f852 +5e 1e92a3 +5c 1e92aa +ce 11f859 +5e 1e92b1 +ff 1240a4 +94 29d759 +ce 120b0d +fd 1240ab +cc 120b14 +ff 1240b2 +ce 120b1b +26 1d93af +6f 1eca88 +24 1d93b6 +6d 1eca8f +36 1d9d10 +7f 1ed3e9 +4f 1f0c2f +6b 63ed9 +49 1f0c67 +5b 1f15c1 +26 1d93bd +6f 1eca96 +36 1d9d1e +7f 1ed3f7 +a6 11031a +ef 1239f3 +a4 110321 +5 42d66 +ed 1239fa +b6 110c7b +ff 124354 +4b 1e88bc +b4 110c82 +15 436c7 +2 1d51e3 +fd 12435b +1a 3a49d +cf 127b9a +cd 127ba1 +df 1284fb +dd 128502 +36 1d9d72 +7f 1ed44b +34 1d9d79 +7d 1ed452 +4f 1f0c91 +7 42d6d +a6 110328 +ef 123a01 +b6 110c89 +17 436ce +ff 124362 +cf 127ba8 +df 128509 +36 1d9d80 +7f 1ed459 +63 5a712 +6f 1edd4a +69 1edd82 +43 5e8b9 +4f 1f1ef1 +49 1f1f29 +b5 2a1901 +ef 124cb5 +ed 124cbc +1a 3b75f +cf 128e5c +cd 128e63 +63 5a774 +6f 1eddac +61 5a77b +6d 1eddb3 +43 5e91b +4f 1f1f53 +7d 632a2 +ef 124cc3 +cf 128e6a +63 5a782 +6f 1eddba +e3 2bdaf7 +10 3b347 +e7 2bdb28 +14 3b378 +eb 2bdc4e +18 3b49e +ef 2bdc7f +1c 3b4cf +90 10443e +94 10446f +98 104595 +0 1d3f7c +9c 1045c6 +10 3b3a9 +18 3b500 +90 1044a0 +98 1045f7 +e3 2bdda5 +10 3b5f5 +e7 2bddd6 +14 3b626 +90 1046ec +94 10471d +10 3b657 +90 10474e +14 426c0 +66 1ec6e4 +31 3f4ef +1c 42817 +6e 1ec83b +39 3f646 +94 10b7b7 +e6 2b57db +b1 1085e6 +9c 10b90e +ee 2b5932 +b9 10873d +39 3f6a8 +9c 10b970 +b9 10879f +66 1ec992 +31 3f79d +46 1f0b39 +11 43944 +15 43975 +e6 2b5a89 +b1 108894 +b5 1088c5 +c6 2b9c30 +91 10ca3b +31 3f7ff +b1 1088f6 +8b ca57a +d6 121007 +89 ca581 +d4 12100e +da 12112d +52 57eed +d6 121015 +35 1d19bc +52 57f41 +50 57f48 +58 5809f +35 1d19ca +52 57f4f +b5 29aac1 +d2 121046 +52 5819b +52 581ef +50 581f6 +52 581fd +d2 1212f4 +55 60266 +f7 1251af +f5 1251b6 +de 1284a6 +fb 1252d5 +f9 1252dc +fd 12530d +d1 12932c +d7 129356 +d5 12935d +db 12947c +d9 129483 +dd 1294b4 +f3 12518c +f7 1251bd +fb 1252e3 +d3 129333 +d7 129364 +db 12948a +79 5c247 +f9 12533e +57 6050d +f1 125433 +f7 12545d +73 5c343 +53 604ea +57 6051b +f3 12543a +f7 12546b +d3 1295e1 +53 6053e +8b 29e1e6 +73 5c3a5 +f 39b60 +53 6054c +f3 12549c +14 1cd566 +10 3a085 +1c 1cd6bd +18 1cd6ee +94 1044d1 +9c 104628 +10 3a0e7 +1c 1cd71f +14 1cd814 +cb 2b1a15 +82 29e33c +95 10c820 +31 3e28f +3d 1d18c7 +92 104445 +15 1d5917 +11 42436 +9a 10459c +1d 1d5a6e +31 1d19ed +15 1d5b63 +11 1d5b94 +3 1cce88 +b5 108927 +95 10cace +92 1046f3 +15 1d5bc5 +b 193671 +56 1ea0fe +52 56c1d +5e 1ea255 +52 1ea13d +52 56c2b +5e 1ea263 +31 1d06d7 +5a 1ea294 +d6 121069 +d4 121070 +56 1ea160 +54 1ea167 +35 1d06fa +52 56c7f +5e 1ea2b7 +d6 121077 +56 1ea16e +b 19391f +56 1ea3ac +52 1ea3eb +d6 121317 +56 1ea40e +54 1ea415 +56 1ea41c +f6 2bfa08 +57 1f244d +77 1ee2b4 +73 5add3 +7f 1ee40b +57 1f245b +43 1e9779 +f5 125218 +4b 1e98d0 +fd 12536f +d5 1293bf +27 5752 +dd 129516 +77 1ee308 +73 5ae27 +7f 1ee45f +71 5ae2e +7d 1ee466 +57 1f24af +d2 120fe4 +55 1f24b6 +53 5efce +5f 1f2606 +51 5efd5 +da 12113b +5d 1f260d +a7 299f1b +f7 12521f +d7 1293c6 +77 1ee316 +85 264bd5 +73 5ae35 +7f 1ee46d +57 1f24bd +53 5efdc +b4 261142 +5f 1f2614 +71 1ee58c +57 1f26fb +73 1ee593 +57 1f2709 +f 1cbd4e +53 1f273a +f7 1254bf +d7 129666 +d5 12966d +83 29cdbf +77 1ee5b6 +57 1f275d +d2 121292 +55 1f2764 +f7 1254cd +d7 129674 +83 29cdcd +77 1ee5c4 +57 1f276b +eb 2bdefc +18 3b74c +ef 2bdf2d +1c 3b77d +98 104843 +9c 104874 +18 3b7ae +98 1048a5 +6e 1ecae9 +39 3f8f4 +4e 1f0c90 +19 43a9b +1d 43acc +ee 2b5be0 +b9 1089eb +bd 108a1c +ce 2b9d87 +99 10cb92 +39 3f956 +b9 108a4d +5a 582e4 +ea 1236bf +6d 1f4b91 +5e 58315 +da 1213db +5a 582f2 +5e 58323 +51 5f283 +5d 1f28bb +da 1213e9 +5a 58346 +da 12144b +5b 60633 +5f 60664 +fb 125583 +fd 1255bb +db 12972a +7b 5c49a +b0 cf16f +5b 60641 +b4 cf1a0 +5f 60672 +fb 125591 +ff 1255c2 +5b 60695 +46 1f1b4d +f9 1255ec +94 c9d37 +fb 1255f3 +18 1cd99c +9c 1048d6 +10 3a395 +1c 1cd9cd +39 1d1b44 +19 1d5ceb +b 1ccfdf +bd 108a7e +9d 10cc25 +31 3e53d +3d 1d1b75 +11 426e4 +9a 10484a +1d 1d5d1c +52 56ecb +5e 1ea503 +52 56ed9 +5e 1ea511 +52 56f2d +5e 1ea565 +50 56f34 +5c 1ea56c +52 56f3b +5e 1ea573 +73 5b073 +7f 1ee6ab +5b 1f2883 +4b 1e9b7e +fd 12561d +df 1297bd +dd 1297c4 +73 5b0d5 +8b 29cf16 +7f 1ee70d +89 29cf1d +71 5b0dc +7d 1ee714 +ff 125624 +df 1297cb +8b 29cf24 +73 5b0e3 +7f 1ee71b +3d 1d9bb3 +31 4657b +4 39761 +a5 cd342 +39 466d2 +bd 2a2caa +b1 10f672 +b5 10f6a3 +84 102858 +b9 10f7c9 +bd 10f7fa +d8 e6742 +29 45dd3 +39 46734 +a9 10eeca +84 1028ba +b9 10f82b +4a 5fa85 +2d 1d9500 +21 45ec8 +5a 603e6 +3d 1d9e61 +31 46829 +35 4685a +ad 2a25f7 +ca 128b7c +a1 10efbf +ce 128bad +a5 10eff0 +bd 2a2f58 +da 1294dd +b1 10f920 +de 12950e +b5 10f951 +bd 2a2fba +b1 10f982 +21 46edc +29 47033 +a1 10ffd3 +a5 110004 +79 64843 +a9 11012a +ad 11015b +d8 e7a04 +29 47095 +a9 11018c +21 4718a +34 3e25d +23 19639f +a1 110281 +d0 e7b5b +21 471ec +23 196401 +a1 1102e3 +6f 1f5dea +63 627b2 +67 627e3 +65 627ea +6b 62909 +6f 6293a +6d 62941 +77 63144 +e7 e9eda +46 562f9 +7b 6326a +7f 6329b +ef 2beee1 +e3 12b8a9 +e7 12b8da +e5 12b8e1 +eb 12ba00 +e9 12ba07 +ef 12ba31 +ed 12ba38 +f7 12c23b +f5 12c242 +c6 11f3f0 +fb 12c361 +f9 12c368 +ff 12c392 +fd 12c399 +6f 1f5df8 +63 627c0 +67 627f1 +6f 62948 +7f 1f6759 +73 63121 +77 63152 +7f 632a9 +e7 12b8e8 +ef 12ba3f +6b 6296b +7b 632cc +eb 12ba62 +e9 12ba69 +c6 11f452 +fb 12c3c3 +f9 12c3ca +6f 1f6098 +63 62a60 +67 62a91 +7d 1f6a00 +ae d6aa1 +71 633c8 +77 633f2 +ef 2bf18f +e3 12bb57 +ed 2bf196 +e1 12bb5e +e7 12bb88 +fd 2bfaf7 +8 477 +f1 12c4bf +f7 12c4e9 +67 62a9f +7f 1f6a07 +73 633cf +77 63400 +e7 12bb96 +ff 2bfafe +f3 12c4c6 +ff 2bfb60 +f3 12c528 +69 63bd2 +e9 12ccc9 +ed 12ccfa +69 63c34 +e9 12cd2b +61 63d29 +63 63d84 +63 1b2fa0 +e1 12ce82 +63 63d92 +e3 12ce89 +29 1d7fc1 +35 1d879a +3d 1d88f1 +4 1cb9b1 +39 1d8922 +a5 10eda4 +ad 10eefb +3 1d3c66 +b5 10f705 +b 1d3dbd +bd 10f85c +a2 1069c9 +ae 29a001 +d4 27880a +25 1d7e9b +aa 106b20 +dc 278961 +2d 1d7ff2 +b2 10732a +be 29a962 +35 1d87fc +ba 107481 +3d 1d8953 +21 1d8118 +52 5efcd +5e 1f2605 +35 1d8a48 +31 1d8a79 +29 1d9283 +d4 279acc +a2 107c8b +25 1d915d +57 1b8fd7 +3 1d3f24 +dc 279c23 +21 45c7c +aa 107de2 +d0 e65eb +2d 1d92b4 +53 25af6 +5f 1b912e +b 1d407b +d4 279d7a +a2 107f39 +25 1d940b +dc e6771 +67 1f49d1 +1 3a9f3 +6b 1f4b59 +77 1f5332 +7f 1f5489 +11 3b354 +46 1e8549 +7b 1f54ba +dc e677f +67 1f49df +6f 1f4b36 +6b 1f4b67 +77 1f5340 +7f 1f5497 +7b 1f54c8 +32 3e23f +3e 1d1877 +e7 12b93c +e5 12b943 +3a 3e396 +ef 12ba93 +ed 12ba9a +f7 12c29d +43 1f0805 +f5 12c2a4 +ff 12c3f4 +4b 1f095c +fd 12c3fb +5 3aa24 +6f 1f4b8a +15 3b385 +7f 1f54eb +fa 124020 +7d 1f54f2 +e7 12b94a +ef 12baa1 +f7 12c2ab +ff 12c402 +dc e67e1 +67 1f4a41 +6f 1f4b98 +77 1f53a2 +7f 1f54f9 +dc e6a1f +67 1f4c7f +d8 e6a50 +63 1f4cb0 +61 1f4cb7 +77 1f55e0 +dc e6a2d +67 1f4c8d +d8 e6a5e +63 1f4cbe +77 1f55ee +73 1f561f +f7 12c54b +ee 2b6e4e +e2 123816 +65 1f4ce8 +77 1f5642 +f2 124177 +fe 2b77af +75 1f5649 +f7 12c559 +dc e6a8f +67 1f4cef +77 1f5650 +6b 1f5e1b +4 19a56d +6b 1f5e29 +32 3f501 +e7 12cbfe +e5 12cc05 +ed 12cd5c +e2 12482a +65 1f5cfc +63 62814 +6f 1f5e4c +61 6281b +ea 124981 +6d 1f5e53 +e7 12cc0c +dc e7aa3 +67 1f5d03 +63 62822 +6f 1f5e5a +32 3f7af +e7 12ceac +e2 124ad8 +65 1f5faa +d7 ee970 +8f 29cef5 +dc e7d51 +67 1f5fb1 +29 4601f +4 39a0f +a5 cd5f0 +39 46980 +3d 469b1 +a9 10f116 +ad 10f147 +84 102b06 +b9 10fa77 +bd 10faa8 +d8 e69f0 +29 46081 +39 469e2 +a9 10f178 +84 102b68 +b9 10fad9 +29 472e1 +3c 3e3b4 +2b 1964f6 +a9 1103d8 +d8 e7cb2 +29 47343 +2b 196558 +a9 11043a +6b 62bb7 +6f 62be8 +7f 63549 +ef 12bcdf +ff 12c640 +6f 62bf6 +7f 63557 +ef 12bced +7b 6357a +69 63e80 +69 63ee2 +6b 1b30f7 +e9 12cfd9 +84 d1724 +eb 12cfe0 +29 1d826f +4 1cbc5f +39 1d8bd0 +ad 10f1a9 +b 1d406b +bd 10fb0a +aa 106dce +dc 278c0f +2d 1d82a0 +ba 10772f +3d 1d8c01 +29 1d9531 +dc 279ed1 +21 45f2a +aa 108090 +d0 e6899 +2d 1d9562 +6f 1f4dd6 +69 1f4e0e +7f 1f5737 +11 3b602 +46 1e87f7 +7b 1f5768 +6f 1f4de4 +7f 1f5745 +7b 1f5776 +3a 3e644 +ef 12bd41 +ed 12bd48 +4b 1f0c0a +fd 12c6a9 +ea 12396d +6d 1f4e3f +15 3b633 +7f 1f5799 +fa 1242ce +7d 1f57a0 +ef 12bd4f +ff 12c6b0 +6f 1f4e46 +7f 1f57a7 +69 1f60d0 +3a 3f906 +ef 12d003 +6f 1b3128 +ed 12d00a +63 62ac2 +6f 1f60fa +61 62ac9 +ea 124c2f +6d 1f6101 +ef 12d011 +63 62ad0 +6f 1f6108 +b5 110965 +bd 110abc +84 103b7c +b9 110aed +66 1f4ce0 +31 47aeb +e6 2bddd7 +33 196d00 +b1 110be2 +31 47b4d +33 196d62 +b1 110c44 +f7 12d4fd +c6 1206b2 +fb 12d623 +73 643e3 +f3 12d4da +f7 12d50b +fb 12d631 +71 6468a +73 64691 +f3 12d788 +73 646e5 +73 1b3901 +ad 106df7 +f1 12d7e3 +2f 3dd07 +73 646f3 +af 106dfe +f3 12d7ea +4 1ccc73 +39 1d9be4 +b2 1085ec +35 1d9abe +13 1d4885 +31 465dd +ba 108743 +3d 1d9c15 +1b 1d49dc +31 1d9d3b +37 196d93 +3 1d51d6 +b5 110c75 +b2 10889a +35 1d9d6c +46 1e980b +7b 1f677c +14 19aece +7b 1f678a +f7 12d55f +43 1f1ac7 +f5 12d566 +98 103343 +77 1f6656 +f2 12518b +75 1f665d +73 63175 +7f 1f67ad +71 6317c +fa 1252e2 +7d 1f67b4 +f7 12d56d +77 1f6664 +73 63183 +7f 1f67bb +2f 1cfef5 +73 1f68e1 +f7 12d80d +77 1b3932 +43 1f1d75 +f5 12d814 +98 1035f1 +77 1f6904 +f2 125439 +75 1f690b +9f 29d856 +f7 12d81b +77 1f6912 +4 3acd1 +a5 ce8b2 +6e 1f4e37 +39 47c42 +3b 196e57 +84 103dc8 +ee 2bdf2e +b9 110d39 +39 47ca4 +3b 196eb9 +84 103e2a +b9 110d9b +79 647e1 +c6 120960 +fb 12d8d1 +7b 1b39f6 +f9 12d8d8 +7b 647e8 +fb 12d8df +7b 1b3a58 +f9 12d93a +94 d2085 +fb 12d941 +4 1ccf21 +39 1d9e92 +3f 196eea +b 1d532d +bd 110dcc +ba 1089f1 +31 4688b +3d 1d9ec3 +46 1e9ab9 +7b 1f6a2a +14 19b17c +7b 1f6a38 +7f 1b3a89 +4b 1f1ecc +fd 12d96b +73 63423 +7f 1f6a5b +fa 125590 +71 6342a +7d 1f6a62 +ff 12d972 +73 63431 +7f 1f6a69 +29 1964fd +2b d916 +9 19a6a4 +29 1977bf +9 19b966 +c8 128dcf +f9 12c366 +4a 1aeeed +d0 1295d9 +18 b28 +52 1af6f7 +56 1b09da +2 1cb927 +12 1cc288 +1a 1cc3df +f9 12d628 +4a 1b01af +2 1ccbe9 +e1 12ce20 +29 436f +74 5adfc +63 1b2f3e +7c 5af53 +e9 12cf77 +6b 1b3095 +9 8516 +54 5efa3 +43 1b70e5 +5c 5f0fa +4b 1b723c +77 1b4b82 +23 1cfacf +73 216a1 +7f 1b4cd9 +42 1e10a +2b 1cfc26 +33 1d0430 +52 1ea6b +3b 1d0587 +57 1b8d29 +3 1d3c76 +53 25848 +5f 1b8e80 +b 1d3dcd +13 1d45d7 +1b 1d472e +77 1b4e30 +23 1cfd7d +33 1d06de +74 5c0be +29 5631 +63 1b4200 +7c 5c215 +6b 1b4357 +9 97d8 +54 60265 +43 1b83a7 +5c 603bc +4b 1b84fe +73 5b071 +7f 1ee6a9 +8e 25b4d8 +96 25bce2 +9c 25be32 +9e 25be39 +cc 278062 +ce 278069 +9 19bc14 +be cf0b2 +9c 25be40 +ee eb2e2 +cc 278070 +a6 ce846 +84 25b5d4 +58 26c59 +96 25bf90 +18 1d49c6 +a6 ce8a8 +84 25b636 +e6 eb439 +c4 2781c7 +ce e6135 +80 25c619 +88 25c770 +86 25c643 +c6 2791d4 +c0 e5cec +cc 279324 +c2 e5cf3 +a5 25f76e +ce 27932b +18 1d59da +84 25c64a +71 5c33a +80 c9169 +8c 25c7a1 +c0 e5cfa +cc 279332 +a5 25f4ce +0 1cb91e +7 8387 +a1 25f4ff +ad 25f625 +f 84de +8 1cba75 +a9 25f656 +1f 8e3f +18 1cc3d6 +b9 25ffb7 +af d6a3e +8d 2637cc +bf d739f +9d 26412d +c 1cba98 +50 1f2484 +ad 25f679 +1c 1cc3f9 +bd 25ffda +a0 299ed2 +85 2636c9 +99 10358e +a2 299ed9 +87 2636d0 +a8 29a029 +8d 263820 +d0 278839 +aa 29a030 +8f 263827 +b8 29a98a +9d 264181 +ba 29a991 +9f 264188 +e5 27c0b3 +a4 268b31 +ed 27c20a +9d d21ed +4e 1e8630 +b8 1089f6 +a6 268b38 +ef 27c211 +b4 269492 +fd 27cb6b +10 192b5d +c5 28025a +d9 12011f +e2 2b6a6a +90 10ca46 +c7 280261 +18 192cb4 +cd 2803b1 +ab 10eed1 +dd 280d12 +1c 1cc407 +bd 25ffe8 +4c 1e8637 +a4 268b3f +ed 27c218 +5c 1e8f98 +b4 2694a0 +fd 27cb79 +e8 2b6bc8 +ef f3631 +cd 2803bf +ab 10eedf +ff f3f92 +f8 2b7529 +dd 280d20 +0 1cbbcc +7 8635 +a1 25f7ad +b7 d74f6 +95 264284 +e5 27c361 +10 192e0b +c5 280508 +39 1d8b6e +4 1cbbfd +a5 25f7de +b7 d7558 +11 1cd534 +b0 29aaef +95 2642e6 +44 1e878e +e5 27c36f +e7 f3788 +e0 2b6d1f +c5 280516 +af 2a266e +f0 2b7680 +f7 f40e9 +a3 10f036 +d5 280e77 +a5 260790 +0 1ccbe0 +ef ea2dd +7 9649 +a1 2607c1 +a1 cd2af +ad 2608e7 +8 1ccd37 +f 97a0 +a9 260918 +81 d1456 +8d 264a8e +4 1ccc03 +a5 2607e4 +c 1ccd5a +0 39722 +a1 cd303 +ad 26093b +cc 2b1a3e +85 26498b +99 104850 +ce 2b1a45 +87 264992 +81 d14aa +8d 264ae2 +83 d14b1 +d0 279afb +8f 264ae9 +e5 27d375 +95 d3358 +46 1e979b +e7 27d37c +e1 e9e94 +ed 27d4cc +9d d34af +4e 1e98f2 +42 562ba +e3 e9e9b +ef 27d4d3 +10 193e1f +c5 28151c +d9 1213e1 +c7 281523 +c1 ee03b +18 193f76 +cd 281673 +c3 ee042 +cf 28167a +44 1e97a2 +e5 27d383 +4c 1e98f9 +40 562c1 +e1 e9ea2 +ed 27d4da +0 1cce8e +7 98f7 +a1 260a6f +4 1cceb1 +a5 260a92 +e5 27d623 +39 1d9e30 +4 1ccebf +a5 260aa0 +44 1e9a50 +e5 27d631 +c5 2817d8 +86 294a4f +86 294aa3 +a6 107d1c +84 294aaa +ae 107e73 +d4 e667c +8c 294c01 +96 295404 +9e 29555b +be 1087d4 +9c 295562 +d6 278881 +de 2789d8 +8c c959d +c6 27816c +9c c9efe +27 1d815e +d6 278acd +a6 107fca +84 294d58 +c4 e6feb +f9 f3f5c +96 2956b2 +5a 1f155e +c6 2781ce +d6 278b2f +48 5fa28 +e9 f3609 +86 294d5f +58 60389 +f9 f3f6a +96 2956c0 +ca 279308 +86 295d11 +82 295d42 +d6 e78e3 +82 102830 +5 1d3d02 +8e 295e68 +d2 e7914 +b5 26138f +8a 295e99 +86 295d65 +84 295d6c +80 10288b +d4 e793e +8c 295ec3 +ad cd497 +e7 27c066 +f4 123f55 +42 1e84b6 +e3 27c097 +ef 27c1bd +4a 1e860d +fc 1240ac +eb 27c1ee +d4 1280fc +c3 28023e +26 448f +dc 128253 +cb 280395 +c6 e7232 +f7 ea7c9 +af 298d4e +87 29cd9e +97 29d6ff +a7 298c4b +b7 2995ac +bf 299703 +87 29cdf2 +8f 29cf49 +20 19e9a1 +97 29d753 +28 19eaf8 +9f 29d8aa +bf 110b23 +9d 29d8b1 +5e 1e8f9f +ff 27cb80 +f2 2b73d9 +d7 280bd0 +fa 2b7530 +df 280d27 +b7 2995ba +d4 1283aa +c3 2804ec +a5 298f00 +a7 110319 +85 29d0a7 +ad ce759 +e7 27d328 +f4 125217 +42 1e9778 +e3 27d359 +e3 e9e47 +ef 27d47f +4a 1e98cf +fc 12536e +eb 27d4b0 +d4 1293be +c3 281500 +26 5751 +dc 129515 +cb 281657 +83 29e091 +d3 efc63 +8b 29e1e8 +a7 299f0d +a3 106a2c +af 29a064 +87 29e0b4 +83 10abd3 +8f 29e20b +4e 1e9900 +42 562c8 +e3 e9ea9 +ef 27d4e1 +87 29e0c2 +d7 efc94 +83 10abe1 +d0 2b322b +8f 29e219 +56 1af78a +d4 12966c +c3 2817ae +16 1cc31b +83 29e33f +a5 29a1c2 +50 1b09a4 +12 193e1a +18 1ddc +52 1b09ab +58 1b0afb +12 1cd53c +1a 1cd693 +b9 10fa75 +a 1925fc +48 1af186 +4a 1af18d +58 1afae7 +2a 5875 +8 192603 +6a 22406 +48 1af194 +20 19fc55 +69 1b332e +22 19fc5c +6b 1b3335 +30 1a05b6 +79 1b3c8f +49 1b74d5 +4b 1b74dc +59 1b7e36 +29 1967ab +2b dbc4 +9 19a952 +6b 2a755 +49 1b74e3 +3c 3f924 +2b 197a66 +9 19bc06 +1c 43acb +b 19bc0d +49 1b8797 +4b 1b879e +49 1b87a5 +7d 1b4f80 +71 21948 +29 1cfecd +39 1d082e +2b 472e6 +51 25aef +5d 1b9127 +9 1d4074 +3b 47c47 +19 1d49d5 +7c 5b201 +6b 1b3343 +7f 1b4f87 +73 2194f +2b 1cfed4 +3b 1d0835 +71 22c0a +29 1d118f +51 26db1 +9 1d5336 +88 10accc +7c 5c4c3 +6b 1b4605 +5c 6066a +4b 1b87ac +30 1d0728 +9e 25c085 +cc 2782ae +ce 2782b5 +ae ce99d +8c 25b72b +ee eb52e +cc 2782bc +9e 25c0e7 +cc 278310 +ce 278317 +ee eb590 +cc 27831e +a 1cbd1c +bc 1077bb +ab 25f8fd +da e67ab +bd 260226 +18 1cc676 +b9 260257 +1a 1cc67d +bb 26025e +89 263a9d +9c 10b962 +8b 263aa4 +a4 268d7d +ed 27c456 +a0 268dae +e9 27c487 +a2 268db5 +4a 1e88ad +eb 27c48e +b4 2696de +fd 27cdb7 +b0 26970f +f9 27cde8 +5a 1e920e +b2 269716 +fb 27cdef +cd 2805fd +c9 28062e +cb 280635 +f 878c +8 1cbd23 +a9 25f904 +1f 90ed +18 1cc684 +b9 260265 +af d6cec +8d 263a7a +a0 268dbc +48 1e88b4 +4f 2531d +1 19b801 +e9 27c495 +ef f387d +cd 28060b +eb f38ae +c9 28063c +c 1cbd46 +50 1f2732 +ad 25f927 +1c 1cc6a7 +bd 260288 +a4 268ddf +ed 27c4b8 +b4 269740 +fd 27ce19 +18 192f62 +cd 28065f +1c 1cc6b5 +bd 260296 +a4 268ded +5 19b832 +4c 1e88e5 +ed 27c4c6 +5c 1e9246 +15 19c193 +b4 26974e +fd 27ce27 +ef f38df +e8 2b6e76 +cd 28066d +a 1ccfde +bc 108a7d +ab 260bbf +81 d16f6 +8d 264d2e +e9 27d749 +2e 3da5a +c1 ee287 +cd 2818bf +5c 1af8cc +2a 3da8b +c9 2818f0 +f 9a4e +8 1ccfe5 +a9 260bc6 +81 d1704 +8d 264d3c +48 1e9b76 +4f 265df +e9 27d757 +2e 3da68 +c1 ee295 +cd 2818cd +5c 1af8da +2a 3da99 +c9 2818fe +0 399d0 +a1 cd5b1 +c 1cd008 +ad 260be9 +e1 ea142 +ed 27d77a +c1 ee2e9 +18 194224 +cd 281921 +0 399de +c 1cd016 +a1 cd5bf +ad 260bf7 +40 5656f +e1 ea150 +4c 1e9ba7 +ed 27d788 +c1 ee2f7 +cd 28192f +cc e70e0 +9e 2957a7 +60 1ec966 +ce 2782c3 +70 1ed2c7 +2f 1d82b5 +de 278c24 +ae 108121 +d4 e692a +8c 294eaf +cc e7142 +9e 295809 +be 108a82 +9c 295810 +ce 278325 +de 278c86 +d6 e6931 +8e 294eb6 +9e 295817 +ab 29901f +f 41eae +f1 eaaa1 +fd 27e0d9 +a9 299026 +e9 eb2b9 +bb 299980 +1f 4280f +b9 299987 +af 11040e +d5 eec17 +8d 29d19c +8b 29d1c6 +d1 eec48 +ab 11043f +dd 282280 +89 29d1cd +bf 110d6f +9d 29dafd +dc 128501 +cb 280643 +f5 eaad2 +ad 299057 +bd 2999b8 +af 110470 +d5 eec79 +8d 29d1fe +bf 110dd1 +9d 29db5f +ab 29a2e1 +f1 ebd63 +f 43170 +a9 29a2e8 +d5 efed9 +81 10ae26 +8d 29e45e +5e 1af8e1 +dc 1297c3 +cb 281905 +f5 ebd94 +a1 106ce1 +ad 29a319 +50 1b0c52 +14 19b18c +7f 21ad7 +31 197fbb +e6 2bf099 +33 197fc2 +58 1e9215 +5f 25c7e +b0 26971d +f9 27cdf6 +11 19c162 +5a 1e921c +fb 27cdfd +13 19c169 +39 5f84 +56 1b7d24 +73 1b4b53 +f8 286405 +59 1b8e4a diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/sha_table_generators.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/sha_table_generators.hpp new file mode 100644 index 000000000..8c398a0c8 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/sha_table_generators.hpp @@ -0,0 +1,347 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + // Works only for small-ish powers + template + struct SumHash { + using integral_type = typename BlueprintFieldType::integral_type; + std::size_t operator()(const std::pair &a) const { + return std::size_t((a.first << Power) + a.second); + } + }; + + template + void print_sha_table_to_stream( + const std::unordered_set, + SumHash> &input, + std::ostream &stream) { + using value_type = typename BlueprintFieldType::value_type; + using Endianness = nil::marshalling::option::big_endian; + using TTypeBase = nil::marshalling::field_type; + using marshalling_value_type = crypto3::marshalling::types::field_element; + stream << input.size() << std::endl; + for (const auto &[preimage, image] : input) { + std::vector pair = { + marshalling_value_type(preimage), + marshalling_value_type(image)}; + stream << std::hex << pair[0].value() << " " << std::hex << pair[1].value() << std::endl; + } + } + + template + void base4_reverse_table_iter( + std::unordered_set, + SumHash> &output_set, + const typename BlueprintFieldType::integral_type &i, + const std::size_t table_size, + std::mutex &set_guard) { + + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + + static const std::vector a_sizes = {3, 4, 11, 14}; + static const std::vector b_sizes = {10, 7, 2, 13}; + static const std::vector c_sizes = {2, 11, 9, 10}; + static const std::vector sigma_sizes = {8, 8, 8, 8}; + static const integral_type one = 1; + static const std::array a_mult = { + (one << 50) + (1 << 28), + 1 + (one << 56) + (one << 34), + (1 << 8) + 1 + (one << 42), + (1 << 30) + (1 << 22) + 1, + }; + static const std::array b_mult = { + (1 << 30) + (1 << 26), + 1 + (one << 50) + (one << 46), + (1 << 14) + 1 + (one << 60), + (1 << 18) + (1 << 4) + 1 + }; + static const std::array sigma_mult = { + (one << 38) + (1 << 20) + (one << 60), + (one << 42) + 1 + (1 << 24), + (1 << 22) + (one << 46) + 1, + (one << 40) + (1 << 18) + 1 + }; + static const std::size_t base4 = 4; + static const value_type base4_value = value_type(base4); + + std::vector value(table_size); + for (std::size_t j = 0; j < table_size; j++) { + value[table_size - j - 1] = boost::multiprecision::bit_test(i, j); + } + // s0 + const std::array, 2> a_chunks = + nil::blueprint::components::detail::split_and_sparse(value, a_sizes, base4); + integral_type sparse_sigma0 = + a_chunks[1][1] * a_mult[1] + + a_chunks[1][2] * a_mult[2] + + a_chunks[1][3] * a_mult[3] + + a_chunks[1][0] * a_mult[0]; + const std::array, 2> sigma0_chunks = + nil::blueprint::components::detail::reversed_sparse_and_split( + sparse_sigma0, sigma_sizes, base4); + // s1 + const std::array, 2> b_chunks = + nil::blueprint::components::detail::split_and_sparse(value, b_sizes, base4); + + integral_type sparse_sigma1 = + b_chunks[1][1] * b_mult[1] + + b_chunks[1][2] * b_mult[2] + + b_chunks[1][3] * b_mult[3] + + b_chunks[1][0] * b_mult[0]; + + const std::array, 2> sigma1_chunks = + nil::blueprint::components::detail::reversed_sparse_and_split(sparse_sigma1, sigma_sizes, base4); + // S0 + const std::array, 2> c_chunks = + nil::blueprint::components::detail::split_and_sparse( + value, c_sizes, base4); + integral_type sparse_Sigma0 = + c_chunks[1][0] * sigma_mult[0] + + c_chunks[1][1] * sigma_mult[1] + + c_chunks[1][2] * sigma_mult[2] + + c_chunks[1][3] * sigma_mult[3]; + std::array, 2> Sigma0_chunks = + nil::blueprint::components::detail::reversed_sparse_and_split( + sparse_Sigma0, sigma_sizes, base4); + { + std::scoped_lock lock(set_guard); + + output_set.insert(std::make_pair(sigma0_chunks[0][0], sigma0_chunks[1][0])); + output_set.insert(std::make_pair(sigma0_chunks[0][1], sigma0_chunks[1][1])); + output_set.insert(std::make_pair(sigma0_chunks[0][2], sigma0_chunks[1][2])); + output_set.insert(std::make_pair(sigma0_chunks[0][3], sigma0_chunks[1][3])); + + output_set.insert(std::make_pair(sigma1_chunks[0][0], sigma1_chunks[1][0])); + output_set.insert(std::make_pair(sigma1_chunks[0][1], sigma1_chunks[1][1])); + output_set.insert(std::make_pair(sigma1_chunks[0][2], sigma1_chunks[1][2])); + output_set.insert(std::make_pair(sigma1_chunks[0][3], sigma1_chunks[1][3])); + + output_set.insert(std::make_pair(Sigma0_chunks[0][0], Sigma0_chunks[1][0])); + output_set.insert(std::make_pair(Sigma0_chunks[0][1], Sigma0_chunks[1][1])); + output_set.insert(std::make_pair(Sigma0_chunks[0][2], Sigma0_chunks[1][2])); + output_set.insert(std::make_pair(Sigma0_chunks[0][3], Sigma0_chunks[1][3])); + } + } + + template + void base4_reverse_table_worker( + std::unordered_set, + SumHash> &output_set, + typename BlueprintFieldType::integral_type start, + typename BlueprintFieldType::integral_type end, + const std::size_t table_size, + std::mutex &set_guard) { + + using integral_type = typename BlueprintFieldType::integral_type; + for (integral_type i = start; i < end; i++) { + base4_reverse_table_iter(output_set, i, table_size, set_guard); + } + } + + template + void generate_base4_reverse_table( + std::unordered_set, + SumHash> &output_set, + const std::size_t table_size) { + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + + const integral_type one = 1; + auto start = std::chrono::high_resolution_clock::now(); + std::mutex set_guard; + std::array threads; + // Dispatching does not work otherwise + static_assert(ThreadNum == 1 || ThreadNum % 2 == 0 && ThreadNum > 1); + // Filling the cache + // We have to do this, because cache modification is not thread safe + base4_reverse_table_worker( + std::ref(output_set), + 0, + 1, + table_size, + std::ref(set_guard)); + for (std::size_t i = 0; i < ThreadNum; i++) { + threads[i] = std::thread( + base4_reverse_table_worker, + std::ref(output_set), + (integral_type(one << table_size) / ThreadNum) * i, + (integral_type(one << table_size) / ThreadNum) * (i + 1), + table_size, + std::ref(set_guard)); + } + for (std::size_t i = 0; i < ThreadNum; i++) { + threads[i].join(); + } + auto duration = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start); + std::cerr << "Time elapsed: " << duration.count() << " seconds" << std::endl; + std::cerr << "Total size: " << std::dec << output_set.size() << std::endl; + } + + template + void base7_reverse_table_iter( + std::unordered_set, + SumHash> &output_set, + const typename BlueprintFieldType::integral_type &i, + const std::size_t table_size, + std::mutex &set_guard) { + + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + + static const std::vector e_sizes = {6, 5, 14, 7}; + static const std::vector sigma_sizes = {8, 8, 8, 8}; + static const integral_type one = 1; + static const std::size_t base7 = 7; + static const value_type base7_value = value_type(base7); + static const std::array e_mult = { + integral_type((base7_value.pow(26) + base7_value.pow(21) + base7_value.pow(7)).data), + integral_type((base7_value.pow(27) + base7_value.pow(13) + 1).data), + integral_type((base7_value.pow(5) + base7_value.pow(18) + 1).data), + integral_type((base7_value.pow(19) + base7_value.pow(14) + 1).data) + }; + + std::vector value(table_size); + for (std::size_t j = 0; j < table_size; j++) { + value[table_size - j - 1] = boost::multiprecision::bit_test(i, j); + } + + std::array, 2> e_chunks = + nil::blueprint::components::detail::split_and_sparse( + value, e_sizes, base7); + + integral_type sparse_Sigma1 = + e_chunks[1][1] * e_mult[1] + + e_chunks[1][2] * e_mult[2] + + e_chunks[1][3] * e_mult[3] + + e_chunks[1][0] * e_mult[0]; + std::array, 2> Sigma1_chunks = + nil::blueprint::components::detail::reversed_sparse_and_split( + sparse_Sigma1, sigma_sizes, base7); + { + std::scoped_lock lock(set_guard); + + output_set.insert(std::make_pair(Sigma1_chunks[0][0], Sigma1_chunks[1][0])); + output_set.insert(std::make_pair(Sigma1_chunks[0][1], Sigma1_chunks[1][1])); + output_set.insert(std::make_pair(Sigma1_chunks[0][2], Sigma1_chunks[1][2])); + output_set.insert(std::make_pair(Sigma1_chunks[0][3], Sigma1_chunks[1][3])); + } + } + + template + void base7_reverse_table_worker( + std::unordered_set, + SumHash> &output_set, + typename BlueprintFieldType::integral_type start, + typename BlueprintFieldType::integral_type end, + const std::size_t table_size, + std::mutex &set_guard) { + + using integral_type = typename BlueprintFieldType::integral_type; + for (integral_type i = start; i < end; i++) { + base7_reverse_table_iter(output_set, i, table_size, set_guard); + } + } + + template + void generate_base7_reverse_table( + std::unordered_set, + SumHash> &output_set, + const std::size_t table_size) { + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + + const integral_type one = 1; + + auto start = std::chrono::high_resolution_clock::now(); + std::mutex set_guard; + std::array threads; + + // Dispatching does not work otherwise + static_assert(ThreadNum == 1 || ThreadNum % 2 == 0 && ThreadNum > 1); + // Filling the cache + // We have to do this, because cache modification is not thread safe + base7_reverse_table_worker( + std::ref(output_set), + 0, + 1, + table_size, + std::ref(set_guard)); + for (std::size_t i = 0; i < ThreadNum; i++) { + threads[i] = std::thread( + base7_reverse_table_worker, + std::ref(output_set), + (integral_type(one << table_size) / ThreadNum) * i, + (integral_type(one << table_size) / ThreadNum) * (i + 1), + table_size, + std::ref(set_guard)); + } + for (std::size_t i = 0; i < ThreadNum; i++) { + threads[i].join(); + } + auto duration = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start); + std::cerr << "Time elapsed: " << duration.count() << " seconds" << std::endl; + std::cerr << "Total size: " << std::dec << output_set.size() << std::endl; + } + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/split_functions.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/split_functions.hpp new file mode 100644 index 000000000..3d9f83c3e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/detail/split_functions.hpp @@ -0,0 +1,177 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA512_PROCESS component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA2_SPLIT_FUNCTIONS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA2_SPLIT_FUNCTIONS_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + std::array, 2> split_and_sparse( + const std::vector &bits, const std::vector &sizes, std::size_t base) { + using integral_type = typename BlueprintFieldType::integral_type; + + const std::size_t size = sizes.size() - 1; + std::array, 2> res = { + std::vector(size + 1), + std::vector(size + 1) + }; + std::size_t k = 0; + for (int i = size; i > -1; i--) { + res[0][i] = int(bits[k]); + res[1][i] = int(bits[k]); + for (std::size_t j = 1; j < sizes[i]; j++) { + res[0][i] = res[0][i] * 2 + int(bits[k + j]); + res[1][i] = res[1][i] * base + int(bits[k + j]); + } + k = k + sizes[i]; + } + return res; + } + + template + inline typename BlueprintFieldType::integral_type cached_pow( + const std::size_t base, const std::size_t k) { + + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + static std::map, integral_type> cache; + const auto pair = std::make_pair(base, k); + if (cache.find(pair) == cache.end()) { [[unlikely]] + cache[pair] = integral_type(value_type(base).pow(k).data); + } + return cache[pair]; + } + + template + std::array, 2> + reversed_sparse_and_split(const typename BlueprintFieldType::integral_type sparse_value, + const std::vector &sizes, std::size_t base) { + using integral_type = typename BlueprintFieldType::integral_type; + std::size_t size = sizes.size(); + std::array, 2> res = { + std::vector(size), + std::vector(size) + }; + const integral_type sparse_base = base; + std::size_t k = -1; + for (int i = sizes.size() - 1; i > -1; i--) { + k = k + sizes[i]; + } + integral_type tmp = sparse_value, r; + for (int i = sizes.size() - 1; i > -1; i--) { + res[0][i] = 0; + res[1][i] = 0; + for (int j = sizes[i] - 1; j > -1; j--) { + const integral_type k_pow = cached_pow(base, k); + divide_qr(tmp, k_pow, r, tmp); + res[0][i] = res[0][i] * 2 + (r&1); + res[1][i] = res[1][i] * sparse_base + r; + k--; + } + } + return res; + } + + template + std::array, 2> + reversed_sparse_and_split_maj(const typename BlueprintFieldType::integral_type sparse_value, + const std::vector &sizes, std::size_t base) { + using integral_type = typename BlueprintFieldType::integral_type; + + std::size_t size = sizes.size(); + std::array, 2> res = { + std::vector(size), + std::vector(size)}; + integral_type sparse_base = base; + std::size_t k = -1; + for (int i = sizes.size() - 1; i > -1; i--) { + k = k + sizes[i]; + } + const std::array r_values = {0,0,1,1}; + integral_type tmp = sparse_value, r; + for (int i = sizes.size() - 1; i > -1; i--) { + res[0][i] = 0; + res[1][i] = 0; + for (int j = sizes[i] - 1; j > -1; j--) { + const integral_type k_pow = cached_pow(base, k); + divide_qr(tmp, k_pow, r, tmp); + res[0][i] = res[0][i] * 2 + r_values[std::size_t(r)]; + res[1][i] = res[1][i] * sparse_base + r; + k--; + } + } + return res; + } + + template + std::array, 2> + reversed_sparse_and_split_ch(const typename BlueprintFieldType::integral_type sparse_value, + const std::vector &sizes, std::size_t base) { + using integral_type = typename BlueprintFieldType::integral_type; + + std::size_t size = sizes.size(); + std::array, 2> res = { + std::vector(size), + std::vector(size)}; + integral_type sparse_base = base; + std::size_t k = -1; + for (int i = sizes.size() - 1; i > -1; i--) { + k = k + sizes[i]; + } + std::array r_values = {0, 0, 0, 1, 0, 1, 1}; + integral_type tmp = sparse_value, r; + for (int i = sizes.size() - 1; i > -1; i--) { + res[0][i] = 0; + res[1][i] = 0; + for (int j = sizes[i] - 1; j > -1; j--) { + const integral_type k_pow = cached_pow(base, k); + divide_qr(tmp, k_pow, r, tmp); + res[0][i] = res[0][i] * 2 + r_values[std::size_t(r)]; + res[1][i] = res[1][i] * sparse_base + r; + k--; + } + } + return res; + } + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA2_SPLIT_FUNCTIONS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp new file mode 100644 index 000000000..51da0ed01 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp @@ -0,0 +1,578 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: + // Output: + template + class sha256; + + template + class sha256> + : public plonk_component { + + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + using sha256_process_type = + sha256_process< + crypto3::zk::snark::plonk_constraint_system>; + using decomposition_type = + decomposition< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return sha256::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(sha256_process_type::get_gate_manifest(witness_amount)) + .merge_with(decomposition_type::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + true + ).merge_with(sha256_process_type::get_manifest()) + .merge_with(decomposition_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return + 2 * sha256_process_type::get_rows_amount(witness_amount) + + 2 * decomposition_type::get_rows_amount(witness_amount) + + 2; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + + constexpr static const std::size_t gates_amount = 1; + const std::string component_name = "sha256 hash"; + + struct input_type { + std::array block_data; + + std::vector> all_vars() { + return {block_data[0], block_data[1], block_data[2], block_data[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(const sha256 &component, std::uint32_t start_row_index) { + output = {var(component.W(0), start_row_index + component.rows_amount - 1, false), + var(component.W(1), start_row_index + component.rows_amount - 1, false)}; + } + + result_type(const sha256 &component, std::uint32_t start_row_index, bool skip) { + output = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1]}; + } + }; + + template + sha256(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + sha256(std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + using lookup_table_definition = typename + nil::crypto3::zk::snark::lookup_table_definition; + + std::vector> component_custom_lookup_tables(){ + std::vector> result = {}; + + auto sparse_values_base4 = std::shared_ptr(new typename sha256_process_type::sparse_values_base4_table()); + result.push_back(sparse_values_base4); + + auto sparse_values_base7 = std::shared_ptr(new typename sha256_process_type::sparse_values_base7_table()); + result.push_back(sparse_values_base7); + + auto maj = std::shared_ptr(new typename sha256_process_type::maj_function_table()); + result.push_back(maj); + + auto reverse_sparse_sigmas_base4 = std::shared_ptr(new typename sha256_process_type::reverse_sparse_sigmas_base4_table()); + result.push_back(reverse_sparse_sigmas_base4); + + auto reverse_sparse_sigmas_base7 = std::shared_ptr(new typename sha256_process_type::reverse_sparse_sigmas_base7_table()); + result.push_back(reverse_sparse_sigmas_base7); + + auto ch = std::shared_ptr(new typename sha256_process_type::ch_function_table()); + result.push_back(ch); + + return result; + } + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["sha256_sparse_base4/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base4/first_column"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_reverse_sparse_base4/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base7/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base7/first_column"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_reverse_sparse_base7/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_maj/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_ch/full"] = 0; // REQUIRED_TABLE + + return lookup_tables; + } + + static std::array + calculate(std::array block_data) { + std::array decomposition_input = {block_data[0], block_data[1]}; + std::array sha_block_part_1 = decomposition_type::calculate(decomposition_input); + + decomposition_input = {block_data[2], block_data[3]}; + std::array sha_block_part_2 = decomposition_type::calculate(decomposition_input); + + std::array input_words; + for (int i = 0; i < 8; i++) { + input_words[i] = sha_block_part_1[i]; + input_words[8 + i] = sha_block_part_2[i]; + } + std::array constants = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + std::array first_block_state = + sha256_process_type::calculate(constants, input_words); + + std::array constants2 = { + 2147483648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 << 9 + }; + std::array second_block_state = + sha256_process_type::calculate(first_block_state, constants2); + + typename BlueprintFieldType::integral_type one = 1; + std::array result; + result[1] = second_block_state[7] + + second_block_state[6] * (one << 32) + + second_block_state[5] * (one << 64) + + second_block_state[4] * (one << 96); + result[0] = second_block_state[3] + + second_block_state[2] * (one << 32) + + second_block_state[1] * (one << 64) + + second_block_state[0] * (one << 96); + return result; + } + }; + + template + using plonk_sha256 = + sha256>; + + template + typename plonk_sha256::result_type generate_assignments( + const plonk_sha256 &component, + assignment> + &assignment, + const typename plonk_sha256::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using var = typename plonk_sha256::var; + using ArithmetizationType = + crypto3::zk::snark::plonk_constraint_system; + using component_type = plonk_sha256; + using decomposition_type = typename component_type::decomposition_type; + + decomposition_type decomposition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), component.W(5), + component.W(6), component.W(7), component.W(8)}, + {}, {}); + + std::array input_1 = {instance_input.block_data[0], instance_input.block_data[1]}; + typename decomposition_type::input_type decomposition_input = {input_1}; + typename decomposition_type::result_type sha_block_part_1 = + generate_assignments(decomposition_instance, assignment, decomposition_input, row); + row += decomposition_instance.rows_amount; + + std::array input_2 = {instance_input.block_data[2], instance_input.block_data[3]}; + decomposition_input = {input_2}; + + typename decomposition_type::result_type sha_block_part_2 = + generate_assignments(decomposition_instance, assignment, decomposition_input, row); + row += decomposition_instance.rows_amount; + + sha256_process sha256_process_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), component.W(5), + component.W(6), component.W(7), component.W(8)}, + {component.C(0)}, {}); + + std::array input_words_vars; + for (int i = 0; i < 8; i++) { + input_words_vars[i] = sha_block_part_1.output[i]; + input_words_vars[8 + i] = sha_block_part_2.output[i]; + } + + std::array constants_vars = { + var(component.C(0), start_row_index, false, var::column_type::constant), + var(component.C(0), start_row_index + 1, false, var::column_type::constant), + var(component.C(0), start_row_index + 2, false, var::column_type::constant), + var(component.C(0), start_row_index + 3, false, var::column_type::constant), + var(component.C(0), start_row_index + 4, false, var::column_type::constant), + var(component.C(0), start_row_index + 5, false, var::column_type::constant), + var(component.C(0), start_row_index + 6, false, var::column_type::constant), + var(component.C(0), start_row_index + 7, false, var::column_type::constant)}; + + typename sha256_process::input_type sha256_process_input = { + constants_vars, input_words_vars}; + + std::array first_block_state = + generate_assignments(sha256_process_instance, assignment, sha256_process_input, row).output_state; + row += sha256_process_instance.rows_amount; + + std::array input_words2_vars = { + var(component.C(0), start_row_index + 8, false, var::column_type::constant), + var(component.C(0), start_row_index + 9, false, var::column_type::constant), + var(component.C(0), start_row_index + 10, false, var::column_type::constant), + var(component.C(0), start_row_index + 11, false, var::column_type::constant), + var(component.C(0), start_row_index + 12, false, var::column_type::constant), + var(component.C(0), start_row_index + 13, false, var::column_type::constant), + var(component.C(0), start_row_index + 14, false, var::column_type::constant), + var(component.C(0), start_row_index + 15, false, var::column_type::constant), + var(component.C(0), start_row_index + 16, false, var::column_type::constant), + var(component.C(0), start_row_index + 17, false, var::column_type::constant), + var(component.C(0), start_row_index + 18, false, var::column_type::constant), + var(component.C(0), start_row_index + 19, false, var::column_type::constant), + var(component.C(0), start_row_index + 20, false, var::column_type::constant), + var(component.C(0), start_row_index + 21, false, var::column_type::constant), + var(component.C(0), start_row_index + 22, false, var::column_type::constant), + var(component.C(0), start_row_index + 23, false, var::column_type::constant)}; + + typename sha256_process::input_type sha256_process_input_2 = { + first_block_state, input_words2_vars}; + + std::array second_block_state = + generate_assignments(sha256_process_instance, assignment, sha256_process_input_2, row).output_state; + + row += sha256_process_instance.rows_amount; + typename ArithmetizationType::field_type::integral_type one = 1; + for (std::size_t i = 0; i < 8; i++) { + assignment.witness(component.W(i), row) = var_value(assignment, second_block_state[i]); + } + + row++; + + assignment.witness(component.W(1), row) = var_value(assignment, second_block_state[7]) + + var_value(assignment, second_block_state[6]) * (one << 32) + + var_value(assignment, second_block_state[5]) * (one << 64) + + var_value(assignment, second_block_state[4]) * (one << 96); + assignment.witness(component.W(0), row) = var_value(assignment, second_block_state[3]) + + var_value(assignment, second_block_state[2]) * (one << 32) + + var_value(assignment, second_block_state[1]) * (one << 64) + + var_value(assignment, second_block_state[0]) * (one << 96); + return typename component_type::result_type(component, start_row_index); + } + template + typename plonk_sha256::result_type generate_empty_assignments( + const plonk_sha256 &component, + assignment> + &assignment, + const typename plonk_sha256::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + using component_type = plonk_sha256; + + std::array input = + {var_value(assignment, instance_input.block_data[0]), + var_value(assignment, instance_input.block_data[1]), + var_value(assignment, instance_input.block_data[2]), + var_value(assignment, instance_input.block_data[3])}; + + std::array output = component_type::calculate(input); + + assignment.witness(component.W(0), row) = output[0]; + assignment.witness(component.W(1), row) = output[1]; + + return typename component_type::result_type(component, start_row_index, true); + } + + template + std::size_t generate_gates( + const plonk_sha256 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256::input_type &instance_input) { + + using var = typename plonk_sha256::var; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(1), +1) - + (var(component.W(7), 0) + var(component.W(6), 0) * (one << 32) + + var(component.W(5), 0) * (one << 64) + var(component.W(4), 0) * (one << 96)); + auto constraint_2 = + var(component.W(0), +1) - + (var(component.W(3), 0) + var(component.W(2), 0) * (one << 32) + + var(component.W(1), 0) * (one << 64) + var(component.W(0), 0) * (one << 96)); + return bp.add_gate({constraint_1, constraint_2}); + } + + template + void generate_copy_constraints( + const plonk_sha256 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256::input_type &instance_input, + const std::size_t start_row_index) { + } + + template + typename plonk_sha256::result_type generate_circuit( + const plonk_sha256 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + using var = typename plonk_sha256::var; + using component_type = plonk_sha256; + using sha256_process_type = typename component_type::sha256_process_type; + using decomposition_type = typename component_type::decomposition_type; + + generate_assignments_constant(component, bp, assignment, instance_input, row); + + decomposition_type decomposition_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), component.W(5), + component.W(6), component.W(7), component.W(8)}, + {}, {}); + + std::array input_1 = {instance_input.block_data[0], instance_input.block_data[1]}; + typename decomposition_type::input_type decomposition_input = { + input_1}; + typename decomposition_type::result_type sha_block_part_1 = + generate_circuit(decomposition_instance, bp, assignment, decomposition_input, row); + row += decomposition_instance.rows_amount; + + std::array input_2 = {instance_input.block_data[2], instance_input.block_data[3]}; + decomposition_input = {input_2}; + typename decomposition_type::result_type sha_block_part_2 = + generate_circuit(decomposition_instance, bp, assignment, decomposition_input, row); + row += decomposition_instance.rows_amount; + + sha256_process_type sha256_process_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), component.W(5), + component.W(6), component.W(7), component.W(8)}, + {component.C(0)}, {}); + + std::array input_words_vars; + for (int i = 0; i < 8; i++) { + input_words_vars[i] = sha_block_part_1.output[i]; + input_words_vars[8 + i] = sha_block_part_2.output[i]; + } + std::array constants_vars = {var(component.C(0), start_row_index, false, var::column_type::constant), + var(component.C(0), start_row_index + 1, false, var::column_type::constant), + var(component.C(0), start_row_index + 2, false, var::column_type::constant), + var(component.C(0), start_row_index + 3, false, var::column_type::constant), + var(component.C(0), start_row_index + 4, false, var::column_type::constant), + var(component.C(0), start_row_index + 5, false, var::column_type::constant), + var(component.C(0), start_row_index + 6, false, var::column_type::constant), + var(component.C(0), start_row_index + 7, false, var::column_type::constant)}; + + typename sha256_process_type::input_type sha256_process_input = { + constants_vars, input_words_vars}; + typename sha256_process_type::result_type first_block_state = + generate_circuit(sha256_process_instance, bp, assignment, sha256_process_input, row); + + row += sha256_process_instance.rows_amount; + std::array input_words2_vars = { + var(component.C(0), start_row_index + 8, false, var::column_type::constant), + var(component.C(0), start_row_index + 9, false, var::column_type::constant), + var(component.C(0), start_row_index + 10, false, var::column_type::constant), + var(component.C(0), start_row_index + 11, false, var::column_type::constant), + var(component.C(0), start_row_index + 12, false, var::column_type::constant), + var(component.C(0), start_row_index + 13, false, var::column_type::constant), + var(component.C(0), start_row_index + 14, false, var::column_type::constant), + var(component.C(0), start_row_index + 15, false, var::column_type::constant), + var(component.C(0), start_row_index + 16, false, var::column_type::constant), + var(component.C(0), start_row_index + 17, false, var::column_type::constant), + var(component.C(0), start_row_index + 18, false, var::column_type::constant), + var(component.C(0), start_row_index + 19, false, var::column_type::constant), + var(component.C(0), start_row_index + 20, false, var::column_type::constant), + var(component.C(0), start_row_index + 21, false, var::column_type::constant), + var(component.C(0), start_row_index + 22, false, var::column_type::constant), + var(component.C(0), start_row_index + 23, false, var::column_type::constant)}; + + typename sha256_process_type::input_type sha256_process_input_2 = { + first_block_state.output_state, input_words2_vars}; + + generate_circuit(sha256_process_instance, bp, assignment, sha256_process_input_2, row); + + row = row + sha256_process_instance.rows_amount; + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, row); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_sha256 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256::input_type + &instance_input, + const std::size_t start_row_index) { + + std::array constants = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + for (std::size_t i = 0; i < constants.size(); i++) { + assignment.constant(component.C(0), start_row_index + i) = constants[i]; + } + + std::array constants2 = { + 2147483648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 << 9 + }; + for (std::size_t i = 0; i < constants2.size(); i++) { + assignment.constant(component.C(0), start_row_index + constants.size() + i) = constants2[i]; + } + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter> { + + using component_type = plonk_sha256; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + for (std::size_t i = 0; i < 4; i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.block_data[i]); + } + + input_type new_input; + for (std::size_t i = 0; i < 4; i++) { + new_input.block_data[i] = var(0, i, false, var::column_type::public_input); + } + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + return input.block_data[variable.rotation]; + } + }; + + template + class result_type_converter> { + + using component_type = plonk_sha256; + using result_type = typename component_type::result_type; + using input_type = typename component_type::input_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + new_result.output[0] = + component.move_var( + old_result.output[0], + start_row_index + component.line_mapping[old_result.output[0].rotation], + instance_input); + new_result.output[1] = + component.move_var( + old_result.output[1], + start_row_index + component.line_mapping[old_result.output[1].rotation], + instance_input); + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp new file mode 100644 index 000000000..d68874c4f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp @@ -0,0 +1,1899 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256_PROCESS component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_PROCESS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_PROCESS_HPP + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: [x_0, x_1, x_2] \in Fp + // Output: [y_0, y_1, y_2] - SHA256 permutation of [x_0, x_1, x_2] + template + class sha256_process; + + template + class sha256_process> + : public plonk_component { + + public: + using component_type = + plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return sha256_process::gates_amount + sha256_process::lookup_gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 762; + } + constexpr static std::size_t get_empty_rows_amount() { + return 1; + } + + constexpr static const std::size_t rounds_amount = 64; + + constexpr static const std::size_t base4 = 4; + constexpr static const std::size_t base7 = 7; + + constexpr static const std::array + round_constant = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::size_t empty_rows_amount = get_empty_rows_amount(); + constexpr static const std::size_t gates_amount = 11; + constexpr static const std::size_t lookup_gates_amount = 8; + struct input_type { + std::array input_state; + std::array input_words; + + std::vector> all_vars() { + std::vector> result; + result.reserve(24); + result.insert(result.end(), input_state.begin(), input_state.end()); + result.insert(result.end(), input_words.begin(), input_words.end()); + return result; + } + }; + + struct result_type { + std::array output_state; + + result_type(const sha256_process> + &component, + std::uint32_t start_row_index) { + output_state = {var(component.W(0), start_row_index + component.rows_amount - 7, false), + var(component.W(1), start_row_index + component.rows_amount - 7, false), + var(component.W(2), start_row_index + component.rows_amount - 7, false), + var(component.W(3), start_row_index + component.rows_amount - 7, false), + var(component.W(0), start_row_index + component.rows_amount - 5, false), + var(component.W(1), start_row_index + component.rows_amount - 5, false), + var(component.W(2), start_row_index + component.rows_amount - 5, false), + var(component.W(3), start_row_index + component.rows_amount - 5, false)}; + } + + result_type(const sha256_process> + &component, + std::uint32_t start_row_index, + bool skip) { + output_state = {var(component.W(0), start_row_index, false), + var(component.W(1), start_row_index, false), + var(component.W(2), start_row_index, false), + var(component.W(3), start_row_index, false), + var(component.W(4), start_row_index, false), + var(component.W(5), start_row_index, false), + var(component.W(6), start_row_index, false), + var(component.W(7), start_row_index, false)}; + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(8); + result.insert(result.end(), output_state.begin(), output_state.end()); + return result; + } + }; + + template + sha256_process(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + sha256_process(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["sha256_sparse_base4/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base4/first_column"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_reverse_sparse_base4/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base7/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_sparse_base7/first_column"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_reverse_sparse_base7/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_maj/full"] = 0; // REQUIRED_TABLE + lookup_tables["sha256_ch/full"] = 0; // REQUIRED_TABLE + + return lookup_tables; + } + + static std::array + calculate(std::array input_state, + std::array input_words) { + + std::size_t row = 0; + typename BlueprintFieldType::integral_type one = 1; + std::array message_scheduling_words; + for (std::size_t i = 0; i < 16; i++) { + message_scheduling_words[i] = input_words[i]; + } + typename BlueprintFieldType::value_type a = input_state[0]; + typename BlueprintFieldType::value_type b = input_state[1]; + typename BlueprintFieldType::value_type c = input_state[2]; + typename BlueprintFieldType::value_type d = input_state[3]; + typename BlueprintFieldType::value_type e = input_state[4]; + typename BlueprintFieldType::value_type f = input_state[5]; + typename BlueprintFieldType::value_type g = input_state[6]; + typename BlueprintFieldType::value_type h = input_state[7]; + + std::array sparse_values {}; + for (std::size_t i = 0; i < 4; i++) { + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(32); + { + nil::marshalling::status_type status; + std::vector input_state_sparse_all = + nil::marshalling::pack(integral_input_state_sparse, + status); + std::copy(input_state_sparse_all.end() - 32, input_state_sparse_all.end(), + input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {32}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse( + input_state_sparse, input_state_sparse_sizes, base4); + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + for (std::size_t i = 4; i < 8; i++) { + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(32); + { + nil::marshalling::status_type status; + std::vector input_state_sparse_all = + nil::marshalling::pack(integral_input_state_sparse, + status); + std::copy(input_state_sparse_all.end() - 32, input_state_sparse_all.end(), + input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {32}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse( + input_state_sparse, input_state_sparse_sizes, base7); + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + row = row + 2; + std::vector sigma_sizes = {8, 8, 8, 8}; + std::vector ch_and_maj_sizes = {8, 8, 8, 8}; + typename BlueprintFieldType::value_type base4_value = base4; + typename BlueprintFieldType::value_type base7_value = base7; + for (std::size_t i = row; i < row + 236; i = i + 5) { + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type(message_scheduling_words[(i - row) / 5 + 1].data); + std::vector a(32); + { + nil::marshalling::status_type status; + std::vector a_all = + nil::marshalling::pack(integral_a, status); + std::copy(a_all.end() - 32, a_all.end(), a.begin()); + } + + std::vector a_sizes = {3, 4, 11, 14}; + std::array, 2> a_chunks = + detail::split_and_sparse( + a, a_sizes, base4); + typename BlueprintFieldType::integral_type sparse_sigma0 = + a_chunks[1][1] * (1 + (one << 56) + (one << 34)) + + a_chunks[1][2] * ((1 << 8) + 1 + (one << 42)) + a_chunks[1][3] * ((1 << 30) + (1 << 22) + 1) + + a_chunks[1][0] * ((one << 50) + (1 << 28)); + std::array, 2> sigma0_chunks = + detail::reversed_sparse_and_split( + sparse_sigma0, sigma_sizes, base4); + + typename BlueprintFieldType::integral_type integral_b = + typename BlueprintFieldType::integral_type(message_scheduling_words[(i - row) / 5 + 14].data); + std::vector b(32); + { + nil::marshalling::status_type status; + std::vector b_all = + nil::marshalling::pack(integral_b, status); + std::copy(b_all.end() - 32, b_all.end(), b.begin()); + } + + std::vector b_sizes = {10, 7, 2, 13}; + std::array, 2> b_chunks = + detail::split_and_sparse( + b, b_sizes, base4); + + typename BlueprintFieldType::integral_type sparse_sigma1 = + b_chunks[1][1] * (1 + (one << 50) + (one << 46)) + + b_chunks[1][2] * ((1 << 14) + 1 + (one << 60)) + b_chunks[1][3] * ((1 << 18) + (1 << 4) + 1) + + b_chunks[1][0] * ((1 << 30) + (1 << 26)); + + std::array, 2> sigma1_chunks = + detail::reversed_sparse_and_split( + sparse_sigma1, sigma_sizes, base4); + + typename BlueprintFieldType::value_type sum = + message_scheduling_words[(i - row) / 5 + 9] + message_scheduling_words[(i - row) / 5] + + sigma1_chunks[0][0] + sigma0_chunks[0][0] + + (one << 8) * (sigma1_chunks[0][1] + sigma0_chunks[0][1]) + + (one << 16) * (sigma1_chunks[0][2] + sigma0_chunks[0][2]) + + (one << 24) * (sigma1_chunks[0][3] + sigma0_chunks[0][3]); + message_scheduling_words[(i - row) / 5 + 16] = + typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + } + row = row + 240; + for (std::size_t i = row; i < row + 512; i = i + 8) { + typename BlueprintFieldType::integral_type integral_e = + typename BlueprintFieldType::integral_type(e.data); + std::vector e_bits(32); + { + nil::marshalling::status_type status; + std::vector e_bits_all = + nil::marshalling::pack(integral_e, status); + std::copy(e_bits_all.end() - 32, e_bits_all.end(), e_bits.begin()); + } + + std::vector e_sizes = {6, 5, 14, 7}; + std::array, 2> e_chunks = + detail::split_and_sparse( + e_bits, e_sizes, base7); + + sparse_values[4] = typename BlueprintFieldType::integral_type( + (e_chunks[1][0] + e_chunks[1][1] * base7_value.pow(e_sizes[0]) + + e_chunks[1][2] * base7_value.pow(e_sizes[0] + e_sizes[1]) + + e_chunks[1][3] * base7_value.pow(e_sizes[0] + e_sizes[1] + e_sizes[2])) + .data); + typename BlueprintFieldType::integral_type sparse_Sigma1 = + typename BlueprintFieldType::integral_type( + (e_chunks[1][1] * (base7_value.pow(27) + base7_value.pow(13) + 1) + + e_chunks[1][2] * (base7_value.pow(5) + base7_value.pow(18) + 1) + + e_chunks[1][3] * (base7_value.pow(19) + base7_value.pow(14) + 1) + + e_chunks[1][0] * (base7_value.pow(26) + base7_value.pow(21) + base7_value.pow(7))) + .data); + std::array, 2> Sigma1_chunks = + detail::reversed_sparse_and_split( + sparse_Sigma1, sigma_sizes, base7); + typename BlueprintFieldType::integral_type Sigma1 = + Sigma1_chunks[0][0] + Sigma1_chunks[0][1] * (1 << (sigma_sizes[0])) + + Sigma1_chunks[0][2] * (1 << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma1_chunks[0][3] * (1 << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])); + typename BlueprintFieldType::integral_type sparse_ch = + sparse_values[4] + 2 * sparse_values[5] + 3 * sparse_values[6]; + + std::array, 2> ch_chunks = + detail::reversed_sparse_and_split_ch( + sparse_ch, ch_and_maj_sizes, base7); + + typename BlueprintFieldType::integral_type ch = ch_chunks[0][0] + ch_chunks[0][1] * (1 << 8) + + ch_chunks[0][2] * (1 << 16) + + ch_chunks[0][3] * (1 << 24); + + typename BlueprintFieldType::value_type tmp1 = h + Sigma1 + ch + + round_constant[(i - row) / 8] + + message_scheduling_words[(i - row) / 8]; + typename BlueprintFieldType::value_type sum = tmp1 + d; + typename BlueprintFieldType::value_type e_new = + typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + + typename BlueprintFieldType::integral_type integral_a2 = + typename BlueprintFieldType::integral_type(e_new.data); + + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type(a.data); + std::vector a_bits(32); + { + nil::marshalling::status_type status; + std::vector a_bits_all = + nil::marshalling::pack(integral_a, status); + std::copy(a_bits_all.end() - 32, a_bits_all.end(), a_bits.begin()); + } + + std::vector a_sizes = {2, 11, 9, 10}; + std::array, 2> a_chunks = + detail::split_and_sparse( + a_bits, a_sizes, base4); + + sparse_values[0] = typename BlueprintFieldType::integral_type( + (a_chunks[1][0] + a_chunks[1][1] * base4_value.pow(a_sizes[0]) + + a_chunks[1][2] * base4_value.pow(a_sizes[0] + a_sizes[1]) + + a_chunks[1][3] * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2])) + .data); + typename BlueprintFieldType::integral_type sparse_Sigma0 = + (a_chunks[1][0] * ((one << 38) + (1 << 20) + (one << 60)) + + a_chunks[1][1] * ((one << 42) + 1 + (1 << 24)) + + a_chunks[1][2] * ((1 << 22) + (one << 46) + 1) + + a_chunks[1][3] * ((one << 40) + (1 << 18) + 1)); + std::array, 2> Sigma0_chunks = + detail::reversed_sparse_and_split( + sparse_Sigma0, sigma_sizes, base4); + + typename BlueprintFieldType::integral_type Sigma0 = + Sigma0_chunks[0][0] + Sigma0_chunks[0][1] * (1 << sigma_sizes[0]) + + Sigma0_chunks[0][2] * (1 << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma0_chunks[0][3] * (1 << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])); + + typename BlueprintFieldType::integral_type sparse_maj = + (sparse_values[0] + sparse_values[1] + sparse_values[2]); + std::array, 2> maj_chunks = + detail::reversed_sparse_and_split_maj( + sparse_maj, ch_and_maj_sizes, base4); + typename BlueprintFieldType::integral_type maj = maj_chunks[0][0] + maj_chunks[0][1] * (1 << 8) + + maj_chunks[0][2] * (1 << 16) + + maj_chunks[0][3] * (1 << 24); + typename BlueprintFieldType::value_type sum1 = tmp1 + Sigma0 + maj; + typename BlueprintFieldType::value_type a_new = + typename BlueprintFieldType::integral_type(sum1.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + + integral_a2 = + typename BlueprintFieldType::integral_type(a_new.data); + + h = g; + sparse_values[7] = sparse_values[6]; + g = f; + sparse_values[6] = sparse_values[5]; + f = e; + sparse_values[5] = sparse_values[4]; + e = e_new; + d = c; + sparse_values[3] = sparse_values[2]; + c = b; + sparse_values[2] = sparse_values[1]; + b = a; + sparse_values[1] = sparse_values[0]; + a = a_new; + } + std::array output_state = {a, b, c, d, e, f, g, h}; + std::array result; + row = row + 512; + for (std::size_t i = 0; i < 8; i++) { + auto sum = typename BlueprintFieldType::integral_type(input_state[i].data) + + typename BlueprintFieldType::integral_type(output_state[i].data); + result[i] = sum % typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + } + + return result; + } + }; + + template + using plonk_sha256_process = + sha256_process>; + + namespace detail { + + template + void generate_assignments_constant( + const plonk_sha256_process &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256_process::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index + 242 + 3; + for (std::size_t i = 0; i < 64; i++) { + assignment.constant(component.C(0), row + i * 8) = + plonk_sha256_process::round_constant[i]; + } + } + + template + std::array generate_sigma0_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> + &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + std::array selectors; + + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + const std::array a_chunks_0 = { + var(component.W(1), -1), var(component.W(2), -1), + var(component.W(3), -1), var(component.W(4), -1)}; + const std::array a_chunks_1 = { + var(component.W(7), -1), var(component.W(2), 0), + var(component.W(3), 0), var(component.W(4), 0)}; + const std::array, 2> a_chunks = {a_chunks_0, a_chunks_1}; + + const std::array sigma0_chunks_0 = { + var(component.W(1), +1), var(component.W(2), +1), + var(component.W(3), +1), var(component.W(4), +1)}; + const std::array sigma0_chunks_1 = { + var(component.W(5), 0), var(component.W(6), 0), + var(component.W(7), 0), var(component.W(8), 0)}; + const std::array, 2> sigma0_chunks = {sigma0_chunks_0, sigma0_chunks_1}; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(0), -1) - + (a_chunks[0][0] + a_chunks[0][1] * (one << 3) + + a_chunks[0][2] * (one << 7) + a_chunks[0][3] * (one << 18)); + auto constraint_2 = + (a_chunks[0][0] - 7) * (a_chunks[0][0] - 6) * (a_chunks[0][0] - 5) * + (a_chunks[0][0] - 4) * (a_chunks[0][0] - 3) * (a_chunks[0][0] - 2) * + (a_chunks[0][0] - 1) * a_chunks[0][0]; + auto constraint_3 = + sigma0_chunks[1][0] + sigma0_chunks[1][1] * (1 << 16) + + sigma0_chunks[1][2] * (one << 32) + sigma0_chunks[1][3] * (one << 48) - + (a_chunks[1][1] * (1 + (one << 56) + (one << 34)) + + a_chunks[1][2] * ((one << 8) + 1 + (one << 42)) + + a_chunks[1][3] * ((1 << 30) + (1 << 22) + 1) + + a_chunks[1][0] * ((one << 50) + (1 << 28))); + + selectors[0] = bp.add_gate( + {constraint_1, constraint_2, constraint_3}); + + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][0], a_chunks[1][0]}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_chunks[0][1] * 1024}}; + lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][1], a_chunks[1][1]}}; + lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_chunks[0][2] * 8}}; + lookup_constraint lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][2], a_chunks[1][2]}}; + lookup_constraint lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][3], a_chunks[1][3]}}; + lookup_constraint lookup_constraint_7 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma0_chunks[0][0], sigma0_chunks[1][0]}}; + lookup_constraint lookup_constraint_8 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma0_chunks[0][1], sigma0_chunks[1][1]}}; + lookup_constraint lookup_constraint_9 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma0_chunks[0][2], sigma0_chunks[1][2]}}; + lookup_constraint lookup_constraint_10 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma0_chunks[0][3], sigma0_chunks[1][3]}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, + lookup_constraint_2, + lookup_constraint_3, + lookup_constraint_4, + lookup_constraint_5, + lookup_constraint_6, + lookup_constraint_7, + lookup_constraint_8, + lookup_constraint_9, + lookup_constraint_10 + }); + return selectors; + } + + template + std::array generate_sigma1_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + std::array selectors; + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + + const std::array b_chunks_0 = { + var(component.W(1), 0), var(component.W(2), 0), + var(component.W(3), 0), var(component.W(4), 0)}; + const std::array b_chunks_1 = { + var(component.W(1), -1), var(component.W(2), -1), + var(component.W(3), -1), var(component.W(4), -1)}; + const std::array, 2> b_chunks = {b_chunks_0, b_chunks_1}; + + const std::array sigma1_chunks_1 = { + var(component.W(5), -1), var(component.W(6), -1), + var(component.W(7), -1), var(component.W(8), -1)}; + const var integral_a2 = var(component.W(8), 0); + const std::array integral_a2_chunks = { + var(component.W(5), 0), var(component.W(6), 0), + var(component.W(7), 0)}; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(0), 0) - + (b_chunks[0][0] + b_chunks[0][1] * (1 << 10) + + b_chunks[0][2] * (1 << 17) + b_chunks[0][3] * (1 << 19)); + auto constraint_2 = + (b_chunks[0][2] - 3) * (b_chunks[0][2] - 2) * + (b_chunks[0][2] - 1) * b_chunks[0][2]; + auto constraint_3 = + sigma1_chunks_1[0] + sigma1_chunks_1[1] * (one << 16) + + sigma1_chunks_1[2] * (one << 32) + sigma1_chunks_1[3] * (one << 48) - + (b_chunks[1][1] * (1 + (one << 50) + (one << 46)) + + b_chunks[1][2] * ((one << 14) + 1 + (one << 60)) + + b_chunks[1][3] * ((one << 18) + (one << 4) + 1) + + b_chunks[1][0] * ((one << 30) + (1 << 26))); + + auto constraint_4 = -integral_a2 + + integral_a2_chunks[0] + integral_a2_chunks[1] * (1 << 14) + integral_a2_chunks[2] * (1 << 28); + + selectors[0] = bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {b_chunks[0][0] * 16}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {b_chunks[0][0], b_chunks[1][0]}}; + lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {b_chunks[0][1] * 128}}; + lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {b_chunks[0][1], b_chunks[1][1]}}; + lookup_constraint lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {b_chunks[0][3] * 2}}; + lookup_constraint lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {b_chunks[0][2], b_chunks[1][2]}}; + lookup_constraint lookup_constraint_7 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {b_chunks[0][3], b_chunks[1][3]}}; + + lookup_constraint lookup_constraint_8 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[0]}}; + lookup_constraint lookup_constraint_9 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[1]}}; + lookup_constraint lookup_constraint_10 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[2]}}; + lookup_constraint lookup_constraint_11 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[2] * (1 << 10)}}; + lookup_constraint lookup_constraint_12 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(0), -1)}}; + lookup_constraint lookup_constraint_13 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(0), -1) * (1 << 10)}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, + lookup_constraint_2, + lookup_constraint_3, + lookup_constraint_4, + lookup_constraint_5, + lookup_constraint_6, + lookup_constraint_7, + lookup_constraint_8, + lookup_constraint_9, + lookup_constraint_10, + lookup_constraint_11, + lookup_constraint_12, + lookup_constraint_13 + }); + return selectors; + } + + template + std::array generate_message_scheduling_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + + std::array sigma0_selectors = + generate_sigma0_gates(component, bp, assignment, lookup_tables_indices); + typename BlueprintFieldType::integral_type one = 1; + auto m = typename BlueprintFieldType::value_type(2).pow(32); + std::array sigma0_chunks_0 = { + var(component.W(1), 0), var(component.W(2), 0), + var(component.W(3), 0), var(component.W(4), 0)}; + std::array sigma1_chunks_0 = { + var(component.W(5), 0), var(component.W(6), 0), + var(component.W(7), 0), var(component.W(8), 0)}; + std::array sigma1_chunks_1 = { + var(component.W(5), +1), var(component.W(6), +1), + var(component.W(7), +1), var(component.W(8), +1)}; + std::array, 2> sigma1_chunks = {sigma1_chunks_0, sigma1_chunks_1}; + auto constraint_1 = + var(component.W(0), 0) + m * var(component.W(0), +1) - + (var(component.W(0), -1) + var(component.W(1), -1) + sigma0_chunks_0[0] + + sigma0_chunks_0[1] * (one << 8) + sigma0_chunks_0[2] * (one << 16) + + sigma0_chunks_0[3] * (one << 24) + sigma1_chunks[0][0] + + sigma1_chunks[0][1] * (one << 8) + sigma1_chunks[0][2] * (one << 16) + + sigma1_chunks[0][3] * (one << 24)); + auto constraint_2 = + (var(component.W(0), +1) - 3) * (var(component.W(0), +1) - 2) * + (var(component.W(0), +1) - 1) * var(component.W(0), +1); + std::size_t selector_1 = bp.add_gate({constraint_1, constraint_2}); + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma1_chunks[0][0], sigma1_chunks[1][0]}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma1_chunks[0][1], sigma1_chunks[1][1]}}; + lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma1_chunks[0][2], sigma1_chunks[1][2]}}; + lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {sigma1_chunks[0][3], sigma1_chunks[1][3]}}; + std::size_t sigma1_check = bp.add_lookup_gate({ + lookup_constraint_1, + lookup_constraint_2, + lookup_constraint_3, + lookup_constraint_4 + }); + std::array sigma_1_selectors = + generate_sigma1_gates(component, bp, assignment, lookup_tables_indices); + return {sigma0_selectors[0], sigma0_selectors[1], selector_1, + sigma1_check, sigma_1_selectors[0], sigma_1_selectors[1]}; + } + + template + std::array generate_Sigma0_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + using var = typename plonk_sha256_process::var; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + + std::array selectors; + typename BlueprintFieldType::integral_type one = 1; + + var a2 = var(component.W(0), +1); + var a_new = var(component.W(2), -1); + + std::array a_chunks_0 = { + var(component.W(2), +1), var(component.W(3), +1), + var(component.W(4), +1), var(component.W(5), +1)}; + std::array a_chunks_1 = { + var(component.W(2), 0), var(component.W(3), 0), + var(component.W(4), 0), var(component.W(5), 0)}; + std::array, 2> a_chunks = {a_chunks_0, a_chunks_1}; + + std::array Sigma0_chunks_0 = { + var(component.W(5), -1), var(component.W(6), -1), + var(component.W(7), -1), var(component.W(8), -1)}; + std::array Sigma0_chunks_1 = { + var(component.W(0), 0), var(component.W(1), 0), + var(component.W(6), 0), var(component.W(7), 0)}; + std::array, 2> Sigma0_chunks = {Sigma0_chunks_0, Sigma0_chunks_1}; + + std::array a_new_chunks = { + var(component.W(6), +1), var(component.W(7), +1), var(component.W(8), +1)}; + + auto constraint_1 = + a2 - (a_chunks[0][0] + a_chunks[0][1] * (1 << 2) + + a_chunks[0][2] * (1 << 13) + a_chunks[0][3] * (1 << 22)); + auto constraint_2 = + var(component.W(0), -1) - + (a_chunks[1][0] + a_chunks[1][1] * (1 << 4) + + a_chunks[1][2] * (1 << 26) + a_chunks[1][3] * (one << 44)); + auto constraint_3 = + (a_chunks[0][0] - 3) * (a_chunks[0][0] - 2) * + (a_chunks[0][0] - 1) * a_chunks[0][0]; + auto constraint_4 = + Sigma0_chunks[1][0] + Sigma0_chunks[1][1] * (1 << 16) + + Sigma0_chunks[1][2] * (one << 32) + Sigma0_chunks[1][3] * (one << 48) - + (a_chunks[1][0] * ((one << 38) + (1 << 20) + (one << 60)) + + a_chunks[1][1] * ((one << 42) + 1 + (1 << 24)) + + a_chunks[1][2] * ((1 << 22) + (one << 46) + 1) + + a_chunks[1][3] * ((one << 40) + (1 << 18) + 1)); + + auto constraint_7 = -a_new + + a_new_chunks[0] + a_new_chunks[1] * (1 << 14) + a_new_chunks[2] * (1 << 28); + + selectors[0] = bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4, constraint_7}); + + lookup_constraint_type lookup_constraint_1 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_chunks[0][1] * 8}}; + lookup_constraint_type lookup_constraint_2 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][0], a_chunks[1][0]}}; + lookup_constraint_type lookup_constraint_3 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_chunks[0][2] * 32}}; + lookup_constraint_type lookup_constraint_4 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][1], a_chunks[1][1]}}; + lookup_constraint_type lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_chunks[0][3] * 16}}; + lookup_constraint_type lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][2], a_chunks[1][2]}}; + lookup_constraint_type lookup_constraint_7 = + {lookup_tables_indices.at("sha256_sparse_base4/full"), + {a_chunks[0][3], a_chunks[1][3]}}; + lookup_constraint_type lookup_constraint_8 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {Sigma0_chunks[0][0], Sigma0_chunks[1][0]}}; + lookup_constraint_type lookup_constraint_9 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {Sigma0_chunks[0][1], Sigma0_chunks[1][1]}}; + lookup_constraint_type lookup_constraint_10 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {Sigma0_chunks[0][2], Sigma0_chunks[1][2]}}; + lookup_constraint_type lookup_constraint_11 = + {lookup_tables_indices.at("sha256_reverse_sparse_base4/full"), + {Sigma0_chunks[0][3], Sigma0_chunks[1][3]}}; + + lookup_constraint_type lookup_constraint_12 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_new_chunks[0]}}; + lookup_constraint_type lookup_constraint_13 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_new_chunks[1]}}; + lookup_constraint_type lookup_constraint_14 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_new_chunks[2]}}; + lookup_constraint_type lookup_constraint_15 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {a_new_chunks[2] * (1 << 10)}}; + lookup_constraint_type lookup_constraint_16 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(3), -1)}}; + lookup_constraint_type lookup_constraint_17 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(3), -1) * (1 << 8)}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, + lookup_constraint_2, + lookup_constraint_3, + lookup_constraint_4, + lookup_constraint_5, + lookup_constraint_6, + lookup_constraint_7, + lookup_constraint_8, + lookup_constraint_9, + lookup_constraint_10, + lookup_constraint_11, + lookup_constraint_12, + lookup_constraint_13, + lookup_constraint_14, + lookup_constraint_15, + lookup_constraint_16, + lookup_constraint_17 + }); + return selectors; + } + + template + std::array generate_Sigma1_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + std::array selectors; + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + + typename BlueprintFieldType::value_type base7_value = + plonk_sha256_process::base7; + std::array e_chunks_0 = { + var(component.W(2), -1), var(component.W(3), -1), + var(component.W(4), -1), var(component.W(5), -1)}; + std::array e_chunks_1 = { + var(component.W(1), -1), var(component.W(2), 0), + var(component.W(3), 0), var(component.W(4), 0)}; + std::array, 4> e_chunks = {e_chunks_0, e_chunks_1}; + std::array Sigma1_chunks_0 = { + var(component.W(5), +1), var(component.W(6), +1), + var(component.W(7), +1), var(component.W(8), +1) + }; + std::array Sigma1_chunks_1 = { + var(component.W(5), 0), var(component.W(6), -1), + var(component.W(7), -1), var(component.W(8), -1) + }; + std::array, 2> Sigma1_chunks = {Sigma1_chunks_0, Sigma1_chunks_1}; + auto constraint_1 = + var(component.W(0), -1) - + (e_chunks[0][0] + e_chunks[0][1] * (1 << 6) + + e_chunks[0][2] * (1 << 11) + e_chunks[0][3] * (1 << 25)); + auto constraint_2 = + var(component.W(0), 0) - + (e_chunks[1][0] + e_chunks[1][1] * base7_value.pow(6) + + e_chunks[1][2] * base7_value.pow(11) + e_chunks[1][3] * base7_value.pow(25)); + auto constraint_3 = + Sigma1_chunks[1][0] + Sigma1_chunks[1][1] * base7_value.pow(8) + + Sigma1_chunks[1][2] * base7_value.pow(16) + Sigma1_chunks[1][3] * base7_value.pow(24) - + (e_chunks[1][1] * (base7_value.pow(27) + base7_value.pow(13) + 1) + + e_chunks[1][2] * (base7_value.pow(5) + 1 + base7_value.pow(18)) + + e_chunks[1][3] * (base7_value.pow(19) + base7_value.pow(14) + 1) + + e_chunks[1][0] * (base7_value.pow(26) + base7_value.pow(21) + base7_value.pow(7))); + + selectors[0] = bp.add_gate({constraint_1, constraint_2, constraint_3}); + + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_sparse_base7/first_column"), + {e_chunks[0][1] * 256}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_sparse_base7/full"), + {e_chunks[0][0], e_chunks[1][0]}}; + lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_sparse_base7/first_column"), + {e_chunks[0][1] * 512}}; + lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_sparse_base7/full"), + {e_chunks[0][1], e_chunks[1][1]}}; + lookup_constraint lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base7/first_column"), + {e_chunks[0][3] * 128}}; + lookup_constraint lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base7/full"), + {e_chunks[0][2], e_chunks[1][2]}}; + lookup_constraint lookup_constraint_7 = + {lookup_tables_indices.at("sha256_sparse_base7/full"), + {e_chunks[0][3], e_chunks[1][3]}}; + lookup_constraint lookup_constraint_8 = + {lookup_tables_indices.at("sha256_reverse_sparse_base7/full"), + {Sigma1_chunks[0][0], Sigma1_chunks[1][0]}}; + lookup_constraint lookup_constraint_9 = + {lookup_tables_indices.at("sha256_reverse_sparse_base7/full"), + {Sigma1_chunks[0][1], Sigma1_chunks[1][1]}}; + lookup_constraint lookup_constraint_10 = + {lookup_tables_indices.at("sha256_reverse_sparse_base7/full"), + {Sigma1_chunks[0][2], Sigma1_chunks[1][2]}}; + lookup_constraint lookup_constraint_11 = + {lookup_tables_indices.at("sha256_reverse_sparse_base7/full"), + {Sigma1_chunks[0][3], Sigma1_chunks[1][3]}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, + lookup_constraint_2, + lookup_constraint_3, + lookup_constraint_4, + lookup_constraint_5, + lookup_constraint_6, + lookup_constraint_7, + lookup_constraint_8, + lookup_constraint_9, + lookup_constraint_10, + lookup_constraint_11 + }); + return selectors; + } + + template + std::array generate_Maj_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type lookup_tables_indices + ) { + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + std::array selectors; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(0), 0) + var(component.W(1), 0) * (1 << 16) + + var(component.W(2), 0) * (one << 32) + var(component.W(3), 0) * (one << 48) - + (var(component.W(0), +1) + var(component.W(1), +1) + var(component.W(4), +1)); + selectors[0] = bp.add_gate({constraint_1}); + + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_maj/full"), {var(component.W(5), 0), var(component.W(0), 0)}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_maj/full"), {var(component.W(6), 0), var(component.W(1), 0)}}; + lookup_constraint lookup_constraint_3= + {lookup_tables_indices.at("sha256_maj/full"), {var(component.W(7), 0), var(component.W(2), 0)}}; + lookup_constraint lookup_constraint_4= + {lookup_tables_indices.at("sha256_maj/full"), {var(component.W(8), 0), var(component.W(3), 0)}}; + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, lookup_constraint_2, lookup_constraint_3, lookup_constraint_4 + }); + return selectors; + } + + template + std::array generate_Ch_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + using var = typename plonk_sha256_process::var; + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + + std::array selectors; + + typename BlueprintFieldType::value_type base7_value = + plonk_sha256_process::base7; + var e_new = var(component.W(4), +1); + std::array integral_a2_chunks = { + var(component.W(6), -1), var(component.W(7), -1), var(component.W(8), -1)}; + std::array ch_chunks_0 = { + var(component.W(5), +1), var(component.W(6), +1), + var(component.W(7), +1), var(component.W(8), +1)}; + std::array ch_chunks_1 = { + var(component.W(0), 0), var(component.W(1), 0), + var(component.W(2), 0), var(component.W(3), 0)}; + std::array, 2> ch_chunks = {ch_chunks_0, ch_chunks_1}; + + auto constraint_1 = + ch_chunks[1][0] + ch_chunks[1][1] * base7_value.pow(8) + + ch_chunks[1][2] * base7_value.pow(16) + ch_chunks[1][3] * base7_value.pow(24) - + (var(component.W(0), -1) + 2 * var(component.W(1), -1) + 3 * var(component.W(0), +1)); + + + auto constraint_2 = -1 * e_new + + integral_a2_chunks[0] + integral_a2_chunks[1] * (1 << 14) + integral_a2_chunks[2] * (1 << 28); + + selectors[0] = bp.add_gate({constraint_1, constraint_2}); + + lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_ch/full"), + {ch_chunks[0][0], ch_chunks[1][0]}}; + lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_ch/full"), + {ch_chunks[0][1], ch_chunks[1][1]}}; + lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_ch/full"), + {ch_chunks[0][2], ch_chunks[1][2]}}; + lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_ch/full"), + {ch_chunks[0][3], ch_chunks[1][3]}}; + + lookup_constraint lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[0]}}; + lookup_constraint lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[1]}}; + lookup_constraint lookup_constraint_7 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {integral_a2_chunks[2]}}; + lookup_constraint lookup_constraint_8 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(4), 0)}}; + lookup_constraint lookup_constraint_9 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(4), 0) * (1 << 9)}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, lookup_constraint_2, lookup_constraint_3, lookup_constraint_4, + lookup_constraint_5, lookup_constraint_6, lookup_constraint_7, lookup_constraint_8, lookup_constraint_9 + }); + return selectors; + } + + template + std::array generate_compression_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type &lookup_tables_indices + ) { + using var = typename plonk_sha256_process::var; + + std::array sigma_1_selectors = generate_Sigma1_gates(component, bp, assignment, lookup_tables_indices); + std::array ch_selectors = generate_Ch_gates(component, bp, assignment, lookup_tables_indices); + auto m = typename BlueprintFieldType::value_type(2).pow(32); + auto constraint_1 = + var(component.W(4), +1) - + (var(component.W(2), 0) + var(component.W(5), -1) + var(component.W(6), -1) * (1 << 8) + + var(component.W(7), -1) * (1 << 16) + var(component.W(8), -1) * (1 << 24) + + var(component.W(5), 0) + var(component.W(6), 0) * (1 << 8) + + var(component.W(7), 0) * (1 << 16) + var(component.W(8), 0) * (1 << 24) + + var(component.C(0), 0, true, var::column_type::constant) + var(component.W(3), 0)); + auto constraint_2 = var(component.W(4), 0) + m * var(component.W(4), -1) - + (var(component.W(1), 0) + var(component.W(4), +1)); + auto constraint_3 = + (var(component.W(4), -1) - 5) * (var(component.W(4), -1) - 4) * (var(component.W(4), -1) - 3) * + (var(component.W(4), -1) - 2) * (var(component.W(4), -1) - 1) * var(component.W(4), -1); + std::size_t selector_2 = bp.add_gate({constraint_1, constraint_2, constraint_3}); + auto constraint_4 = + var(component.W(2), +1) + m * var(component.W(3), +1) - + (var(component.W(4), 0) + var(component.W(5), +1) + var(component.W(6), +1) * (1 << 8) + + var(component.W(7), +1) * (1 << 16) + var(component.W(8), +1) * (1 << 24) + + var(component.W(5), 0) + var(component.W(6), 0) * (1 << 8) + + var(component.W(7), 0) * (1 << 16) + var(component.W(8), 0) * (1 << 24)); + auto constraint_5 = + (var(component.W(3), +1) - 6) * (var(component.W(3), +1) - 5) * (var(component.W(3), +1) - 4) * + (var(component.W(3), +1) - 3) * (var(component.W(3), +1) - 2) * (var(component.W(3), +1) - 1) * + var(component.W(3), +1); + std::size_t selector_3 = bp.add_gate({constraint_4, constraint_5}); + std::array maj_selectors = generate_Maj_gates(component, bp, assignment, lookup_tables_indices); + std::array sigma_0_selectors = generate_Sigma0_gates(component, bp, assignment, lookup_tables_indices); + + auto constraint_out_1 = var(component.W(0), +1) + m * var(component.W(4), +1) - + (var(component.W(0), 0) + var(component.W(4), 0)); + auto constraint_out_2 = var(component.W(1), +1) + m * var(component.W(5), +1) - + (var(component.W(1), 0) + var(component.W(5), 0)); + auto constraint_out_3 = var(component.W(2), +1) + m * var(component.W(6), +1) - + (var(component.W(2), 0) + var(component.W(6), 0)); + auto constraint_out_4 = var(component.W(3), +1) + m * var(component.W(7), +1) - + (var(component.W(3), 0) + var(component.W(7), 0)); + + auto constraint_out_5 = var(component.W(4), +1) * (var(component.W(4), +1) - 1); + auto constraint_out_6 = var(component.W(5), +1) * (var(component.W(5), +1) - 1); + auto constraint_out_7 = var(component.W(6), +1) * (var(component.W(6), +1) - 1); + auto constraint_out_8 = var(component.W(7), +1) * (var(component.W(7), +1) - 1); + + std::size_t selector_6 = + bp.add_gate({constraint_out_1, constraint_out_2, constraint_out_3, constraint_out_4, + constraint_out_5, constraint_out_6, constraint_out_7, constraint_out_8}); + return { + sigma_1_selectors[0], sigma_1_selectors[1], + sigma_0_selectors[0], sigma_0_selectors[1], + selector_2, selector_3, + maj_selectors[0], maj_selectors[1], + ch_selectors[0], ch_selectors[1], + selector_6 + }; + } + + template + std::array generate_output_check_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename lookup_library::left_reserved_type lookup_tables_indices + ) { + using var = typename plonk_sha256_process::var; + + std::array selectors; + + auto constraint_1 = -1 * var(component.W(3), 0) + + var(component.W(0), 0) + var(component.W(1), 0) * (1 << 14) + var(component.W(2), 0) * (1 << 28); + auto constraint_2 = -1 * var(component.W(7), 0) + + var(component.W(4), 0) + var(component.W(5), 0) * (1 << 14) + var(component.W(6), 0) * (1 << 28); + + selectors[0] = bp.add_gate({constraint_1, constraint_2}); + + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_1 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(0), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_2 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(1), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_3 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(2), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_4 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(2), 0) * (1 << 10)}}; + + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_5 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(4), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_6 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(5), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_7 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(6), 0)}}; + crypto3::zk::snark::plonk_lookup_constraint lookup_constraint_8 = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), {var(component.W(6), 0) * (1 << 10)}}; + + selectors[1] = bp.add_lookup_gate({ + lookup_constraint_1, lookup_constraint_2, lookup_constraint_3, lookup_constraint_4, + lookup_constraint_5, lookup_constraint_6, lookup_constraint_7, lookup_constraint_8 + }); + + return selectors; + } + + } // namespace detail + + template + std::array generate_gates( + const plonk_sha256_process &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha256_process::input_type &instance_input, + const typename lookup_library::left_reserved_type lookup_tables_indices + ) { + auto message_scheduling_selectors = + detail::generate_message_scheduling_gates(component, bp, assignment, lookup_tables_indices); + auto compression_selectors = detail::generate_compression_gates(component, bp, assignment, lookup_tables_indices); + auto final_selectors = detail::generate_output_check_gates(component, bp, assignment, lookup_tables_indices); + + return { + message_scheduling_selectors[0], message_scheduling_selectors[1], + message_scheduling_selectors[2], message_scheduling_selectors[3], + message_scheduling_selectors[4], message_scheduling_selectors[5], + compression_selectors[0], compression_selectors[1], compression_selectors[2], + compression_selectors[3], compression_selectors[4], compression_selectors[5], + compression_selectors[6], compression_selectors[7], compression_selectors[8], + compression_selectors[9], compression_selectors[10], + final_selectors[0], final_selectors[1] + }; + } + + template + void generate_copy_constraints( + const plonk_sha256_process &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256_process::input_type + &instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index + 2; + using var = typename plonk_sha256_process::var; + + for (std::size_t i = 1; i <= 15; ++i) { + bp.add_copy_constraint( + {var(component.W(0), row + (i - 1) * 5 + 0, false), instance_input.input_words[i]}); + } + for (std::size_t i = 9; i <= 15; ++i) { + bp.add_copy_constraint( + {var(component.W(0), row + (i - 9) * 5 + 1, false), instance_input.input_words[i]}); + } + for (std::size_t i = 0; i <= 15; ++i) { + bp.add_copy_constraint( + {var(component.W(1), row + (i - 0) * 5 + 1, false), instance_input.input_words[i]}); + } + for (std::size_t i = 14; i <= 15; ++i) { + bp.add_copy_constraint( + {var(component.W(0), row + (i - 14) * 5 + 4, false), instance_input.input_words[i]}); + } + + for (std::size_t round = 0; round < 48; round++) { + if (round >= 2) { + bp.add_copy_constraint({var(component.W(0), row + round * 5 + 4, false), + var(component.W(0), row + (round - 2) * 5 + 2, false)}); + } + if (round >= 7) { + bp.add_copy_constraint({var(component.W(0), row + round * 5 + 1, false), + var(component.W(0), row + (round - 7) * 5 + 2, false)}); + } + if (round >= 15) { + bp.add_copy_constraint({var(component.W(0), row + round * 5 + 0, false), + var(component.W(0), row + (round - 15) * 5 + 2, false)}); + } + if (round >= 16) { + bp.add_copy_constraint({var(component.W(1), row + round * 5 + 1, false), + var(component.W(0), row + (round - 16) * 5 + 2, false)}); + } + + bp.add_copy_constraint({var(component.W(0), row + round * 5 + 2, false), + var(component.W(8), row + round * 5 + 4, false)}); + } + row = row + 240; + + for (std::size_t round = 1; round < 64; round++) { + bp.add_copy_constraint({var(component.W(0), row + round * 8 + 0, false), + var(component.W(4), row + (round - 1) * 8 + 3, false)}); // e = e_new + bp.add_copy_constraint({var(component.W(0), row + round * 8 + 7, false), + var(component.W(2), row + (round - 1) * 8 + 5, false)}); // a = a_new + + bp.add_copy_constraint({var(component.W(1), row + round * 8 + 5, false), + var(component.W(0), row + (round - 1) * 8 + 5, + false)}); // sparse_values[1] = sparse_values[0] + bp.add_copy_constraint({var(component.W(4), row + round * 8 + 5, false), + var(component.W(1), row + (round - 1) * 8 + 5, + false)}); // sparse_values[2] = sparse_values[1] + bp.add_copy_constraint({var(component.W(1), row + round * 8 + 1, false), + var(component.W(0), row + (round - 1) * 8 + 1, + false)}); // sparse_values[5] = sparse_values[4] + bp.add_copy_constraint({var(component.W(0), row + round * 8 + 3, false), + var(component.W(1), row + (round - 1) * 8 + 1, + false)}); // sparse_values[6] = sparse_values[5] + } + + row = row + 512; + + bp.add_copy_constraint({var(component.W(0), row - 1, false), var(component.W(5), row, false)}); + bp.add_copy_constraint({var(component.W(0), row - 9, false), var(component.W(6), row, false)}); + bp.add_copy_constraint({var(component.W(0), row - 17, false), var(component.W(7), row, false)}); + + bp.add_copy_constraint({var(component.W(0), row - 8, false), var(component.W(5), row + 2, false)}); + bp.add_copy_constraint({var(component.W(0), row - 16, false), var(component.W(6), row + 2, false)}); + bp.add_copy_constraint({var(component.W(0), row - 24, false), var(component.W(7), row + 2, false)}); + + for (std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint({var(component.W(i), row, false), instance_input.input_state[i]}); + bp.add_copy_constraint({var(component.W(i), row + 2, false), instance_input.input_state[i + 4]}); + } + + bp.add_copy_constraint({var(component.W(4), row, false), var(component.W(3), row + 4, false)}); + bp.add_copy_constraint({var(component.W(5), row, false), var(component.W(7), row + 4, false)}); + bp.add_copy_constraint({var(component.W(6), row, false), var(component.W(3), row + 5, false)}); + bp.add_copy_constraint({var(component.W(7), row, false), var(component.W(7), row + 5, false)}); + + bp.add_copy_constraint({var(component.W(4), row + 2, false), var(component.W(3), row + 6, false)}); + bp.add_copy_constraint({var(component.W(5), row + 2, false), var(component.W(7), row + 6, false)}); + bp.add_copy_constraint({var(component.W(6), row + 2, false), var(component.W(3), row + 7, false)}); + bp.add_copy_constraint({var(component.W(7), row + 2, false), var(component.W(7), row + 7, false)}); + } + + template + typename plonk_sha256_process::result_type + generate_circuit( + const plonk_sha256_process &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha256_process::input_type + &instance_input, + const std::size_t start_row_index + ) { + + detail::generate_assignments_constant(component, bp, assignment, instance_input, start_row_index); + std::size_t j = start_row_index; + j = j + 2; + auto selector_indices = generate_gates(component, bp, assignment, instance_input, bp.get_reserved_indices()); + + // Message selectors + assignment.enable_selector(selector_indices[0], j + 1, j + 239, 5);//sigma0 + assignment.enable_selector(selector_indices[1], j + 1, j + 239, 5);//sigma0 + assignment.enable_selector(selector_indices[2], j + 2, j + 239, 5);//selector1 + assignment.enable_selector(selector_indices[3], j + 2, j + 239, 5);//sigma1 chunk check + assignment.enable_selector(selector_indices[4], j + 4, j + 239, 5);//sigma1 + assignment.enable_selector(selector_indices[5], j + 4, j + 239, 5);//sigma1 + + // Compression selectors + j = j + 240; + assignment.enable_selector(selector_indices[6], j + 1, j + 511, 8); // Sigma1 + assignment.enable_selector(selector_indices[7], j + 1, j + 511, 8); // Sigma1 + assignment.enable_selector(selector_indices[8], j + 6, j + 511, 8); // Sigma0 + assignment.enable_selector(selector_indices[9], j + 6, j + 511, 8); // Sigma0 + assignment.enable_selector(selector_indices[10], j + 3, j + 511, 8); // selector2 + assignment.enable_selector(selector_indices[11], j + 4, j + 511, 8); // selector3 + assignment.enable_selector(selector_indices[12], j + 4, j + 511, 8); // Maj_selector + assignment.enable_selector(selector_indices[13], j + 4, j + 511, 8); // Maj_selector + assignment.enable_selector(selector_indices[14], j + 2, j + 511, 8); // Ch_selector + assignment.enable_selector(selector_indices[15], j + 2, j + 511, 8); // Ch_selector + + j = j + 512; + assignment.enable_selector(selector_indices[16], j, j + 2, 2); // selector6 + + j = j + 4; + assignment.enable_selector(selector_indices[17], j, j + 3, 1); + assignment.enable_selector(selector_indices[18], j, j + 3, 1); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + return typename plonk_sha256_process::result_type( + component, start_row_index); + } + + template + typename plonk_sha256_process::result_type + generate_assignments( + const plonk_sha256_process &component, + assignment> + &assignment, + const typename plonk_sha256_process::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_sha256_process; + + std::size_t row = start_row_index; + typename BlueprintFieldType::integral_type one = 1; + std::array input_state = { + var_value(assignment, instance_input.input_state[0]), + var_value(assignment, instance_input.input_state[1]), + var_value(assignment, instance_input.input_state[2]), + var_value(assignment, instance_input.input_state[3]), + var_value(assignment, instance_input.input_state[4]), + var_value(assignment, instance_input.input_state[5]), + var_value(assignment, instance_input.input_state[6]), + var_value(assignment, instance_input.input_state[7])}; + std::array message_scheduling_words; + for (std::size_t i = 0; i < 16; i++) { + message_scheduling_words[i] = var_value(assignment, instance_input.input_words[i]); + } + typename BlueprintFieldType::value_type a = input_state[0]; + typename BlueprintFieldType::value_type b = input_state[1]; + typename BlueprintFieldType::value_type c = input_state[2]; + typename BlueprintFieldType::value_type d = input_state[3]; + typename BlueprintFieldType::value_type e = input_state[4]; + typename BlueprintFieldType::value_type f = input_state[5]; + typename BlueprintFieldType::value_type g = input_state[6]; + typename BlueprintFieldType::value_type h = input_state[7]; + + std::array sparse_values {}; + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(component.W(i), row) = input_state[i]; + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(32); + { + nil::marshalling::status_type status; + std::vector input_state_sparse_all = + nil::marshalling::pack(integral_input_state_sparse, + status); + std::copy(input_state_sparse_all.end() - 32, input_state_sparse_all.end(), + input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {32}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse( + input_state_sparse, input_state_sparse_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(i), row + 1) = input_state_sparse_chunks[1][0]; + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + for (std::size_t i = 4; i < 8; i++) { + assignment.witness(component.W(i), row) = input_state[i]; + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(32); + { + nil::marshalling::status_type status; + std::vector input_state_sparse_all = + nil::marshalling::pack(integral_input_state_sparse, + status); + std::copy(input_state_sparse_all.end() - 32, input_state_sparse_all.end(), + input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {32}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse( + input_state_sparse, input_state_sparse_sizes, + plonk_sha256_process::base7); + assignment.witness(component.W(i), row + 1) = input_state_sparse_chunks[1][0]; + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + row = row + 2; + std::vector sigma_sizes = {8, 8, 8, 8}; + std::vector ch_and_maj_sizes = {8, 8, 8, 8}; + typename BlueprintFieldType::value_type base4_value = + plonk_sha256_process::base4; + typename BlueprintFieldType::value_type base7_value = + plonk_sha256_process::base7; + for (std::size_t i = row; i < row + 236; i = i + 5) { + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type(message_scheduling_words[(i - row) / 5 + 1].data); + assignment.witness(component.W(0), i) = message_scheduling_words[(i - row) / 5 + 1]; + std::vector a(32); + { + nil::marshalling::status_type status; + std::vector a_all = + nil::marshalling::pack(integral_a, status); + std::copy(a_all.end() - 32, a_all.end(), a.begin()); + } + + std::vector a_sizes = {3, 4, 11, 14}; + std::array, 2> a_chunks = + detail::split_and_sparse( + a, a_sizes, plonk_sha256_process::base4); + assignment.witness(component.W(1), i) = a_chunks[0][0]; + assignment.witness(component.W(2), i) = a_chunks[0][1]; + assignment.witness(component.W(3), i) = a_chunks[0][2]; + assignment.witness(component.W(4), i) = a_chunks[0][3]; + assignment.witness(component.W(7), i) = a_chunks[1][0]; + assignment.witness(component.W(0), i + 1) = message_scheduling_words[(i - row) / 5 + 9]; + assignment.witness(component.W(1), i + 1) = message_scheduling_words[(i - row) / 5]; + assignment.witness(component.W(2), i + 1) = a_chunks[1][1]; + assignment.witness(component.W(3), i + 1) = a_chunks[1][2]; + assignment.witness(component.W(4), i + 1) = a_chunks[1][3]; + typename BlueprintFieldType::integral_type sparse_sigma0 = + a_chunks[1][1] * (1 + (one << 56) + (one << 34)) + + a_chunks[1][2] * ((1 << 8) + 1 + (one << 42)) + a_chunks[1][3] * ((1 << 30) + (1 << 22) + 1) + + a_chunks[1][0] * ((one << 50) + (1 << 28)); + std::array, 2> sigma0_chunks = + detail::reversed_sparse_and_split( + sparse_sigma0, sigma_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(5), i + 1) = sigma0_chunks[1][0]; + assignment.witness(component.W(6), i + 1) = sigma0_chunks[1][1]; + assignment.witness(component.W(7), i + 1) = sigma0_chunks[1][2]; + assignment.witness(component.W(8), i + 1) = sigma0_chunks[1][3]; + + assignment.witness(component.W(1), i + 2) = sigma0_chunks[0][0]; + assignment.witness(component.W(2), i + 2) = sigma0_chunks[0][1]; + assignment.witness(component.W(3), i + 2) = sigma0_chunks[0][2]; + assignment.witness(component.W(4), i + 2) = sigma0_chunks[0][3]; + + typename BlueprintFieldType::integral_type integral_b = + typename BlueprintFieldType::integral_type(message_scheduling_words[(i - row) / 5 + 14].data); + std::vector b(32); + { + nil::marshalling::status_type status; + std::vector b_all = + nil::marshalling::pack(integral_b, status); + std::copy(b_all.end() - 32, b_all.end(), b.begin()); + } + + std::vector b_sizes = {10, 7, 2, 13}; + std::array, 2> b_chunks = + detail::split_and_sparse( + b, b_sizes, plonk_sha256_process::base4); + assignment.witness(component.W(0), i + 4) = message_scheduling_words[(i - row) / 5 + 14]; + assignment.witness(component.W(1), i + 4) = b_chunks[0][0]; + assignment.witness(component.W(2), i + 4) = b_chunks[0][1]; + assignment.witness(component.W(3), i + 4) = b_chunks[0][2]; + assignment.witness(component.W(4), i + 4) = b_chunks[0][3]; + + assignment.witness(component.W(1), i + 3) = b_chunks[1][0]; + assignment.witness(component.W(2), i + 3) = b_chunks[1][1]; + assignment.witness(component.W(3), i + 3) = b_chunks[1][2]; + assignment.witness(component.W(4), i + 3) = b_chunks[1][3]; + + typename BlueprintFieldType::integral_type sparse_sigma1 = + b_chunks[1][1] * (1 + (one << 50) + (one << 46)) + + b_chunks[1][2] * ((1 << 14) + 1 + (one << 60)) + b_chunks[1][3] * ((1 << 18) + (1 << 4) + 1) + + b_chunks[1][0] * ((1 << 30) + (1 << 26)); + + std::array, 2> sigma1_chunks = + detail::reversed_sparse_and_split( + sparse_sigma1, sigma_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(5), i + 3) = sigma1_chunks[1][0]; + assignment.witness(component.W(6), i + 3) = sigma1_chunks[1][1]; + assignment.witness(component.W(7), i + 3) = sigma1_chunks[1][2]; + assignment.witness(component.W(8), i + 3) = sigma1_chunks[1][3]; + + assignment.witness(component.W(5), i + 2) = sigma1_chunks[0][0]; + assignment.witness(component.W(6), i + 2) = sigma1_chunks[0][1]; + assignment.witness(component.W(7), i + 2) = sigma1_chunks[0][2]; + assignment.witness(component.W(8), i + 2) = sigma1_chunks[0][3]; + typename BlueprintFieldType::value_type sum = + message_scheduling_words[(i - row) / 5 + 9] + message_scheduling_words[(i - row) / 5] + + sigma1_chunks[0][0] + sigma0_chunks[0][0] + + (one << 8) * (sigma1_chunks[0][1] + sigma0_chunks[0][1]) + + (one << 16) * (sigma1_chunks[0][2] + sigma0_chunks[0][2]) + + (one << 24) * (sigma1_chunks[0][3] + sigma0_chunks[0][3]); + message_scheduling_words[(i - row) / 5 + 16] = + typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + assignment.witness(component.W(0), i + 2) = message_scheduling_words[(i - row) / 5 + 16]; + + typename BlueprintFieldType::integral_type integral_a2 = + typename BlueprintFieldType::integral_type(message_scheduling_words[(i - row) / 5 + 16].data); + assignment.witness(component.W(5), i + 4) = ( ((1 << 14) - 1) & (integral_a2) ); + assignment.witness(component.W(6), i + 4) = ( ((1 << 14) - 1) & (integral_a2 >> 14) ); + assignment.witness(component.W(7), i + 4) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); + assignment.witness(component.W(8), i + 4) = integral_a2; + + assignment.witness(component.W(0), i + 3) = + (sum - message_scheduling_words[(i - row) / 5 + 16]) * + typename BlueprintFieldType::value_type(2).pow(32).inversed(); + } + row = row + 240; + for (std::size_t i = row; i < row + 512; i = i + 8) { + assignment.witness(component.W(0), i) = e; + typename BlueprintFieldType::integral_type integral_e = + typename BlueprintFieldType::integral_type(e.data); + std::vector e_bits(32); + { + nil::marshalling::status_type status; + std::vector e_bits_all = + nil::marshalling::pack(integral_e, status); + std::copy(e_bits_all.end() - 32, e_bits_all.end(), e_bits.begin()); + } + + std::vector e_sizes = {6, 5, 14, 7}; + std::array, 2> e_chunks = + detail::split_and_sparse( + e_bits, e_sizes, + plonk_sha256_process::base7); + assignment.witness(component.W(2), i) = e_chunks[0][0]; + assignment.witness(component.W(3), i) = e_chunks[0][1]; + assignment.witness(component.W(4), i) = e_chunks[0][2]; + assignment.witness(component.W(5), i) = e_chunks[0][3]; + + assignment.witness(component.W(1), i) = e_chunks[1][0]; + assignment.witness(component.W(2), i + 1) = e_chunks[1][1]; + assignment.witness(component.W(3), i + 1) = e_chunks[1][2]; + assignment.witness(component.W(4), i + 1) = e_chunks[1][3]; + + sparse_values[4] = typename BlueprintFieldType::integral_type( + (e_chunks[1][0] + e_chunks[1][1] * base7_value.pow(e_sizes[0]) + + e_chunks[1][2] * base7_value.pow(e_sizes[0] + e_sizes[1]) + + e_chunks[1][3] * base7_value.pow(e_sizes[0] + e_sizes[1] + e_sizes[2])) + .data); + assignment.witness(component.W(0), i + 1) = sparse_values[4]; + assignment.witness(component.W(1), i + 1) = sparse_values[5]; + typename BlueprintFieldType::integral_type sparse_Sigma1 = + typename BlueprintFieldType::integral_type( + (e_chunks[1][1] * (base7_value.pow(27) + base7_value.pow(13) + 1) + + e_chunks[1][2] * (base7_value.pow(5) + base7_value.pow(18) + 1) + + e_chunks[1][3] * (base7_value.pow(19) + base7_value.pow(14) + 1) + + e_chunks[1][0] * (base7_value.pow(26) + base7_value.pow(21) + base7_value.pow(7))) + .data); + std::array, 2> Sigma1_chunks = + detail::reversed_sparse_and_split( + sparse_Sigma1, sigma_sizes, + plonk_sha256_process::base7); + assignment.witness(component.W(5), i + 2) = Sigma1_chunks[0][0]; + assignment.witness(component.W(6), i + 2) = Sigma1_chunks[0][1]; + assignment.witness(component.W(7), i + 2) = Sigma1_chunks[0][2]; + assignment.witness(component.W(8), i + 2) = Sigma1_chunks[0][3]; + assignment.witness(component.W(5), i + 1) = Sigma1_chunks[1][0]; + assignment.witness(component.W(6), i + 0) = Sigma1_chunks[1][1]; + assignment.witness(component.W(7), i + 0) = Sigma1_chunks[1][2]; + assignment.witness(component.W(8), i + 0) = Sigma1_chunks[1][3]; + typename BlueprintFieldType::integral_type Sigma1 = + Sigma1_chunks[0][0] + Sigma1_chunks[0][1] * (1 << (sigma_sizes[0])) + + Sigma1_chunks[0][2] * (1 << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma1_chunks[0][3] * (1 << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])); + typename BlueprintFieldType::integral_type sparse_ch = + sparse_values[4] + 2 * sparse_values[5] + 3 * sparse_values[6]; + + std::array, 2> ch_chunks = + detail::reversed_sparse_and_split_ch( + sparse_ch, ch_and_maj_sizes, + plonk_sha256_process::base7); + assignment.witness(component.W(5), i + 3) = ch_chunks[0][0]; + assignment.witness(component.W(6), i + 3) = ch_chunks[0][1]; + assignment.witness(component.W(7), i + 3) = ch_chunks[0][2]; + assignment.witness(component.W(8), i + 3) = ch_chunks[0][3]; + assignment.witness(component.W(0), i + 2) = ch_chunks[1][0]; + assignment.witness(component.W(1), i + 2) = ch_chunks[1][1]; + assignment.witness(component.W(2), i + 2) = ch_chunks[1][2]; + assignment.witness(component.W(3), i + 2) = ch_chunks[1][3]; + + assignment.witness(component.W(0), i + 3) = sparse_values[6]; + assignment.witness(component.W(1), i + 3) = d; + assignment.witness(component.W(2), i + 3) = h; + assignment.witness(component.W(3), i + 3) = message_scheduling_words[(i - row) / 8]; + typename BlueprintFieldType::integral_type ch = ch_chunks[0][0] + ch_chunks[0][1] * (1 << 8) + + ch_chunks[0][2] * (1 << 16) + + ch_chunks[0][3] * (1 << 24); + + typename BlueprintFieldType::value_type tmp1 = h + Sigma1 + ch + + component_type::round_constant[(i - row) / 8] + + message_scheduling_words[(i - row) / 8]; + typename BlueprintFieldType::value_type sum = tmp1 + d; + typename BlueprintFieldType::value_type e_new = + typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + assignment.witness(component.W(4), i + 4) = tmp1; + assignment.witness(component.W(4), i + 3) = e_new; + assignment.witness(component.W(4), i + 2) = + (sum - e_new) * typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).inversed().data); + + typename BlueprintFieldType::integral_type integral_a2 = + typename BlueprintFieldType::integral_type(e_new.data); + assignment.witness(component.W(6), i + 1) = ( ((1 << 14) - 1) & (integral_a2) ); + assignment.witness(component.W(7), i + 1) = ( ((1 << 14) - 1) & (integral_a2 >> 14) ); + assignment.witness(component.W(8), i + 1) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); + + assignment.witness(component.W(0), i + 7) = a; + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type(a.data); + std::vector a_bits(32); + { + nil::marshalling::status_type status; + std::vector a_bits_all = + nil::marshalling::pack(integral_a, status); + std::copy(a_bits_all.end() - 32, a_bits_all.end(), a_bits.begin()); + } + + std::vector a_sizes = {2, 11, 9, 10}; + std::array, 2> a_chunks = + detail::split_and_sparse( + a_bits, a_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(2), i + 7) = a_chunks[0][0]; + assignment.witness(component.W(3), i + 7) = a_chunks[0][1]; + assignment.witness(component.W(4), i + 7) = a_chunks[0][2]; + assignment.witness(component.W(5), i + 7) = a_chunks[0][3]; + + assignment.witness(component.W(2), i + 6) = a_chunks[1][0]; + assignment.witness(component.W(3), i + 6) = a_chunks[1][1]; + assignment.witness(component.W(4), i + 6) = a_chunks[1][2]; + assignment.witness(component.W(5), i + 6) = a_chunks[1][3]; + + sparse_values[0] = typename BlueprintFieldType::integral_type( + (a_chunks[1][0] + a_chunks[1][1] * base4_value.pow(a_sizes[0]) + + a_chunks[1][2] * base4_value.pow(a_sizes[0] + a_sizes[1]) + + a_chunks[1][3] * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2])) + .data); + assignment.witness(component.W(0), i + 5) = sparse_values[0]; + assignment.witness(component.W(1), i + 5) = sparse_values[1]; + typename BlueprintFieldType::integral_type sparse_Sigma0 = + (a_chunks[1][0] * ((one << 38) + (1 << 20) + (one << 60)) + + a_chunks[1][1] * ((one << 42) + 1 + (1 << 24)) + + a_chunks[1][2] * ((1 << 22) + (one << 46) + 1) + + a_chunks[1][3] * ((one << 40) + (1 << 18) + 1)); + std::array, 2> Sigma0_chunks = + detail::reversed_sparse_and_split( + sparse_Sigma0, sigma_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(5), i + 5) = Sigma0_chunks[0][0]; + assignment.witness(component.W(6), i + 5) = Sigma0_chunks[0][1]; + assignment.witness(component.W(7), i + 5) = Sigma0_chunks[0][2]; + assignment.witness(component.W(8), i + 5) = Sigma0_chunks[0][3]; + assignment.witness(component.W(0), i + 6) = Sigma0_chunks[1][0]; + assignment.witness(component.W(1), i + 6) = Sigma0_chunks[1][1]; + assignment.witness(component.W(6), i + 6) = Sigma0_chunks[1][2]; + assignment.witness(component.W(7), i + 6) = Sigma0_chunks[1][3]; + + typename BlueprintFieldType::integral_type Sigma0 = + Sigma0_chunks[0][0] + Sigma0_chunks[0][1] * (1 << sigma_sizes[0]) + + Sigma0_chunks[0][2] * (1 << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma0_chunks[0][3] * (1 << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])); + + typename BlueprintFieldType::integral_type sparse_maj = + (sparse_values[0] + sparse_values[1] + sparse_values[2]); + std::array, 2> maj_chunks = + detail::reversed_sparse_and_split_maj( + sparse_maj, ch_and_maj_sizes, + plonk_sha256_process::base4); + assignment.witness(component.W(5), i + 4) = maj_chunks[0][0]; + assignment.witness(component.W(6), i + 4) = maj_chunks[0][1]; + assignment.witness(component.W(7), i + 4) = maj_chunks[0][2]; + assignment.witness(component.W(8), i + 4) = maj_chunks[0][3]; + assignment.witness(component.W(0), i + 4) = maj_chunks[1][0]; + assignment.witness(component.W(1), i + 4) = maj_chunks[1][1]; + assignment.witness(component.W(2), i + 4) = maj_chunks[1][2]; + assignment.witness(component.W(3), i + 4) = maj_chunks[1][3]; + typename BlueprintFieldType::integral_type maj = maj_chunks[0][0] + maj_chunks[0][1] * (1 << 8) + + maj_chunks[0][2] * (1 << 16) + + maj_chunks[0][3] * (1 << 24); + assignment.witness(component.W(4), i + 5) = sparse_values[2]; + typename BlueprintFieldType::value_type sum1 = tmp1 + Sigma0 + maj; + typename BlueprintFieldType::value_type a_new = + typename BlueprintFieldType::integral_type(sum1.data) % + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + assignment.witness(component.W(2), i + 5) = a_new; + assignment.witness(component.W(3), i + 5) = + (sum1 - a_new) * typename BlueprintFieldType::value_type(2).pow(32).inversed(); + + integral_a2 = + typename BlueprintFieldType::integral_type(a_new.data); + assignment.witness(component.W(6), i + 7) = ( ((1 << 14) - 1) & (integral_a2) ); + assignment.witness(component.W(7), i + 7) = ( ((1 << 14) - 1) & (integral_a2 >> 14) ); + assignment.witness(component.W(8), i + 7) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); + + h = g; + sparse_values[7] = sparse_values[6]; + g = f; + sparse_values[6] = sparse_values[5]; + f = e; + sparse_values[5] = sparse_values[4]; + e = e_new; + d = c; + sparse_values[3] = sparse_values[2]; + c = b; + sparse_values[2] = sparse_values[1]; + b = a; + sparse_values[1] = sparse_values[0]; + a = a_new; + } + std::array output_state = {a, b, c, d, e, f, g, h}; + row = row + 512; + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(component.W(i), row) = input_state[i]; + auto sum = typename BlueprintFieldType::integral_type(input_state[i].data) + + typename BlueprintFieldType::integral_type(output_state[i].data); + assignment.witness(component.W(i), row + 1) = + sum % typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + assignment.witness(component.W(i + 4), row) = output_state[i]; + assignment.witness(component.W(i + 4), row + 1) = + (sum - sum % typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data)) / + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + } + row = row + 2; + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(component.W(i), row) = input_state[i + 4]; + auto sum = typename BlueprintFieldType::integral_type(input_state[i + 4].data) + + typename BlueprintFieldType::integral_type(output_state[i + 4].data); + assignment.witness(component.W(i), row + 1) = + sum % typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + assignment.witness(component.W(i + 4), row) = output_state[i + 4]; + assignment.witness(component.W(i + 4), row + 1) = + (sum - sum % typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data)) / + typename BlueprintFieldType::integral_type( + typename BlueprintFieldType::value_type(2).pow(32).data); + } + + row = row + 2; + typename BlueprintFieldType::integral_type integral_a2; + for (std::size_t i = 0; i < 4; i++) { + integral_a2 = typename BlueprintFieldType::integral_type(output_state[2 * i].data); + assignment.witness(component.W(0), row + i) = ( ((1 << 14) - 1) & (integral_a2) ); + assignment.witness(component.W(1), row + i) = ( ((1 << 14) - 1) & (integral_a2 >> 14) ); + assignment.witness(component.W(2), row + i) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); + assignment.witness(component.W(3), row + i) = integral_a2; + + integral_a2 = typename BlueprintFieldType::integral_type(output_state[2 * i + 1].data); + assignment.witness(component.W(4), row + i) = ( ((1 << 14) - 1) & (integral_a2) ); + assignment.witness(component.W(5), row + i) = ( ((1 << 14) - 1) & (integral_a2 >> 14) ); + assignment.witness(component.W(6), row + i) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); + assignment.witness(component.W(7), row + i) = integral_a2; + } + /*std::vector value_sizes = {14}; + // lookup table for sparse values with base = 4 + for (typename CurveType::scalar_field_type::integral_type i = 0; + i < typename CurveType::scalar_field_type::integral_type(16384); + i++) { + std::vector value(14); + for (std::size_t j = 0; j < 14; j++) { + value[14 - j - 1] = multiprecision::bit_test(i, j); + } + std::array, 2> value_chunks = + detail::split_and_sparse(value, value_sizes, + plonk_sha256_process::base4); + assignment.constant(0)[start_row_index + std::size_t(i)] = value_chunks[0][0]; + assignment.constant(1)[start_row_index + std::size_t(i)] = value_chunks[1][0]; + } + // lookup table for sparse values with base = 7 + for (typename CurveType::scalar_field_type::integral_type i = 0; + i < typename CurveType::scalar_field_type::integral_type(16384); + i++) { + std::vector value(14); + for (std::size_t j = 0; j < 14; j++) { + value[14 - j - 1] = multiprecision::bit_test(i, j); + } + std::array, 2> value_chunks = + detail::split_and_sparse(value, value_sizes, + plonk_sha256_process::base7); + assignment.constant(2)[start_row_index + std::size_t(i)] = value_chunks[0][0]; + assignment.constant(3)[start_row_index + std::size_t(i)] = value_chunks[1][0]; + } + // lookup table for maj function + value_sizes = {8}; + for (typename CurveType::scalar_field_type::integral_type i = 0; + i < typename CurveType::scalar_field_type::integral_type(65535); + i++) { + static std::array, 2> + value = detail::reversed_sparse_and_split(i, value_sizes, + plonk_sha256_process::base4); + assignment.constant(4)[start_row_index + std::size_t(i)] = value[0][0]; + assignment.constant(5)[start_row_index + std::size_t(i)] = i; + } + + // lookup table for ch function + for (typename CurveType::scalar_field_type::integral_type i = 0; + i < typename CurveType::scalar_field_type::integral_type(5765041); + i++) { + static std::array, 2> + value = detail::reversed_sparse_and_split(i, value_sizes, + plonk_sha256_process::base7); + assignment.constant(4)[start_row_index + std::size_t(i)] = value[0][0]; + assignment.constant(5)[start_row_index + std::size_t(i)] = i; + }*/ + + return typename plonk_sha256_process::result_type( + component, start_row_index); + } + + template + typename plonk_sha256_process::result_type + generate_empty_assignments( + const plonk_sha256_process &component, + assignment> + &assignment, + const typename plonk_sha256_process::input_type + instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_sha256_process; + + std::array input_state = { + var_value(assignment, instance_input.input_state[0]), + var_value(assignment, instance_input.input_state[1]), + var_value(assignment, instance_input.input_state[2]), + var_value(assignment, instance_input.input_state[3]), + var_value(assignment, instance_input.input_state[4]), + var_value(assignment, instance_input.input_state[5]), + var_value(assignment, instance_input.input_state[6]), + var_value(assignment, instance_input.input_state[7])}; + std::array input_words; + for (std::size_t i = 0; i < 16; i++) { + input_words[i] = var_value(assignment, instance_input.input_words[i]); + } + + std::array output_state = component_type::calculate(input_state, input_words); + + for (std::size_t i = 0; i < 8; ++i) { + assignment.witness(component.W(i), start_row_index) = output_state[i]; + } + + return typename plonk_sha256_process::result_type( + component, start_row_index, true); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA256_PROCESS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512.hpp new file mode 100644 index 000000000..d318a1d81 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512.hpp @@ -0,0 +1,1068 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA512 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_HPP + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class sha512; + + template + class sha512>: + public plonk_component { + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + + public: + using component_type = plonk_component; + + using sha512_process_component = sha512_process; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return sha512::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(sha512_process_component::get_gate_manifest(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + true + ).merge_with(sha512_process_component::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return + rows_amount_creating_input_words_component + + sha512_process_component::get_rows_amount(witness_amount) * 2 /* + 2 */; + } + + constexpr static const std::size_t gates_amount = 5; + constexpr static const std::size_t rows_amount_creating_input_words_component = 15; + const std::string component_name = "sha512 hash"; +// + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct var_ec_point { + std::array x; + std::array y; + }; + + struct input_type { + var_ec_point R; + var_ec_point A; + std::array M; + + std::vector> all_vars() { + std::vector> result; + result.reserve(20); + result.insert(result.end(), R.x.begin(), R.x.end()); + result.insert(result.end(), R.y.begin(), R.y.end()); + result.insert(result.end(), A.x.begin(), A.x.end()); + result.insert(result.end(), A.y.begin(), A.y.end()); + result.insert(result.end(), M.begin(), M.end()); + return result; + } + }; + + struct result_type { + std::array output_state; + + result_type(const sha512 &component, const std::size_t &start_row_index) { + output_state = {var(component.W(0), start_row_index + component.rows_amount - 3, false), + var(component.W(1), start_row_index + component.rows_amount - 3, false), + var(component.W(2), start_row_index + component.rows_amount - 3, false), + var(component.W(3), start_row_index + component.rows_amount - 3, false), + var(component.W(0), start_row_index + component.rows_amount - 1, false), + var(component.W(1), start_row_index + component.rows_amount - 1, false), + var(component.W(2), start_row_index + component.rows_amount - 1, false), + var(component.W(3), start_row_index + component.rows_amount - 1, false)}; + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(8); + result.insert(result.end(), output_state.begin(), output_state.end()); + return result; + } + }; + + template + sha512(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + sha512(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + sha512(std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + }; + + template + using plonk_sha512 = sha512>; + + template + typename plonk_sha512::result_type + generate_circuit( + const plonk_sha512 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha512::input_type &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_sha512; + using sha512_process_component = typename component_type::sha512_process_component; + using var = typename component_type::var; + + generate_assignments_constant(component, bp, assignment, instance_input, start_row_index); + + auto selector_indices = generate_gates(component, bp, assignment, instance_input); + + std::size_t j = start_row_index; + + assignment.enable_selector(selector_indices[0], j + 1); + assignment.enable_selector(selector_indices[1], j + 4); + assignment.enable_selector(selector_indices[2], j + 7); + assignment.enable_selector(selector_indices[3], j + 10); + assignment.enable_selector(selector_indices[4], j + 13); + + std::array input_words_vars_1; + + for(std::size_t k = 0; k < 4; k++) { + for(std::size_t i = 0; i < 4; i++) { + input_words_vars_1[4*k + i] = var(2*i, start_row_index + 1 + 3*k, false); + } + } + + std::array constants_var = {var(0, start_row_index, false, var::column_type::constant), + var(0, start_row_index + 1, false, var::column_type::constant), + var(0, start_row_index + 2, false, var::column_type::constant), + var(0, start_row_index + 3, false, var::column_type::constant), + var(0, start_row_index + 4, false, var::column_type::constant), + var(0, start_row_index + 5, false, var::column_type::constant), + var(0, start_row_index + 6, false, var::column_type::constant), + var(0, start_row_index + 7, false, var::column_type::constant)}; + sha512_process_component sha512_process_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + typename sha512_process_component::input_type sha_params = {constants_var, input_words_vars_1}; + j = j + 15; + auto sha_output = + generate_circuit(sha512_process_instance, bp, assignment, sha_params, j).output_state; + j += sha512_process_instance.rows_amount; + + // second chunk + std::array input_words_vars_2; + + for(std::size_t i = 0; i < 4; i++) { + input_words_vars_2[i] = var(2*i, start_row_index + 1 + 12, false); + } + + for (std::size_t i = 4; i < 15; i++) { + input_words_vars_2[i] = var(0, start_row_index + 8, false, var::column_type::constant); + } + input_words_vars_2[15] = var(0, start_row_index + 9, false, var::column_type::constant); + + + sha_params = {sha_output, input_words_vars_2}; + generate_circuit(sha512_process_instance, bp, assignment, sha_params, j); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + return typename plonk_sha512::result_type(component, start_row_index); + } + + template + typename plonk_sha512::result_type + generate_assignments( + const plonk_sha512 &component, + assignment> &assignment, + const typename plonk_sha512::input_type &instance_input, + const std::uint32_t start_row_index) { + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = typename sha512::var; + + std::size_t row = start_row_index; + + std::array RAM = { + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.x[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.x[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.x[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.x[3]).data), + + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.y[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.y[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.y[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.R.y[3]).data), + + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.x[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.x[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.x[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.x[3]).data), + + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.y[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.y[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.y[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.A.y[3]).data), + + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.M[0]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.M[1]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.M[2]).data), + typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.M[3]).data) + }; + + + + std::array input_words_values; + typename BlueprintFieldType::integral_type integral_one = 1; + typename BlueprintFieldType::integral_type mask = ((integral_one<<64) - 1); + input_words_values[0] = (RAM[0]) & mask; + input_words_values[1] = ((RAM[0] >> 64) + (RAM[1] << 2)) & mask; + input_words_values[2] = ((RAM[1] >> 62) + (RAM[2] << 4)) & mask; + input_words_values[3] = ((RAM[2] >> 60) + (RAM[3] << 6) + (RAM[4] << 63)) & mask; + input_words_values[4] = ((RAM[4] >> 1)) & mask; + input_words_values[5] = ((RAM[4] >> 65) + (RAM[5] << 1)) & mask; + input_words_values[6] = ((RAM[5] >> 63) + (RAM[6] << 3)) & mask; + input_words_values[7] = ((RAM[6] >> 61) + (RAM[7] << 5) + (RAM[8] << 62)) & mask; + input_words_values[8] = ((RAM[8] >> 2)) & mask; + input_words_values[9] = ((RAM[9])) & mask; + input_words_values[10] = ((RAM[9] >> 64) + (RAM[10] << 2)) & mask; + input_words_values[11] = ((RAM[10] >> 62) + (RAM[11] << 4) + (RAM[12] << 61)) & mask; + input_words_values[12] = ((RAM[12] >> 3) + (RAM[13] << 63)) & mask; + input_words_values[13] = ((RAM[13] >> 1)) & mask; + input_words_values[14] = ((RAM[13] >> 65) + (RAM[14] << 1)) & mask; + input_words_values[15] = ((RAM[14] >> 63) + (RAM[15] << 3) + (RAM[16] << 60)) & mask; + input_words_values[16] = ((RAM[16] >> 4) + (RAM[17] << 62)) & mask; + input_words_values[17] = ((RAM[17] >> 2)) & mask; + input_words_values[18] = ((RAM[18])) & mask; + input_words_values[19] = ((RAM[18] >> 64) + (RAM[19] << 2) + (integral_one << 60)); + + + for (std::size_t i = 20; i < 31; ++i) { + input_words_values[i] = 0; + } + input_words_values[31] = 1024 + 252; + + + std::array range_chunks; + + typename BlueprintFieldType::integral_type mask22 = ((integral_one<<22) - 1); + typename BlueprintFieldType::integral_type mask21 = ((integral_one<<21) - 1); + typename BlueprintFieldType::integral_type mask20 = ((integral_one<<20) - 1); + typename BlueprintFieldType::integral_type mask19 = ((integral_one<<19) - 1); + typename BlueprintFieldType::integral_type mask18 = ((integral_one<<18) - 1); + typename BlueprintFieldType::integral_type mask17 = ((integral_one<<17) - 1); + typename BlueprintFieldType::integral_type mask16 = ((integral_one<<16) - 1); + typename BlueprintFieldType::integral_type mask14 = ((integral_one<<14) - 1); + typename BlueprintFieldType::integral_type mask13 = ((integral_one<<13) - 1); + + auto row_witness = row + 1; + + // W0,1 W1,1 W1,0 W1, -1 + // 12|34567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[0] = RAM[0] & mask22; + range_chunks[1] = (RAM[0] >> 22) & mask22; + range_chunks[2] = (RAM[0] >> 44) & mask20; + range_chunks[3] = (RAM[0] >> 64) & 0b11; + + assignment.witness(component.W(0), row_witness - 1) = RAM[0]; + assignment.witness(component.W(0), row_witness - 0) = input_words_values[0]; + assignment.witness(component.W(1), row_witness - 1) = range_chunks[0]; + assignment.witness(component.W(1), row_witness - 0) = range_chunks[1]; + assignment.witness(component.W(1), row_witness + 1) = range_chunks[2]; + assignment.witness(component.W(0), row_witness + 1) = range_chunks[3]; + + + // W2,1 W3,1 W3,0 W3, -1 + // 1234|567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[4] = (RAM[1]) & mask22; + range_chunks[5] = (RAM[1] >> 22) & mask22; + range_chunks[6] = (RAM[1] >> 44) & mask18; + range_chunks[7] = (RAM[1] >> 62) & 15; + + assignment.witness(component.W(2), row_witness - 1) = RAM[1]; + assignment.witness(component.W(2), row_witness - 0) = input_words_values[1]; + assignment.witness(component.W(3), row_witness - 1) = range_chunks[4]; + assignment.witness(component.W(3), row_witness - 0) = range_chunks[5]; + assignment.witness(component.W(3), row_witness + 1) = range_chunks[6]; + assignment.witness(component.W(2), row_witness + 1) = range_chunks[7]; + + + + + + // W4,1 W5,1 W5,0 W5, -1 + // 123456|7890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[8] = (RAM[2]) & mask22; + range_chunks[9] = (RAM[2] >> 22) & mask22; + range_chunks[10] = (RAM[2] >> 44) & mask16; + range_chunks[11] = (RAM[2] >> 60) & 0b111111; + + assignment.witness(component.W(4), row_witness - 1) = RAM[2]; + assignment.witness(component.W(4), row_witness - 0) = input_words_values[2]; + assignment.witness(component.W(5), row_witness - 1) = range_chunks[8]; + assignment.witness(component.W(5), row_witness - 0) = range_chunks[9]; + assignment.witness(component.W(5), row_witness + 1) = range_chunks[10]; + assignment.witness(component.W(4), row_witness + 1) = range_chunks[11]; + + + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + range_chunks[12] = (RAM[3]) & mask22; + range_chunks[13] = (RAM[3] >> 22) & mask22; + range_chunks[14] = (RAM[3] >> 44) & mask13; + + assignment.witness(component.W(6), row_witness - 1) = RAM[3]; + assignment.witness(component.W(6), row_witness - 0) = input_words_values[3]; + assignment.witness(component.W(7), row_witness - 1) = range_chunks[12]; + assignment.witness(component.W(7), row_witness - 0) = range_chunks[13]; + assignment.witness(component.W(7), row_witness + 1) = range_chunks[14]; + + + + row_witness += 3; + + // W0,1 W1,1 W1,0 W1,-1 W8,-1 + // 1|234567890123456789012.3456789012345678901234.567890123456789012345|6 + range_chunks[15] = (RAM[4]) & 1; + range_chunks[16] = (RAM[4] >> 1) & mask21; + range_chunks[17] = (RAM[4] >> 22) & mask22; + range_chunks[18] = (RAM[4] >> 44) & mask21; + range_chunks[19] = (RAM[4] >> 65) & 1; + + assignment.witness(component.W(6), row_witness-3 + 1) = range_chunks[15]; + assignment.witness(component.W(8), row_witness - 1) = range_chunks[15]; + + + assignment.witness(component.W(0), row_witness - 1) = RAM[4]; + assignment.witness(component.W(0), row_witness - 0) = input_words_values[4]; + assignment.witness(component.W(1), row_witness - 1) = range_chunks[16]; + assignment.witness(component.W(1), row_witness - 0) = range_chunks[17]; + assignment.witness(component.W(1), row_witness + 1) = range_chunks[18]; + assignment.witness(component.W(0), row_witness + 1) = range_chunks[19]; + + + // W2,1 W3,1 W3,0 W3, -1 + // 123|4567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[20] = (RAM[5]) & mask22; + range_chunks[21] = (RAM[5] >> 22) & mask22; + range_chunks[22] = (RAM[5] >> 44) & mask19; + range_chunks[23] = (RAM[5] >> 63) & 0b111; + + assignment.witness(component.W(2), row_witness - 1) = RAM[5]; + assignment.witness(component.W(2), row_witness - 0) = input_words_values[5]; + assignment.witness(component.W(3), row_witness - 1) = range_chunks[20]; + assignment.witness(component.W(3), row_witness - 0) = range_chunks[21]; + assignment.witness(component.W(3), row_witness + 1) = range_chunks[22]; + assignment.witness(component.W(2), row_witness + 1) = range_chunks[23]; + + + + // W4,1 W5,1 W5,0 W5, -1 + // 12345|67890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[24] = (RAM[6]) & mask22; + range_chunks[25] = (RAM[6] >> 22) & mask22; + range_chunks[26] = (RAM[6] >> 44) & mask17; + range_chunks[27] = (RAM[6] >> 61) & 0b11111; + + assignment.witness(component.W(4), row_witness - 1) = RAM[6]; + assignment.witness(component.W(4), row_witness - 0) = input_words_values[6]; + assignment.witness(component.W(5), row_witness - 1) = range_chunks[24]; + assignment.witness(component.W(5), row_witness - 0) = range_chunks[25]; + assignment.witness(component.W(5), row_witness + 1) = range_chunks[26]; + assignment.witness(component.W(4), row_witness + 1) = range_chunks[27]; + + + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + range_chunks[28] = (RAM[7]) & mask22; + range_chunks[29] = (RAM[7] >> 22) & mask22; + range_chunks[30] = (RAM[7] >> 44) & mask13; + + assignment.witness(component.W(6), row_witness - 1) = RAM[7]; + assignment.witness(component.W(6), row_witness - 0) = input_words_values[7]; + assignment.witness(component.W(7), row_witness - 1) = range_chunks[28]; + assignment.witness(component.W(7), row_witness - 0) = range_chunks[29]; + assignment.witness(component.W(7), row_witness + 1) = range_chunks[30]; + + row_witness += 3; + + + + // W0,1 W1,1 W1,0 W1,-1 + // |1234567890123456789012.3456789012345678901234.56789012345678901234|56 + range_chunks[31] = RAM[8] & 0b11; + range_chunks[32] = (RAM[8] >> 2) & mask20; + range_chunks[33] = (RAM[8] >> 22) & mask22; + range_chunks[34] = (RAM[8] >> 44) & mask22; + + assignment.witness(component.W(6), row_witness-3 + 1) = range_chunks[31]; + + assignment.witness(component.W(0), row_witness - 1) = RAM[8]; + assignment.witness(component.W(0), row_witness - 0) = input_words_values[8]; + assignment.witness(component.W(1), row_witness - 1) = range_chunks[31]; + assignment.witness(component.W(1), row_witness - 0) = range_chunks[32]; + assignment.witness(component.W(1), row_witness + 1) = range_chunks[33]; + assignment.witness(component.W(0), row_witness + 1) = range_chunks[34]; + + + + // W2,1 W3,1 W3,0 W3, -1 + // 12|34567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[35] = (RAM[9]) & mask22; + range_chunks[36] = (RAM[9] >> 22) & mask22; + range_chunks[37] = (RAM[9] >> 44) & mask20; + range_chunks[38] = (RAM[9] >> 64) & 0b11; + + assignment.witness(component.W(2), row_witness - 1) = RAM[9]; + assignment.witness(component.W(2), row_witness - 0) = input_words_values[9]; + assignment.witness(component.W(3), row_witness - 1) = range_chunks[35]; + assignment.witness(component.W(3), row_witness - 0) = range_chunks[36]; + assignment.witness(component.W(3), row_witness + 1) = range_chunks[37]; + assignment.witness(component.W(2), row_witness + 1) = range_chunks[38]; + + + + // W4,1 W5,1 W5,0 W5, -1 + // 1234|567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[39] = (RAM[10]) & mask22; + range_chunks[40] = (RAM[10] >> 22) & mask22; + range_chunks[41] = (RAM[10] >> 44) & mask18; + range_chunks[42] = (RAM[10] >> 62) & 0b1111; + + assignment.witness(component.W(4), row_witness - 1) = RAM[10]; + assignment.witness(component.W(4), row_witness - 0) = input_words_values[10]; + assignment.witness(component.W(5), row_witness - 1) = range_chunks[39]; + assignment.witness(component.W(5), row_witness - 0) = range_chunks[40]; + assignment.witness(component.W(5), row_witness + 1) = range_chunks[41]; + assignment.witness(component.W(4), row_witness + 1) = range_chunks[42]; + + + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + range_chunks[43] = (RAM[11]) & mask22; + range_chunks[44] = (RAM[11] >> 22) & mask22; + range_chunks[45] = (RAM[11] >> 44) & mask13; + + assignment.witness(component.W(6), row_witness - 1) = RAM[11]; + assignment.witness(component.W(6), row_witness - 0) = input_words_values[11]; + assignment.witness(component.W(7), row_witness - 1) = range_chunks[43]; + assignment.witness(component.W(7), row_witness - 0) = range_chunks[44]; + assignment.witness(component.W(7), row_witness + 1) = range_chunks[45]; + + row_witness += 3; + + + // W0,1 W1,1 W1,0 (W1,-1 & W6,1-3) + // 1234567890123456789012.3456789012345678901234.5678901234567890123|456 + range_chunks[46] = (RAM[12]) & 0b111; + range_chunks[47] = (RAM[12] >> 3) & mask19; + range_chunks[48] = (RAM[12] >> 22) & mask22; + range_chunks[49] = (RAM[12] >> 44) & mask22; + + assignment.witness(component.W(6), row_witness-3 + 1) = range_chunks[46]; + + assignment.witness(component.W(0), row_witness - 1) = RAM[12]; + assignment.witness(component.W(0), row_witness - 0) = input_words_values[12]; + assignment.witness(component.W(1), row_witness - 1) = range_chunks[46]; + assignment.witness(component.W(1), row_witness - 0) = range_chunks[47]; + assignment.witness(component.W(1), row_witness + 1) = range_chunks[48]; + assignment.witness(component.W(0), row_witness + 1) = range_chunks[49]; + + + + // W2,1 W3,1 W3,0 W3, -1 W8, -1 + // 1|234567890123456789012.3456789012345678901234.567890123456789012345|6 + range_chunks[50] = (RAM[13]) & 1; + range_chunks[51] = (RAM[13] >> 1) & mask21; + range_chunks[52] = (RAM[13] >> 22) & mask22; + range_chunks[53] = (RAM[13] >> 44) & mask21; + range_chunks[54] = (RAM[13] >> 65) & 1; + + assignment.witness(component.W(2), row_witness - 1) = RAM[13]; + assignment.witness(component.W(2), row_witness - 0) = input_words_values[13]; + assignment.witness(component.W(8), row_witness - 1) = range_chunks[50]; + assignment.witness(component.W(3), row_witness - 1) = range_chunks[51]; + assignment.witness(component.W(3), row_witness - 0) = range_chunks[52]; + assignment.witness(component.W(3), row_witness + 1) = range_chunks[53]; + assignment.witness(component.W(2), row_witness + 1) = range_chunks[54]; + + + + + // W4,1 W5,1 W5,0 W5, -1 + // 123|4567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[55] = (RAM[14]) & mask22; + range_chunks[56] = (RAM[14] >> 22) & mask22; + range_chunks[57] = (RAM[14] >> 44) & mask19; + range_chunks[58] = (RAM[14] >> 63) & 0b111; + + assignment.witness(component.W(4), row_witness - 1) = RAM[14]; + assignment.witness(component.W(4), row_witness - 0) = input_words_values[14]; + assignment.witness(component.W(5), row_witness - 1) = range_chunks[55]; + assignment.witness(component.W(5), row_witness - 0) = range_chunks[56]; + assignment.witness(component.W(5), row_witness + 1) = range_chunks[57]; + assignment.witness(component.W(4), row_witness + 1) = range_chunks[58]; + + + + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + range_chunks[59] = (RAM[15]) & mask22; + range_chunks[60] = (RAM[15] >> 22) & mask22; + range_chunks[61] = (RAM[15] >> 44) & mask13; + + assignment.witness(component.W(6), row_witness - 1) = RAM[15]; + assignment.witness(component.W(6), row_witness - 0) = input_words_values[15]; + assignment.witness(component.W(7), row_witness - 1) = range_chunks[59]; + assignment.witness(component.W(7), row_witness - 0) = range_chunks[60]; + assignment.witness(component.W(7), row_witness + 1) = range_chunks[61]; + + row_witness += 3; + + + + + // W0,1 W1,1 W1,0 (W1,-1 & W6,1-3) + // 1234567890123456789012.3456789012345678901234.567890123456789012|3456 + range_chunks[62] = (RAM[16]) & 0b1111; + range_chunks[63] = (RAM[16] >> 4) & mask18; + range_chunks[64] = (RAM[16] >> 22) & mask22; + range_chunks[65] = (RAM[16] >> 44) & mask22; + + assignment.witness(component.W(6), row_witness-3 + 1) = range_chunks[62]; + + assignment.witness(component.W(0), row_witness - 1) = RAM[16]; + assignment.witness(component.W(0), row_witness - 0) = input_words_values[16]; + assignment.witness(component.W(1), row_witness - 1) = range_chunks[62]; + assignment.witness(component.W(1), row_witness - 0) = range_chunks[63]; + assignment.witness(component.W(1), row_witness + 1) = range_chunks[64]; + assignment.witness(component.W(0), row_witness + 1) = range_chunks[65]; + + + + // W2,1 W3,1 W3,0 W3, -1 + // |1234567890123456789012.3456789012345678901234.56789012345678901234|56 + range_chunks[66] = (RAM[17]) & 3; + range_chunks[67] = (RAM[17] >> 2) & mask20; + range_chunks[68] = (RAM[17] >> 22) & mask22; + range_chunks[69] = (RAM[17] >> 44) & mask22; + assignment.witness(component.W(2), row_witness - 1) = RAM[17]; + assignment.witness(component.W(2), row_witness - 0) = input_words_values[17]; + assignment.witness(component.W(3), row_witness - 1) = range_chunks[66]; + assignment.witness(component.W(3), row_witness - 0) = range_chunks[67]; + assignment.witness(component.W(3), row_witness + 1) = range_chunks[68]; + assignment.witness(component.W(2), row_witness + 1) = range_chunks[69]; + + + + + // W4,1 W5,1 W5,0 W5, -1 + // 12|34567890123456789012.3456789012345678901234.5678901234567890123456 + range_chunks[70] = (RAM[18]) & mask22; + range_chunks[71] = (RAM[18] >> 22) & mask22; + range_chunks[72] = (RAM[18] >> 44) & mask20; + range_chunks[73] = (RAM[18] >> 64) & 0b11; + + assignment.witness(component.W(4), row_witness - 1) = RAM[18]; + assignment.witness(component.W(4), row_witness - 0) = input_words_values[18]; + assignment.witness(component.W(5), row_witness - 1) = range_chunks[70]; + assignment.witness(component.W(5), row_witness - 0) = range_chunks[71]; + assignment.witness(component.W(5), row_witness + 1) = range_chunks[72]; + assignment.witness(component.W(4), row_witness + 1) = range_chunks[73]; + + + + + // W7, 1 W7, 0 W7, -1 + // 12345678901234.5678901234567890123456.7890123456789012345678 + range_chunks[74] = (RAM[19]) & mask22; + range_chunks[75] = (RAM[19] >> 22) & mask22; + range_chunks[76] = (RAM[19] >> 44) & mask14; + + assignment.witness(component.W(6), row_witness - 1) = RAM[19]; + assignment.witness(component.W(6), row_witness - 0) = input_words_values[19]; + assignment.witness(component.W(7), row_witness - 1) = range_chunks[74]; + assignment.witness(component.W(7), row_witness - 0) = range_chunks[75]; + assignment.witness(component.W(7), row_witness + 1) = range_chunks[76]; + assignment.witness(component.W(8), row_witness + 1) = 1; + + + + + std::array input_words_vars_1; + std::array input_words_vars_2; + + + for(std::size_t j = 0; j < 4; j++) { + for(std::size_t i = 0; i < 4; i++) { + input_words_vars_1[4*j + i] = var(component.W(2*i), row + 1 + 3*j, false); + } + } + + for(std::size_t i = 0; i < 4; i++) { + input_words_vars_2[i] = var(component.W(2*i), row + 1 + 12, false); + } + + for (std::size_t i = 4; i < 15; i++) { + input_words_vars_2[i] = + var(component.C(0), start_row_index + 8, false, var::column_type::constant); + } + input_words_vars_2[15] = + var(component.C(0), start_row_index + 9, false, var::column_type::constant); + + + row = start_row_index + component.rows_amount_creating_input_words_component; + + std::array constants_var = { + var(component.C(0), start_row_index, false, var::column_type::constant), + var(component.C(0), start_row_index + 1, false, var::column_type::constant), + var(component.C(0), start_row_index + 2, false, var::column_type::constant), + var(component.C(0), start_row_index + 3, false, var::column_type::constant), + var(component.C(0), start_row_index + 4, false, var::column_type::constant), + var(component.C(0), start_row_index + 5, false, var::column_type::constant), + var(component.C(0), start_row_index + 6, false, var::column_type::constant), + var(component.C(0), start_row_index + 7, false, var::column_type::constant)}; + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + typename sha512_process::input_type sha512_process_input = {constants_var, input_words_vars_1}; + + sha512_process sha512_process_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8)},{component.C(0)},{}); + + typename sha512_process::result_type sha_output = generate_assignments(sha512_process_instance, assignment, sha512_process_input, row); + row += sha512_process_instance.rows_amount; + + //TODO + + /*for (std::size_t i = 0; i < 8; i++) { + assignment.witness(i), row) = input_words_values[16 + i]; + assignment.witness(i), row+1) = input_words_values[16 + i+8]; + input_words_vars_2[i] = var(i, row, false); + input_words_vars_2[i+8] = var(i, row+1, false); + }*/ + + // row = row + 2; + sha512_process_input = {sha_output.output_state, input_words_vars_2}; + + + + /*std::array input_words2 = { + 1 << 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 << 9}; + for (int i = 0; i < 16; i++) { + assignment.constant(0), component_start_row + 8 + i) = input_words2[i]; + } + std::vector input_words2_var = {var(0, row + 8, false, var::column_type::constant), + var(0, row + 9, false, var::column_type::constant), + var(0, row + 10, false, var::column_type::constant), + var(0, row + 11, false, var::column_type::constant), + var(0, row + 12, false, var::column_type::constant), + var(0, row + 13, false, var::column_type::constant), + var(0, row + 14, false, var::column_type::constant), + var(0, row + 15, false, var::column_type::constant), + var(0, row + 16, false, var::column_type::constant), + var(0, row + 17, false, var::column_type::constant), + var(0, row + 18, false, var::column_type::constant), + var(0, row + 19, false, var::column_type::constant), + var(0, row + 20, false, var::column_type::constant), + var(0, row + 21, false, var::column_type::constant), + var(0, row + 22, false, var::column_type::constant), + var(0, row + 23, false, var::column_type::constant)}; + typename sha512_process_component::params_type sha_params2 = {sha_output.output_state, + input_words2_var}; */ + + sha_output = generate_assignments(sha512_process_instance, assignment, sha512_process_input, row); + row += sha512_process_instance.rows_amount; + return typename plonk_sha512::result_type(component, start_row_index); + } + + template + std::array generate_gates( + const plonk_sha512 &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512::input_type + &instance_input) { + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = typename sha512::var; + + typename BlueprintFieldType::integral_type one = 1; + + + auto constraint_ram_0 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(1), 0) * (one << 22) + var(component.W(1), 1) * (one << 44) + var(component.W(0), 1) * (one << 64)); + auto constraint_word_0 = + var(component.W(0), 0) - (var(component.W(1), -1) + var(component.W(1), 0) * (one << 22) + var(component.W(1), 1) * (one << 44)); + + // W2,1 W3,1 W3,0 W3, -1 + // 1234|567890123456789012.3456789012345678901234.5678901234567890123456 + + auto constraint_ram_1 = + var(component.W(2), -1) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << 22) + var(component.W(3), 1) * (one << 44) + var(component.W(2), 1) * (one << 62)); + auto constraint_word_1 = + var(component.W(2), 0) - (var(component.W(0), 1) + var(component.W(3), -1) * (one << 2) + var(component.W(3), 0) * (one << 24) + var(component.W(3), 1) * (one << 46)); + + // W4,1 W5,1 W5,0 W5, -1 + // 123456|7890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_2 = + var(component.W(4), -1) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44) + var(component.W(4), 1) * (one << 60)); + auto constraint_word_2 = + var(component.W(4), 0) - (var(component.W(2), 1) + var(component.W(5), -1) * (one << 4) + var(component.W(5), 0) * (one << (4 + 22)) + var(component.W(5), 1) * (one << (4 + 44))); + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + + auto constraint_ram_3 = + var(component.W(6), -1) - (var(component.W(7), -1) + var(component.W(7), 0) * (one << 22) + var(component.W(7), 1) * (one << 44)); + auto constraint_word_3 = + var(component.W(6), 0) - (var(component.W(4), 1) + var(component.W(7), -1) * (one << 6) + var(component.W(7), 0) * (one << (6 + 22)) + var(component.W(7), 1) * (one << (6 + 44)) + var(component.W(6), 1) * (one << 63)); + + std::size_t selector_1 = bp.add_gate( + {constraint_ram_0, constraint_ram_1, constraint_ram_2, constraint_ram_3, constraint_word_0, + constraint_word_1, constraint_word_2, constraint_word_3}); + + // W0,1 W1,1 W1,0 W1,-1 W8,-1 + // 1|234567890123456789012.3456789012345678901234.567890123456789012345|6 + auto constraint_ram_4 = + var(component.W(0), -1) - (var(component.W(8), -1) + var(component.W(1), -1) * (1 << 1) + var(component.W(1), 0) * (one << 22) + var(component.W(1), 1) * (one << 44) + var(component.W(0), 1) * (one << 65)); + auto constraint_word_4 = + var(component.W(0), 0) - (var(component.W(1), -1) + var(component.W(1), 0) * (one << (22-1)) + var(component.W(1), 1) * (one << (22 + 22 - 1))); + + // W2,1 W3,1 W3,0 W3, -1 + // 123|4567890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_5 = + var(component.W(2), -1) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << 22) + var(component.W(3), 1) * (one << 44) + var(component.W(2), 1) * (one << 63)); + auto constraint_word_5 = + var(component.W(2), 0) - (var(component.W(0), 1) + var(component.W(3), -1) * (1 << 1) + var(component.W(3), 0) * (one << (22 + 1)) + var(component.W(3), 1) * (one << (44 + 1))); + + // W4,1 W5,1 W5,0 W5, -1 + // 12345|67890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_6 = + var(component.W(4), -1) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44) + var(component.W(4), 1) * (one << 61)); + auto constraint_word_6 = + var(component.W(4), 0) - (var(component.W(2), 1) + var(component.W(5), -1) * (one << 3) + var(component.W(5), 0) * (one << (3 + 22)) + var(component.W(5), 1) * (one << (3 + 44))); + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + auto constraint_ram_7 = + var(component.W(6), -1) - (var(component.W(7), -1) + var(component.W(7), 0) * (one << 22) + var(component.W(7), 1) * (one << 44)); + auto constraint_word_7 = + var(component.W(6), 0) - (var(component.W(4), 1) + var(component.W(7), -1) * (one << 5) + var(component.W(7), 0) * (one << (5 + 22)) + var(component.W(7), 1) * (one << (5 + 44)) + var(component.W(6), 1) * (one << 62)); + + std::size_t selector_2 = bp.add_gate( + {constraint_ram_4, constraint_ram_5, constraint_ram_6, constraint_ram_7, constraint_word_4, + constraint_word_5, constraint_word_6, constraint_word_7}); + + + + // W0,1 W1,1 W1,0 W1,-1 + // |1234567890123456789012.3456789012345678901234.56789012345678901234|56 + auto constraint_ram_8 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(1), 0) * (1 << 2) + var(component.W(1), 1) * (one << 22) + var(component.W(0), 1) * (one << 44)); + auto constraint_word_8 = + var(component.W(0), 0) - (var(component.W(1), 0) + var(component.W(1), 1) * (one << 20) + var(component.W(0), 1) * (one << 42)); + + // W2,1 W3,1 W3,0 W3, -1 + // 12|34567890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_9 = + var(component.W(2), -1) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << 22) + var(component.W(3), 1) * (one << 44) + var(component.W(2), 1) * (one << 64)); + auto constraint_word_9 = + var(component.W(2), 0) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << 22) + var(component.W(3), 1) * (one << 44)); + + // W4,1 W5,1 W5,0 W5, -1 + // 1234|567890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_10 = + var(component.W(4), -1) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44) + var(component.W(4), 1) * (one << 62)); + auto constraint_word_10 = + var(component.W(4), 0) - (var(component.W(2), 1) + var(component.W(5), -1) * (one << 2) + var(component.W(5), 0) * (one << 24) + var(component.W(5), 1) * (one << 46)); + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + auto constraint_ram_11 = + var(component.W(6), -1) - (var(component.W(7), -1) + var(component.W(7), 0) * (one << 22) + var(component.W(7), 1) * (one << 44)); + auto constraint_word_11 = + var(component.W(6), 0) - (var(component.W(4), 1) + var(component.W(7), -1) * (one << 4) + var(component.W(7), 0) * (one << (4 + 22)) + var(component.W(7), 1) * (one << (4 + 44)) + var(component.W(6), 1) * (one << 61)); + + std::size_t selector_3 = bp.add_gate( + {constraint_ram_8, constraint_ram_9, constraint_ram_10, constraint_ram_11, constraint_word_8, + constraint_word_9, constraint_word_10, constraint_word_11}); + + + + // W0,1 W1,1 W1,0 (W1,-1 & W6,1-3) + // 1234567890123456789012.3456789012345678901234.5678901234567890123|456 + auto constraint_ram_12 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(1), 0) * (one << 3) + var(component.W(1), 1) * (one << 22) + var(component.W(0), 1) * (one << 44)); + auto constraint_word_12 = + var(component.W(0), 0) - (var(component.W(1), 0) + var(component.W(1), 1) * (one << 19) + var(component.W(0), 1) * (one << (19+22)) + var(component.W(8), -1) * (one << 63)); + + // W2,1 W3,1 W3,0 W3, -1 W8, -1 + // 1|234567890123456789012.3456789012345678901234.567890123456789012345|6 + auto constraint_ram_13 = + var(component.W(2), -1) - (var(component.W(8), -1) + var(component.W(3), -1) * (1 << 1) + var(component.W(3), 0) * (one << 22) + var(component.W(3), 1) * (one << 44) + var(component.W(2), 1) * (one << 65)); + auto constraint_word_13 = + var(component.W(2), 0) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << (22-1)) + var(component.W(3), 1) * (one << (22 + 22 - 1))); + + // W4,1 W5,1 W5,0 W5, -1 + // 123|4567890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_14 = + var(component.W(4), -1) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44) + var(component.W(4), 1) * (one << 63)); + auto constraint_word_14 = + var(component.W(4), 0) - (var(component.W(2), 1) + var(component.W(5), -1) * (1 << 1) + var(component.W(5), 0) * (one << (22 + 1)) + var(component.W(5), 1) * (one << (44 + 1))); + + // W7, 1 W7, 0 W7, -1 + // 1234567890123.4567890123456789012345.6789012345678901234567 + auto constraint_ram_15 = + var(component.W(6), -1) - (var(component.W(7), -1) + var(component.W(7), 0) * (one << 22) + var(component.W(7), 1) * (one << 44)); + auto constraint_word_15 = + var(component.W(6), 0) - (var(component.W(4), 1) + var(component.W(7), -1) * (one << 3) + var(component.W(7), 0) * (one << (3 + 22)) + var(component.W(7), 1) * (one << (3 + 44)) + var(component.W(6), 1) * (one << 60)); + + std::size_t selector_4 = bp.add_gate( + {constraint_ram_12, constraint_ram_13, constraint_ram_14, constraint_ram_15, + constraint_word_12, constraint_word_13, constraint_word_14, constraint_word_15}); + + + + // W0,1 W1,1 W1,0 (W1,-1 & W6,1-3) + // 1234567890123456789012.3456789012345678901234.567890123456789012|3456 + auto constraint_ram_16 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(1), 0) * (one << 4) + var(component.W(1), 1) * (one << 22) + var(component.W(0), 1) * (one << 44)); + auto constraint_word_16 = + var(component.W(0), 0) - (var(component.W(1), 0) + var(component.W(1), 1) * (one << 18) + var(component.W(0), 1) * (one << (18+22)) + var(component.W(3), -1) * (one << 62)); + + // W2,1 W3,1 W3,0 W3, -1 + // |1234567890123456789012.3456789012345678901234.56789012345678901234|56 + auto constraint_ram_17 = + var(component.W(2), -1) - (var(component.W(3), -1) + var(component.W(3), 0) * (one << 2) + var(component.W(3), 1) * (one << 22) + var(component.W(2), 1) * (one << 44)); + auto constraint_word_17 = + var(component.W(2), 0) - (var(component.W(3), 0) + var(component.W(3), 1) * (one << 20) + var(component.W(2), 1) * (one << 42)); + + // W4,1 W5,1 W5,0 W5, -1 + // 12|34567890123456789012.3456789012345678901234.5678901234567890123456 + auto constraint_ram_18 = + var(component.W(4), -1) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44) + var(component.W(4), 1) * (one << 64)); + auto constraint_word_18 = + var(component.W(4), 0) - (var(component.W(5), -1) + var(component.W(5), 0) * (one << 22) + var(component.W(5), 1) * (one << 44)); + + // W7, 1 W7, 0 W7, -1 + // 12345678901234.5678901234567890123456.7890123456789012345678 + auto constraint_ram_19 = + var(component.W(6), -1) - (var(component.W(7), -1) + var(component.W(7), 0) * (one << 22) + var(component.W(7), 1) * (one << 44)); + auto constraint_word_19 = + var(component.W(6), 0) - (var(component.W(4), 1) + var(component.W(7), -1) * (one << 2) + var(component.W(7), 0) * (one << (2 + 22)) + var(component.W(7), 1) * (one << (2 + 44)) + var(component.W(8), 1) * (one << 60)); + + std::size_t selector_5 = bp.add_gate( + {constraint_ram_16, constraint_ram_17, constraint_ram_18, constraint_ram_19, + constraint_word_16, constraint_word_17, constraint_word_18, constraint_word_19}); + + return {selector_1, selector_2, selector_3, selector_4, selector_5}; + } + + template + void generate_copy_constraints( + const plonk_sha512 &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512::input_type &instance_input, + const std::uint32_t start_row_index) { + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = typename sha512::var; + + std::size_t row = start_row_index; + + for(std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint ( { var(component.W(2*i), row + 0, false), instance_input.R.x[i] } ); + bp.add_copy_constraint ( { var(component.W(2*i), row + 3, false), instance_input.R.y[i] } ); + bp.add_copy_constraint ( { var(component.W(2*i), row + 6, false), instance_input.A.x[i] } ); + bp.add_copy_constraint ( { var(component.W(2*i), row + 9, false), instance_input.A.y[i] } ); + bp.add_copy_constraint ( { var(component.W(2*i), row + 12, false), instance_input.M[i] } ); + + } + + bp.add_copy_constraint( { var(component.W(6), (row+4) - 3 + 1, false), var(component.W(8), (row+4) - 1, false) }); + for(std::size_t i = 0; i < 3; i++){ + std::size_t current_row = row + 1 + 6 + 3*i; + bp.add_copy_constraint( { var(component.W(6), (current_row - 3) + 1, false), var(component.W(1), current_row - 1, false) }); + } + + } + + template + void generate_assignments_constant( + const plonk_sha512 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_sha512::input_type + &instance_input, + const std::size_t start_row_index) { + + std::array constants = { + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 + }; + + for (int i = 0; i < 8; i++) { + assignment.constant(component.C(0), start_row_index + i) = constants[i]; + } + assignment.constant(component.C(0), start_row_index + 8) = 0; + assignment.constant(component.C(0), start_row_index + 9) = 252 + 1024; + } + + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter> { + + using component_type = plonk_sha512; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input; + for (std::size_t i = 0; i < input.R.x.size(); i++) { + tmp_assignment.public_input(0, i) = var_value(assignment, input.R.x[i]); + new_input.R.x[i] = var(0, i, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.R.y.size(); i++) { + std::size_t new_index = input.R.x.size(); + tmp_assignment.public_input(0, i + new_index) = var_value(assignment, input.R.y[i]); + new_input.R.y[i] = var(0, i + new_index, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.A.x.size(); i++) { + std::size_t new_index = input.R.x.size() + input.R.y.size(); + tmp_assignment.public_input(0, i + new_index) = var_value(assignment, input.A.x[i]); + new_input.A.x[i] = var(0, i + new_index, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < input.A.y.size(); i++) { + std::size_t new_index = input.R.x.size() + input.R.y.size() + input.A.x.size(); + tmp_assignment.public_input(0, i + new_index) = var_value(assignment, input.A.y[i]); + new_input.A.y[i] = var(0, i + new_index, false, var::column_type::public_input); + } + for (std::size_t i = 0; i < 4; i++) { + std::size_t new_index = input.R.x.size() + input.R.y.size() + + input.A.x.size() + input.A.y.size(); + tmp_assignment.public_input(0, i + new_index) = var_value(assignment, input.M[i]); + new_input.M[i] = var(0, i + new_index, false, var::column_type::public_input); + } + + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + if (std::size_t(variable.rotation) < input.R.x.size()) { + return input.R.x[variable.rotation]; + } else if (std::size_t(variable.rotation) < input.R.x.size() + input.R.y.size()) { + return input.R.y[variable.rotation - input.R.x.size()]; + } else if (std::size_t(variable.rotation) < input.R.x.size() + input.R.y.size() + input.A.x.size()) { + return input.A.x[variable.rotation - input.R.x.size() - input.R.y.size()]; + } else if (std::size_t(variable.rotation) < input.R.x.size() + input.R.y.size() + + input.A.x.size() + input.A.y.size()) { + return input.A.y[variable.rotation - input.R.x.size() - input.R.y.size() - input.A.x.size()]; + } else { + return input.M[variable.rotation - input.R.x.size() - input.R.y.size() + - input.A.x.size() - input.A.y.size()]; + } + } + }; + + template + class result_type_converter> { + + using component_type = plonk_sha512; + using result_type = typename component_type::result_type; + using input_type = typename component_type::input_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 8; i++) { + new_result.output_state[i] = + component.move_var( + old_result.output_state[i], + start_row_index + component.line_mapping[old_result.output_state[i].rotation], + instance_input); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512_process.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512_process.hpp new file mode 100644 index 000000000..3f1242676 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/plonk/sha512_process.hpp @@ -0,0 +1,975 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA512_PROCESS component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_PROCESS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_PROCESS_HPP + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Input: [x_0, x_1, x_2] \in Fp + // Output: [y_0, y_1, y_2] - SHA512 permutation of [x_0, x_1, x_2] + template + class sha512_process; + + template + class sha512_process>: + public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return sha512_process::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(9)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 6*64 + 2 + 9*80 + 4; + } + + constexpr static const std::size_t rounds_amount = 80; + + constexpr static const std::size_t base4 = 4; + constexpr static const std::size_t base7 = 7; + + constexpr static const std::array + round_constant = { + 0x428a2f98d728ae22_cppui_modular64, 0x7137449123ef65cd_cppui_modular64, 0xb5c0fbcfec4d3b2f_cppui_modular64, 0xe9b5dba58189dbbc_cppui_modular64, + 0x3956c25bf348b538_cppui_modular64, 0x59f111f1b605d019_cppui_modular64, 0x923f82a4af194f9b_cppui_modular64, 0xab1c5ed5da6d8118_cppui_modular64, + 0xd807aa98a3030242_cppui_modular64, 0x12835b0145706fbe_cppui_modular64, 0x243185be4ee4b28c_cppui_modular64, 0x550c7dc3d5ffb4e2_cppui_modular64, + 0x72be5d74f27b896f_cppui_modular64, 0x80deb1fe3b1696b1_cppui_modular64, 0x9bdc06a725c71235_cppui_modular64, 0xc19bf174cf692694_cppui_modular64, + 0xe49b69c19ef14ad2_cppui_modular64, 0xefbe4786384f25e3_cppui_modular64, 0x0fc19dc68b8cd5b5_cppui_modular64, 0x240ca1cc77ac9c65_cppui_modular64, + 0x2de92c6f592b0275_cppui_modular64, 0x4a7484aa6ea6e483_cppui_modular64, 0x5cb0a9dcbd41fbd4_cppui_modular64, 0x76f988da831153b5_cppui_modular64, + 0x983e5152ee66dfab_cppui_modular64, 0xa831c66d2db43210_cppui_modular64, 0xb00327c898fb213f_cppui_modular64, 0xbf597fc7beef0ee4_cppui_modular64, + 0xc6e00bf33da88fc2_cppui_modular64, 0xd5a79147930aa725_cppui_modular64, 0x06ca6351e003826f_cppui_modular64, 0x142929670a0e6e70_cppui_modular64, + 0x27b70a8546d22ffc_cppui_modular64, 0x2e1b21385c26c926_cppui_modular64, 0x4d2c6dfc5ac42aed_cppui_modular64, 0x53380d139d95b3df_cppui_modular64, + 0x650a73548baf63de_cppui_modular64, 0x766a0abb3c77b2a8_cppui_modular64, 0x81c2c92e47edaee6_cppui_modular64, 0x92722c851482353b_cppui_modular64, + 0xa2bfe8a14cf10364_cppui_modular64, 0xa81a664bbc423001_cppui_modular64, 0xc24b8b70d0f89791_cppui_modular64, 0xc76c51a30654be30_cppui_modular64, + 0xd192e819d6ef5218_cppui_modular64, 0xd69906245565a910_cppui_modular64, 0xf40e35855771202a_cppui_modular64, 0x106aa07032bbd1b8_cppui_modular64, + 0x19a4c116b8d2d0c8_cppui_modular64, 0x1e376c085141ab53_cppui_modular64, 0x2748774cdf8eeb99_cppui_modular64, 0x34b0bcb5e19b48a8_cppui_modular64, + 0x391c0cb3c5c95a63_cppui_modular64, 0x4ed8aa4ae3418acb_cppui_modular64, 0x5b9cca4f7763e373_cppui_modular64, 0x682e6ff3d6b2b8a3_cppui_modular64, + 0x748f82ee5defb2fc_cppui_modular64, 0x78a5636f43172f60_cppui_modular64, 0x84c87814a1f0ab72_cppui_modular64, 0x8cc702081a6439ec_cppui_modular64, + 0x90befffa23631e28_cppui_modular64, 0xa4506cebde82bde9_cppui_modular64, 0xbef9a3f7b2c67915_cppui_modular64, 0xc67178f2e372532b_cppui_modular64, + 0xca273eceea26619c_cppui_modular64, 0xd186b8c721c0c207_cppui_modular64, 0xeada7dd6cde0eb1e_cppui_modular64, 0xf57d4f7fee6ed178_cppui_modular64, + 0x06f067aa72176fba_cppui_modular64, 0x0a637dc5a2c898a6_cppui_modular64, 0x113f9804bef90dae_cppui_modular64, 0x1b710b35131c471b_cppui_modular64, + 0x28db77f523047d84_cppui_modular64, 0x32caab7b40c72493_cppui_modular64, 0x3c9ebe0a15c9bebc_cppui_modular64, 0x431d67c49c100d4c_cppui_modular64, + 0x4cc5d4becb3e42b6_cppui_modular64, 0x597f299cfc657e2a_cppui_modular64, 0x5fcb6fab3ad6faec_cppui_modular64, 0x6c44198c4a475817_cppui_modular64}; + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 10; + + struct input_type { + std::array input_state; + std::array input_words; + + std::vector> all_vars() { + std::vector> result; + result.reserve(24); + result.insert(result.end(), input_state.begin(), input_state.end()); + result.insert(result.end(), input_words.begin(), input_words.end()); + return result; + } + }; + + struct result_type { + std::array output_state; + + result_type(const sha512_process> &component, std::uint32_t start_row_index) { + output_state = {var(component.W(0), start_row_index + component.rows_amount - 3, false), + var(component.W(1), start_row_index + component.rows_amount - 3, false), + var(component.W(2), start_row_index + component.rows_amount - 3, false), + var(component.W(3), start_row_index + component.rows_amount - 3, false), + var(component.W(0), start_row_index + component.rows_amount - 1, false), + var(component.W(1), start_row_index + component.rows_amount - 1, false), + var(component.W(2), start_row_index + component.rows_amount - 1, false), + var(component.W(3), start_row_index + component.rows_amount - 1, false)}; + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(8); + result.insert(result.end(), output_state.begin(), output_state.end()); + return result; + } + }; + + template + sha512_process(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + sha512_process(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + }; + + template + using plonk_sha512_process = + sha512_process>; + + namespace detail { + + template + void generate_assignments_constant( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512_process::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index + 386 + 3; + for (std::size_t i = 0; i < 80; i ++){ + assignment.constant(component.C(0), row + i*9) = + plonk_sha512_process::round_constant[i]; + } + } + + template + std::size_t generate_sigma0_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(2), -1) * (one << 1) + var(component.W(3), -1) * (one << 7) + + var(component.W(4), -1) * (one << 8) + var(component.W(5), -1) * (one << 22) + var(component.W(6), -1) * (one << 36) + + var(component.W(7), - 1) * (one << 50)); + auto constraint_2 = (var(component.W(1), -1) - 1) * (var(component.W(1), - 1)); + auto constraint_3 = (var(component.W(3), -1) - 1) * (var(component.W(3), - 1)); + auto constraint_4 = + var(component.W(6), 0) + var(component.W(7), 0) * (one << (2*14)) + var(component.W(8), 0) * (one << (2*28)) + + var(component.W(0), +1) * (one << (2*42)) + var(component.W(1), +1) * (one << (2*56)) - + (var(component.W(8), -1) * ((one << (63*2)) + (one << (56*2))) + + var(component.W(0), 0) * (1 + (one << (57*2))) + + var(component.W(1), 0) * ((one << (6*2)) + (one << (63*2)) + 1) + + var(component.W(2), 0) * ((one << (7*2)) + 1 + (one << (1*2))) + + var(component.W(3), 0) * ((one << (21*2)) + (one << (14*2)) + (one << (15*2))) + + var(component.W(4), 0) * ((one << (35*2)) + (one << (28*2)) + (one << (29*2))) + + var(component.W(5), 0) * ((one << (49*2)) + (one << (42*2)) + (one << (43*2)))); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + } + + template + std::size_t generate_sigma1_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(0), +1) - (var(component.W(1), 1) + var(component.W(2), 1) * (one << 6) + var(component.W(3), 1) * (one << 19) + + var(component.W(4), 1) * (one << 33) + var(component.W(5), 1) * (one << 47) + var(component.W(6), 1) * (one << 61)); + auto constraint_2 = (var(component.W(6), 1) - 7) * (var(component.W(6), 1) - 6) * (var(component.W(6), 1) - 5) * + (var(component.W(6), 1) - 4) * (var(component.W(6), 1) - 3) * (var(component.W(6), 1) - 2) * + (var(component.W(6), 1) - 1) * var(component.W(6), 1); + auto constraint_3 = + var(component.W(4), 0) + var(component.W(5), 0) * (one << 28) + var(component.W(6), 0) * (one << 56) + var(component.W(7), 0) * (one << (42*2)) + + var(component.W(8), 0) * (one << 112) - + (var(component.W(7), 1) * ((one << (2*45)) + (one << (2*3))) + + var(component.W(8), 1) * ((one << (2*51)) + (one << (2*9)) + 1) + + var(component.W(0), 0) * (1 + (one << (2*22)) + (one << (2*13))) + + var(component.W(1), 0) * ((one << (2*14)) + (one << (2*36)) + (one << (2 * 27))) + + var(component.W(2), 0) * ((one << (2*28)) + (one << (2*50)) + (one << (2*41))) + + var(component.W(3), 0) * ((one << (2*42)) + 1 + (one << (2 * 55)))); + + return bp.add_gate({constraint_1, constraint_2, constraint_3}); + } + + template + std::array generate_message_scheduling_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + std::size_t sigma0_selector = generate_sigma0_gates(component, bp, assignment); + typename BlueprintFieldType::integral_type one = 1; + auto m = typename BlueprintFieldType::value_type(2).pow(64); + auto constraint_1 = + (var(component.W(5), 0) + m*var(component.W(6), 0) - (var(component.W(7), -1) + var(component.W(8), -1) + var(component.W(2), -1) + var(component.W(3), -1) * (one << 14) + + var(component.W(4), -1) * (one << 28) + var(component.W(5), -1) * (one << 42) + var(component.W(6), -1) * (one << 56) + + var(component.W(0), 0) + var(component.W(1), 0) * (one << 14) + var(component.W(2), 0) * (one << 28) + + var(component.W(3), 0) * (one << 42) + var(component.W(4), 0) * (one << 56))); + auto constraint_2 = (var(component.W(6), 0) - 3) * (var(component.W(6), 0) - 2) * (var(component.W(6), 0) - 1) * var(component.W(6), 0); + std::size_t selector_2 = bp.add_gate({constraint_1, constraint_2}); + std::size_t sigma1_selector = generate_sigma1_gates(component, bp, assignment); + + return {sigma0_selector, sigma1_selector, selector_2}; + } + + template + std::size_t generate_Sigma0_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + typename BlueprintFieldType::integral_type one = 1; + std::vector a_sizes = {14, 14, 6, 5, 14, 11}; + typename BlueprintFieldType::value_type base4_value = + plonk_sha512_process::base4; + auto constraint_1 = + var(component.W(0), +1) - (var(component.W(1), +1) + var(component.W(2), 1) * (one << 14) + var(component.W(3), +1) * (one << 28) + + var(component.W(4), +1) * (one << 34) + var(component.W(5), 1) * (one << 39) + var(component.W(6), 1) * (one << 53)); + auto constraint_2 = + var(component.W(5), -1) - (var(component.W(7), +1)+ var(component.W(8), +1) * base4_value.pow(a_sizes[0]) + + var(component.W(0), 0) * base4_value.pow(a_sizes[0] + a_sizes[1]) + + var(component.W(1), 0) * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2]) + + var(component.W(2), 0) * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2] + a_sizes[3]) + + var(component.W(3), 0) * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2] + a_sizes[3] + a_sizes[4])); + auto constraint_3 = + var(component.W(4), 0) + var(component.W(5), 0) * (one << (2*14)) + var(component.W(6), 0) * (one << (2*28)) + + var(component.W(7), 0) * (one << (2*42)) + var(component.W(8), 0) * (one << 112) - + (var(component.W(7), +1) * ((one << (36 *2)) + (one << (30*2)) + (one << (25*2))) + + var(component.W(8), +1) * ((one << (50 * 2)) + (one << (44*2)) + (one << (39*2))) + + var(component.W(0), 0) * (1 + (one << (58*2)) + (one << (53*2))) + + var(component.W(1), 0) * ((one << (6*2)) + 1 + (one << (59*2))) + + var(component.W(2), 0) * ((one << (11*2)) + (one << (5*2)) + 1) + + var(component.W(3), 0) * ((one << (25*2)) + (one << (19*2)) + (one << (14*2)))); + + return bp.add_gate({constraint_1, constraint_2}); + } + + template + std::size_t generate_Sigma1_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> + &assignment) { + + using var = typename plonk_sha512_process::var; + + typename BlueprintFieldType::integral_type one = 1; + typename BlueprintFieldType::value_type base7_value = + plonk_sha512_process::base7; + auto constraint_1 = + var(component.W(0), -1) - (var(component.W(1), -1) + var(component.W(2), -1) * (one << 14) + + var(component.W(3), -1) * (one << 18) + var(component.W(4), -1) * (one << 32) + + var(component.W(5), -1) * (one << 41) + var(component.W(6), -1) * (one << 55)); + auto constraint_2 = + var(component.W(5), +1) - (var(component.W(7), -1) + var(component.W(8), -1) * (base7_value.pow(14)) + + var(component.W(0), 0) * (base7_value.pow(18)) + var(component.W(1), 0) * (base7_value.pow(32)) + + var(component.W(2), 0) * (base7_value.pow(41)) + var(component.W(3), 0) * (base7_value.pow(55))); + + auto constraint_3 = + var(component.W(4), 0) + var(component.W(5), 0) * base7_value.pow(14) + var(component.W(6), 0) * base7_value.pow(28) + + var(component.W(7), 0) * base7_value.pow(42) + + var(component.W(8), 0) * base7_value.pow(56) - + (var(component.W(7), -1) * (base7_value.pow(50) + base7_value.pow(46) + base7_value.pow(23)) + + var(component.W(8), -1) * (1 + base7_value.pow(60) + base7_value.pow(37)) + + var(component.W(0), 0) * (base7_value.pow(4) + 1 + base7_value.pow(41)) + + var(component.W(1), 0) * (base7_value.pow(18) + base7_value.pow(14) + base7_value.pow(55))+ + var(component.W(2), 0) * (base7_value.pow(27) + base7_value.pow(23) + 1)+ + var(component.W(3), 0)* (base7_value.pow(41) + base7_value.pow(37) + base7_value.pow(14))); + + return bp.add_gate({constraint_1, constraint_2, constraint_3}); + } + + template + std::size_t generate_Maj_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + typename BlueprintFieldType::integral_type one = 1; + auto constraint_1 = + var(component.W(7), 0) + var(component.W(8), 0) * (one << 32) + + var(component.W(0), -1) * (one << 64) + + var(component.W(1), -1) * (one << 96) - (var(component.W(5), 0) + var(component.W(6), 0) + + var(component.W(6), -1)); + + return bp.add_gate({constraint_1}); + } + + template + std::size_t generate_Ch_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + typename BlueprintFieldType::value_type base7_value = + plonk_sha512_process::base7; + auto constraint_1 = + var(component.W(7), 0) + var(component.W(8), 0) * base7_value.pow(16) + var(component.W(0), +1) * base7_value.pow(32) + + var(component.W(1), +1) * base7_value.pow(48) - (var(component.W(5), 0) + 2 * var(component.W(6), 0) + 3 * var(component.W(6), +1)); + + return bp.add_gate({constraint_1}); + } + + template + std::array generate_compression_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment) { + + using var = typename plonk_sha512_process::var; + + std::vector sigma_sizes = {14, 14, 14, 14, 8}; + typename BlueprintFieldType::integral_type one = 1; + auto m = typename BlueprintFieldType::value_type(2).pow(64); + std::size_t sigma1_selector = generate_Sigma1_gates(component, bp, assignment); + std::size_t ch_selector = generate_Ch_gates(component, bp, assignment); + auto constraint_1 = + var(component.W(1), +1) - + (var(component.W(8), 0) + var(component.W(0), +1) + + var(component.W(0), -1) + var(component.W(1), -1) * (1 << (sigma_sizes[0])) + + var(component.W(2), -1) * (one << (sigma_sizes[0] + sigma_sizes[1])) + + var(component.W(3), -1) * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])) + + var(component.W(4), -1) * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2] + sigma_sizes[3])) + + var(component.W(2), 0) + var(component.W(3), 0) * (1 << 16) + + var(component.W(4), 0) * (one << 32) + var(component.W(5), 0) * (one << 48) + + var(component.W(0), 0, true, var::column_type::constant)); + auto constraint_2 = + var(component.W(1), +1) + var(component.W(7), 0) - (var(component.W(2), +1) + m*var(component.W(3), +1)); + auto constraint_3 = + (var(component.W(3), +1) - 5)* (var(component.W(3), +1) - 4)*(var(component.W(3), +1) - 3)* + (var(component.W(3), +1) - 2) * (var(component.W(3), +1) - 1) * var(component.W(3), +1); + std::size_t selector_4 = bp.add_gate({constraint_1, constraint_2, constraint_3}); + + auto constraint_4 = + var(component.W(7), 0) + m*var(component.W(8), 0)- + (var(component.W(1), -1) + + var(component.W(0), +1) + var(component.W(1), +1) * (1 << sigma_sizes[0]) + + var(component.W(2), +1) * (one << (sigma_sizes[0] + sigma_sizes[1])) + + var(component.W(3), +1) * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])) + + var(component.W(4), +1) * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2] + sigma_sizes[3])) + + var(component.W(2), 0) + var(component.W(3), 0) * (1 << 16) + + var(component.W(4), 0) * (one << 32) + var(component.W(5), 0) * (one << 48)); + auto constraint_5 = (var(component.W(8), 0) - 6) * (var(component.W(8), 0) - 5) * + (var(component.W(8), 0) - 4)* (var(component.W(8), 0) - 3) * (var(component.W(8), 0) - 2) * (var(component.W(8), 0) - 1) *var(component.W(8), 0); + std::size_t selector_5 = bp.add_gate({constraint_4, constraint_5}); + std::size_t maj_selector = generate_Maj_gates(component, bp, assignment); + + std::size_t sigma0_selector = generate_Sigma0_gates(component, bp, assignment); + auto constraint_out_1 = var(component.W(0), +1) + m*var(component.W(4), +1)- (var(component.W(0), 0) + var(component.W(4), 0)); + auto constraint_out_2 = var(component.W(1), +1) + m*var(component.W(5), +1) - (var(component.W(1), 0) + var(component.W(5), 0)); + auto constraint_out_3 = var(component.W(2), +1) + m*var(component.W(6), +1) - (var(component.W(2), 0) + var(component.W(6), 0)); + auto constraint_out_4 = var(component.W(3), +1) + m*var(component.W(7), +1) - (var(component.W(3), 0) + var(component.W(7), 0)); + + auto selector_6 = bp.add_gate( + {constraint_out_1, constraint_out_2, constraint_out_3, constraint_out_4}); + + return {sigma1_selector, sigma0_selector, ch_selector, maj_selector, + selector_4, selector_5, selector_6}; + } + } // namespace detail + + template + typename plonk_sha512_process::result_type + generate_assignments( + const plonk_sha512_process &component, + assignment> &assignment, + const typename plonk_sha512_process::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + typename BlueprintFieldType::integral_type one = 1; + std::array input_state = { + var_value(assignment, instance_input.input_state[0]), var_value(assignment, instance_input.input_state[1]), + var_value(assignment, instance_input.input_state[2]), var_value(assignment, instance_input.input_state[3]), + var_value(assignment, instance_input.input_state[4]), var_value(assignment, instance_input.input_state[5]), + var_value(assignment, instance_input.input_state[6]), var_value(assignment, instance_input.input_state[7])}; + std::array message_scheduling_words; + for (std::size_t i = 0; i < 16; i++) { + message_scheduling_words[i] = var_value(assignment, instance_input.input_words[i]); + } + typename BlueprintFieldType::value_type a = input_state[0]; + typename BlueprintFieldType::value_type b = input_state[1]; + typename BlueprintFieldType::value_type c = input_state[2]; + typename BlueprintFieldType::value_type d = input_state[3]; + typename BlueprintFieldType::value_type e = input_state[4]; + typename BlueprintFieldType::value_type f = input_state[5]; + typename BlueprintFieldType::value_type g = input_state[6]; + typename BlueprintFieldType::value_type h = input_state[7]; + + std::array sparse_values {}; + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(component.W(i), row) = input_state[i]; + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(64); + { + nil::marshalling::status_type status; + std::array input_state_sparse_all = nil::marshalling::pack(integral_input_state_sparse, status); + std::copy(input_state_sparse_all.end() - 64, input_state_sparse_all.end(), input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {64}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse(input_state_sparse, input_state_sparse_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(i), row + 1) = input_state_sparse_chunks[1][0]; + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + for (std::size_t i = 4; i < 8; i++) { + assignment.witness(component.W(i), row) = input_state[i]; + typename BlueprintFieldType::integral_type integral_input_state_sparse = + typename BlueprintFieldType::integral_type(input_state[i].data); + std::vector input_state_sparse(64); + { + nil::marshalling::status_type status; + std::array input_state_sparse_all = nil::marshalling::pack(integral_input_state_sparse, status); + std::copy(input_state_sparse_all.end() - 64, input_state_sparse_all.end(), input_state_sparse.begin()); + } + + std::vector input_state_sparse_sizes = {64}; + std::array, 2> input_state_sparse_chunks = + detail::split_and_sparse(input_state_sparse, input_state_sparse_sizes, + plonk_sha512_process::base7); + assignment.witness(component.W(i), row + 1) = input_state_sparse_chunks[1][0]; + sparse_values[i] = input_state_sparse_chunks[1][0]; + } + row = row + 2; + std::vector sigma_sizes = {14, 14, 14, 14, 8}; + std::vector ch_and_maj_sizes = {16, 16, 16, 16}; + typename BlueprintFieldType::value_type base4_value = + plonk_sha512_process::base4; + typename BlueprintFieldType::value_type base7_value = + plonk_sha512_process::base7; + for (std::size_t i = row; i < row + 379; i = i + 6) { + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type( + message_scheduling_words[(i - row) / 6 + 1].data); + assignment.witness(component.W(0), i) = message_scheduling_words[(i - row) / 6 + 1]; + std::vector a(64); + { + nil::marshalling::status_type status; + std::array a_all = nil::marshalling::pack(integral_a, status); + std::copy(a_all.end() - 64, a_all.end(), a.begin()); + } + + std::vector a_sizes = {1, 6, 1, 14, 14, 14, 14}; + std::array, 2> a_chunks = + detail::split_and_sparse(a, a_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(1), i) = a_chunks[0][0]; + assignment.witness(component.W(2), i) = a_chunks[0][1]; + assignment.witness(component.W(3), i) = a_chunks[0][2]; + assignment.witness(component.W(4), i) = a_chunks[0][3]; + assignment.witness(component.W(5), i) = a_chunks[0][4]; + assignment.witness(component.W(6), i) = a_chunks[0][5]; + assignment.witness(component.W(7), i) = a_chunks[0][6]; + assignment.witness(component.W(8), i) = a_chunks[1][0]; + assignment.witness(component.W(0), i + 1) = a_chunks[1][1]; + assignment.witness(component.W(1), i + 1) = a_chunks[1][2]; + assignment.witness(component.W(2), i + 1) = a_chunks[1][3]; + assignment.witness(component.W(3), i + 1) = a_chunks[1][4]; + assignment.witness(component.W(4), i + 1) = a_chunks[1][5]; + assignment.witness(component.W(5), i + 1) = a_chunks[1][6]; + typename BlueprintFieldType::integral_type sparse_sigma0 = + a_chunks[1][0] * ((one << (63*2)) + (one << (56*2))) + + a_chunks[1][1] * (1 + (one << (57*2))) + + a_chunks[1][2] * ((one << (6*2)) + (one << (63*2)) + 1) + + a_chunks[1][3] * ((one << (7*2)) + 1 + (one << (1*2))) + + a_chunks[1][4] * ((one << (21*2)) + (one << (14*2)) + (one << (15*2))) + + a_chunks[1][5] * ((one << (35*2)) + (one << (28*2)) + (one << (29*2))) + + a_chunks[1][6] * ((one << (49*2)) + (one << (42*2)) + (one << (43*2))); + std::array, 2> + sigma0_chunks = + detail::reversed_sparse_and_split(sparse_sigma0, sigma_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(6), i + 1) = sigma0_chunks[1][0]; + assignment.witness(component.W(7), i + 1) = sigma0_chunks[1][1]; + assignment.witness(component.W(8), i + 1) = sigma0_chunks[1][2]; + assignment.witness(component.W(0), i + 2) = sigma0_chunks[1][3]; + assignment.witness(component.W(1), i + 2) = sigma0_chunks[1][4]; + + assignment.witness(component.W(2), i + 2) = sigma0_chunks[0][0]; + assignment.witness(component.W(3), i + 2) = sigma0_chunks[0][1]; + assignment.witness(component.W(4), i + 2) = sigma0_chunks[0][2]; + assignment.witness(component.W(5), i + 2) = sigma0_chunks[0][3]; + assignment.witness(component.W(6), i + 2) = sigma0_chunks[0][4]; + assignment.witness(component.W(7), i + 2) = message_scheduling_words[(i - row) / 6 + 9]; + assignment.witness(component.W(8), i + 2) = message_scheduling_words[(i - row) / 6]; + + typename BlueprintFieldType::integral_type integral_b = + typename BlueprintFieldType::integral_type( + message_scheduling_words[(i - row) / 6 + 14].data); + std::vector b(64); + { + nil::marshalling::status_type status; + std::array b_all = nil::marshalling::pack(integral_b, status); + std::copy(b_all.end() - 64, b_all.end(), b.begin()); + } + + std::vector b_sizes = {6, 13, 14, 14, 14, 3}; + std::array, 2> b_chunks = + detail::split_and_sparse(b, b_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(0), i + 5) = message_scheduling_words[(i - row) / 6 + 14]; + assignment.witness(component.W(1), i + 5) = b_chunks[0][0]; + assignment.witness(component.W(2), i + 5) = b_chunks[0][1]; + assignment.witness(component.W(3), i + 5) = b_chunks[0][2]; + assignment.witness(component.W(4), i + 5) = b_chunks[0][3]; + assignment.witness(component.W(5), i + 5) = b_chunks[0][4]; + assignment.witness(component.W(6), i + 5) = b_chunks[0][5]; + + assignment.witness(component.W(7), i + 5) = b_chunks[1][0]; + assignment.witness(component.W(8), i + 5) = b_chunks[1][1]; + assignment.witness(component.W(0), i + 4) = b_chunks[1][2]; + assignment.witness(component.W(1), i + 4) = b_chunks[1][3]; + assignment.witness(component.W(2), i + 4) = b_chunks[1][4]; + assignment.witness(component.W(3), i + 4) = b_chunks[1][5]; + + typename BlueprintFieldType::integral_type sparse_sigma1 = + b_chunks[1][0] * ((one << (2*45)) + (one << (2*3))) + + b_chunks[1][1] * ((one << (2*51)) + (one << (2*9)) + 1) + + b_chunks[1][2] * (1 + (one << (2*22)) + (one << (2*13))) + + b_chunks[1][3] * ((one << (2*14)) + (one << (2*36)) + (one << (2 * 27))) + + b_chunks[1][4] * ((one << (2*28)) + (one << (2*50)) + (one << (2*41))) + + b_chunks[1][5] * ((one << (2*42)) + 1 + (one << (2 * 55))); + + std::array, 2> + sigma1_chunks = + detail::reversed_sparse_and_split(sparse_sigma1, sigma_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(4), i + 4) = sigma1_chunks[1][0]; + assignment.witness(component.W(5), i + 4) = sigma1_chunks[1][1]; + assignment.witness(component.W(6), i + 4) = sigma1_chunks[1][2]; + assignment.witness(component.W(7), i + 4) = sigma1_chunks[1][3]; + assignment.witness(component.W(8), i + 4) = sigma1_chunks[1][4]; + + assignment.witness(component.W(0), i + 3) = sigma1_chunks[0][0]; + assignment.witness(component.W(1), i + 3) = sigma1_chunks[0][1]; + assignment.witness(component.W(2), i + 3) = sigma1_chunks[0][2]; + assignment.witness(component.W(3), i + 3) = sigma1_chunks[0][3]; + assignment.witness(component.W(4), i + 3) = sigma1_chunks[0][4]; + typename BlueprintFieldType::value_type sum = message_scheduling_words[(i - row) / 6 + 9] + message_scheduling_words[(i - row) / 6] + + sigma1_chunks[0][0] + sigma0_chunks[0][0] + + (one << 14) * (sigma1_chunks[0][1] + sigma0_chunks[0][1]) + + (one << 28) * (sigma1_chunks[0][2] + sigma0_chunks[0][2]) + + (one << 42) * (sigma1_chunks[0][3] + sigma0_chunks[0][3]) + + (one << 56) * (sigma1_chunks[0][4] + sigma0_chunks[0][4]) ; + message_scheduling_words[(i - row) / 6 + 16] = + typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + assignment.witness(component.W(5), i + 3) = message_scheduling_words[(i - row) / 6 + 16]; + assignment.witness(component.W(6), i + 3) = (sum - message_scheduling_words[(i - row) / 6 + 16]) * + typename BlueprintFieldType::value_type(2).pow(64).inversed(); + } + row = row + 384; + for (std::size_t i = row; i < row + 720; i = i + 9) { + assignment.witness(component.W(0), i) = e; + typename BlueprintFieldType::integral_type integral_e = + typename BlueprintFieldType::integral_type(e.data); + std::vector e_bits(64); + { + nil::marshalling::status_type status; + std::array e_bits_all = nil::marshalling::pack(integral_e, status); + std::copy(e_bits_all.end() - 64, e_bits_all.end(), e_bits.begin()); + } + + std::vector e_sizes = {14, 4, 14, 9, 14, 9}; + std::array, 2> e_chunks = + detail::split_and_sparse(e_bits, e_sizes, + plonk_sha512_process::base7); + assignment.witness(component.W(1), i) = e_chunks[0][0]; + assignment.witness(component.W(2), i) = e_chunks[0][1]; + assignment.witness(component.W(3), i) = e_chunks[0][2]; + assignment.witness(component.W(4), i) = e_chunks[0][3]; + assignment.witness(component.W(5), i) = e_chunks[0][4]; + assignment.witness(component.W(6), i) = e_chunks[0][5]; + + assignment.witness(component.W(7), i) = e_chunks[1][0]; + assignment.witness(component.W(8), i) = e_chunks[1][1]; + assignment.witness(component.W(0), i + 1) = e_chunks[1][2]; + assignment.witness(component.W(1), i + 1) = e_chunks[1][3]; + assignment.witness(component.W(2), i + 1) = e_chunks[1][4]; + assignment.witness(component.W(3), i + 1) = e_chunks[1][5]; + + typename BlueprintFieldType::integral_type sparse_Sigma1 = + typename BlueprintFieldType::integral_type(( + e_chunks[1][0] * (base7_value.pow(50) + base7_value.pow(46) + base7_value.pow(23)) + + e_chunks[1][1] * (1 + base7_value.pow(60) + base7_value.pow(37)) + + e_chunks[1][2] * (base7_value.pow(4) + 1 + base7_value.pow(41)) + + e_chunks[1][3] * (base7_value.pow(18) + base7_value.pow(14) + base7_value.pow(55))+ + e_chunks[1][4] * (base7_value.pow(27) + base7_value.pow(23) + 1)+ + e_chunks[1][5] * (base7_value.pow(41) + base7_value.pow(37) + base7_value.pow(14)) + ).data); + std::array, 2> + Sigma1_chunks = + detail::reversed_sparse_and_split(sparse_Sigma1, sigma_sizes, + plonk_sha512_process::base7); + assignment.witness(component.W(4), i + 1) = Sigma1_chunks[1][0]; + assignment.witness(component.W(5), i + 1) = Sigma1_chunks[1][1]; + assignment.witness(component.W(6), i + 1) = Sigma1_chunks[1][2]; + assignment.witness(component.W(7), i + 1) = Sigma1_chunks[1][3]; + assignment.witness(component.W(8), i + 1) = Sigma1_chunks[1][4]; + + assignment.witness(component.W(0), i + 2) = Sigma1_chunks[0][0]; + assignment.witness(component.W(1), i + 2) = Sigma1_chunks[0][1]; + assignment.witness(component.W(2), i + 2) = Sigma1_chunks[0][2]; + assignment.witness(component.W(3), i + 2) = Sigma1_chunks[0][3]; + assignment.witness(component.W(4), i + 2) = Sigma1_chunks[0][4]; + typename BlueprintFieldType::integral_type Sigma1 = + Sigma1_chunks[0][0] + Sigma1_chunks[0][1] * (1 << (sigma_sizes[0])) + + Sigma1_chunks[0][2] * (one << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma1_chunks[0][3] * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])) + + Sigma1_chunks[0][4] * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2] + sigma_sizes[3])); + + + sparse_values[4] = typename BlueprintFieldType::integral_type((e_chunks[1][0] + + e_chunks[1][1] * base7_value.pow(e_sizes[0]) + + e_chunks[1][2] * base7_value.pow(e_sizes[0] + e_sizes[1]) + + e_chunks[1][3] * base7_value.pow(e_sizes[0] + e_sizes[1] + e_sizes[2]) + + e_chunks[1][4] * base7_value.pow(e_sizes[0] + e_sizes[1] + e_sizes[2] + e_sizes[3]) + + e_chunks[1][5] * base7_value.pow(e_sizes[0] + e_sizes[1] + e_sizes[2] + e_sizes[3] + e_sizes[4])).data); + assignment.witness(component.W(5), i + 2) = sparse_values[4]; + assignment.witness(component.W(6), i + 2) = sparse_values[5]; + + typename BlueprintFieldType::integral_type sparse_ch = + sparse_values[4] + 2 * sparse_values[5] + 3 * sparse_values[6]; + + std::array, 2> + ch_chunks = + detail::reversed_sparse_and_split_ch(sparse_ch, ch_and_maj_sizes, + plonk_sha512_process::base7); + assignment.witness(component.W(7), i + 2) = ch_chunks[1][0]; + assignment.witness(component.W(8), i + 2) = ch_chunks[1][1]; + assignment.witness(component.W(0), i + 3) = ch_chunks[1][2]; + assignment.witness(component.W(1), i + 3) = ch_chunks[1][3]; + + assignment.witness(component.W(2), i + 3) = ch_chunks[0][0]; + assignment.witness(component.W(3), i + 3) = ch_chunks[0][1]; + assignment.witness(component.W(4), i + 3) = ch_chunks[0][2]; + assignment.witness(component.W(5), i + 3) = ch_chunks[0][3]; + + assignment.witness(component.W(6), i + 3) = sparse_values[6]; + assignment.witness(component.W(7), i + 3) = d; + assignment.witness(component.W(8), i + 3) = h; + assignment.witness(component.W(0), i + 4) = message_scheduling_words[(i - row) / 9]; + typename BlueprintFieldType::integral_type ch = + ch_chunks[0][0] + ch_chunks[0][1] * (1 << 16) + ch_chunks[0][2] * (one << 32) + + ch_chunks[0][3] * (one << 48); + + typename BlueprintFieldType::value_type tmp1 = h + Sigma1 + ch + + plonk_sha512_process::round_constant[(i - row) / 9] + + message_scheduling_words[(i - row) / 9]; + typename BlueprintFieldType::value_type sum = tmp1 + d; + typename BlueprintFieldType::value_type e_new = typename BlueprintFieldType::integral_type(sum.data) % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + assignment.witness(component.W(1), i + 4) = tmp1; + assignment.witness(component.W(2), i + 4) = e_new; + assignment.witness(component.W(3), i + 4) = (sum - e_new) * typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).inversed().data); + + assignment.witness(component.W(0), i + 8) = a; + typename BlueprintFieldType::integral_type integral_a = + typename BlueprintFieldType::integral_type(a.data); + std::vector a_bits(64); + { + nil::marshalling::status_type status; + std::array a_bits_all = nil::marshalling::pack(integral_a, status); + std::copy(a_bits_all.end() - 64, a_bits_all.end(), a_bits.begin()); + } + + std::vector a_sizes = {14, 14, 6, 5, 14, 11}; + std::array, 2> a_chunks = + detail::split_and_sparse(a_bits, a_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(1), i + 8) = a_chunks[0][0]; + assignment.witness(component.W(2), i + 8) = a_chunks[0][1]; + assignment.witness(component.W(3), i + 8) = a_chunks[0][2]; + assignment.witness(component.W(4), i + 8) = a_chunks[0][3]; + assignment.witness(component.W(5), i + 8) = a_chunks[0][4]; + assignment.witness(component.W(6), i + 8) = a_chunks[0][5]; + + assignment.witness(component.W(7), i + 8) = a_chunks[1][0]; + assignment.witness(component.W(8), i + 8) = a_chunks[1][1]; + assignment.witness(component.W(0), i + 7) = a_chunks[1][2]; + assignment.witness(component.W(1), i + 7) = a_chunks[1][3]; + assignment.witness(component.W(2), i + 7) = a_chunks[1][4]; + assignment.witness(component.W(3), i + 7) = a_chunks[1][5]; + + typename BlueprintFieldType::integral_type sparse_Sigma0 = + (a_chunks[1][0] * ((one << (36 *2)) + (one << (30*2)) + (one << (25*2))) + + a_chunks[1][1] * ((one << (50 * 2)) + (one << (44*2)) + (one << (39*2))) + + a_chunks[1][2] * (1 + (one << (58*2)) + (one << (53*2))) + + a_chunks[1][3] * ((one << (6*2)) + 1 + (one << (59*2))) + + a_chunks[1][4] * ((one << (11*2)) + (one << (5*2)) + 1) + + a_chunks[1][5] * ((one << (25*2)) + (one << (19*2)) + (one << (14*2)))); + std::array, 2> + Sigma0_chunks = + detail::reversed_sparse_and_split(sparse_Sigma0, sigma_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(4), i + 7) = Sigma0_chunks[1][0]; + assignment.witness(component.W(5), i + 7) = Sigma0_chunks[1][1]; + assignment.witness(component.W(6), i + 7) = Sigma0_chunks[1][2]; + assignment.witness(component.W(7), i + 7) = Sigma0_chunks[1][3]; + assignment.witness(component.W(8), i + 7) = Sigma0_chunks[1][4]; + + assignment.witness(component.W(0), i + 6) = Sigma0_chunks[0][0]; + assignment.witness(component.W(1), i + 6) = Sigma0_chunks[0][1]; + assignment.witness(component.W(2), i + 6) = Sigma0_chunks[0][2]; + assignment.witness(component.W(3), i + 6) = Sigma0_chunks[0][3]; + assignment.witness(component.W(4), i + 6) = Sigma0_chunks[0][4]; + + typename BlueprintFieldType::integral_type Sigma0 = + Sigma0_chunks[0][0] + Sigma0_chunks[0][1] * (1 << sigma_sizes[0]) + + Sigma0_chunks[0][2] * (one << (sigma_sizes[0] + sigma_sizes[1])) + + Sigma0_chunks[0][3] * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2])) + + Sigma0_chunks[0][4] * (one << (sigma_sizes[0] + sigma_sizes[1] + sigma_sizes[2] + sigma_sizes[3])); + + sparse_values[0] = typename BlueprintFieldType::integral_type((a_chunks[1][0] + a_chunks[1][1] * base4_value.pow(a_sizes[0]) + + a_chunks[1][2] * base4_value.pow(a_sizes[0] + a_sizes[1]) + + a_chunks[1][3] * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2]) + + a_chunks[1][4] * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2] + a_sizes[3]) + + a_chunks[1][5] * base4_value.pow(a_sizes[0] + a_sizes[1] + a_sizes[2] + a_sizes[3] + a_sizes[4]) + ).data); + assignment.witness(component.W(5), i + 6) = sparse_values[0]; + assignment.witness(component.W(6), i + 6) = sparse_values[1]; + + typename BlueprintFieldType::integral_type sparse_maj = + (sparse_values[0] + sparse_values[1] + sparse_values[2]); + std::array, 2> + maj_chunks = + detail::reversed_sparse_and_split_maj( + sparse_maj, ch_and_maj_sizes, + plonk_sha512_process::base4); + assignment.witness(component.W(7), i + 6) = maj_chunks[1][0]; + assignment.witness(component.W(8), i + 6) = maj_chunks[1][1]; + assignment.witness(component.W(0), i + 5) = maj_chunks[1][2]; + assignment.witness(component.W(1), i + 5) = maj_chunks[1][3]; + + assignment.witness(component.W(2), i + 5) = maj_chunks[0][0]; + assignment.witness(component.W(3), i + 5) = maj_chunks[0][1]; + assignment.witness(component.W(4), i + 5) = maj_chunks[0][2]; + assignment.witness(component.W(5), i + 5) = maj_chunks[0][3]; + typename BlueprintFieldType::integral_type maj = + maj_chunks[0][0] + maj_chunks[0][1] * (1 << 16) + maj_chunks[0][2] * (one << 32) + + maj_chunks[0][3] * (one << 48); + assignment.witness(component.W(6), i + 5) = sparse_values[2]; + typename BlueprintFieldType::value_type sum1 = tmp1 + Sigma0 + maj; + typename BlueprintFieldType::value_type a_new = typename BlueprintFieldType::integral_type(sum1.data) % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + assignment.witness(component.W(7), i + 5) = a_new; + assignment.witness(component.W(8), i + 5) = (sum1 - a_new) * typename BlueprintFieldType::value_type(2).pow(64).inversed(); + h = g; + sparse_values[7] = sparse_values[6]; + g = f; + sparse_values[6] = sparse_values[5]; + f = e; + sparse_values[5] = sparse_values[4]; + e = e_new; + d = c; + sparse_values[3] = sparse_values[2]; + c = b; + sparse_values[2] = sparse_values[1]; + b = a; + sparse_values[1] = sparse_values[0]; + a = a_new; + } + std::array output_state = {a, b, c, d, e, f, g, h}; + row = row + 720; + for(std::size_t i = 0; i < 4; i ++){ + assignment.witness(component.W(i), row)= input_state[i]; + auto sum = typename BlueprintFieldType::integral_type(input_state[i].data) + typename BlueprintFieldType::integral_type(output_state[i].data); + assignment.witness(component.W(i), row + 1) = sum % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + assignment.witness(component.W(i + 4), row) = output_state[i]; + assignment.witness(component.W(i + 4), row + 1) = (sum - sum % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data))/ + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + } + row = row + 2; + for(std::size_t i = 0; i < 4; i ++){ + assignment.witness(component.W(i), row) = input_state[i + 4]; + auto sum = typename BlueprintFieldType::integral_type(input_state[i + 4].data) + typename BlueprintFieldType::integral_type(output_state[i + 4].data); + assignment.witness(component.W(i), row + 1) = sum % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + assignment.witness(component.W(i + 4), row) = output_state[i + 4]; + assignment.witness(component.W(i + 4), row + 1) = (sum - sum % + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data))/ + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + } + return typename plonk_sha512_process::result_type( + component, start_row_index); + } + + template + std::array generate_gates( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512_process::input_type &instance_input) { + + auto message_selectors = detail::generate_message_scheduling_gates(component, bp, assignment); + auto compression_selectors = detail::generate_compression_gates(component, bp, assignment); + + return { + message_selectors[0], message_selectors[1], message_selectors[2], + compression_selectors[0], compression_selectors[1], compression_selectors[2], + compression_selectors[3], compression_selectors[4], compression_selectors[5], + compression_selectors[6] + }; + } + + template + void generate_copy_constraints( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512_process::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_sha512_process::var; + + std::size_t row = start_row_index + 2; + + for (std::size_t i = 1; i <= 15; ++i) { + bp.add_copy_constraint({var(component.W(0), row + (i - 1)*6 + 0, false), instance_input.input_words[i]}); + } + for (std::size_t i = 9; i <= 15; ++i) { + bp.add_copy_constraint({var(component.W(7), row + (i - 9)*6 + 2, false), instance_input.input_words[i]}); + } + for (std::size_t i = 0; i <= 15; ++i) { + bp.add_copy_constraint({var(component.W(8), row + (i - 0)*6 + 2, false), instance_input.input_words[i]}); + } + for (std::size_t i = 14; i <= 15; ++i) { + bp.add_copy_constraint({var(component.W(0), row + (i - 14)*6 + 5, false), instance_input.input_words[i]}); + } + + row = row + 384; + + bp.add_copy_constraint( + {var(component.W(6), row + 2, false), var(component.W(5), start_row_index + 1, false)}); + bp.add_copy_constraint( + {var(component.W(6), row + 3, false), var(component.W(6), start_row_index + 1, false)}); + bp.add_copy_constraint( + {var(component.W(6), row + 6, false), var(component.W(1), start_row_index + 1, false)}); + bp.add_copy_constraint( + {var(component.W(6), row + 5, false), var(component.W(2), start_row_index + 1, false)}); + + for (std::size_t i = row; i < row + 720 - 9; i = i + 9){ + bp.add_copy_constraint({var(component.W(6), (i + 2) + 9, false), var(component.W(5), (i + 2), false)}); + bp.add_copy_constraint({var(component.W(6), (i + 3) + 9, false), var(component.W(6), (i + 2), false)}); + bp.add_copy_constraint({var(component.W(6), (i + 5) + 9, false), var(component.W(6), (i + 6), false)}); + bp.add_copy_constraint({var(component.W(6), (i + 6) + 9, false), var(component.W(5), (i + 6), false)}); + } + + bp.add_copy_constraint({var(component.W(0), row + 8, false), instance_input.input_state[0]}); + bp.add_copy_constraint({var(component.W(7), row + 3, false), instance_input.input_state[3]}); + bp.add_copy_constraint({var(component.W(0), row + 0, false), instance_input.input_state[4]}); + bp.add_copy_constraint({var(component.W(8), row + 3, false), instance_input.input_state[7]}); + + row = row + 720; + + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.input_state[0]}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.input_state[1]}); + bp.add_copy_constraint({var(component.W(2), row, false), instance_input.input_state[2]}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.input_state[3]}); + bp.add_copy_constraint({var(component.W(0), row + 2, false), instance_input.input_state[4]}); + bp.add_copy_constraint({var(component.W(1), row + 2, false), instance_input.input_state[5]}); + bp.add_copy_constraint({var(component.W(2), row + 2, false), instance_input.input_state[6]}); + bp.add_copy_constraint({var(component.W(3), row + 2, false), instance_input.input_state[7]}); + } + + template + typename plonk_sha512_process::result_type + generate_circuit( + const plonk_sha512_process &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_sha512_process::input_type &instance_input, + const std::size_t start_row_index){ + + std::size_t j = start_row_index; + detail::generate_assignments_constant(component, bp, assignment, instance_input, start_row_index); + + j = j + 2; + auto selector_indices = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_indices[0], j + 1, j + 383, 6); + assignment.enable_selector(selector_indices[1], j + 4, j + 383, 6); + assignment.enable_selector(selector_indices[2], j + 3, j + 383, 6); + j = j + 384; + assignment.enable_selector(selector_indices[3], j + 1, j + 719, 9); + assignment.enable_selector(selector_indices[4], j + 7, j + 719, 9); + assignment.enable_selector(selector_indices[5], j + 2, j + 719, 9); + assignment.enable_selector(selector_indices[6], j + 6, j + 719, 9); + assignment.enable_selector(selector_indices[7], j + 3, j + 719, 9); + assignment.enable_selector(selector_indices[8], j + 5, j + 719, 9); + j = j + 720; + assignment.enable_selector(selector_indices[9], j, j + 2, 2); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + return typename plonk_sha512_process::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_SHA512_PROCESS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_aux.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_aux.hpp new file mode 100644 index 000000000..1d1e947f9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_aux.hpp @@ -0,0 +1,345 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_AUX_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_AUX_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class lastbits_component : public component { + public: + blueprint_variable X; + std::size_t X_bits; + blueprint_variable result; + blueprint_linear_combination_vector result_bits; + + blueprint_linear_combination_vector full_bits; + std::shared_ptr> unpack_bits; + std::shared_ptr> pack_result; + + lastbits_component(blueprint &bp, + const blueprint_variable &X, + std::size_t X_bits, + const blueprint_variable &result, + const blueprint_linear_combination_vector &result_bits) : + component(bp), + X(X), X_bits(X_bits), result(result), result_bits(result_bits) { + + full_bits = result_bits; + for (std::size_t i = result_bits.size(); i < X_bits; ++i) { + blueprint_variable full_bits_overflow; + full_bits_overflow.allocate(bp); + full_bits.emplace_back(full_bits_overflow); + } + + unpack_bits.reset(new packing(bp, full_bits, X)); + pack_result.reset(new packing(bp, result_bits, result)); + } + + void generate_gates() { + unpack_bits->generate_gates(true); + pack_result->generate_gates(false); + } + + void generate_assignments() { + unpack_bits->generate_assignments_from_packed(); + pack_result->generate_assignments_from_bits(); + } + }; + + template + class XOR3_component : public component { + private: + blueprint_variable tmp; + + public: + blueprint_linear_combination A; + blueprint_linear_combination B; + blueprint_linear_combination C; + bool assume_C_is_zero; + blueprint_linear_combination out; + + XOR3_component(blueprint &bp, + const blueprint_linear_combination &A, + const blueprint_linear_combination &B, + const blueprint_linear_combination &C, + bool assume_C_is_zero, + const blueprint_linear_combination &out) : + component(bp), + A(A), B(B), C(C), assume_C_is_zero(assume_C_is_zero), out(out) { + if (!assume_C_is_zero) { + tmp.allocate(bp); + } + } + + void generate_gates() { + /* + tmp = A + B - 2AB i.e. tmp = A xor B + out = tmp + C - 2tmp C i.e. out = tmp xor C + */ + if (assume_C_is_zero) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint(2 * A, B, A + B - out)); + } else { + this->bp.add_r1cs_constraint(snark::r1cs_constraint(2 * A, B, A + B - tmp)); + this->bp.add_r1cs_constraint(snark::r1cs_constraint(2 * tmp, C, tmp + C - out)); + } + } + + void generate_assignments() { + if (assume_C_is_zero) { + this->bp.lc_val(out) = + this->bp.lc_val(A) + this->bp.lc_val(B) - + typename FieldType::value_type(0x02) * this->bp.lc_val(A) * this->bp.lc_val(B); + } else { + this->bp.val(tmp) = + this->bp.lc_val(A) + this->bp.lc_val(B) - + typename FieldType::value_type(0x02) * this->bp.lc_val(A) * this->bp.lc_val(B); + this->bp.lc_val(out) = + this->bp.val(tmp) + this->bp.lc_val(C) - + typename FieldType::value_type(0x02) * this->bp.val(tmp) * this->bp.lc_val(C); + } + } + }; + +#define SHA256_COMPONENT_ROTR(A, i, k) A[((i) + (k)) % 32] + + /* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ + template + class small_sigma_component : public component { + private: + blueprint_variable_vector W; + blueprint_variable result; + + public: + blueprint_variable_vector result_bits; + std::vector>> compute_bits; + std::shared_ptr> pack_result; + + small_sigma_component(blueprint &bp, + const blueprint_variable_vector &W, + const blueprint_variable &result, + std::size_t rot1, + std::size_t rot2, + std::size_t shift) : + component(bp), + W(W), result(result) { + + result_bits.allocate(bp, 32); + compute_bits.resize(32); + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i].reset(new XOR3_component( + bp, SHA256_COMPONENT_ROTR(W, i, rot1), SHA256_COMPONENT_ROTR(W, i, rot2), + (i + shift < 32 ? W[i + shift] : blueprint_variable(0)), (i + shift >= 32), + result_bits[i])); + } + pack_result.reset(new packing(bp, result_bits, result)); + } + + void generate_gates() { + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i]->generate_gates(); + } + + pack_result->generate_gates(false); + } + + void generate_assignments() { + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i]->generate_assignments(); + } + + pack_result->generate_assignments_from_bits(); + } + }; + + /* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ + template + class big_sigma_component : public component { + private: + blueprint_linear_combination_vector W; + blueprint_variable result; + + public: + blueprint_variable_vector result_bits; + std::vector>> compute_bits; + std::shared_ptr> pack_result; + + big_sigma_component(blueprint &bp, + const blueprint_linear_combination_vector &W, + const blueprint_variable &result, + std::size_t rot1, + std::size_t rot2, + std::size_t rot3) : + component(bp), + W(W), result(result) { + + result_bits.allocate(bp, 32); + compute_bits.resize(32); + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i].reset(new XOR3_component( + bp, SHA256_COMPONENT_ROTR(W, i, rot1), SHA256_COMPONENT_ROTR(W, i, rot2), + SHA256_COMPONENT_ROTR(W, i, rot3), false, result_bits[i])); + } + + pack_result.reset(new packing(bp, result_bits, result)); + } + + void generate_gates() { + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i]->generate_gates(); + } + + pack_result->generate_gates(false); + } + + void generate_assignments() { + for (std::size_t i = 0; i < 32; ++i) { + compute_bits[i]->generate_assignments(); + } + + pack_result->generate_assignments_from_bits(); + } + }; + + /* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ + template + class choice_component : public component { + private: + blueprint_variable_vector result_bits; + + public: + blueprint_linear_combination_vector X; + blueprint_linear_combination_vector Y; + blueprint_linear_combination_vector Z; + blueprint_variable result; + std::shared_ptr> pack_result; + + choice_component(blueprint &bp, + const blueprint_linear_combination_vector &X, + const blueprint_linear_combination_vector &Y, + const blueprint_linear_combination_vector &Z, + const blueprint_variable &result) : + component(bp), + X(X), Y(Y), Z(Z), result(result) { + + result_bits.allocate(bp, 32); + pack_result.reset(new packing(bp, result_bits, result)); + } + + void generate_gates() { + for (std::size_t i = 0; i < 32; ++i) { + /* + result = x * y + (1-x) * z + result - z = x * (y - z) + */ + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(X[i], Y[i] - Z[i], result_bits[i] - Z[i])); + } + pack_result->generate_gates(false); + } + + void generate_assignments() { + for (std::size_t i = 0; i < 32; ++i) { + this->bp.val(result_bits[i]) = + this->bp.lc_val(X[i]) * this->bp.lc_val(Y[i]) + + (FieldType::value_type::one() - this->bp.lc_val(X[i])) * this->bp.lc_val(Z[i]); + } + pack_result->generate_assignments_from_bits(); + } + }; + + /* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */ + template + class majority_component : public component { + private: + blueprint_variable_vector result_bits; + std::shared_ptr> pack_result; + + public: + blueprint_linear_combination_vector X; + blueprint_linear_combination_vector Y; + blueprint_linear_combination_vector Z; + blueprint_variable result; + + majority_component(blueprint &bp, + const blueprint_linear_combination_vector &X, + const blueprint_linear_combination_vector &Y, + const blueprint_linear_combination_vector &Z, + const blueprint_variable &result) : + component(bp), + X(X), Y(Y), Z(Z), result(result) { + result_bits.allocate(bp, 32); + pack_result.reset(new packing(bp, result_bits, result)); + } + + void generate_gates() { + for (std::size_t i = 0; i < 32; ++i) { + /* + 2*result + aux = x + y + z + x, y, z, aux -- bits + aux = x + y + z - 2*result + */ + generate_boolean_r1cs_constraint(this->bp, result_bits[i]); + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(X[i] + Y[i] + Z[i] - 2 * result_bits[i], + 1 - (X[i] + Y[i] + Z[i] - 2 * result_bits[i]), 0)); + } + pack_result->generate_gates(false); + } + + void generate_assignments() { + + // temporary added until fixed-precision modular adaptor is ready: + typedef boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_backend<>> + non_fixed_precision_integral_type; + + using integral_type = typename FieldType::integral_type; + + for (std::size_t i = 0; i < 32; ++i) { + const non_fixed_precision_integral_type v = non_fixed_precision_integral_type( + (this->bp.lc_val(X[i]) + this->bp.lc_val(Y[i]) + this->bp.lc_val(Z[i])).data); + this->bp.val(result_bits[i]) = typename FieldType::value_type(integral_type(v / 2)); + } + + pack_result->generate_assignments_from_bits(); + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_AUX_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_component.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_component.hpp new file mode 100644 index 000000000..d05d86db4 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_component.hpp @@ -0,0 +1,373 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for top-level SHA256 components. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_HPP + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + /** + * Component for the SHA256 compression function. + */ + template + class sha256_compression_function_component : public component { + public: + std::vector> round_a; + std::vector> round_b; + std::vector> round_c; + std::vector> round_d; + std::vector> round_e; + std::vector> round_f; + std::vector> round_g; + std::vector> round_h; + + blueprint_variable_vector packed_W; + std::shared_ptr> message_schedule; + std::vector> round_functions; + + blueprint_variable_vector unreduced_output; + blueprint_variable_vector reduced_output; + std::vector> reduce_output; + + public: + blueprint_linear_combination_vector prev_output; + blueprint_variable_vector new_block; + digest_variable output; + + sha256_compression_function_component( + blueprint &bp, + const blueprint_linear_combination_vector &prev_output, + const blueprint_variable_vector &new_block, + const digest_variable &output) : + component(bp), + prev_output(prev_output), new_block(new_block), output(output) { + + /* message schedule and inputs for it */ + packed_W.allocate(bp, block::detail::shacal2_policy<256>::rounds); + message_schedule.reset( + new sha256_message_schedule_component(bp, new_block, packed_W)); + + /* initalize */ + round_a.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 7 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 8 * hashes::sha2<256>::word_bits)); + round_b.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 6 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 7 * hashes::sha2<256>::word_bits)); + round_c.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 5 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 6 * hashes::sha2<256>::word_bits)); + round_d.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 4 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 5 * hashes::sha2<256>::word_bits)); + round_e.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 3 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 4 * hashes::sha2<256>::word_bits)); + round_f.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 2 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 3 * hashes::sha2<256>::word_bits)); + round_g.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 1 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 2 * hashes::sha2<256>::word_bits)); + round_h.push_back(blueprint_linear_combination_vector( + prev_output.rbegin() + 0 * hashes::sha2<256>::word_bits, + prev_output.rbegin() + 1 * hashes::sha2<256>::word_bits)); + + /* do the rounds */ + for (std::size_t i = 0; i < block::detail::shacal2_policy<256>::rounds; ++i) { + round_h.push_back(round_g[i]); + round_g.push_back(round_f[i]); + round_f.push_back(round_e[i]); + round_d.push_back(round_c[i]); + round_c.push_back(round_b[i]); + round_b.push_back(round_a[i]); + + blueprint_variable_vector new_round_a_variables; + new_round_a_variables.allocate(bp, hashes::sha2<256>::word_bits); + round_a.emplace_back(new_round_a_variables); + + blueprint_variable_vector new_round_e_variables; + new_round_e_variables.allocate(bp, hashes::sha2<256>::word_bits); + round_e.emplace_back(new_round_e_variables); + + round_functions.push_back(sha256_round_function_component( + bp, round_a[i], round_b[i], round_c[i], round_d[i], round_e[i], round_f[i], round_g[i], + round_h[i], packed_W[i], block::detail::shacal2_policy<256>::constants[i], + round_a[i + 1], round_e[i + 1])); + } + + /* finalize */ + unreduced_output.allocate(bp, 8); + reduced_output.allocate(bp, 8); + for (std::size_t i = 0; i < 8; ++i) { + reduce_output.push_back(lastbits_component( + bp, + unreduced_output[i], + hashes::sha2<256>::word_bits + 1, + reduced_output[i], + blueprint_variable_vector( + output.bits.rbegin() + (7 - i) * hashes::sha2<256>::word_bits, + output.bits.rbegin() + (8 - i) * hashes::sha2<256>::word_bits))); + } + } + void generate_gates() { + message_schedule->generate_gates(); + for (std::size_t i = 0; i < block::detail::shacal2_policy<256>::rounds; ++i) { + round_functions[i].generate_gates(); + } + + for (std::size_t i = 0; i < 4; ++i) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, + round_functions[3 - i].packed_d + round_functions[63 - i].packed_new_a, + unreduced_output[i])); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, + round_functions[3 - i].packed_h + round_functions[63 - i].packed_new_e, + unreduced_output[4 + i])); + } + + for (std::size_t i = 0; i < 8; ++i) { + reduce_output[i].generate_gates(); + } + } + void generate_assignments() { + message_schedule->generate_assignments(); + + for (std::size_t i = 0; i < block::detail::shacal2_policy<256>::rounds; ++i) { + round_functions[i].generate_assignments(); + } + + for (std::size_t i = 0; i < 4; ++i) { + this->bp.val(unreduced_output[i]) = this->bp.val(round_functions[3 - i].packed_d) + + this->bp.val(round_functions[63 - i].packed_new_a); + this->bp.val(unreduced_output[4 + i]) = this->bp.val(round_functions[3 - i].packed_h) + + this->bp.val(round_functions[63 - i].packed_new_e); + } + + for (std::size_t i = 0; i < 8; ++i) { + reduce_output[i].generate_assignments(); + } + } + }; + + /** + * Component for the SHA256 compression function, viewed as a 2-to-1 hash + * function, and using the same initialization vector as in SHA256 + * specification. Thus, any collision for + * sha256_two_to_one_hash_component trivially extends to a collision for + * full SHA256 (by appending the same padding). + */ + template + class sha256_two_to_one_hash_component : public component { + public: + typedef std::vector hash_value_type; + typedef digest_variable hash_variable_type; + typedef snark::merkle_authentication_path merkle_authentication_path_type; + + std::shared_ptr> f; + + sha256_two_to_one_hash_component(blueprint &bp, + const digest_variable &left, + const digest_variable &right, + const digest_variable &output) : + component(bp) { + + /* concatenate block = left || right */ + blueprint_variable_vector block; + block.insert(block.end(), left.bits.begin(), left.bits.end()); + block.insert(block.end(), right.bits.begin(), right.bits.end()); + + /* compute the hash itself */ + f.reset(new sha256_compression_function_component( + bp, SHA256_default_IV(bp), block, output)); + } + sha256_two_to_one_hash_component(blueprint &bp, + std::size_t block_length, + const block_variable &input_block, + const digest_variable &output) : + component(bp) { + + assert(block_length == hashes::sha2<256>::block_bits); + assert(input_block.bits.size() == block_length); + f.reset(new sha256_compression_function_component( + bp, SHA256_default_IV(bp), input_block.bits, output)); + } + + void generate_gates(bool ensure_output_bitness = true) { // TODO: ignored for now + f->generate_gates(); + } + + void generate_assignments() { + f->generate_assignments(); + } + + static std::size_t get_block_len() { + return hashes::sha2<256>::block_bits; + } + + static std::size_t get_digest_len() { + return hashes::sha2<256>::digest_bits; + } + + static std::vector get_hash(const std::vector &input) { + blueprint bp; + + block_variable input_variable(bp, hashes::sha2<256>::block_bits); + digest_variable output_variable(bp, hashes::sha2<256>::digest_bits); + sha256_two_to_one_hash_component f(bp, hashes::sha2<256>::block_bits, input_variable, + output_variable); + + input_variable.generate_assignments(input); + f.generate_assignments(); + + return output_variable.get_digest(); + } + + static std::size_t + expected_constraints(bool ensure_output_bitness = true) { // TODO: ignored for now + return 27280; /* hardcoded for now */ + } + }; + + /** + * Component for arbitary length sha256 hash based on + * Merkle-Damagard padding. (i.e. standard sha256). + */ + template + class sha256_hash_component : public component { + public: + typedef std::vector hash_value_type; + typedef digest_variable hash_variable_type; + typedef snark::merkle_authentication_path merkle_authentication_path_type; + + std::vector>> blocks_components; + std::vector> blocks_bits; + std::vector>> intermediate_outputs; + std::shared_ptr> padding; + + sha256_hash_component(blueprint &bp, + std::size_t input_len, + const block_variable &block_input, + const digest_variable &output) : + component(bp) { + + assert(input_len == block_input.block_size); + const int length_bits_size = 64; + + padding.reset(new merkle_damagard_padding(bp, input_len, length_bits_size, + hashes::sha2<256>::block_bits)); + blueprint_variable_vector bits = block_input.bits; + bits.insert(bits.end(), padding->bits.begin(), padding->bits.end()); + assert(bits.size() % hashes::sha2<256>::block_bits == 0); + std::size_t num_blocks = bits.size() / hashes::sha2<256>::block_bits; + + intermediate_outputs.resize(num_blocks - 1); + blocks_components.resize(num_blocks); + blocks_bits.resize(num_blocks); + + const std::size_t chunk = hashes::sha2<256>::block_bits; + + for (std::size_t i = 0; i < num_blocks; ++i) { + blocks_bits[i] = blueprint_variable_vector(bits.begin() + i * chunk, + bits.begin() + (i + 1) * chunk); + } + + for (std::size_t i = 0; i < num_blocks - 1; ++i) { + intermediate_outputs[i].reset( + new digest_variable(bp, hashes::sha2<256>::digest_bits)); + } + + if (num_blocks == 1) { + blocks_components[0].reset(new sha256_compression_function_component( + bp, SHA256_default_IV(bp), blocks_bits[0], output)); + } else { + blocks_components[0].reset(new sha256_compression_function_component( + bp, SHA256_default_IV(bp), blocks_bits[0], *intermediate_outputs[0])); + for (std::size_t i = 1; i < num_blocks - 1; ++i) { + blueprint_linear_combination_vector lcv(intermediate_outputs[i - 1]->bits); + blocks_components[i].reset(new sha256_compression_function_component( + bp, lcv, blocks_bits[i], *intermediate_outputs[i])); + } + blueprint_linear_combination_vector lcv( + intermediate_outputs[num_blocks - 2]->bits); + blocks_components[num_blocks - 1].reset( + new sha256_compression_function_component( + bp, lcv, blocks_bits[num_blocks - 1], output)); + } + } + + void generate_gates(bool ensure_output_bitness = true) { // TODO: ignored for now + padding->generate_gates(); + for (auto f : blocks_components) { + f->generate_gates(); + } + } + + void generate_assignments() { + padding->generate_assignments(); + for (auto f : blocks_components) { + f->generate_assignments(); + } + } + + static std::size_t get_block_len() { + return hashes::sha2<256>::block_bits; + } + + static std::size_t get_digest_len() { + return hashes::sha2<256>::digest_bits; + } + + static std::vector get_hash(const std::vector &input) { + blueprint bp; + + block_variable input_variable(bp, input.size()); + digest_variable output_variable(bp, hashes::sha2<256>::digest_bits); + sha256_hash_component f(bp, input_variable.block_size, input_variable, + output_variable); + + input_variable.generate_assignments(input); + f.generate_assignments(); + + return output_variable.get_digest(); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_construction.hpp b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_construction.hpp new file mode 100644 index 000000000..acc0bbd0c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/hashes/sha2/r1cs/sha256_construction.hpp @@ -0,0 +1,300 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for components for the SHA256 message schedule and round function. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENTS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class sha256_message_schedule_component : public component { + public: + std::vector> W_bits; + std::vector>> pack_W; + + std::vector> sigma0; + std::vector> sigma1; + std::vector>> compute_sigma0; + std::vector>> compute_sigma1; + std::vector> unreduced_W; + std::vector>> mod_reduce_W; + + public: + blueprint_variable_vector M; + blueprint_variable_vector packed_W; + sha256_message_schedule_component(blueprint &bp, + const blueprint_variable_vector &M, + const blueprint_variable_vector &packed_W) : + component(bp), + M(M), packed_W(packed_W) { + + W_bits.resize(64); + + pack_W.resize(16); + for (std::size_t i = 0; i < 16; ++i) { + W_bits[i] = blueprint_variable_vector( + M.rbegin() + (15 - i) * hashes::sha2<256>::word_bits, + M.rbegin() + (16 - i) * hashes::sha2<256>::word_bits); + + pack_W[i].reset(new packing(bp, W_bits[i], packed_W[i])); + } + + /* NB: some of those will be un-allocated */ + sigma0.resize(64); + sigma1.resize(64); + compute_sigma0.resize(64); + compute_sigma1.resize(64); + unreduced_W.resize(64); + mod_reduce_W.resize(64); + + for (std::size_t i = 16; i < block::detail::shacal2_policy<256>::rounds; ++i) { + /* allocate result variables for sigma0/sigma1 invocations */ + sigma0[i].allocate(bp); + sigma1[i].allocate(bp); + + /* compute sigma0/sigma1 */ + compute_sigma0[i].reset( + new small_sigma_component(bp, W_bits[i - 15], sigma0[i], 7, 18, 3)); + compute_sigma1[i].reset( + new small_sigma_component(bp, W_bits[i - 2], sigma1[i], 17, 19, 10)); + + /* unreduced_W = sigma0(W_{i-15}) + sigma1(W_{i-2}) + W_{i-7} + W_{i-16} before modulo + * 2^32 + */ + unreduced_W[i].allocate(bp); + + /* allocate the bit representation of packed_W[i] */ + W_bits[i].allocate(bp, hashes::sha2<256>::word_bits); + + /* and finally reduce this into packed and bit representations */ + mod_reduce_W[i].reset(new lastbits_component( + bp, unreduced_W[i], hashes::sha2<256>::word_bits + 2, packed_W[i], W_bits[i])); + } + } + + void generate_gates() { + for (std::size_t i = 0; i < 16; ++i) { + pack_W[i]->generate_gates( + false); // do not enforce bitness here; caller be aware. + } + + for (std::size_t i = 16; i < block::detail::shacal2_policy<256>::rounds; ++i) { + compute_sigma0[i]->generate_gates(); + compute_sigma1[i]->generate_gates(); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, sigma0[i] + sigma1[i] + packed_W[i - 16] + packed_W[i - 7], unreduced_W[i])); + + mod_reduce_W[i]->generate_gates(); + } + } + + void generate_assignments() { + for (std::size_t i = 0; i < 16; ++i) { + pack_W[i]->generate_assignments_from_bits(); + } + + for (std::size_t i = 16; i < block::detail::shacal2_policy<256>::rounds; ++i) { + compute_sigma0[i]->generate_assignments(); + compute_sigma1[i]->generate_assignments(); + + this->bp.val(unreduced_W[i]) = this->bp.val(sigma0[i]) + this->bp.val(sigma1[i]) + + this->bp.val(packed_W[i - 16]) + + this->bp.val(packed_W[i - 7]); + + mod_reduce_W[i]->generate_assignments(); + } + } + }; + + template + class sha256_round_function_component : public component { + public: + blueprint_variable sigma0; + blueprint_variable sigma1; + std::shared_ptr> compute_sigma0; + std::shared_ptr> compute_sigma1; + blueprint_variable choice; + blueprint_variable majority; + std::shared_ptr> compute_choice; + std::shared_ptr> compute_majority; + blueprint_variable packed_d; + std::shared_ptr> pack_d; + blueprint_variable packed_h; + std::shared_ptr> pack_h; + blueprint_variable unreduced_new_a; + blueprint_variable unreduced_new_e; + std::shared_ptr> mod_reduce_new_a; + std::shared_ptr> mod_reduce_new_e; + blueprint_variable packed_new_a; + blueprint_variable packed_new_e; + + public: + blueprint_linear_combination_vector a; + blueprint_linear_combination_vector b; + blueprint_linear_combination_vector c; + blueprint_linear_combination_vector d; + blueprint_linear_combination_vector e; + blueprint_linear_combination_vector f; + blueprint_linear_combination_vector g; + blueprint_linear_combination_vector h; + blueprint_variable W; + long K; + blueprint_linear_combination_vector new_a; + blueprint_linear_combination_vector new_e; + + sha256_round_function_component(blueprint &bp, + const blueprint_linear_combination_vector &a, + const blueprint_linear_combination_vector &b, + const blueprint_linear_combination_vector &c, + const blueprint_linear_combination_vector &d, + const blueprint_linear_combination_vector &e, + const blueprint_linear_combination_vector &f, + const blueprint_linear_combination_vector &g, + const blueprint_linear_combination_vector &h, + const blueprint_variable &W, + const long &K, + const blueprint_linear_combination_vector &new_a, + const blueprint_linear_combination_vector &new_e) : + component(bp), + a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h), W(W), K(K), new_a(new_a), new_e(new_e) { + + /* compute sigma0 and sigma1 */ + sigma0.allocate(bp); + sigma1.allocate(bp); + compute_sigma0.reset(new big_sigma_component(bp, a, sigma0, 2, 13, 22)); + compute_sigma1.reset(new big_sigma_component(bp, e, sigma1, 6, 11, 25)); + + /* compute choice */ + choice.allocate(bp); + compute_choice.reset(new choice_component(bp, e, f, g, choice)); + + /* compute majority */ + majority.allocate(bp); + compute_majority.reset(new majority_component(bp, a, b, c, majority)); + + /* pack d */ + packed_d.allocate(bp); + pack_d.reset(new packing(bp, d, packed_d)); + + /* pack h */ + packed_h.allocate(bp); + pack_h.reset(new packing(bp, h, packed_h)); + + /* compute the actual results for the round */ + unreduced_new_a.allocate(bp); + unreduced_new_e.allocate(bp); + + packed_new_a.allocate(bp); + packed_new_e.allocate(bp); + + mod_reduce_new_a.reset(new lastbits_component( + bp, unreduced_new_a, hashes::sha2<256>::word_bits + 3, packed_new_a, new_a)); + mod_reduce_new_e.reset(new lastbits_component( + bp, unreduced_new_e, hashes::sha2<256>::word_bits + 3, packed_new_e, new_e)); + } + + void generate_gates() { + compute_sigma0->generate_gates(); + compute_sigma1->generate_gates(); + + compute_choice->generate_gates(); + compute_majority->generate_gates(); + + pack_d->generate_gates(false); + pack_h->generate_gates(false); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, packed_h + sigma1 + choice + K + W + sigma0 + majority, unreduced_new_a)); + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, packed_d + packed_h + sigma1 + choice + K + W, unreduced_new_e)); + + mod_reduce_new_a->generate_gates(); + mod_reduce_new_e->generate_gates(); + } + + void generate_assignments() { + compute_sigma0->generate_assignments(); + compute_sigma1->generate_assignments(); + + compute_choice->generate_assignments(); + compute_majority->generate_assignments(); + pack_d->generate_assignments_from_bits(); + pack_h->generate_assignments_from_bits(); + + this->bp.val(unreduced_new_a) = this->bp.val(packed_h) + this->bp.val(sigma1) + + this->bp.val(choice) + typename FieldType::value_type(K) + + this->bp.val(W) + this->bp.val(sigma0) + this->bp.val(majority); + this->bp.val(unreduced_new_e) = this->bp.val(packed_d) + this->bp.val(packed_h) + + this->bp.val(sigma1) + this->bp.val(choice) + + typename FieldType::value_type(K) + this->bp.val(W); + + mod_reduce_new_a->generate_assignments(); + mod_reduce_new_e->generate_assignments(); + } + }; + + template + blueprint_linear_combination_vector SHA256_default_IV(blueprint &bp) { + using namespace hashes::detail; + + typename sha2_policy<256>::state_type iv = sha2_policy<256>::iv_generator()(); + + blueprint_linear_combination_vector result; + result.reserve(hashes::sha2<256>::digest_bits); + + for (std::size_t i = 0; i < hashes::sha2<256>::digest_bits; ++i) { + int iv_val = + iv[i / hashes::sha2<256>::word_bits] >> (31 - (i % hashes::sha2<256>::word_bits)) & 1; + + blueprint_linear_combination iv_element; + iv_element.assign(bp, iv_val * blueprint_variable(0)); + iv_element.evaluate(bp); + + result.emplace_back(iv_element); + } + + return result; + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENTS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/plonk/merkle_tree.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/plonk/merkle_tree.hpp new file mode 100644 index 000000000..470a6c479 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/plonk/merkle_tree.hpp @@ -0,0 +1,152 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the MERKLE_TREE component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_HPP + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class merkle_tree; + + template + class merkle_tree, CurveType, + W0, W1, W2, W3, W4, W5, W6, W7, W8> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using sha256_component = + sha256; + + public: + constexpr static const std::size_t rows_amount = 1023 * sha256_component::rows_amount; + + struct params_type { + std::array data; + }; + + struct allocated_data_type { + allocated_data_type() { + previously_allocated = false; + } + + // TODO access modifiers + bool previously_allocated; + std::array selectors; + }; + + struct result_type { + std::array output = {var(0, 0, false), var(0, 0, false)}; + + result_type(std::size_t component_start_row) { + std::array output = {var(W0, component_start_row + rows_amount - 1, false), + var(W1, component_start_row + rows_amount - 1, false)}; + } + }; + + static std::size_t allocate_rows(blueprint &bp) { + return bp.allocate_rows(rows_amount); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + std::size_t component_start_row) { + + generate_gates(bp, assignment, params, allocated_data, component_start_row); + generate_copy_constraints(bp, assignment, params, component_start_row); + return result_type(component_start_row); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + std::array data; + for (std::size_t i = 0; i < 2048; i++) { + data[i] = params.data[i]; + } + int k; + for (std::size_t i = 11; i > -1; i -= 2) { + k = 0; + for (std::size_t j = 0; j < (1 << i); j += 4) { + std::array sha_blocks = {data[j], data[j + 1], data[j + 2], data[j + 3]}; + typename sha256_component::params_type sha_params = {sha_blocks}; + auto sha_output = sha256_component::generate_assignments(assignment, sha_params, row); + data[k] = sha_output.output[0]; + data[k + 1] = sha_output.output[0]; + } + k += 2; + } + return result_type(component_start_row); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + allocated_data_type &allocated_data, + std::size_t component_start_row) { + std::size_t row = component_start_row; + for (std::size_t i = 11; i > -1; i -= 2) { + for (std::size_t j = 0; j < (1 << i); j += 4) { + sha256_component::generate_gates(bp, assignment, allocated_data, row); + } + } + } + + static void generate_copy_constraints(blueprint &bp, + blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + for (std::size_t i = 11; i > -1; i -= 2) { + for (std::size_t j = 0; j < (1 << i); j += 4) { + sha256_component::generate_copy_constraints(bp, assignment, allocated_data, row); + } + } + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MERKLE_TREE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/authentication_path.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/authentication_path.hpp new file mode 100644 index 000000000..c157a7132 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/authentication_path.hpp @@ -0,0 +1,92 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Test program that exercises the SEppzkSNARK (first generator, then +// prover, then verifier) on a synthetic R1CS instance. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + struct merkle_authentication_path_variable : public component { + + const std::size_t tree_depth; + std::vector> left_digests; + std::vector> right_digests; + + merkle_authentication_path_variable(blueprint &bp, const std::size_t tree_depth) : + component(bp), tree_depth(tree_depth) { + for (std::size_t i = 0; i < tree_depth; ++i) { + left_digests.emplace_back(digest_variable(bp, Hash::get_digest_len())); + right_digests.emplace_back(digest_variable(bp, Hash::get_digest_len())); + } + } + + void generate_gates() { + for (std::size_t i = 0; i < tree_depth; ++i) { + left_digests[i].generate_gates(); + right_digests[i].generate_gates(); + } + } + + void generate_assignments(const std::size_t address, + const snark::merkle_authentication_path &path) { + assert(path.size() == tree_depth); + + for (std::size_t i = 0; i < tree_depth; ++i) { + if (address & (1ul << (tree_depth - 1 - i))) { + left_digests[i].generate_assignments(path[i]); + } else { + right_digests[i].generate_assignments(path[i]); + } + } + } + + snark::merkle_authentication_path get_authentication_path(const std::size_t address) const { + snark::merkle_authentication_path result; + for (std::size_t i = 0; i < tree_depth; ++i) { + if (address & (1ul << (tree_depth - 1 - i))) { + result.emplace_back(left_digests[i].get_digest()); + } else { + result.emplace_back(right_digests[i].get_digest()); + } + } + + return result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_read.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_read.hpp new file mode 100644 index 000000000..baa6aacc5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_read.hpp @@ -0,0 +1,188 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the Merkle tree check read component. +// +// The component checks the following: given a root R, address A, value V, and +// authentication path P, check that P is a valid authentication path for the +// value V as the A-th leaf in a Merkle tree with root R. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class merkle_tree_check_read_component : public component { + private: + std::vector hashers; + std::vector> hasher_inputs; + std::vector> propagators; + std::vector> internal_output; + + std::shared_ptr> computed_root; + std::shared_ptr> check_root; + + public: + const std::size_t digest_size; + const std::size_t tree_depth; + blueprint_linear_combination_vector address_bits; + digest_variable leaf; + digest_variable root; + merkle_authentication_path_variable path; + blueprint_linear_combination read_successful; + + merkle_tree_check_read_component(blueprint &bp, + const std::size_t tree_depth, + const blueprint_linear_combination_vector &address_bits, + const digest_variable &leaf_digest, + const digest_variable &root_digest, + const merkle_authentication_path_variable &path, + const blueprint_linear_combination &read_successful); + + void generate_gates(); + void generate_assignments(); + + static std::size_t root_size_in_bits(); + /* for debugging purposes */ + static std::size_t expected_constraints(const std::size_t tree_depth); + }; + + template + merkle_tree_check_read_component::merkle_tree_check_read_component( + blueprint &bp, + const std::size_t tree_depth, + const blueprint_linear_combination_vector &address_bits, + const digest_variable &leaf, + const digest_variable &root, + const merkle_authentication_path_variable &path, + const blueprint_linear_combination &read_successful) : + component(bp), + digest_size(Hash::get_digest_len()), tree_depth(tree_depth), address_bits(address_bits), leaf(leaf), + root(root), path(path), read_successful(read_successful) { + /* + The tricky part here is ordering. For Merkle tree + authentication paths, path[0] corresponds to one layer below + the root (and path[tree_depth-1] corresponds to the layer + containing the leaf), while address_bits has the reverse order: + address_bits[0] is LSB, and corresponds to layer containing the + leaf, and address_bits[tree_depth-1] is MSB, and corresponds to + the subtree directly under the root. + */ + assert(tree_depth > 0); + assert(tree_depth == address_bits.size()); + + for (std::size_t i = 0; i < tree_depth - 1; ++i) { + internal_output.emplace_back(digest_variable(bp, digest_size)); + } + + computed_root.reset(new digest_variable(bp, digest_size)); + + for (std::size_t i = 0; i < tree_depth; ++i) { + block_variable inp(bp, path.left_digests[i], path.right_digests[i]); + hasher_inputs.emplace_back(inp); + hashers.emplace_back( + Hash(bp, 2 * digest_size, inp, (i == 0 ? *computed_root : internal_output[i - 1]))); + } + + for (std::size_t i = 0; i < tree_depth; ++i) { + /* + The propagators take a computed hash value (or leaf in the + base case) and propagate it one layer up, either in the left + or the right slot of authentication_path_variable. + */ + propagators.emplace_back(digest_selector_component( + bp, digest_size, i < tree_depth - 1 ? internal_output[i] : leaf, + address_bits[tree_depth - 1 - i], path.left_digests[i], path.right_digests[i])); + } + + check_root.reset(new bit_vector_copy_component(bp, computed_root->bits, root.bits, + read_successful, FieldType::number_bits)); + } + + template + void merkle_tree_check_read_component::generate_gates() { + /* ensure correct hash computations */ + for (std::size_t i = 0; i < tree_depth; ++i) { + // Note that we check root outside and have enforced booleanity of + // path.left_digests/path.right_digests outside in path.generate_gates + hashers[i].generate_gates(false); + } + + /* ensure consistency of path.left_digests/path.right_digests with internal_output */ + for (std::size_t i = 0; i < tree_depth; ++i) { + propagators[i].generate_gates(); + } + + check_root->generate_gates(false, false); + } + + template + void merkle_tree_check_read_component::generate_assignments() { + /* do the hash computations bottom-up */ + for (int i = tree_depth - 1; i >= 0; --i) { + /* propagate previous input */ + propagators[i].generate_assignments(); + + /* compute hash */ + hashers[i].generate_assignments(); + } + + check_root->generate_assignments(); + } + + template + std::size_t merkle_tree_check_read_component::root_size_in_bits() { + return Hash::get_digest_len(); + } + + template + std::size_t merkle_tree_check_read_component::expected_constraints( + const std::size_t tree_depth) { + /* NB: this includes path constraints */ + const std::size_t hasher_constraints = tree_depth * Hash::expected_constraints(false); + const std::size_t propagator_constraints = tree_depth * Hash::get_digest_len(); + const std::size_t authentication_path_constraints = 2 * tree_depth * Hash::get_digest_len(); + const std::size_t check_root_constraints = + 3 * (Hash::get_digest_len() + (FieldType::capacity()) - 1) / FieldType::capacity(); + + return hasher_constraints + propagator_constraints + authentication_path_constraints + + check_root_constraints; + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_update.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_update.hpp new file mode 100644 index 000000000..5f2e0e24a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/check_update.hpp @@ -0,0 +1,217 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the Merkle tree check update component. +// +// The component checks the following: given two roots R1 and R2, address A, two +// values V1 and V2, and authentication path P, check that +// - P is a valid authentication path for the value V1 as the A-th leaf in a Merkle tree with root R1, and +// - P is a valid authentication path for the value V2 as the A-th leaf in a Merkle tree with root R2. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP + +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template, typename FieldType = typename HashComponent::field_type, + std::size_t Arity = 2> + class merkle_proof_update : public component { + using hash_type = typename HashComponent::hash_type; + + static_assert(std::is_same, typename HashComponent::result_type>::value); + // TODO: add support of the trees with arity more than 2 + static_assert(Arity == 2); + + std::vector prev_hashers; + std::vector> prev_hasher_inputs; + std::vector> prev_propagators; + std::vector> prev_internal_output; + + std::vector next_hashers; + std::vector> next_hasher_inputs; + std::vector> next_propagators; + std::vector> next_internal_output; + + std::shared_ptr> computed_next_root; + std::shared_ptr> check_next_root; + + public: + const std::size_t digest_size; + const std::size_t tree_depth; + + blueprint_variable_vector address_bits; + digest_variable prev_leaf_digest; + digest_variable prev_root_digest; + merkle_proof prev_path; + digest_variable next_leaf_digest; + digest_variable next_root_digest; + merkle_proof next_path; + blueprint_linear_combination update_successful; + + /* Note that while it is necessary to generate R1CS constraints + for prev_path, it is not necessary to do so for next_path. See + comment in the implementation of generate_gates() */ + + merkle_proof_update(blueprint &bp, + const std::size_t tree_depth, + const blueprint_variable_vector &address_bits, + const digest_variable &prev_leaf_digest, + const digest_variable &prev_root_digest, + const merkle_proof &prev_path, + const digest_variable &next_leaf_digest, + const digest_variable &next_root_digest, + const merkle_proof &next_path, + const blueprint_linear_combination &update_successful) : + component(bp), + digest_size(hash_type::digest_bits), tree_depth(tree_depth), address_bits(address_bits), + prev_leaf_digest(prev_leaf_digest), prev_root_digest(prev_root_digest), prev_path(prev_path), + next_leaf_digest(next_leaf_digest), next_root_digest(next_root_digest), next_path(next_path), + update_successful(update_successful) { + assert(tree_depth > 0); + assert(tree_depth == address_bits.size()); + + for (std::size_t i = 0; i < tree_depth - 1; ++i) { + prev_internal_output.emplace_back(digest_variable(bp, digest_size)); + next_internal_output.emplace_back(digest_variable(bp, digest_size)); + } + + computed_next_root.reset(new digest_variable(bp, digest_size)); + + for (std::size_t i = 0; i < tree_depth; ++i) { + // TODO: generalize for Arity > 2 + block_variable prev_inp(bp, prev_path.path[i][0], prev_path.path[i][1]); + prev_hasher_inputs.emplace_back(prev_inp); + prev_hashers.emplace_back( + HashComponent(bp, prev_inp, (i == 0 ? prev_root_digest : prev_internal_output[i - 1]))); + + // TODO: generalize for Arity > 2 + block_variable next_inp(bp, next_path.path[i][0], next_path.path[i][1]); + next_hasher_inputs.emplace_back(next_inp); + next_hashers.emplace_back(HashComponent( + bp, next_inp, (i == 0 ? *computed_next_root : next_internal_output[i - 1]))); + } + + for (std::size_t i = 0; i < tree_depth; ++i) { + // TODO: generalize for Arity > 2 + prev_propagators.emplace_back(digest_selector_component( + bp, digest_size, i < tree_depth - 1 ? prev_internal_output[i] : prev_leaf_digest, + address_bits[tree_depth - 1 - i], prev_path.path[i][0], prev_path.path[i][1])); + // TODO: generalize for Arity > 2 + next_propagators.emplace_back(digest_selector_component( + bp, digest_size, i < tree_depth - 1 ? next_internal_output[i] : next_leaf_digest, + address_bits[tree_depth - 1 - i], next_path.path[i][0], next_path.path[i][1])); + } + + check_next_root.reset(new bit_vector_copy_component( + bp, computed_next_root->bits, next_root_digest.bits, update_successful, + FieldType::value_bits - 1)); + } + + void generate_gates() { + /* ensure correct hash computations */ + for (std::size_t i = 0; i < tree_depth; ++i) { + prev_hashers[i].generate_gates( + false); // we check root outside and prev_left/prev_right above + next_hashers[i].generate_gates( + true); // however we must check right side hashes + } + + /* ensure consistency of internal_left/internal_right with internal_output */ + for (std::size_t i = 0; i < tree_depth; ++i) { + prev_propagators[i].generate_gates(); + next_propagators[i].generate_gates(); + } + + /* ensure that prev auxiliary input and next auxiliary input match */ + for (std::size_t i = 0; i < tree_depth; ++i) { + for (std::size_t j = 0; j < digest_size; ++j) { + /* + addr * (prev_left - next_left) + (1 - addr) * (prev_right - next_right) = 0 + addr * (prev_left - next_left - prev_right + next_right) = next_right - prev_right + */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + address_bits[tree_depth - 1 - i], + prev_path.left_digests[i].bits[j] - next_path.left_digests[i].bits[j] - + prev_path.right_digests[i].bits[j] + next_path.right_digests[i].bits[j], + next_path.right_digests[i].bits[j] - prev_path.right_digests[i].bits[j])); + } + } + + /* Note that while it is necessary to generate R1CS constraints + for prev_path, it is not necessary to do so for next_path. + + This holds, because { next_path.left_inputs[i], + next_path.right_inputs[i] } is a pair { hash_output, + auxiliary_input }. The bitness for hash_output is enforced + above by next_hashers[i].generate_gates. + + Because auxiliary input is the same for prev_path and next_path + (enforced above), we have that auxiliary_input part is also + constrained to be boolean, because prev_path is *all* + constrained to be all boolean. */ + + check_next_root->generate_gates(false, false); + } + + void generate_assignments() { + /* do the hash computations bottom-up */ + for (int i = tree_depth - 1; i >= 0; --i) { + /* ensure consistency of prev_path and next_path */ + if (this->bp.val(address_bits[tree_depth - 1 - i]) == FieldType::value_type::zero()) { + next_path.left_digests[i].generate_assignments(prev_path.left_digests[i].get_digest()); + } else { + next_path.right_digests[i].generate_assignments( + prev_path.right_digests[i].get_digest()); + } + + /* propagate previous input */ + prev_propagators[i].generate_assignments(); + next_propagators[i].generate_assignments(); + + /* compute hash */ + prev_hashers[i].generate_assignments(); + next_hashers[i].generate_assignments(); + } + + check_next_root->generate_assignments(); + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/prove.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/prove.hpp new file mode 100644 index 000000000..26df8676b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/prove.hpp @@ -0,0 +1,122 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Test program that exercises the SEppzkSNARK (first generator, then +// prover, then verifier) on a synthetic R1CS instance. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template, + typename FieldType = typename HashComponent::field_type, std::size_t Arity = 2> + struct merkle_proof : public component { + using merkle_tree_container = + nil::crypto3::containers::merkle_tree; + using merkle_proof_container = + nil::crypto3::containers::merkle_proof; + using path_type = std::vector>>; + + std::size_t address; + const std::size_t tree_depth; + path_type path; + + merkle_proof(blueprint &bp, const std::size_t tree_depth) : + component(bp), tree_depth(tree_depth) { + + for (std::size_t i = 0; i < tree_depth; ++i) { + std::vector> layer; + + for (std::size_t j = 0; j < Arity; ++j) { + layer.template emplace_back( + digest_variable(this->bp, HashComponent::digest_bits)); + } + + path.emplace_back(layer); + } + } + + void generate_gates() { + for (std::size_t i = 0; i < tree_depth; ++i) { + for (std::size_t j = 0; j < Arity; ++j) { + path[i][j].generate_gates(); + } + } + } + + void generate_assignments(const merkle_proof_container &proof, bool do_clear = false) { + // TODO: generalize for Arity > 2 + assert(Arity == 2); + assert(proof._path.size() == tree_depth); + + this->address = 0; + for (std::size_t i = 0; i < tree_depth; ++i) { + for (std::size_t j = 0; j < Arity - 1; ++j) { + auto position = proof._path[tree_depth - 1 - i][j]._position; + path[i][position].generate_assignments(proof._path[tree_depth - 1 - i][j]._hash); + this->address |= (position ? 0 : 1ul << (tree_depth - 1 - i)); + if (do_clear) { + path[i][position ? 0 : 1].generate_assignments( + std::vector(HashComponent::digest_bits, false)); + } + } + } + } + + void generate_assignments(std::size_t address, const std::vector> &proof) { + // TODO: generalize for Arity > 2 + assert(Arity == 2); + assert(proof.size() == tree_depth); + + for (std::size_t i = 0; i < tree_depth; ++i) { + if (address & (1ul << (tree_depth - 1 - i))) { + path[i][0].generate_assignments(proof[i]); + } else { + path[i][1].generate_assignments(proof[i]); + } + } + + this->address = address; + } + + /// For test only + static auto root(const merkle_proof_container &proof) { + return proof.root(); + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/validate.hpp b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/validate.hpp new file mode 100644 index 000000000..80c26064c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/merkle_tree/r1cs/validate.hpp @@ -0,0 +1,161 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the Merkle tree check read component. +// +// The component checks the following: given a root R, address A, value V, and +// authentication path P, check that P is a valid authentication path for the +// value V as the A-th leaf in a Merkle tree with root R. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template, typename Field = typename HashComponent::field_type, + std::size_t Arity = 2> + struct merkle_proof_validate : public component { + static constexpr std::size_t arity = Arity; + + using field_type = Field; + using hash_component = HashComponent; + using merkle_proof_component = merkle_proof; + + // TODO: add support of the trees with arity more than 2 + static_assert(arity == 2); + static_assert( + std::is_same, typename HashComponent::result_type>::value); + + private: + std::vector hashers; + std::vector> hasher_inputs; + std::vector> propagators; + std::vector> internal_output; + + std::shared_ptr> computed_root; + std::shared_ptr> check_root; + + public: + const std::size_t digest_size; + const std::size_t tree_depth; + detail::blueprint_linear_combination_vector address_bits; + digest_variable leaf; + digest_variable root; + merkle_proof_component path; + detail::blueprint_linear_combination read_successful; + + merkle_proof_validate(blueprint &bp, + const std::size_t tree_depth, + const detail::blueprint_linear_combination_vector &address_bits, + const digest_variable &leaf, + const digest_variable &root, + const merkle_proof_component &path, + const detail::blueprint_linear_combination &read_successful) : + component(bp), + digest_size(HashComponent::digest_bits), tree_depth(tree_depth), address_bits(address_bits), + leaf(leaf), root(root), path(path), read_successful(read_successful) { + /* + The tricky part here is ordering. For Merkle tree + authentication paths, path[0] corresponds to one layer below + the root (and path[tree_depth-1] corresponds to the layer + containing the leaf), while address_bits has the reverse order: + address_bits[0] is LSB, and corresponds to layer containing the + leaf, and address_bits[tree_depth-1] is MSB, and corresponds to + the subtree directly under the root. + */ + assert(tree_depth > 0); + assert(tree_depth == address_bits.size()); + + for (std::size_t i = 0; i < tree_depth - 1; ++i) { + internal_output.emplace_back(digest_variable(bp, digest_size)); + } + + computed_root.reset(new digest_variable(bp, digest_size)); + + for (std::size_t i = 0; i < tree_depth; ++i) { + // TODO: generalize for arity > 2 + block_variable inp(bp, path.path[i][0], path.path[i][1]); + hasher_inputs.emplace_back(inp); + hashers.emplace_back( + HashComponent(bp, inp, (i == 0 ? *computed_root : internal_output[i - 1]))); + } + + for (std::size_t i = 0; i < tree_depth; ++i) { + /* + The propagators take a computed hash value (or leaf in the + base case) and propagate it one layer up, either in the left + or the right slot of authentication_path_variable. + */ + // TODO: generalize for arity > 2 + propagators.emplace_back(digest_selector_component( + bp, digest_size, i < tree_depth - 1 ? internal_output[i] : leaf, + address_bits[tree_depth - 1 - i], path.path[i][0], path.path[i][1])); + } + + check_root.reset(new bit_vector_copy_component( + bp, computed_root->bits, root.bits, read_successful, field_type::number_bits)); + } + + void generate_gates() { + /* ensure correct hash computations */ + for (std::size_t i = 0; i < tree_depth; ++i) { + // Note that we check root outside and have enforced booleanity of + // path.left_digests/path.right_digests outside in path.generate_gates + hashers[i].generate_gates(false); + } + + /* ensure consistency of path.left_digests/path.right_digests with internal_output */ + for (std::size_t i = 0; i < tree_depth; ++i) { + propagators[i].generate_gates(); + } + + check_root->generate_gates(false, false); + } + + void generate_assignments() { + /* do the hash computations bottom-up */ + for (int i = tree_depth - 1; i >= 0; --i) { + /* propagate previous input */ + propagators[i].generate_assignments(); + + /* compute hash */ + hashers[i].generate_assignments(); + } + + check_root->generate_assignments(); + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MERKLE_TREE_CHECK_READ_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/mock/mocked_component_base.hpp b/libs/blueprint/include/nil/blueprint/components/mock/mocked_component_base.hpp new file mode 100644 index 000000000..8fc9c7008 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/mock/mocked_component_base.hpp @@ -0,0 +1,168 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tablain +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // Generic mocked component interface class. + // Designed to make it easy to conjure small mocked components out of thin air. + template + class mocked_component_base; + + template + class mocked_component_base< + crypto3::zk::snark::plonk_constraint_system, + InputType, ResultType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using value_type = typename BlueprintFieldType::value_type; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 0; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new nil::blueprint::manifest_single_value_param(ResultType::result_size)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + constexpr static const std::size_t gates_amount = 1; + + typedef InputType input_type; + typedef ResultType result_type; + + virtual std::array result_values_calculator( + const assignment> + &assignment, + const input_type &instance_input) const = 0; + + virtual result_type result_builder(const std::size_t start_row_index) const = 0; + + void assigner( + const std::array &result_values, + assignment> + &assignment, + const std::size_t start_row_index) const { + + for (std::size_t i = 0; i < ResultType::result_size; i++) { + assignment.witness(this->W(i), start_row_index) = result_values[i]; + } + } + + template + explicit mocked_component_base(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mocked_component_base(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mocked_component_base(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mocked_component_base = + mocked_component_base, + InputType, ResultType>; + + template + typename plonk_mocked_component_base::result_type + generate_assignments( + const plonk_mocked_component_base + &component, + assignment> + &assignment, + const typename plonk_mocked_component_base::input_type + &instance_input, + const std::uint32_t start_row_index) { + + auto result = component.result_values_calculator(assignment, instance_input); + component.assigner(result, assignment, start_row_index); + + return component.result_builder(start_row_index); + } + + template + typename plonk_mocked_component_base::result_type + generate_circuit( + const plonk_mocked_component_base + &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mocked_component_base::input_type + &instance_input, + const std::size_t start_row_index) { + + return component.result_builder(start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/mock/mocked_components.hpp b/libs/blueprint/include/nil/blueprint/components/mock/mocked_components.hpp new file mode 100644 index 000000000..ad305010a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/mock/mocked_components.hpp @@ -0,0 +1,692 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tablain +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + struct one_var_type { + using non_native_policy_type = basic_non_native_policy; + using var = crypto3::zk::snark::plonk_variable; + typename non_native_policy_type::template field::value_type a; + + one_var_type(var _a) : a(_a) {} + + template + one_var_type(const ComponentType &component, const std::size_t start_row_index) + : one_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {a}; + } + static constexpr std::size_t result_size = 1; + }; + + template + struct two_var_type { + using non_native_policy_type = basic_non_native_policy; + using var = crypto3::zk::snark::plonk_variable; + typename non_native_policy_type::template field::value_type a, b; + + two_var_type(var _a, var _b) : a(_a), b(_b) {} + + template + two_var_type(const ComponentType &component, const std::size_t start_row_index) + : two_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {a, b}; + } + static constexpr std::size_t result_size = 2; + }; + + template + struct signed_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type value; + + signed_var_type(var sign, var mod) : value({sign, mod}) {} + + template + signed_var_type(const ComponentType &component, const std::size_t start_row_index) + : signed_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {value[0], value[1]}; + } + static constexpr std::size_t result_size = non_native_policy_type::ratio; + }; + + template + struct two_signed_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type a, b; + + two_signed_var_type(var a_sign, var a_mod, var b_sign, var b_mod) + : a({a_sign, a_mod}), b({b_sign, b_mod}) {} + + //template + //two_signed_var_type(const ComponentType &component, const std::size_t start_row_index) { + // return component.result_builder(start_row_index); + //} + + std::vector> all_vars() { + return {a[0], a[1], b[0], b[1]}; + } + static constexpr std::size_t result_size = 4; + }; + + + template + struct pair_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type value; + + pair_var_type(var first, var second) : value({first, second}) {} + + template + pair_var_type(const ComponentType &component, const std::size_t start_row_index) + : pair_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {value[0], value[1]}; + } + static constexpr std::size_t result_size = 2; + }; + + template + struct two_pair_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type a, b; + + two_pair_var_type(var a_first, var a_second, var b_first, var b_second) + : a({a_first, a_second}), b({b_first, b_second}) {} + + template + two_pair_var_type(const ComponentType &component, const std::size_t start_row_index) + : two_pair_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {a[0], a[1], b[0], b[1]}; + } + static constexpr std::size_t result_size = 4; + }; + + template + struct signed_pair_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type value; + + signed_pair_var_type(var sign, var first, var second) + : value({sign, first, second}) {} + + template + signed_pair_var_type(const ComponentType &component, const std::size_t start_row_index) + : signed_pair_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {value[0], value[1], value[2]}; + } + static constexpr std::size_t result_size = 3; + }; + + template + struct two_signed_pair_var_type { + using var = crypto3::zk::snark::plonk_variable; + using non_native_policy_type = + nil::blueprint::detail::basic_non_native_policy_field_type>>; + typename non_native_policy_type::non_native_var_type a, b; + + two_signed_pair_var_type(var a_sign, var a_first, var a_second, var b_sign, var b_first, + var b_second) + : a({a_sign, a_first, a_second}), b({b_sign, b_first, b_second}) {} + + template + two_signed_pair_var_type(const ComponentType &component, const std::size_t start_row_index) + : two_signed_pair_var_type(component.result_builder(start_row_index)) {} + + std::vector> all_vars() { + return {a[0], a[1], a[2], b[0], b[1], b[2]}; + } + static constexpr std::size_t result_size = 6; + }; + } // namespace detail + + #define BOILERPLATING(CLASS_NAME) \ + using input_type = typename component_type::input_type; \ + using result_type = typename component_type::result_type; \ + using value_type = typename BlueprintFieldType::value_type; \ + using integral_type = typename BlueprintFieldType::integral_type; \ + using var = typename component_type::var; \ + \ + const std::string component_name = #CLASS_NAME; \ + \ + template \ + explicit CLASS_NAME (ContainerType witness) : \ + component_type(witness, {}, {}) {}; \ + \ + template \ + CLASS_NAME (WitnessContainerType witness, ConstantContainerType constant, \ + PublicInputContainerType public_input) : \ + component_type(witness, constant, public_input) {}; \ + \ + CLASS_NAME ( \ + std::initializer_list \ + witnesses, \ + std::initializer_list \ + constants, \ + std::initializer_list \ + public_inputs) : \ + component_type(witnesses, constants, public_inputs) {}; \ + + // for BitsAmount < 256 + #define UNSIGNED_SMALL_OP_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType, BitsAmount> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_var_type, \ + detail::one_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_var_type, \ + detail::one_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend> uint_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + /*TODO(martun): on the next line we are converting 255-bit number to 8/32/64 bit numbers, probably losing some bits*/\ + uint_type a = static_cast(var_value(assignment, instance_input.a).data.backend().base_data()), \ + b = static_cast(var_value(assignment, instance_input.b).data.backend().base_data()); \ + return {value_type(OP(a, b))}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return var(0, start_row_index, false); \ + } \ + }; \ + + // for BitsAmount < 256 + #define SIGNED_SMALL_OP_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType, BitsAmount> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_var_type, \ + detail::signed_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_var_type, \ + detail::signed_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend> int_type; \ + typedef boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend> uint_type; \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend> modular_uint_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + int_type a = static_cast(var_value(assignment, instance_input.a[1]).data), \ + b = static_cast(var_value(assignment, instance_input.b[1]).data); \ + int_type sign_a = var_value(assignment, instance_input.a[0]) == 0 ? 1 : -1, \ + sign_b = var_value(assignment, instance_input.b[0]) == 0 ? 1 : -1; \ + int_type result = OP((sign_a * a), (sign_b * b)); \ +/*On the following line we need to convert from signed to unsigned boost::cpp_int, then to our cpp_int_modular, then change the size, then to value_type.*/\ + return {value_type(result.sign() >= 0 ? 0 : 1), \ + value_type(typename BlueprintFieldType::integral_type(typename modular_uint_type::backend_type( \ + uint_type(boost::multiprecision::abs(result)).backend())))}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return {var(0, start_row_index, false), \ + var(1, start_row_index, false)}; \ + } \ + }; \ + + template + class signed_abs_component_small; + + template + class signed_abs_component_small< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType, BitsAmount> : public mocked_component_base< + crypto3::zk::snark::plonk_constraint_system, + detail::signed_var_type, + detail::signed_var_type> { + public: + using component_type = mocked_component_base< + crypto3::zk::snark::plonk_constraint_system, + detail::signed_var_type, + detail::signed_var_type>; + + typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend> int_type; + + BOILERPLATING(signed_abs_component_small) + + std::array result_values_calculator( + const assignment> + &assignment, + const input_type &instance_input) const override { + + return {value_type(0), var_value(assignment, instance_input.value[1])}; + } + + result_type result_builder(const std::size_t start_row_index) const override { + + return {var(0, start_row_index, false), + var(1, start_row_index, false)}; + } + }; + + // for BitsAmount == 256 + #define UNSIGNED_BIG_OP_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_pair_var_type, \ + detail::pair_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_pair_var_type, \ + detail::pair_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256>> uint_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + static const uint_type two_128 = \ + boost::multiprecision::pow(uint_type(2), 128); \ + static const uint_type top_mask = ((uint_type(1) << 128) - 1) << 128; \ + static const uint_type bottom_mask = (uint_type(1) << 128) - 1; \ + uint_type \ + a = static_cast(var_value(assignment, instance_input.a[0]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.a[1]).data), \ + b = static_cast(var_value(assignment, instance_input.b[0]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.b[1]).data); \ + uint_type result = OP(a, b); \ + uint_type result_top = (top_mask & result) >> 128; \ + uint_type result_bottom = bottom_mask & result; \ + return { \ + value_type(typename value_type::integral_type(result_top)), \ + value_type(typename value_type::integral_type(result_bottom))}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return {var(0, start_row_index, false), \ + var(1, start_row_index, false)}; \ + } \ + }; \ + + // for BitsAmount == 256 + #define UNSIGNED_BIG_OP_BOOL_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_pair_var_type, \ + detail::one_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_pair_var_type, \ + detail::one_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256>> uint_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + static const uint_type two_128 = \ + boost::multiprecision::pow(uint_type(2), 128); \ + uint_type \ + a = static_cast(var_value(assignment, instance_input.a[0]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.a[1]).data), \ + b = static_cast(var_value(assignment, instance_input.b[0]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.b[1]).data); \ + uint_type result = OP(a, b); \ + return {value_type(result)}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return {var(0, start_row_index, false)}; \ + } \ + }; \ + + // for BitsAmount == 256 + #define SIGNED_BIG_OP_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_pair_var_type, \ + detail::signed_pair_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_pair_var_type, \ + detail::signed_pair_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend<256, 256, \ + boost::multiprecision::signed_magnitude, \ + boost::multiprecision::unchecked, void>> int_type; \ + typedef boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend<256, 256, \ + boost::multiprecision::unsigned_magnitude, \ + boost::multiprecision::unchecked, void>> uint_type; \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256>> modular_uint_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + static const int_type two_128 = \ + boost::multiprecision::pow(int_type(2), 128); \ + static const int_type top_mask = ((int_type(1) << 128) - 1) << 128; \ + static const int_type bottom_mask = (int_type(1) << 128) - 1; \ + int_type \ + a = (var_value(assignment, instance_input.a[0]) == 0 ? 1 : -1) * \ + static_cast(var_value(assignment, instance_input.a[1]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.a[2]).data), \ + b = (var_value(assignment, instance_input.b[0]) == 0 ? 1 : -1) * \ + static_cast(var_value(assignment, instance_input.b[1]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.b[2]).data); \ + int_type result = OP(a, b); \ + int_type result_top = (top_mask & result) >> 128; \ + int_type result_bottom = bottom_mask & result; \ + modular_uint_type result_top_modular = typename modular_uint_type::backend_type(uint_type(result_top).backend()); \ + modular_uint_type result_bottom_modular = typename modular_uint_type::backend_type(uint_type(result_bottom).backend()); \ + return {value_type(result.sign() >= 0 ? 0 : 1), \ + value_type(result_top_modular), value_type(result_bottom_modular)}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return {var(0, start_row_index, false), \ + var(1, start_row_index, false), \ + var(2, start_row_index, false)}; \ + } \ + }; \ + + // for BitsAmount == 256 + #define SIGNED_BIG_BOOL_OP_COMPONENT(COMPONENT_NAME, OP) \ + template \ + class COMPONENT_NAME; \ + \ + template \ + class COMPONENT_NAME< \ + crypto3::zk::snark::plonk_constraint_system, \ + BlueprintFieldType> : public mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_pair_var_type, \ + detail::one_var_type> { \ + public: \ + using component_type = mocked_component_base< \ + crypto3::zk::snark::plonk_constraint_system, \ + detail::two_signed_pair_var_type, \ + detail::one_var_type>; \ + \ + typedef boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256>> int_type; \ + \ + BOILERPLATING(COMPONENT_NAME) \ + \ + std::array result_values_calculator( \ + const assignment> \ + &assignment, \ + const input_type &instance_input) const override { \ + \ + static const int_type two_128 = \ + boost::multiprecision::pow(int_type(2), 128); \ + int_type \ + a = (var_value(assignment, instance_input.a[0]) == 0 ? 1 : -1) * \ + static_cast(var_value(assignment, instance_input.a[1]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.a[2]).data), \ + b = (var_value(assignment, instance_input.b[0]) == 0 ? 1 : -1) * \ + static_cast(var_value(assignment, instance_input.b[1]).data) * two_128 + \ + static_cast(var_value(assignment, instance_input.b[2]).data); \ + return {OP(a, b)}; \ + } \ + \ + result_type result_builder(const std::size_t start_row_index) const override { \ + \ + return {var(0, start_row_index, false)}; \ + } \ + }; \ + + template + class signed_abs_component_big; + + template + class signed_abs_component_big< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType> : public mocked_component_base< + crypto3::zk::snark::plonk_constraint_system, + detail::signed_pair_var_type, + detail::signed_pair_var_type> { + public: + using component_type = mocked_component_base< + crypto3::zk::snark::plonk_constraint_system, + detail::signed_pair_var_type, + detail::signed_pair_var_type>; + + typedef boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<256>> int_type; + + BOILERPLATING(signed_abs_component_big) + + std::array result_values_calculator( + const assignment> + &assignment, + const input_type &instance_input) const override { + + return {value_type(0), + var_value(assignment, instance_input.value[1]), + var_value(assignment, instance_input.value[2])}; + } + + result_type result_builder(const std::size_t start_row_index) const override { + + return {var(0, start_row_index, false), + var(1, start_row_index, false), + var(2, start_row_index, false)}; + } + }; + + #define OP_ADDITION(a, b) (a + b) + #define OP_SUBTRACTION(a, b) (a - b) + #define OP_MULTIPLICATION(a, b) (a * b) + #define OP_DIVISION(a, b) (a / b) + #define OP_REMAINDER(a, b) (a % b) + #define OP_LESS(a, b) (a < b) + #define OP_GREATER(a, b) (a > b) + #define OP_GREATER_EQUAL(a, b) (a >= b) + #define OP_LESS_EQUAL(a, b) (a <= b) + + UNSIGNED_SMALL_OP_COMPONENT(unsigned_addition_component_small, OP_ADDITION) + UNSIGNED_SMALL_OP_COMPONENT(unsigned_subtraction_component_small, OP_SUBTRACTION) + UNSIGNED_SMALL_OP_COMPONENT(unsigned_multiplication_component_small, OP_MULTIPLICATION) + UNSIGNED_SMALL_OP_COMPONENT(unsigned_division_component_small, OP_DIVISION) + UNSIGNED_SMALL_OP_COMPONENT(unsigned_remainder_component_small, OP_REMAINDER) + + UNSIGNED_BIG_OP_COMPONENT(unsigned_addition_component_big, OP_ADDITION) + UNSIGNED_BIG_OP_COMPONENT(unsigned_subtraction_component_big, OP_SUBTRACTION) + UNSIGNED_BIG_OP_COMPONENT(unsigned_multiplication_component_big, OP_MULTIPLICATION) + UNSIGNED_BIG_OP_COMPONENT(unsigned_division_component_big, OP_DIVISION) + UNSIGNED_BIG_OP_COMPONENT(unsigned_remainder_component_big, OP_REMAINDER) + + UNSIGNED_BIG_OP_BOOL_COMPONENT(unsinged_less_component_big, OP_LESS) + UNSIGNED_BIG_OP_BOOL_COMPONENT(unsinged_greater_component_big, OP_GREATER) + UNSIGNED_BIG_OP_BOOL_COMPONENT(unsinged_greater_equal_component_big, OP_GREATER_EQUAL) + UNSIGNED_BIG_OP_BOOL_COMPONENT(unsinged_less_equal_component_big, OP_LESS_EQUAL) + + SIGNED_SMALL_OP_COMPONENT(signed_addition_component_small, OP_ADDITION) + SIGNED_SMALL_OP_COMPONENT(signed_subtraction_component_small, OP_SUBTRACTION) + SIGNED_SMALL_OP_COMPONENT(signed_multiplication_component_small, OP_MULTIPLICATION) + SIGNED_SMALL_OP_COMPONENT(signed_division_component_small, OP_DIVISION) + SIGNED_SMALL_OP_COMPONENT(signed_remainder_component_small, OP_REMAINDER) + + SIGNED_BIG_OP_COMPONENT(signed_addition_component_big, OP_ADDITION) + SIGNED_BIG_OP_COMPONENT(signed_subtraction_component_big, OP_SUBTRACTION) + SIGNED_BIG_OP_COMPONENT(signed_multiplication_component_big, OP_MULTIPLICATION) + SIGNED_BIG_OP_COMPONENT(signed_division_component_big, OP_DIVISION) + SIGNED_BIG_OP_COMPONENT(signed_remainder_component_big, OP_REMAINDER) + + SIGNED_BIG_BOOL_OP_COMPONENT(signed_less_component_big, OP_LESS) + SIGNED_BIG_BOOL_OP_COMPONENT(signed_greater_component_big, OP_GREATER) + SIGNED_BIG_BOOL_OP_COMPONENT(signed_greater_equal_component_big, OP_GREATER_EQUAL) + SIGNED_BIG_BOOL_OP_COMPONENT(signed_less_equal_component_big, OP_LESS_EQUAL) + + #undef BOILERPLATING + #undef UNSIGNED_SMALL_OP_COMPONENT + #undef SIGNED_SMALL_OP_COMPONENT + #undef UNSIGNED_BIG_OP_COMPONENT + #undef UNSIGNED_BIG_OP_BOOL_COMPONENT + #undef SIGNED_BIG_OP_COMPONENT + #undef SIGNED_BIG_BOOL_OP_COMPONENT + + #undef OP_ADDITION + #undef OP_SUBTRACTION + #undef OP_MULTIPLICATION + #undef OP_DIVISION + #undef OP_REMAINDER + #undef OP_LESS + #undef OP_GREATER + #undef OP_GREATER_EQUAL + #undef OP_LESS_EQUAL + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/batched_verification.hpp b/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/batched_verification.hpp new file mode 100644 index 000000000..053e81441 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/batched_verification.hpp @@ -0,0 +1,152 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the EDDSA25519 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_BATCHED_VERIFICATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_BATCHED_VERIFICATION_HPP + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class batched_verification; + + template + class batched_verification, + CurveType, + Ed25519Type, + k, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8> { + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + using ed25519_component = eddsa25519; + + using var = crypto3::zk::snark::plonk_variable; + using var_ec_point = typename ed25519_component::params_type::var_ec_point; + using signature = typename ed25519_component::params_type::signature; + constexpr static const std::size_t selector_seed = 0xfcc7; + + public: + constexpr static const std::size_t rows_amount = ed25519_component::rows_amount*k; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array signatures; + std::array public_keys; + std::array M; + }; + + struct result_type { + result_type(std::size_t component_start_row) { + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + for (std::size_t i = 0; i < k; i++){ + ed25519_component::generate_assignments(assignment, {params.signatures[i], params.public_keys[i], params.M}, row); + row += ed25519_component::rows_amount; + } + return result_type(component_start_row); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t component_start_row){ + std::size_t row = component_start_row; + for (std::size_t i = 0; i < k; i++){ + ed25519_component::generate_circuit(bp, assignment, {params.signatures[i], params.public_keys[i], params.M}, row); + row += ed25519_component::rows_amount; + } + return result_type(component_start_row); + } + + private: + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + } + + static void generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + std::size_t component_start_row) { + } + + static void generate_lookup_table(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + + std::size_t row = component_start_row; + std::size_t n = (1 << 16); + for(std::size_t i = 0; i < 2; i++) { + assignment.constant(1)[i] = 0; + } + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_BATCHED_VERIFICATION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/verification.hpp b/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/verification.hpp new file mode 100644 index 000000000..f1e5bdaff --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/pubkey/eddsa/plonk/non_native/verification.hpp @@ -0,0 +1,306 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the EDDSA25519 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_VERIFICATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_VERIFICATION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class verification; + + template + class verification, + CurveType, + Ed25519Type, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + using check_ec_point_component = + ec_point; + + using variable_base_mult_component = variable_base_multiplication; + using fixed_base_mult_component = fixed_base_multiplication; + using addition_component = + complete_addition; + + using reduction_component = + reduction; + using non_native_range_component = + non_native_range; + using scalar_non_native_range_component = scalar_non_native_range; + using non_addition_component = non_native_field_element_addition; + using sha512_component = sha512; + + using var = snark::plonk_variable; + constexpr static const std::size_t selector_seed = 0xfcc2; + + public: + constexpr static const std::size_t rows_amount = + /*262144;*/ scalar_non_native_range_component::rows_amount + + variable_base_mult_component::rows_amount + fixed_base_mult_component::rows_amount + + addition_component::rows_amount + reduction_component::rows_amount + + 2 * check_ec_point_component::rows_amount + sha512_component::rows_amount; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + struct var_ec_point { + std::array x; + std::array y; + }; + struct signature { + var_ec_point R; + var s; + }; + signature e; + var_ec_point public_key; + std::array M; + }; + + // TODO: check if points R and public_key lie on the curve + + struct result_type { + result_type(std::size_t component_start_row) { + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + // generate_lookup_table(assignment, params, component_start_row); + /*std::size_t n = (1 << 18); + for(std::size_t i = 0; i < n; i++) { + assignment.constant(1)[i] = i; + }*/ + var s = params.e.s; + auto R = params.e.R; + auto pk = params.public_key; + std::array M = params.M; + + /* here we check if s lies in range */ + scalar_non_native_range_component::generate_assignments(assignment, {s}, row); + row += scalar_non_native_range_component::rows_amount; + check_ec_point_component::generate_assignments(assignment, {{R.x, R.y}}, row); + row += check_ec_point_component::rows_amount; + check_ec_point_component::generate_assignments(assignment, {{pk.x, pk.y}}, row); + row += check_ec_point_component::rows_amount; + + /* here we get k = SHA(R||A||M) */ + auto k_vec = sha512_component::generate_assignments( + assignment, + {{{R.x[0], R.x[1], R.x[2], R.x[3]}, {R.y[0], R.y[1], R.y[2], R.y[3]}}, + {{pk.x[0], pk.x[1], pk.x[2], pk.x[3]}, {pk.y[0], pk.y[1], pk.y[2], pk.y[3]}}, + M}, + row) + .output_state; + row += sha512_component::rows_amount; + var k = reduction_component::generate_assignments(assignment, {k_vec}, row).output; + row += reduction_component::rows_amount; + /* here we check sB == R + kA */ + + auto S = fixed_base_mult_component::generate_assignments(assignment, {s}, row).output; + row += fixed_base_mult_component::rows_amount; + auto A = + variable_base_mult_component::generate_assignments(assignment, {{pk.x, pk.y}, k}, row).output; + row += variable_base_mult_component::rows_amount; + typename addition_component::params_type add_params = {{A.x, A.y}, {R.x, R.y}}; + auto res = addition_component::generate_assignments(assignment, add_params, row).output; + row += addition_component::rows_amount; + return result_type(component_start_row); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + var s = params.e.s; + auto R = params.e.R; + auto pk = params.public_key; + std::array M = params.M; + + /* here we check if s lies in range */ + scalar_non_native_range_component::generate_circuit(bp, assignment, {s}, row); + row += scalar_non_native_range_component::rows_amount; + check_ec_point_component::generate_circuit(bp, assignment, {{R.x, R.y}}, row); + row += check_ec_point_component::rows_amount; + check_ec_point_component::generate_circuit(bp, assignment, {{pk.x, pk.y}}, row); + row += check_ec_point_component::rows_amount; + + /* here we get k = SHA(R||A||M) */ + auto k_vec = sha512_component::generate_circuit( + bp, + assignment, + {{{R.x[0], R.x[1], R.x[2], R.x[3]}, {R.y[0], R.y[1], R.y[2], R.y[3]}}, + {{pk.x[0], pk.x[1], pk.x[2], pk.x[3]}, {pk.y[0], pk.y[1], pk.y[2], pk.y[3]}}, + M}, + row) + .output_state; + row += sha512_component::rows_amount; + var k = reduction_component::generate_circuit(bp, assignment, {k_vec}, row).output; + row += reduction_component::rows_amount; + /* here we check sB == R + kA */ + auto S = fixed_base_mult_component::generate_circuit(bp, assignment, {s}, row).output; + row += fixed_base_mult_component::rows_amount; + auto A = + variable_base_mult_component::generate_circuit(bp, assignment, {{pk.x, pk.y}, k}, row).output; + row += variable_base_mult_component::rows_amount; + typename addition_component::params_type add_params = {{A.x, A.y}, {R.x, R.y}}; + auto res = addition_component::generate_circuit(bp, assignment, add_params, row).output; + row += addition_component::rows_amount; + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + row += scalar_non_native_range_component::rows_amount + 2 * check_ec_point_component::rows_amount + + reduction_component::rows_amount + sha512_component::rows_amount + + fixed_base_mult_component::rows_amount; + auto S = + (typename fixed_base_mult_component::result_type(row - 1 - addition_component::rows_amount)) + .output; + row += variable_base_mult_component::rows_amount; + auto res = (typename addition_component::result_type(row)).output; + bp.add_copy_constraint({{S.x[0]}, {res.x[0]}}); + bp.add_copy_constraint({{S.x[1]}, {res.x[1]}}); + bp.add_copy_constraint({{S.x[2]}, {res.x[2]}}); + bp.add_copy_constraint({{S.x[3]}, {res.x[3]}}); + bp.add_copy_constraint({{S.y[0]}, {res.y[0]}}); + bp.add_copy_constraint({{S.y[1]}, {res.y[1]}}); + bp.add_copy_constraint({{S.y[2]}, {res.y[2]}}); + bp.add_copy_constraint({{S.y[3]}, {res.y[3]}}); + } + + static void generate_lookup_table(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + + std::size_t row = component_start_row; + std::size_t n = (1 << 16); + for (std::size_t i = 0; i < 2; i++) { + assignment.constant(1)[i] = 0; + } + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PUBKEY_EDDSA_PLONK_NON_NATIVE_VERIFICATION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/routing/r1cs/as_waksman.hpp b/libs/blueprint/include/nil/blueprint/components/routing/r1cs/as_waksman.hpp new file mode 100644 index 000000000..760f2052e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/routing/r1cs/as_waksman.hpp @@ -0,0 +1,328 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the AS-Waksman routing component. +// +// The component verifies that the outputs are a permutation of the inputs, +// by use of an AS-Waksman network. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_ROUTING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_ROUTING_COMPONENT_HPP + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct as_waksman_routing : public component { + + /* + Indexing conventions: + + routed_packets[column_idx][packet_idx][subpacket_idx] + pack_inputs/unpack_outputs[packet_idx] + asw_switch_bits[column_idx][row_idx] + + Where column_idx ranges is in range 0 .. width and packet_idx is + in range 0 .. num_packets-1. + + Note that unlike in Bene\v{s} routing networks row_idx are + *not* necessarily consecutive; similarly for straight edges + routed_packets[column_idx][packet_idx] will *reuse* previously + allocated variables. + + */ + std::vector>> routed_packets; + std::vector> pack_inputs, unpack_outputs; + + /* + If #packets = 1 then we can route without explicit switch bits + (and save half the constraints); in this case asw_switch_bits will + be unused. + + For asw_switch_bits 0 corresponds to switch off (straight + connection), and 1 corresponds to switch on (crossed + connection). + */ + std::vector>> asw_switch_bits; + as_waksman_topology neighbors; + + public: + const std::size_t num_packets; + const std::size_t num_columns; + const std::vector> routing_input_bits; + const std::vector> routing_output_bits; + + const std::size_t packet_size, num_subpackets; + + as_waksman_routing_component( + blueprint &bp, + const std::size_t num_packets, + const std::vector> &routing_input_bits, + const std::vector> &routing_output_bits); + void generate_gates(); + void generate_assignments(const integer_permutation &permutation); + }; + + template + void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size); + + template + as_waksman_routing_component::as_waksman_routing_component( + blueprint &bp, + const std::size_t num_packets, + const std::vector> &routing_input_bits, + const std::vector> &routing_output_bits) : + component(bp), + num_packets(num_packets), num_columns(as_waksman_num_columns(num_packets)), + routing_input_bits(routing_input_bits), routing_output_bits(routing_output_bits), + packet_size(routing_input_bits[0].size()), + num_subpackets((packet_size + FieldType::capacity() - 1) / FieldType::capacity()) { + neighbors = generate_as_waksman_topology(num_packets); + routed_packets.resize(num_columns + 1); + + /* Two pass allocation. First allocate LHS packets, then for every + switch either copy over the variables from previously allocated + to allocate target packets */ + routed_packets[0].resize(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + routed_packets[0][packet_idx].allocate(bp, num_subpackets); + } + + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + routed_packets[column_idx + 1].resize(num_packets); + + for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) { + if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) { + /* This is a straight edge, so just copy over the previously allocated subpackets */ + routed_packets[column_idx + 1][neighbors[column_idx][row_idx].first] = + routed_packets[column_idx][row_idx]; + } else { + const std::size_t straight_edge = neighbors[column_idx][row_idx].first; + const std::size_t cross_edge = neighbors[column_idx][row_idx].second; + routed_packets[column_idx + 1][straight_edge].allocate(bp, num_subpackets); + routed_packets[column_idx + 1][cross_edge].allocate(bp, num_subpackets); + ++row_idx; /* skip the next idx, as it to refers to the same packets */ + } + } + } + + /* create packing/unpacking components */ + pack_inputs.reserve(num_packets); + unpack_outputs.reserve(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs.emplace_back(multipacking_component( + bp, + blueprint_variable_vector(routing_input_bits[packet_idx].begin(), + routing_input_bits[packet_idx].end()), + routed_packets[0][packet_idx], + FieldType::capacity())); + unpack_outputs.emplace_back(multipacking_component( + bp, + blueprint_variable_vector(routing_output_bits[packet_idx].begin(), + routing_output_bits[packet_idx].end()), + routed_packets[num_columns][packet_idx], + FieldType::capacity())); + } + + /* allocate switch bits */ + if (num_subpackets > 1) { + asw_switch_bits.resize(num_columns); + + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) { + if (neighbors[column_idx][row_idx].first != neighbors[column_idx][row_idx].second) { + asw_switch_bits[column_idx][row_idx].allocate(bp); + ++row_idx; /* next row_idx corresponds to the same switch, so skip it */ + } + } + } + } + } + + template + void as_waksman_routing_component::generate_gates() { + /* packing/unpacking */ + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs[packet_idx].generate_gates(false); + unpack_outputs[packet_idx].generate_gates(true); + } + + /* actual routing constraints */ + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) { + if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) { + /* if there is no switch at this position, then just continue with next row_idx */ + continue; + } + + if (num_subpackets == 1) { + /* easy case: require that + (cur-straight_edge)*(cur-cross_edge) = 0 for both + switch inputs */ + for (std::size_t switch_input : {row_idx, row_idx + 1}) { + const std::size_t straight_edge = neighbors[column_idx][switch_input].first; + const std::size_t cross_edge = neighbors[column_idx][switch_input].second; + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + routed_packets[column_idx][switch_input][0] - + routed_packets[column_idx + 1][straight_edge][0], + routed_packets[column_idx][switch_input][0] - + routed_packets[column_idx + 1][cross_edge][0], + 0)); + } + } else { + /* require switching bit to be boolean */ + generate_boolean_r1cs_constraint(this->bp, + asw_switch_bits[column_idx][row_idx]); + + /* route forward according to the switch bit */ + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) { + /* + (1-switch_bit) * (cur-straight_edge) + switch_bit * (cur-cross_edge) = 0 + switch_bit * (cross_edge-straight_edge) = cur-straight_edge + */ + for (std::size_t switch_input : {row_idx, row_idx + 1}) { + const std::size_t straight_edge = neighbors[column_idx][switch_input].first; + const std::size_t cross_edge = neighbors[column_idx][switch_input].second; + + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + asw_switch_bits[column_idx][row_idx], + routed_packets[column_idx + 1][cross_edge][subpacket_idx] - + routed_packets[column_idx + 1][straight_edge][subpacket_idx], + routed_packets[column_idx][switch_input][subpacket_idx] - + routed_packets[column_idx + 1][straight_edge][subpacket_idx])); + } + } + } + + /* we processed both switch inputs at once, so skip the next iteration */ + ++row_idx; + } + } + } + + template + void as_waksman_routing_component::generate_assignments( + const integer_permutation &permutation) { + /* pack inputs */ + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs[packet_idx].generate_assignments_from_bits(); + } + + /* do the routing */ + as_waksman_routing routing = get_as_waksman_routing(permutation); + + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) { + if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) { + /* this is a straight edge, so just pass the values forward */ + const std::size_t next = neighbors[column_idx][row_idx].first; + + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) { + this->bp.val(routed_packets[column_idx + 1][next][subpacket_idx]) = + this->bp.val(routed_packets[column_idx][row_idx][subpacket_idx]); + } + } else { + if (num_subpackets > 1) { + /* update the switch bit */ + this->bp.val(asw_switch_bits[column_idx][row_idx]) = + typename FieldType::value_type(routing[column_idx][row_idx] ? 1 : 0); + } + + /* route according to the switch bit */ + const bool switch_val = routing[column_idx][row_idx]; + + for (std::size_t switch_input : {row_idx, row_idx + 1}) { + const std::size_t straight_edge = neighbors[column_idx][switch_input].first; + const std::size_t cross_edge = neighbors[column_idx][switch_input].second; + + const std::size_t switched_edge = (switch_val ? cross_edge : straight_edge); + + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; + ++subpacket_idx) { + this->bp.val(routed_packets[column_idx + 1][switched_edge][subpacket_idx]) = + this->bp.val(routed_packets[column_idx][switch_input][subpacket_idx]); + } + } + + /* we processed both switch inputs at once, so skip the next iteration */ + ++row_idx; + } + } + } + + /* unpack outputs */ + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + unpack_outputs[packet_idx].generate_assignments_from_packed(); + } + } + + template + void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size) { + blueprint bp; + integer_permutation permutation(num_packets); + permutation.random_shuffle(); + + std::vector> randbits(num_packets), outbits(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + randbits[packet_idx].allocate(bp, packet_size); + outbits[packet_idx].allocate(bp, packet_size); + + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + bp.val(randbits[packet_idx][bit_idx]) = + (rand() % 2) ? FieldType::value_type::zero() : FieldType::value_type::zero(); + } + } + as_waksman_routing_component r(bp, num_packets, randbits, outbits); + r.generate_gates(); + + r.generate_assignments(permutation); + + assert(bp.is_satisfied()); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + assert(bp.val(outbits[permutation.get(packet_idx)][bit_idx]) == + bp.val(randbits[packet_idx][bit_idx])); + } + } + + bp.val(blueprint_variable(10)) = typename FieldType::value_type(12345); + assert(!bp.is_satisfied()); + } + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_AS_WAKSMAN_ROUTING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/routing/r1cs/benes.hpp b/libs/blueprint/include/nil/blueprint/components/routing/r1cs/benes.hpp new file mode 100644 index 000000000..0d40caa9d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/routing/r1cs/benes.hpp @@ -0,0 +1,228 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the Benes routing component. +// +// The component verifies that the outputs are a permutation of the inputs, +// by use of a Benes network. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BENES_ROUTING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BENES_ROUTING_COMPONENT_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class benes_routing : public component { + private: + /* + Indexing conventions: + + routed_packets[column_idx][packet_idx][subpacket_idx] + pack_inputs/unpack_outputs[packet_idx] + benes_switch_bits[column_idx][row_idx] + + Where column_idx ranges is in range 0 .. 2*dimension + (2*dimension-1 for switch bits/topology) and packet_idx is in + range 0 .. num_packets-1. + */ + std::vector>> routed_packets; + std::vector> pack_inputs, unpack_outputs; + + /* + If #packets = 1 then we can route without explicit routing bits + (and save half the constraints); in this case benes_switch_bits will + be unused. + + For benes_switch_bits 0 corresponds to straight edge and 1 + corresponds to cross edge. + */ + std::vector> benes_switch_bits; + benes_topology neighbors; + + public: + const std::size_t num_packets; + const std::size_t num_columns; + + const std::vector> routing_input_bits; + const std::vector> routing_output_bits; + std::size_t lines_to_unpack; + + const std::size_t packet_size, num_subpackets; + + benes_routing_component( + blueprint &bp, + const std::size_t num_packets, + const std::vector> &routing_input_bits, + const std::vector> &routing_output_bits, + const std::size_t lines_to_unpack) : + component(bp), + num_packets(num_packets), num_columns(benes_num_columns(num_packets)), + routing_input_bits(routing_input_bits), routing_output_bits(routing_output_bits), + lines_to_unpack(lines_to_unpack), packet_size(routing_input_bits[0].size()), + num_subpackets((packet_size + FieldType::capacity() - 1) / FieldType::capacity()) { + assert(lines_to_unpack <= routing_input_bits.size()); + assert(num_packets == 1ul << static_cast(std::ceil(std::log2(num_packets)))); + assert(routing_input_bits.size() == num_packets); + + neighbors = generate_benes_topology(num_packets); + + routed_packets.resize(num_columns + 1); + for (std::size_t column_idx = 0; column_idx <= num_columns; ++column_idx) { + routed_packets[column_idx].resize(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + routed_packets[column_idx][packet_idx].allocate(bp, num_subpackets); + } + } + + pack_inputs.reserve(num_packets); + unpack_outputs.reserve(num_packets); + + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs.emplace_back(multipacking_component( + bp, + blueprint_variable_vector(routing_input_bits[packet_idx].begin(), + routing_input_bits[packet_idx].end()), + routed_packets[0][packet_idx], + FieldType::capacity())); + if (packet_idx < lines_to_unpack) { + unpack_outputs.emplace_back(multipacking_component( + bp, + blueprint_variable_vector(routing_output_bits[packet_idx].begin(), + routing_output_bits[packet_idx].end()), + routed_packets[num_columns][packet_idx], + FieldType::capacity())); + } + } + + if (num_subpackets > 1) { + benes_switch_bits.resize(num_columns); + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + benes_switch_bits[column_idx].allocate(bp, num_packets); + } + } + } + + void generate_gates() { + /* packing/unpacking */ + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs[packet_idx].generate_gates(false); + if (packet_idx < lines_to_unpack) { + unpack_outputs[packet_idx].generate_gates(true); + } else { + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) { + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, routed_packets[0][packet_idx][subpacket_idx], + routed_packets[num_columns][packet_idx][subpacket_idx])); + } + } + } + + /* actual routing constraints */ + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + const std::size_t straight_edge = neighbors[column_idx][packet_idx].first; + const std::size_t cross_edge = neighbors[column_idx][packet_idx].second; + + if (num_subpackets == 1) { + /* easy case: (cur-next)*(cur-cross) = 0 */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + routed_packets[column_idx][packet_idx][0] - + routed_packets[column_idx + 1][straight_edge][0], + routed_packets[column_idx][packet_idx][0] - + routed_packets[column_idx + 1][cross_edge][0], + 0)); + } else { + /* routing bit must be boolean */ + generate_boolean_r1cs_constraint( + this->bp, benes_switch_bits[column_idx][packet_idx]); + + /* route forward according to routing bits */ + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; + ++subpacket_idx) { + /* + (1-switch_bit) * (cur-straight_edge) + switch_bit * (cur-cross_edge) = 0 + switch_bit * (cross_edge-straight_edge) = cur-straight_edge + */ + this->bp.add_r1cs_constraint(snark::r1cs_constraint( + benes_switch_bits[column_idx][packet_idx], + routed_packets[column_idx + 1][cross_edge][subpacket_idx] - + routed_packets[column_idx + 1][straight_edge][subpacket_idx], + routed_packets[column_idx][packet_idx][subpacket_idx] - + routed_packets[column_idx + 1][straight_edge][subpacket_idx])); + } + } + } + } + } + + void generate_assignments(const integer_permutation &permutation) { + /* pack inputs */ + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + pack_inputs[packet_idx].generate_assignments_from_bits(); + } + + /* do the routing */ + const benes_routing routing = get_benes_routing(permutation); + + for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) { + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + const std::size_t straight_edge = neighbors[column_idx][packet_idx].first; + const std::size_t cross_edge = neighbors[column_idx][packet_idx].second; + + if (num_subpackets > 1) { + this->bp.val(benes_switch_bits[column_idx][packet_idx]) = + typename FieldType::value_type(routing[column_idx][packet_idx] ? 1 : 0); + } + + for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) { + this->bp.val(routing[column_idx][packet_idx] ? + routed_packets[column_idx + 1][cross_edge][subpacket_idx] : + routed_packets[column_idx + 1][straight_edge][subpacket_idx]) = + this->bp.val(routed_packets[column_idx][packet_idx][subpacket_idx]); + } + } + } + + /* unpack outputs */ + for (std::size_t packet_idx = 0; packet_idx < lines_to_unpack; ++packet_idx) { + unpack_outputs[packet_idx].generate_assignments_from_packed(); + } + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BENES_ROUTING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_commitment_component.hpp b/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_commitment_component.hpp new file mode 100644 index 000000000..08f070a07 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_commitment_component.hpp @@ -0,0 +1,112 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SET_COMMITMENT_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SET_COMMITMENT_COMPONENT_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + namespace components { + + template + using set_commitment_variable = digest_variable; + + template + class set_commitment_component : public component { + private: + std::shared_ptr> element_block; + std::shared_ptr> element_digest; + std::shared_ptr hash_element; + std::shared_ptr> check_membership; + + public: + std::size_t tree_depth; + blueprint_variable_vector element_bits; + set_commitment_variable root_digest; + set_membership_proof_variable proof; + blueprint_linear_combination check_successful; + + set_commitment_component(blueprint &bp, + const std::size_t max_entries, + const blueprint_variable_vector &element_bits, + const set_commitment_variable &root_digest, + const set_membership_proof_variable &proof, + const blueprint_linear_combination &check_successful) : + component(bp), + tree_depth(static_cast(std::ceil(std::log2(max_entries)))), + element_bits(element_bits), root_digest(root_digest), proof(proof), + check_successful(check_successful) { + element_block.reset(new block_variable(bp, {element_bits})); + + if (tree_depth == 0) { + hash_element.reset(new Hash(bp, element_bits.size(), *element_block, root_digest)); + } else { + element_digest.reset(new digest_variable(bp, Hash::get_digest_len())); + hash_element.reset(new Hash(bp, element_bits.size(), *element_block, *element_digest)); + check_membership.reset( + new merkle_tree_check_read_component(bp, + tree_depth, + proof.address_bits, + *element_digest, + root_digest, + *proof.merkle_path, + check_successful)); + } + } + + void generate_gates() { + hash_element->generate_gates(); + + if (tree_depth > 0) { + check_membership->generate_gates(); + } + } + + void generate_assignments() { + hash_element->generate_assignments(); + + if (tree_depth > 0) { + check_membership->generate_assignments(); + } + } + + static std::size_t root_size_in_bits() { + return merkle_tree_check_read_component::root_size_in_bits(); + } + }; + } // namespace components + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SET_COMMITMENT_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_membership_proof_variable.hpp b/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_membership_proof_variable.hpp new file mode 100644 index 000000000..ba98f268b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/set_commitment/set_membership_proof_variable.hpp @@ -0,0 +1,109 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Test program that exercises the SEppzkSNARK (first generator, then +// prover, then verifier) on a synthetic R1CS instance. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SET_MEMBERSHIP_PROOF_VARIABLE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SET_MEMBERSHIP_PROOF_VARIABLE_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + namespace components { + + template + class set_membership_proof_variable : public component { + public: + blueprint_variable_vector address_bits; + std::shared_ptr> merkle_path; + + const std::size_t max_entries; + const std::size_t tree_depth; + + set_membership_proof_variable(blueprint &bp, const std::size_t max_entries) : + component(bp), max_entries(max_entries), + tree_depth(static_cast(std::ceil(std::log2(max_entries)))) { + if (tree_depth > 0) { + address_bits.allocate(bp, tree_depth); + merkle_path.reset( + new merkle_authentication_path_variable(bp, tree_depth)); + } + } + + void generate_gates() { + if (tree_depth > 0) { + for (std::size_t i = 0; i < tree_depth; ++i) { + generate_boolean_r1cs_constraint(this->bp, address_bits[i]); + } + merkle_path->generate_gates(); + } + } + void generate_assignments(const set_membership_proof &proof) { + if (tree_depth > 0) { + address_bits.fill_with_bits_of_field_element( + this->bp, typename FieldType::value_type(proof.address)); + merkle_path->generate_assignments(proof.address, proof.merkle_path); + } + } + + set_membership_proof get_membership_proof() const { + set_membership_proof result; + + if (tree_depth == 0) { + result.address = 0; + } else { + result.address = address_bits.get_field_element_from_bits(this->bp).as_ulong(); + result.merkle_path = merkle_path->get_authentication_path(result.address); + } + + return result; + } + + static snark::r1cs_variable_assignment + as_r1cs_variable_assignment(const set_membership_proof &proof) { + + blueprint bp; + const std::size_t max_entries = (1ul << (proof.merkle_path.size())); + set_membership_proof_variable proof_variable(bp, max_entries, + "proof_variable"); + proof_variable.generate_assignments(proof); + + return bp.full_variable_assignment(); + } + }; + } // namespace components + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SET_MEMBERSHIP_PROOF_VARIABLE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/compliance_predicate/cp_handler.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/compliance_predicate/cp_handler.hpp new file mode 100644 index 000000000..9743bec33 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/compliance_predicate/cp_handler.hpp @@ -0,0 +1,304 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for a compliance predicate handler. +// +// A compliance predicate handler is a base class for creating compliance predicates. +// It relies on classes declared in components. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_CP_HANDLER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_CP_HANDLER_HPP + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + + /***************************** Message variable ******************************/ + + /** + * A variable to represent an r1cs_pcd_message. + */ + template + class r1cs_pcd_message_variable : public components::component { + protected: + std::size_t num_vars_at_construction; + + public: + blueprint_variable type; + + blueprint_variable_vector all_vars; + + r1cs_pcd_message_variable(blueprint &bp); + void update_all_vars(); + + void generate_assignments(const std::shared_ptr> &message); + virtual std::shared_ptr> get_message() const = 0; + + virtual ~r1cs_pcd_message_variable() = default; + }; + /*************************** Local data variable *****************************/ + + /** + * A variable to represent an r1cs_pcd_local_data. + */ + template + class r1cs_pcd_local_data_variable : public components::component { + protected: + std::size_t num_vars_at_construction; + + public: + blueprint_variable_vector all_vars; + + r1cs_pcd_local_data_variable(blueprint &bp); + void update_all_vars(); + + void generate_assignments(const std::shared_ptr> &local_data); + + virtual ~r1cs_pcd_local_data_variable() = default; + }; + + /*********************** Compliance predicate handler ************************/ + + /** + * A base class for creating compliance predicates. + */ + template + class compliance_predicate_handler { + protected: + BlueprintType bp; + + std::shared_ptr> outgoing_message; + blueprint_variable arity; + std::vector>> incoming_messages; + std::shared_ptr> local_data; + + public: + const std::size_t name; + const std::size_t type; + const std::size_t max_arity; + const bool relies_on_same_type_inputs; + const std::set accepted_input_types; + + compliance_predicate_handler( + const BlueprintType &bp, + const std::size_t name, + const std::size_t type, + const std::size_t max_arity, + const bool relies_on_same_type_inputs, + const std::set &accepted_input_types = std::set()); + virtual void generate_gates() = 0; + virtual void generate_assignments( + const std::vector>> &incoming_message_values, + const std::shared_ptr> &local_data_value); + + r1cs_pcd_compliance_predicate get_compliance_predicate() const; + snark::r1cs_variable_assignment get_full_variable_assignment() const; + + std::shared_ptr> get_outgoing_message() const; + std::size_t get_arity() const; + std::shared_ptr> + get_incoming_message(const std::size_t message_idx) const; + std::shared_ptr> get_local_data() const; + snark::r1cs_variable_assignment get_witness() const; + }; + + template + r1cs_pcd_message_variable::r1cs_pcd_message_variable(blueprint &bp) : + components::component(bp) { + type.allocate(bp); + all_vars.emplace_back(type); + + num_vars_at_construction = bp.num_variables(); + } + + template + void r1cs_pcd_message_variable::update_all_vars() { + /* NOTE: this assumes that r1cs_pcd_message_variable has been the + * only component allocating variables on the protoboard and needs to + * be updated, e.g., in multicore variable allocation scenario. */ + + for (std::size_t var_idx = num_vars_at_construction + 1; var_idx <= this->bp.num_variables(); + ++var_idx) { + all_vars.emplace_back(blueprint_variable(var_idx)); + } + } + + template + void r1cs_pcd_message_variable::generate_assignments( + const std::shared_ptr> &message) { + all_vars.fill_with_field_elements(this->bp, message->as_r1cs_variable_assignment()); + } + + template + r1cs_pcd_local_data_variable::r1cs_pcd_local_data_variable(blueprint &bp) : + components::component(bp) { + num_vars_at_construction = bp.num_variables(); + } + + template + void r1cs_pcd_local_data_variable::update_all_vars() { + /* (the same NOTE as for r1cs_message_variable applies) */ + + for (std::size_t var_idx = num_vars_at_construction + 1; var_idx <= this->bp.num_variables(); + ++var_idx) { + all_vars.emplace_back(blueprint_variable(var_idx)); + } + } + + template + void r1cs_pcd_local_data_variable::generate_assignments( + const std::shared_ptr> &local_data) { + all_vars.fill_with_field_elements(this->bp, local_data->as_r1cs_variable_assignment()); + } + + template + compliance_predicate_handler::compliance_predicate_handler( + const BlueprintType &bp, + const std::size_t name, + const std::size_t type, + const std::size_t max_arity, + const bool relies_on_same_type_inputs, + const std::set &accepted_input_types) : + bp(bp), + name(name), type(type), max_arity(max_arity), + relies_on_same_type_inputs(relies_on_same_type_inputs), accepted_input_types(accepted_input_types) { + incoming_messages.resize(max_arity); + } + + template + void compliance_predicate_handler::generate_assignments( + const std::vector>> &incoming_message_values, + const std::shared_ptr> &local_data_value) { + bp.clear_values(); + bp.val(outgoing_message->type) = typename FieldType::value_type(type); + bp.val(arity) = typename FieldType::value_type(incoming_message_values.size()); + + for (std::size_t i = 0; i < incoming_message_values.size(); ++i) { + incoming_messages[i]->generate_assignments(incoming_message_values[i]); + } + + local_data->generate_assignments(local_data_value); + } + + template + r1cs_pcd_compliance_predicate + compliance_predicate_handler::get_compliance_predicate() const { + assert(incoming_messages.size() == max_arity); + + const std::size_t outgoing_message_payload_length = outgoing_message->all_vars.size() - 1; + + std::vector incoming_message_payload_lengths(max_arity); + std::transform(incoming_messages.begin(), incoming_messages.end(), + incoming_message_payload_lengths.begin(), + [](const std::shared_ptr> &msg) { + return msg->all_vars.size() - 1; + }); + + const std::size_t local_data_length = local_data->all_vars.size(); + + const std::size_t all_but_witness_length = + ((1 + outgoing_message_payload_length) + 1 + + (max_arity + std::accumulate(incoming_message_payload_lengths.begin(), + incoming_message_payload_lengths.end(), 0)) + + local_data_length); + const std::size_t witness_length = bp.num_variables() - all_but_witness_length; + + snark::r1cs_constraint_system constraint_system = bp.get_constraint_system(); + constraint_system.primary_input_size = 1 + outgoing_message_payload_length; + constraint_system.auxiliary_input_size = bp.num_variables() - constraint_system.primary_input_size; + + return r1cs_pcd_compliance_predicate(name, + type, + constraint_system, + outgoing_message_payload_length, + max_arity, + incoming_message_payload_lengths, + local_data_length, + witness_length, + relies_on_same_type_inputs, + accepted_input_types); + } + + template + snark::r1cs_variable_assignment + compliance_predicate_handler::get_full_variable_assignment() const { + return bp.full_variable_assignment(); + } + + template + std::shared_ptr> + compliance_predicate_handler::get_outgoing_message() const { + return outgoing_message->get_message(); + } + + template + std::size_t compliance_predicate_handler::get_arity() const { + return bp.val(arity).as_ulong(); + } + + template + std::shared_ptr> + compliance_predicate_handler::get_incoming_message( + const std::size_t message_idx) const { + assert(message_idx < max_arity); + return incoming_messages[message_idx]->get_message(); + } + + template + std::shared_ptr> + compliance_predicate_handler::get_local_data() const { + return local_data->get_local_data(); + } + + template + r1cs_pcd_witness + compliance_predicate_handler::get_witness() const { + const snark::r1cs_variable_assignment va = bp.full_variable_assignment(); + // outgoing_message + arity + incoming_messages + local_data + const std::size_t witness_pos = + (outgoing_message->all_vars.size() + 1 + + std::accumulate( + incoming_messages.begin(), incoming_messages.end(), 0, + [](std::size_t acc, const std::shared_ptr> &msg) { + return acc + msg->all_vars.size(); + }) + + local_data->all_vars.size()); + + return snark::r1cs_variable_assignment(va.begin() + witness_pos, va.end()); + } + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_CP_HANDLER_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/mp_pcd_circuits.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/mp_pcd_circuits.hpp new file mode 100644 index 000000000..f78638865 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/mp_pcd_circuits.hpp @@ -0,0 +1,799 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of functionality for creating and using the two PCD circuits in +// a multi-predicate PCD construction. +// +// The implementation follows, extends, and optimizes the approach described +// in \[CTV15]. At high level, there is a "compliance step" circuit and a +// "translation step" circuit, for each compliance predicate. For more details, +// see \[CTV15]. +// +// +// References: +// +// \[CTV15]: +// "Cluster Computing in Zero Knowledge", +// Alessandro Chiesa, Eran Tromer, Madars Virza +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_MP_PCD_CIRCUITS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_MP_PCD_CIRCUITS_HPP + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + + /**************************** Compliance step ********************************/ + + /** + * A compliance-step PCD circuit. + * + * The circuit is an R1CS that checks compliance (for the given compliance predicate) + * and validity of previous proofs. + */ + template + class mp_compliance_step_pcd_circuit_maker { + + // for now all CRH components are knapsack CRH's; can be easily extended + // later to more expressive selector types. + template + using crh_with_field_out_component = knapsack_crh_with_field_out_component; + + template + using crh_with_bit_out_component = knapsack_crh_with_bit_out_component; + + public: + typedef typename CurveType::scalar_field_type FieldType; + + r1cs_pcd_compliance_predicate compliance_predicate; + + blueprint bp; + + blueprint_variable zero; + + std::shared_ptr> block_for_outgoing_message; + std::shared_ptr> hash_outgoing_message; + + std::vector> block_for_incoming_messages; + std::vector> commitment_and_incoming_message_digests; + std::vector> unpack_commitment_and_incoming_message_digests; + std::vector> commitment_and_incoming_messages_digest_bits; + std::vector> hash_incoming_messages; + + std::vector> translation_step_vks; + std::vector> translation_step_vks_bits; + + blueprint_variable outgoing_message_type; + blueprint_variable_vector outgoing_message_payload; + blueprint_variable_vector outgoing_message_vars; + + blueprint_variable arity; + std::vector> incoming_message_types; + std::vector> incoming_message_payloads; + std::vector> incoming_message_vars; + + blueprint_variable_vector local_data; + blueprint_variable_vector cp_witness; + std::shared_ptr> compliance_predicate_as_component; + + blueprint_variable_vector outgoing_message_bits; + std::shared_ptr> unpack_outgoing_message; + + std::vector> incoming_messages_bits; + std::vector> unpack_incoming_messages; + + blueprint_variable_vector mp_compliance_step_pcd_circuit_input; + blueprint_variable_vector padded_translation_step_vk_and_outgoing_message_digest; + std::vector> padded_commitment_and_incoming_messages_digest; + + std::shared_ptr>> + commitment; + std::vector>> + membership_proofs; + std::vector>> + membership_checkers; + blueprint_variable_vector membership_check_results; + blueprint_variable common_type; + blueprint_variable_vector common_type_check_aux; + + std::vector> verifier_input; + std::vector> proof; + blueprint_variable_vector verification_results; + std::vector> verifier; + + mp_compliance_step_pcd_circuit_maker( + const r1cs_pcd_compliance_predicate &compliance_predicate, + const std::size_t max_number_of_predicates); + void generate_gates(); + snark::r1cs_constraint_system get_circuit() const; + + void generate_assignments( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const std::vector>> + &mp_translation_step_pcd_circuit_vks, + const std::vector &vk_membership_proofs, + const r1cs_pcd_compliance_predicate_primary_input + &compliance_predicate_primary_input, + const r1cs_pcd_compliance_predicate_auxiliary_input + &compliance_predicate_auxiliary_input, + const std::vector>> &translation_step_proofs); + snark::r1cs_primary_input get_primary_input() const; + snark::r1cs_auxiliary_input get_auxiliary_input() const; + + static std::size_t field_logsize(); + static std::size_t field_capacity(); + static std::size_t input_size_in_elts(); + static std::size_t input_capacity_in_bits(); + static std::size_t input_size_in_bits(); + }; + + /*************************** Translation step ********************************/ + + /** + * A translation-step PCD circuit. + * + * The circuit is an R1CS that checks validity of previous proofs. + */ + template + class mp_translation_step_pcd_circuit_maker { + public: + typedef typename CurveType::scalar_field_type FieldType; + + blueprint bp; + + blueprint_variable_vector mp_translation_step_pcd_circuit_input; + blueprint_variable_vector unpacked_mp_translation_step_pcd_circuit_input; + blueprint_variable_vector verifier_input; + std::shared_ptr> unpack_mp_translation_step_pcd_circuit_input; + + std::shared_ptr> + hardcoded_compliance_step_vk; + std::shared_ptr> proof; + std::shared_ptr> online_verifier; + + mp_translation_step_pcd_circuit_maker( + const r1cs_ppzksnark_verification_key> &compliance_step_vk); + void generate_gates(); + snark::r1cs_constraint_system get_circuit() const; + + void generate_assignments(const snark::r1cs_primary_input + translation_step_input, + const r1cs_ppzksnark_proof> &prev_proof); + snark::r1cs_primary_input get_primary_input() const; + snark::r1cs_auxiliary_input get_auxiliary_input() const; + + static std::size_t field_logsize(); + static std::size_t field_capacity(); + static std::size_t input_size_in_elts(); + static std::size_t input_capacity_in_bits(); + static std::size_t input_size_in_bits(); + }; + + /****************************** Input maps ***********************************/ + + /** + * Obtain the primary input for a compliance-step PCD circuit. + */ + template + snark::r1cs_primary_input + get_mp_compliance_step_pcd_circuit_input( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const r1cs_pcd_compliance_predicate_primary_input + &primary_input); + + /** + * Obtain the primary input for a translation-step PCD circuit. + */ + template + snark::r1cs_primary_input + get_mp_translation_step_pcd_circuit_input( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const r1cs_pcd_compliance_predicate_primary_input::scalar_field_type> + &primary_input); + + template + mp_compliance_step_pcd_circuit_maker::mp_compliance_step_pcd_circuit_maker( + const r1cs_pcd_compliance_predicate &compliance_predicate, + const std::size_t max_number_of_predicates) : + compliance_predicate(compliance_predicate) { + /* calculate some useful sizes */ + const std::size_t digest_size = crh_with_field_out_component::get_digest_len(); + const std::size_t outgoing_msg_size_in_bits = + field_logsize() * (1 + compliance_predicate.outgoing_message_payload_length); + assert(compliance_predicate.has_equal_input_lengths()); + const std::size_t translation_step_vk_size_in_bits = + r1cs_ppzksnark_verification_key_variable::size_in_bits( + mp_translation_step_pcd_circuit_maker>::input_size_in_elts()); + const std::size_t padded_verifier_input_size = + mp_translation_step_pcd_circuit_maker>::input_capacity_in_bits(); + const std::size_t commitment_size = + set_commitment_component>::root_size_in_bits(); + + const std::size_t output_block_size = commitment_size + outgoing_msg_size_in_bits; + const std::size_t max_incoming_payload_length = + *std::max_element(compliance_predicate.incoming_message_payload_lengths.begin(), + compliance_predicate.incoming_message_payload_lengths.end()); + const std::size_t max_input_block_size = + commitment_size + field_logsize() * (1 + max_incoming_payload_length); + + crh_with_bit_out_component::sample_randomness( + std::max(output_block_size, max_input_block_size)); + + /* allocate input of the compliance MP_PCD circuit */ + mp_compliance_step_pcd_circuit_input.allocate(bp, input_size_in_elts()); + + /* allocate inputs to the compliance predicate */ + outgoing_message_type.allocate(bp); + outgoing_message_payload.allocate(bp, compliance_predicate.outgoing_message_payload_length); + + outgoing_message_vars.insert(outgoing_message_vars.end(), outgoing_message_type); + outgoing_message_vars.insert(outgoing_message_vars.end(), outgoing_message_payload.begin(), + outgoing_message_payload.end()); + + arity.allocate(bp); + + incoming_message_types.resize(compliance_predicate.max_arity); + incoming_message_payloads.resize(compliance_predicate.max_arity); + incoming_message_vars.resize(compliance_predicate.max_arity); + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + incoming_message_types[i].allocate(bp); + incoming_message_payloads[i].allocate(bp, + compliance_predicate.incoming_message_payload_lengths[i]); + + incoming_message_vars[i].insert(incoming_message_vars[i].end(), incoming_message_types[i]); + incoming_message_vars[i].insert(incoming_message_vars[i].end(), + incoming_message_payloads[i].begin(), + incoming_message_payloads[i].end()); + } + + local_data.allocate(bp, compliance_predicate.local_data_length); + cp_witness.allocate(bp, compliance_predicate.witness_length); + + /* convert compliance predicate from a constraint system into a component */ + blueprint_variable_vector incoming_messages_concat; + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + incoming_messages_concat.insert(incoming_messages_concat.end(), + incoming_message_vars[i].begin(), + incoming_message_vars[i].end()); + } + + compliance_predicate_as_component.reset(new component_from_r1cs( + bp, + {outgoing_message_vars, blueprint_variable_vector(1, arity), + incoming_messages_concat, local_data, cp_witness}, + compliance_predicate.constraint_system)); + + /* unpack messages to bits */ + outgoing_message_bits.allocate(bp, outgoing_msg_size_in_bits); + unpack_outgoing_message.reset(new multipacking_component( + bp, outgoing_message_bits, outgoing_message_vars, field_logsize())); + + incoming_messages_bits.resize(compliance_predicate.max_arity); + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + const std::size_t incoming_msg_size_in_bits = + field_logsize() * (1 + compliance_predicate.incoming_message_payload_lengths[i]); + + incoming_messages_bits[i].allocate(bp, incoming_msg_size_in_bits); + unpack_incoming_messages.emplace_back(multipacking_component( + bp, incoming_messages_bits[i], incoming_message_vars[i], field_logsize())); + } + + /* allocate digests */ + commitment_and_incoming_message_digests.resize(compliance_predicate.max_arity); + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + commitment_and_incoming_message_digests[i].allocate(bp, digest_size); + } + + /* allocate commitment, verification key(s) and membership checker(s)/proof(s) */ + commitment.reset(new set_commitment_variable>( + bp, commitment_size)); + + if (compliance_predicate.relies_on_same_type_inputs) { + /* only one set_commitment_component is needed */ + common_type.allocate(bp); + common_type_check_aux.allocate(bp, compliance_predicate.accepted_input_types.size()); + + translation_step_vks_bits.resize(1); + translation_step_vks_bits[0].allocate(bp, translation_step_vk_size_in_bits); + membership_check_results.allocate(bp, 1); + + membership_proofs.emplace_back( + set_membership_proof_variable>( + bp, max_number_of_predicates)); + membership_checkers.emplace_back( + set_commitment_component>( + bp, max_number_of_predicates, translation_step_vks_bits[0], *commitment, + membership_proofs[0], membership_check_results[0])); + } else { + /* check for max_arity possibly different VKs */ + translation_step_vks_bits.resize(compliance_predicate.max_arity); + membership_check_results.allocate(bp, compliance_predicate.max_arity); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + translation_step_vks_bits[i].allocate(bp, translation_step_vk_size_in_bits); + + membership_proofs.emplace_back( + set_membership_proof_variable>( + bp, max_number_of_predicates)); + membership_checkers.emplace_back( + set_commitment_component>( + bp, + max_number_of_predicates, + translation_step_vks_bits[i], + *commitment, + membership_proofs[i], + membership_check_results[i])); + } + } + + /* allocate blocks */ + block_for_outgoing_message.reset( + new block_variable(bp, {commitment->bits, outgoing_message_bits})); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + block_for_incoming_messages.emplace_back( + block_variable(bp, {commitment->bits, incoming_messages_bits[i]})); + } + + /* allocate hash checkers */ + hash_outgoing_message.reset(new crh_with_field_out_component( + bp, output_block_size, *block_for_outgoing_message, mp_compliance_step_pcd_circuit_input)); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + const std::size_t input_block_size = commitment_size + incoming_messages_bits[i].size(); + hash_incoming_messages.emplace_back(crh_with_field_out_component( + bp, input_block_size, block_for_incoming_messages[i], + commitment_and_incoming_message_digests[i])); + } + + /* allocate useful zero variable */ + zero.allocate(bp); + + /* prepare arguments for the verifier */ + if (compliance_predicate.relies_on_same_type_inputs) { + translation_step_vks.emplace_back(r1cs_ppzksnark_verification_key_variable( + bp, translation_step_vks_bits[0], + mp_translation_step_pcd_circuit_maker>::input_size_in_elts())); + } else { + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + translation_step_vks.emplace_back(r1cs_ppzksnark_verification_key_variable( + bp, translation_step_vks_bits[i], + mp_translation_step_pcd_circuit_maker>::input_size_in_elts())); + } + } + + verification_results.allocate(bp, compliance_predicate.max_arity); + commitment_and_incoming_messages_digest_bits.resize(compliance_predicate.max_arity); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + commitment_and_incoming_messages_digest_bits[i].allocate(bp, digest_size * field_logsize()); + unpack_commitment_and_incoming_message_digests.emplace_back( + multipacking_component(bp, + commitment_and_incoming_messages_digest_bits[i], + commitment_and_incoming_message_digests[i], + field_logsize())); + + verifier_input.emplace_back(commitment_and_incoming_messages_digest_bits[i]); + while (verifier_input[i].size() < padded_verifier_input_size) { + verifier_input[i].emplace_back(zero); + } + + proof.emplace_back(r1cs_ppzksnark_proof_variable(bp)); + const r1cs_ppzksnark_verification_key_variable &vk_to_be_used = + (compliance_predicate.relies_on_same_type_inputs ? translation_step_vks[0] : + translation_step_vks[i]); + verifier.emplace_back(r1cs_ppzksnark_verifier_component( + bp, + vk_to_be_used, + verifier_input[i], + mp_translation_step_pcd_circuit_maker>::field_capacity(), + proof[i], + verification_results[i])); + } + + bp.set_input_sizes(input_size_in_elts()); + } + + template + void mp_compliance_step_pcd_circuit_maker::generate_gates() { + const std::size_t digest_size = crh_with_bit_out_component::get_digest_len(); + const std::size_t dimension = knapsack_dimension::dimension; + unpack_outgoing_message->generate_gates(true); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + unpack_incoming_messages[i].generate_gates(true); + } + + for (std::size_t i = 0; i < translation_step_vks.size(); ++i) { + translation_step_vks[i].generate_gates(true); + } + + hash_outgoing_message->generate_gates(); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + hash_incoming_messages[i].generate_gates(); + } + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + unpack_commitment_and_incoming_message_digests[i].generate_gates(true); + } + + for (auto &membership_proof : membership_proofs) { + membership_proof.generate_gates(); + } + + for (auto &membership_checker : membership_checkers) { + membership_checker.generate_gates(); + } + + compliance_predicate_as_component->generate_gates(); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + proof[i].generate_gates(); + } + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + verifier[i].generate_gates(); + } + + generate_r1cs_equals_const_constraint(bp, zero, FieldType::value_type::zero()); + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + generate_boolean_r1cs_constraint(bp, verification_results[i]); + } + + /* either type = 0 or proof verified w.r.t. a valid verification key */ + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + bp.add_r1cs_constraint(snark::r1cs_constraint(incoming_message_types[i], + 1 - verification_results[i], 0)); + } + + if (compliance_predicate.relies_on_same_type_inputs) { + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + bp.add_r1cs_constraint(snark::r1cs_constraint( + incoming_message_types[i], incoming_message_types[i] - common_type, 0)); + } + + bp.add_r1cs_constraint( + snark::r1cs_constraint(common_type, 1 - membership_check_results[0], 0)); + + auto it = compliance_predicate.accepted_input_types.begin(); + for (std::size_t i = 0; i < compliance_predicate.accepted_input_types.size(); ++i, ++it) { + bp.add_r1cs_constraint(snark::r1cs_constraint( + (i == 0 ? common_type : common_type_check_aux[i - 1]), + common_type - typename FieldType::value_type(*it), + (i == compliance_predicate.accepted_input_types.size() - 1 ? + 0 * blueprint_variable(0) : + common_type_check_aux[i]))); + } + } else { + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + bp.add_r1cs_constraint(snark::r1cs_constraint( + incoming_message_types[i], 1 - membership_check_results[i], 0)); + } + } + bp.add_r1cs_constraint(snark::r1cs_constraint( + 1, outgoing_message_type, typename FieldType::value_type(compliance_predicate.type))); + } + + template + snark::r1cs_constraint_system + mp_compliance_step_pcd_circuit_maker::get_circuit() const { + return bp.get_constraint_system(); + } + + template + snark::r1cs_primary_input + mp_compliance_step_pcd_circuit_maker::get_primary_input() const { + return bp.primary_input(); + } + + template + snark::r1cs_auxiliary_input + mp_compliance_step_pcd_circuit_maker::get_auxiliary_input() const { + return bp.auxiliary_input(); + } + + template + void mp_compliance_step_pcd_circuit_maker::generate_assignments( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const std::vector>> + &mp_translation_step_pcd_circuit_vks, + const std::vector &vk_membership_proofs, + const r1cs_pcd_compliance_predicate_primary_input &compliance_predicate_primary_input, + const r1cs_pcd_compliance_predicate_auxiliary_input + &compliance_predicate_auxiliary_input, + const std::vector>> &translation_step_proofs) { + + this->bp.clear_values(); + this->bp.val(zero) = FieldType::value_type::zero(); + + compliance_predicate_as_component->generate_assignments( + compliance_predicate_primary_input.as_r1cs_primary_input(), + compliance_predicate_auxiliary_input.as_r1cs_auxiliary_input( + compliance_predicate.incoming_message_payload_lengths)); + + unpack_outgoing_message->generate_assignments_from_packed(); + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + unpack_incoming_messages[i].generate_assignments_from_packed(); + } + + for (std::size_t i = 0; i < translation_step_vks.size(); ++i) { + translation_step_vks[i].generate_assignments(mp_translation_step_pcd_circuit_vks[i]); + } + + commitment->generate_assignments(commitment_to_translation_step_r1cs_vks); + + if (compliance_predicate.relies_on_same_type_inputs) { + /* all messages (except base case) must be of the same type */ + this->bp.val(common_type) = FieldType::value_type::zero(); + std::size_t nonzero_type_idx = 0; + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + if (this->bp.val(incoming_message_types[i]) == 0) { + continue; + } + + if (this->bp.val(common_type).is_zero()) { + this->bp.val(common_type) = this->bp.val(incoming_message_types[i]); + nonzero_type_idx = i; + } else { + assert(this->bp.val(common_type) == this->bp.val(incoming_message_types[i])); + } + } + + this->bp.val(membership_check_results[0]) = + (this->bp.val(common_type).is_zero() ? FieldType::value_type::zero() : + FieldType::value_type::zero()); + membership_proofs[0].generate_assignments(vk_membership_proofs[nonzero_type_idx]); + membership_checkers[0].generate_assignments(); + + auto it = compliance_predicate.accepted_input_types.begin(); + for (std::size_t i = 0; i < compliance_predicate.accepted_input_types.size(); ++i, ++it) { + bp.val(common_type_check_aux[i]) = + ((i == 0 ? bp.val(common_type) : bp.val(common_type_check_aux[i - 1])) * + (bp.val(common_type) - typename FieldType::value_type(*it))); + } + + } else { + for (std::size_t i = 0; i < membership_checkers.size(); ++i) { + this->bp.val(membership_check_results[i]) = + (this->bp.val(incoming_message_types[i]).is_zero() ? FieldType::value_type::zero() : + FieldType::value_type::zero()); + membership_proofs[i].generate_assignments(vk_membership_proofs[i]); + membership_checkers[i].generate_assignments(); + } + } + + hash_outgoing_message->generate_assignments(); + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + hash_incoming_messages[i].generate_assignments(); + unpack_commitment_and_incoming_message_digests[i].generate_assignments_from_packed(); + } + + for (std::size_t i = 0; i < compliance_predicate.max_arity; ++i) { + proof[i].generate_assignments(translation_step_proofs[i]); + verifier[i].generate_assignments(); + } + } + + template + std::size_t mp_compliance_step_pcd_circuit_maker::field_logsize() { + return typename CurveType::scalar_field_type::value_bits; + } + + template + std::size_t mp_compliance_step_pcd_circuit_maker::field_capacity() { + return typename CurveType::scalar_field_type::capacity(); + } + + template + std::size_t mp_compliance_step_pcd_circuit_maker::input_size_in_elts() { + const std::size_t digest_size = crh_with_field_out_component::get_digest_len(); + return digest_size; + } + + template + std::size_t mp_compliance_step_pcd_circuit_maker::input_capacity_in_bits() { + return input_size_in_elts() * field_capacity(); + } + + template + std::size_t mp_compliance_step_pcd_circuit_maker::input_size_in_bits() { + return input_size_in_elts() * field_logsize(); + } + + template + mp_translation_step_pcd_circuit_maker::mp_translation_step_pcd_circuit_maker( + const r1cs_ppzksnark_verification_key> &compliance_step_vk) { + /* allocate input of the translation MP_PCD circuit */ + mp_translation_step_pcd_circuit_input.allocate(bp, input_size_in_elts()); + + /* unpack translation step MP_PCD circuit input */ + unpacked_mp_translation_step_pcd_circuit_input.allocate( + bp, mp_compliance_step_pcd_circuit_maker>::input_size_in_bits()); + unpack_mp_translation_step_pcd_circuit_input.reset( + new multipacking_component(bp, unpacked_mp_translation_step_pcd_circuit_input, + mp_translation_step_pcd_circuit_input, field_capacity())); + + /* prepare arguments for the verifier */ + hardcoded_compliance_step_vk.reset( + new r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable( + bp, compliance_step_vk)); + proof.reset(new r1cs_ppzksnark_proof_variable(bp)); + + /* verify previous proof */ + online_verifier.reset(new r1cs_ppzksnark_online_verifier_component( + bp, + *hardcoded_compliance_step_vk, + unpacked_mp_translation_step_pcd_circuit_input, + mp_compliance_step_pcd_circuit_maker>::field_logsize(), + *proof, + blueprint_variable(0))); + + bp.set_input_sizes(input_size_in_elts()); + } + + template + void mp_translation_step_pcd_circuit_maker::generate_gates() { + unpack_mp_translation_step_pcd_circuit_input->generate_gates(true); + + proof->generate_gates(); + + online_verifier->generate_gates(); + } + + template + snark::r1cs_constraint_system + mp_translation_step_pcd_circuit_maker::get_circuit() const { + return bp.get_constraint_system(); + } + + template + void mp_translation_step_pcd_circuit_maker::generate_assignments( + const snark::r1cs_primary_input + translation_step_input, + const r1cs_ppzksnark_proof> &prev_proof) { + this->bp.clear_values(); + mp_translation_step_pcd_circuit_input.fill_with_field_elements(bp, translation_step_input); + unpack_mp_translation_step_pcd_circuit_input->generate_assignments_from_packed(); + + proof->generate_assignments(prev_proof); + online_verifier->generate_assignments(); + } + + template + snark::r1cs_primary_input + mp_translation_step_pcd_circuit_maker::get_primary_input() const { + return bp.primary_input(); + } + + template + snark::r1cs_auxiliary_input + mp_translation_step_pcd_circuit_maker::get_auxiliary_input() const { + return bp.auxiliary_input(); + } + + template + std::size_t mp_translation_step_pcd_circuit_maker::field_logsize() { + return typename CurveType::scalar_field_type::value_bits; + } + + template + std::size_t mp_translation_step_pcd_circuit_maker::field_capacity() { + return typename CurveType::scalar_field_type::capacity(); + } + + template + std::size_t mp_translation_step_pcd_circuit_maker::input_size_in_elts() { + return algebra::div_ceil( + mp_compliance_step_pcd_circuit_maker>::input_size_in_bits(), + mp_translation_step_pcd_circuit_maker::field_capacity()); + } + + template + std::size_t mp_translation_step_pcd_circuit_maker::input_capacity_in_bits() { + return input_size_in_elts() * field_capacity(); + } + + template + std::size_t mp_translation_step_pcd_circuit_maker::input_size_in_bits() { + return input_size_in_elts() * field_logsize(); + } + + template + snark::r1cs_primary_input + get_mp_compliance_step_pcd_circuit_input( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const r1cs_pcd_compliance_predicate_primary_input + &primary_input) { + typedef typename CurveType::scalar_field_type FieldType; + + const snark::r1cs_variable_assignment outgoing_message_as_va = + primary_input.outgoing_message->as_r1cs_variable_assignment(); + std::vector msg_bits; + for (const typename FieldType::value_type &elt : outgoing_message_as_va) { + const std::vector elt_bits = algebra::convert_field_element_to_bit_vector(elt); + msg_bits.insert(msg_bits.end(), elt_bits.begin(), elt_bits.end()); + } + + std::vector block; + block.insert(block.end(), commitment_to_translation_step_r1cs_vks.begin(), + commitment_to_translation_step_r1cs_vks.end()); + block.insert(block.end(), msg_bits.begin(), msg_bits.end()); + + crh_with_field_out_component::sample_randomness(block.size()); + + const std::vector digest = + crh_with_field_out_component::get_hash(block); + + return digest; + } + + template + snark::r1cs_primary_input + get_mp_translation_step_pcd_circuit_input( + const set_commitment &commitment_to_translation_step_r1cs_vks, + const r1cs_pcd_compliance_predicate_primary_input < + other_curve::scalar_field_type::value_type & + primary_input) { + typedef typename CurveType::scalar_field_type FieldType; + + const std::vector < + other_curve::scalar_field_type::value_type mp_compliance_step_pcd_circuit_input = + get_mp_compliance_step_pcd_circuit_input>( + commitment_to_translation_step_r1cs_vks, primary_input); + std::vector mp_compliance_step_pcd_circuit_input_bits; + for (const other_curve::scalar_field_type::value_type &elt : + mp_compliance_step_pcd_circuit_input) { + const std::vector elt_bits = algebra::convert_field_element_to_bit_vector < + other_curve::scalar_field_type::value_type(elt); + mp_compliance_step_pcd_circuit_input_bits.insert( + mp_compliance_step_pcd_circuit_input_bits.end(), elt_bits.begin(), elt_bits.end()); + } + + mp_compliance_step_pcd_circuit_input_bits.resize( + mp_translation_step_pcd_circuit_maker::input_capacity_in_bits(), false); + + const snark::r1cs_primary_input result = + algebra::pack_bit_vector_into_field_element_vector( + mp_compliance_step_pcd_circuit_input_bits, + mp_translation_step_pcd_circuit_maker::field_capacity()); + return result; + } + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_MP_PCD_CIRCUITS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/additions.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/additions.hpp new file mode 100644 index 000000000..5a7d1d6f5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/additions.hpp @@ -0,0 +1,287 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_ADDITIONS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_ADDITIONS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: array of pairs <, , ..., > + // Output: array + // Configuration is suboptimal: we do rows of the form + // a1, b1, o1, a2, b2, o2, ... + template + class flexible_additions; + + template + class flexible_additions, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t n; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return flexible_additions::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t n + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(3, 300, 3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t n) { + std::size_t cells = 3 * n; + std::size_t one_row_cells = (witness_amount / 3)*3; + return cells%one_row_cells == 0? cells/one_row_cells: cells/one_row_cells + 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), n); + + struct input_type { + std::vector> arr; // the array of pairs of elements + + std::vector> all_vars() { + std::vector> result; + for( std::size_t i = 0; i < arr.size(); i++ ){ + result.push_back(arr[i].first); + result.push_back(arr[i].second); + } + return result; + } + }; + + struct result_type { + std::vector output; // the array with sums + std::size_t n; + + result_type(const flexible_additions &component, std::size_t start_row_index) { + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + n = component.n; + + output.reserve(n); + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if( cur >= n ) break; + for(std::size_t block = 0; block < witness_amount / 3; block++, cur++ ){ + if( cur >= n ) break; + output.emplace_back( + var(component.W(block * 3 + 2), start_row_index + row, false) + ); + } + } + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(output.size()); + result.insert(result.end(), output.begin(), output.end()); + return result; + } + }; + + template + explicit flexible_additions(ContainerType witness, std::size_t _n) : + component_type(witness, {}, {}, get_manifest()), + n(_n) {}; + + template + flexible_additions(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t _n) : + component_type(witness, constant, public_input, get_manifest()), + n(_n) {}; + + flexible_additions( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _n) : + component_type(witnesses, constants, public_inputs, get_manifest()), + n(_n) {}; + }; + + template + using plonk_flexible_additions = + flexible_additions, + BlueprintFieldType>; + + template + typename plonk_flexible_additions::result_type generate_assignments( + const plonk_flexible_additions &component, + assignment> + &assignment, + const typename plonk_flexible_additions::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_additions; + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t n = instance_input.arr.size(); + BOOST_ASSERT(component.n == instance_input.arr.size()); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if (cur < n) { + value_type a_val = var_value(assignment, instance_input.arr[cur].first); + value_type b_val = var_value(assignment, instance_input.arr[cur].second); + assignment.witness(component.W(block*3), start_row_index + row) = a_val; + assignment.witness(component.W(block*3 + 1), start_row_index + row) = b_val; + assignment.witness(component.W(block*3 + 2), start_row_index + row) = a_val + b_val; + } else { + assignment.witness(component.W(block*3), start_row_index + row) = 0; + assignment.witness(component.W(block*3 + 1), start_row_index + row) = 0; + assignment.witness(component.W(block*3 + 2), start_row_index + row) = 0; + } + } + for( std::size_t i = (witness_amount/3)*3; i + std::size_t generate_gates( + const plonk_flexible_additions &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_additions::input_type + &instance_input) { + + using component_type = plonk_flexible_additions; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + BOOST_ASSERT(component.n == instance_input.arr.size()); + + std::vector constraints; + constraints.reserve(component.n); + var t = var(component.W(0), 0, true); + const std::size_t witness_amount = component.witness_amount(); + for( std::size_t block = 0; block < witness_amount/3; block++ ) { + var input_a_var = var(component.W(block * 3), 0, true), + input_b_var = var(component.W(block * 3 + 1), 0, true), + output_var = var(component.W(block * 3 + 2), 0, true); + + constraints.emplace_back(input_a_var + input_b_var - output_var); + } + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_flexible_additions &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_additions::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_additions; + using var = typename component_type::var; + + BOOST_ASSERT(component.n == instance_input.arr.size()); + std::size_t n = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if(cur >= n) break; + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if(cur >= n) break; + bp.add_copy_constraint( + {instance_input.arr[cur].first, var(component.W(3*block), start_row_index + row, false)}); + bp.add_copy_constraint( + {instance_input.arr[cur].second, var(component.W(3*block+1), start_row_index + row, false)}); + } + } + } + + template + typename plonk_flexible_additions::result_type generate_circuit( + const plonk_flexible_additions &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_additions::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_additions; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_ADDITIONS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/colinear_checks.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/colinear_checks.hpp new file mode 100644 index 000000000..e17b39232 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/colinear_checks.hpp @@ -0,0 +1,337 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_COLINEAR_CHECKS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_COLINEAR_CHECKS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: array of n tuples x, {b_1, b_2, ..., b_r}, {y_0, y_1, ..., y_2r} {alpha_0, alpha_2, ..., alpha_r-1} + // Constant: omega + // Output: x -- challenge for final polynomial + // If check is wrong -- copy constraints failes + // Structure: + // {x,b,y_0,y_1,omega^\sum{b}}\alpha + template + class flexible_colinear_checks; + + template + class flexible_colinear_checks, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using value_type = typename BlueprintFieldType::value_type; + + std::size_t r; + value_type omega; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return flexible_colinear_checks::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t r + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(9, 300, 5)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t r) { + std::size_t cells = 5 * r + 4; + std::size_t one_row_cells = ((witness_amount-4) / 5); + return (cells-4)%one_row_cells == 0? (cells-4)/one_row_cells: (cells-4)/one_row_cells + 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), r); + + struct input_type { + var x; // first challenge x + std::vector ys; // array of pairs of elements r+1 pairs + std::vector bs; // array of r+1 signs + std::vector alphas; // array size r + std::size_t r; + + input_type(std::size_t r){ + } + + std::vector> all_vars() { + std::vector> result; + BOOST_ASSERT(ys.size() == bs.size()*2); + BOOST_ASSERT(alphas.size() + 1 == bs.size()); + result.push_back(x); + + for( std::size_t i = 0; i < ys.size(); i++ ){ + result.push_back(ys[i]); + } + for( std::size_t i = 0; i < bs.size(); i++ ){ + result.push_back(bs[i]); + } + for( std::size_t i = 0; i < alphas.size(); i++ ){ + result.push_back(alphas[i]); + } + return result; + } + }; + + struct result_type { + result_type(const flexible_colinear_checks &component, std::size_t start_row_index) { + } + + std::vector> all_vars() { + std::vector> result; + return result; + } + }; + + template + explicit flexible_colinear_checks(ContainerType witness, std::size_t _r) : + component_type(witness, {}, {}, get_manifest()), + r(_r) {}; + + template + flexible_colinear_checks(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t _r) : + component_type(witness, constant, public_input, get_manifest()), + r(_r) {}; + + flexible_colinear_checks( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _r) : + component_type(witnesses, constants, public_inputs, get_manifest()), + r(_r) {}; + }; + + template + using plonk_flexible_colinear_checks = + flexible_colinear_checks, + BlueprintFieldType>; + + template + typename plonk_flexible_colinear_checks::result_type generate_assignments( + const plonk_flexible_colinear_checks &component, + assignment> + &assignment, + const typename plonk_flexible_colinear_checks::input_type + &instance_input, + const std::uint32_t start_row_index) { + assert(instance_input.ys.size() == instance_input.bs.size()*2); + assert(instance_input.alphas.size() + 1 == instance_input.bs.size()); + + using component_type = plonk_flexible_colinear_checks; + using value_type = typename BlueprintFieldType::value_type; + + BOOST_ASSERT(component.r == instance_input.alphas.size()); + BOOST_ASSERT(component.r + 1 == instance_input.bs.size()); + BOOST_ASSERT(2 * (component.r + 1) == instance_input.ys.size()); + + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + value_type x = var_value(assignment, instance_input.x); + for (std::size_t row = 0; row < rows_amount; row++) { + std::size_t block = 0; + for (; block < (witness_amount-4)/5; block++) { + if (cur < component.r){ + value_type b = var_value(assignment, instance_input.bs[cur]); + value_type y0_val = var_value(assignment, instance_input.ys[2*cur]); + value_type y1_val = var_value(assignment, instance_input.ys[2*cur+1]); + value_type alpha = var_value(assignment, instance_input.alphas[cur]); + + // value_type s = 2 * b * x - x; + // value_type interpolant = ((alpha + s ) * y0_val - (alpha - s) * y1_val ) / (2 * s); +// std::cout << "Interpolant " << cur << " index " << b << ":"; +// for( std::size_t k = 0; k < instance_input.bs.size(); k++) { +// std::cout << var_value(assignment, instance_input.bs[k]) << " "; +// } +// std::cout << std::endl << interpolant << std::endl; + + assignment.witness(component.W(block*5), start_row_index + row) = x; + assignment.witness(component.W(block*5+1), start_row_index + row) = b; + assignment.witness(component.W(block*5+2), start_row_index + row) = y0_val; + assignment.witness(component.W(block*5+3), start_row_index + row) = y1_val; + assignment.witness(component.W(block*5+4), start_row_index + row) = alpha; + cur++; + x = x * x; + y0_val = var_value(assignment, instance_input.ys[2*cur]); + y1_val = var_value(assignment, instance_input.ys[2*cur+1]); + value_type b_val = var_value(assignment, instance_input.bs[cur]); + //std::cout << b_val * y0_val + (1 - b_val) * y1_val << std::endl; + assignment.witness(component.W(block*5 + 5), start_row_index + row) = x; + assignment.witness(component.W(block*5 + 6), start_row_index + row) = b_val; + assignment.witness(component.W(block*5 + 7), start_row_index + row) = y0_val; + assignment.witness(component.W(block*5 + 8), start_row_index + row) = y1_val; + } else { + // Fill it with something to prevent new gate from being added + value_type x = assignment.witness(component.W(block * 5), start_row_index + row); + value_type b = assignment.witness(component.W(block * 5 + 1), start_row_index + row); + value_type y0 = assignment.witness(component.W(block * 5 + 2), start_row_index + row); + value_type y1 = assignment.witness(component.W(block * 5 + 3), start_row_index + row); + value_type alpha = 0; + + value_type s = 2 * b * x - x; + + assignment.witness(component.W(block*5 + 4), start_row_index + row) = 0; // fake alpha + assignment.witness(component.W(block*5 + 5), start_row_index + row) = x*x; // new fake x + assignment.witness(component.W(block*5 + 6), start_row_index + row) = 0; // new fake b + assignment.witness(component.W(block*5 + 7), start_row_index + row) = 0; // new fake b = 0 so, it doesn't matter + // TODO(martun): check if s being a zero, which I handled on the next line is ok. Maybe it was not supposed to be 0? + assignment.witness(component.W(block*5 + 8), start_row_index + row) = ((alpha + s ) * y0 - (alpha - s) * y1 ) * (s == 0u ? 0u : (2 * s).inversed()); // new fake y + x = x * x; + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_flexible_colinear_checks &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_colinear_checks::input_type + &instance_input) { + + using component_type = plonk_flexible_colinear_checks; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector constraints; + const std::size_t witness_amount = component.witness_amount(); + for( std::size_t block = 0; block < (witness_amount-4)/5; block++ ) { + if( block == 0) continue; + var x = var(component.W(block*5), 0, true); + var b = var(component.W(block*5+1), 0, true); + var y0_var = var(component.W(block * 5+2), 0, true); + var y1_var = var(component.W(block * 5+3), 0, true); + var alpha = var(component.W(block * 5+4), 0, true); + + auto s = 2 * b * x - x; + auto left = ((alpha + s ) * y0_var - (alpha - s) * y1_var );// / (2 * s); + auto y1 = y1_var * b + (1-b) * y0_var; + + var x_next = var(component.W(block * 5 + 5), 0, true); + var b_next = var(component.W(block * 5 + 6), 0, true); + var y0_next = var(component.W(block * 5 + 7), 0, true); + var y1_next = var(component.W(block * 5 + 8), 0, true); + + auto right = (b_next * y0_next + (1 - b_next) * y1_next) * 2 * s; + + constraints.emplace_back(left - right); + } + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_flexible_colinear_checks &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_colinear_checks::input_type + &instance_input, + const std::size_t start_row_index) { +/* + using component_type = plonk_flexible_colinear_checks; + using var = typename component_type::var; + + BOOST_ASSERT(component.n == instance_input.arr.size()); + std::size_t n = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if(cur >= n) break; + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if(cur >= n) break; + bp.add_copy_constraint( + {instance_input.arr[cur].first, var(component.W(3*block), start_row_index + row, false)}); + bp.add_copy_constraint( + {instance_input.arr[cur].second, var(component.W(3*block+1), start_row_index + row, false)}); + } + }*/ + } + + template + typename plonk_flexible_colinear_checks::result_type generate_circuit( + const plonk_flexible_colinear_checks &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_colinear_checks::input_type + &instance_input, + const std::size_t start_row_index) { + assert(instance_input.ys.size() == instance_input.bs.size()*2); + assert(instance_input.alphas.size() + 1 == instance_input.bs.size()); + + using component_type = plonk_flexible_colinear_checks; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_colinear_checks_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/constant_pow.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/constant_pow.hpp new file mode 100644 index 000000000..cdee480a9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/constant_pow.hpp @@ -0,0 +1,323 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_constant_pow_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_constant_pow_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: array of pairs <, , ..., > + // Output: array + // Configuration is suboptimal: we do rows of the form + // a1, b1, o1, a2, b2, o2, ... + template + std::size_t integral_type_log2(typename BlueprintFieldType::integral_type pow){ + std::size_t result = 0; + typename BlueprintFieldType::integral_type a = 1; + while( a < pow ){ + a *= 2; + result++; + } + return result; + } + + template + std::vector integral_type_four_chunks(typename BlueprintFieldType::integral_type pow){ + std::vector result; + typename BlueprintFieldType::integral_type tmp = pow; + while( tmp > 0 ) { + result.push_back(std::size_t(tmp%4)); + tmp /= 4; + } + std::reverse(result.begin(), result.end()); + return result; + } + + template + class flexible_constant_pow; + + template + class flexible_constant_pow, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + typename BlueprintFieldType::integral_type pow; + std::size_t bits; + std::vector four_chunks; + std::size_t cells; + std::size_t row_capacity; + + class gate_manifest_type : public component_gate_manifest { + std::size_t _witness_amount; + typename BlueprintFieldType::integral_type _pow; + public: + gate_manifest_type(std::size_t witness_amount, typename BlueprintFieldType::integral_type pow) : + _witness_amount(witness_amount), _pow(pow) {}; + + std::uint32_t gates_amount() const override { + // related to pow + std::size_t one_row_cells = _witness_amount-2; + std::vector four_chunks = integral_type_four_chunks(_pow); + std::vector larger_chunks; + std::size_t cur = 0; + for(std::size_t i = 0; i < four_chunks.size(); i++){ + if ( i%one_row_cells == 0 ){ + larger_chunks.push_back(0); + cur++; + } + larger_chunks[cur-1] *= 4; + larger_chunks[cur-1] += four_chunks[i]; + } + std::vector unique_chunks; + std::unique_copy(larger_chunks.begin(), larger_chunks.end(), std::back_inserter(unique_chunks)); + return unique_chunks.size(); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + typename BlueprintFieldType::integral_type pow + ) { + // related to pow + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, pow)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(5, 300, 1)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + typename BlueprintFieldType::integral_type pow) { + + std::size_t bits = integral_type_log2(pow); + std::size_t cells = (bits+1)/2; + std::size_t one_row_cells = witness_amount-2; + return cells%one_row_cells == 0? cells/one_row_cells: cells/one_row_cells + 1; + } + + //constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), pow); + + struct input_type { + var x; + + std::vector> all_vars() { + std::vector> result; + result.push_back(x); + return result; + } + }; + + struct result_type { + var y; + + result_type(const flexible_constant_pow &component, std::size_t start_row_index) { + // TODO define output var + std::size_t witness_amount = component.witness_amount(); + std::size_t last_column_id = component.four_chunks.size()%(witness_amount - 2) + 1; + y = var(component.W(last_column_id), start_row_index + component.rows_amount-1, false); + } + + std::vector> all_vars() { + std::vector> result; + result.push_back(y); + return result; + } + }; + + template + explicit flexible_constant_pow(ContainerType witness, typename BlueprintFieldType::integral_type _pow) : + component_type(witness, {}, {}, get_manifest()), + pow(_pow), bits(integral_type_log2(_pow)), + four_chunks(integral_type_four_chunks(_pow)) { + assert(four_chunks.size() == (bits+1)/2); + } + + template + flexible_constant_pow(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, typename BlueprintFieldType::integral_type _pow) : + component_type(witness, constant, public_input, get_manifest()), + pow(_pow), bits(integral_type_log2(_pow)), + four_chunks(integral_type_four_chunks(_pow)) { + assert(four_chunks.size() == (bits+1)/2); + }; + + flexible_constant_pow( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + typename BlueprintFieldType::integral_type _pow) : + component_type(witnesses, constants, public_inputs, get_manifest()), + pow(_pow), bits(integral_type_log2(_pow)), + four_chunks(integral_type_four_chunks(_pow)) { + assert(four_chunks.size() == (bits+1)/2); + }; + }; + + template + using plonk_flexible_constant_pow = + flexible_constant_pow, BlueprintFieldType>; + + template + typename plonk_flexible_constant_pow::result_type generate_assignments( + const plonk_flexible_constant_pow &component, + assignment> + &assignment, + const typename plonk_flexible_constant_pow::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_constant_pow; + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + auto four_chunks = component.four_chunks; + auto x = var_value(assignment, instance_input.x); + + std::size_t cur = 0; + value_type cur_val = 1; + for (std::size_t row = 0; row < rows_amount; row++) { + assignment.witness(component.W(0), start_row_index+row) = x; + assignment.witness(component.W(1), start_row_index+row) = cur_val; + for (std::size_t cell = 2; cell < witness_amount; cell++ ) { + if (cur < four_chunks.size()) { + cur_val = cur_val.pow(4) * x.pow(four_chunks[cur]); + assignment.witness(component.W(cell), start_row_index + row) = cur_val; + cur++; + } else { + assignment.witness(component.W(cell), start_row_index + row) = 0; + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + std::vector + generate_gates( + const plonk_flexible_constant_pow &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_constant_pow::input_type + &instance_input) { + + using component_type = plonk_flexible_constant_pow; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector selectors; + const std::size_t witness_amount = component.witness_amount(); + auto four_chunks = component.four_chunks; + + std::size_t cur = 0; + for( std::size_t row = 0; row < component.rows_amount; row++ ) { + std::vector constraints; + var x_var = var(component.W(0),0,true); + + for (std::size_t cell = 2; cell < witness_amount; cell++ ) { + if (cur < four_chunks.size()) { + var cur_var = var(component.W(cell),0,true); + constraints.push_back(var(component.W(cell), 0, true) - var(component.W(cell-1),0,true).pow(4) * x_var.pow(four_chunks[cur])); + cur++; + } else { + break; + } + } + selectors.push_back(bp.add_gate(constraints)); + } + return selectors; + } + + template + void generate_copy_constraints( + const plonk_flexible_constant_pow &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_constant_pow::input_type + &instance_input, + const std::size_t start_row_index) { + using component_type = plonk_flexible_constant_pow; + using var = typename component_type::var; + + // Input variable + for( std::size_t row = 0; row < component.rows_amount; row++){ + bp.add_copy_constraint({instance_input.x, var(component.W(0), start_row_index+row, false)}); + if(row != 0){ + bp.add_copy_constraint({var(component.W(1), start_row_index+row, false), var(component.W(component.witness_amount()-1), start_row_index+row-1, false)}); + } + } + } + + template + typename plonk_flexible_constant_pow::result_type generate_circuit( + const plonk_flexible_constant_pow &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_constant_pow::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_constant_pow; + + auto selector_indices = generate_gates(component, bp, assignment, instance_input); + for( std::size_t i = 0; i < selector_indices.size(); i++){ + assignment.enable_selector(selector_indices[i], start_row_index+i); + } + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_constant_pow_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/multiplications.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/multiplications.hpp new file mode 100644 index 000000000..87d8b28a9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/multiplications.hpp @@ -0,0 +1,287 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_MULTIPLICATIONS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_MULTIPLICATIONS_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: array of pairs <, , ..., > + // Output: array + // Configuration is suboptimal: we do rows of the form + // a1, b1, o1, a2, b2, o2, ... + template + class flexible_multiplications; + + template + class flexible_multiplications, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t n; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return flexible_multiplications::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t n + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(5, 100500, 5)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t n) { + std::size_t cells = 3 * n; + std::size_t one_row_cells = (witness_amount / 3)*3; + return cells%one_row_cells == 0? cells/one_row_cells: cells/one_row_cells + 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), n); + + struct input_type { + std::vector> arr; // the array of pairs of elements + + std::vector> all_vars() { + std::vector> result; + for( std::size_t i = 0; i < arr.size(); i++ ){ + result.push_back(arr[i].first); + result.push_back(arr[i].second); + } + return result; + } + }; + + struct result_type { + std::vector output; // the array with possibly swapped elements + std::size_t n; + + result_type(const flexible_multiplications &component, std::size_t start_row_index) { + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + n = component.n; + + output.reserve(n); + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if( cur >= n ) break; + for(std::size_t block = 0; block < witness_amount / 3; block++, cur++ ){ + if( cur >= n ) break; + output.emplace_back( + var(component.W(block * 3 + 2), start_row_index + row, false) + ); + } + } + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(output.size()); + result.insert(result.end(), output.begin(), output.end()); + return result; + } + }; + + template + explicit flexible_multiplications(ContainerType witness, std::size_t _n) : + component_type(witness, {}, {}, get_manifest()), + n(_n) {}; + + template + flexible_multiplications(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t _n) : + component_type(witness, constant, public_input, get_manifest()), + n(_n) {}; + + flexible_multiplications( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _n) : + component_type(witnesses, constants, public_inputs, get_manifest()), + n(_n) {}; + }; + + template + using plonk_flexible_multiplications = + flexible_multiplications, + BlueprintFieldType>; + + template + typename plonk_flexible_multiplications::result_type generate_assignments( + const plonk_flexible_multiplications &component, + assignment> + &assignment, + const typename plonk_flexible_multiplications::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_multiplications; + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t n = instance_input.arr.size(); + BOOST_ASSERT(component.n == instance_input.arr.size()); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if (cur < n) { + value_type a_val = var_value(assignment, instance_input.arr[cur].first); + value_type b_val = var_value(assignment, instance_input.arr[cur].second); + assignment.witness(component.W(block*3), start_row_index + row) = a_val; + assignment.witness(component.W(block*3 + 1), start_row_index + row) = b_val; + assignment.witness(component.W(block*3 + 2), start_row_index + row) = a_val * b_val; + } else { + assignment.witness(component.W(block*3), start_row_index + row) = 0; + assignment.witness(component.W(block*3 + 1), start_row_index + row) = 0; + assignment.witness(component.W(block*3 + 2), start_row_index + row) = 0; + } + } + for( std::size_t i = (witness_amount/3)*3; i + std::size_t generate_gates( + const plonk_flexible_multiplications &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_multiplications::input_type + &instance_input) { + + using component_type = plonk_flexible_multiplications; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + BOOST_ASSERT(component.n == instance_input.arr.size()); + + std::vector constraints; + constraints.reserve(component.n); + var t = var(component.W(0), 0, true); + const std::size_t witness_amount = component.witness_amount(); + for( std::size_t block = 0; block < witness_amount/3; block++ ) { + var input_a_var = var(component.W(block * 3), 0, true), + input_b_var = var(component.W(block * 3 + 1), 0, true), + output_var = var(component.W(block * 3 + 2), 0, true); + + constraints.emplace_back(input_a_var * input_b_var - output_var); + } + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_flexible_multiplications &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_multiplications::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_multiplications; + using var = typename component_type::var; + + BOOST_ASSERT(component.n == instance_input.arr.size()); + std::size_t n = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if(cur >= n) break; + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if(cur >= n) break; + bp.add_copy_constraint( + {instance_input.arr[cur].first, var(component.W(3*block), start_row_index + row, false)}); + bp.add_copy_constraint( + {instance_input.arr[cur].second, var(component.W(3*block+1), start_row_index + row, false)}); + } + } + } + + template + typename plonk_flexible_multiplications::result_type generate_circuit( + const plonk_flexible_multiplications &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_multiplications::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_multiplications; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_MULTIPLICATIONS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/poseidon.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/poseidon.hpp new file mode 100644 index 000000000..ea05f3a7f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/poseidon.hpp @@ -0,0 +1,341 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_POSEIDON_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_POSEIDON_HPP + +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: t, array + // Output: if t == 0, if t == 1 + // Does NOT check that t is really a bit. + // Configuration is suboptimal: we do rows of the form + // t, a1, b1, o11, o12, a2, b2, o21, o22, ... + // We could reuse t among multiple different rows for a better configuration, but that would be + // more complex than what we can quickly implement now. + template + class flexible_poseidon; + + template + class flexible_poseidon, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using FieldType = BlueprintFieldType; + + constexpr static const std::uint32_t state_size = 3; + constexpr static const std::uint32_t rounds_amount = 55; + constexpr static const std::size_t sbox_alpha = 7; + + constexpr static const std::array, state_size> + mds = detail::poseidon_constants::mds; + constexpr static const std::array, rounds_amount> + round_constant = detail::poseidon_constants::round_constant; + + constexpr static const std::size_t rate = 2; + constexpr static const std::size_t constraints_amount = rounds_amount * state_size; + constexpr static const std::size_t cells_amount = (rounds_amount + 1) * state_size; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t witness_amount; + public: + gate_manifest_type(std::size_t _witness_amount) : + witness_amount(_witness_amount) {}; + + bool operator<(gate_manifest_type const& other) const { + return witness_amount < other.witness_amount; + } + + std::uint32_t gates_amount() const override { + std::size_t blocks = flexible_poseidon::rounds_amount + 1; + std::size_t row_capacity = witness_amount/flexible_poseidon::state_size; + std::cout << "Poseidon gates amount: " << ((blocks-1)%row_capacity == 0? (blocks-1)/row_capacity : (blocks-1)/row_capacity + 1) << std::endl; + return (blocks-1)%row_capacity == 0? (blocks-1)/row_capacity : (blocks-1)/row_capacity + 1; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(3, 168, 3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount + ) { + std::size_t blocks = flexible_poseidon::rounds_amount + 1; + std::size_t row_capacity = witness_amount/flexible_poseidon::state_size; + return blocks%row_capacity == 0? blocks/row_capacity : blocks/row_capacity + 1; + } + + //constexpr static const std::size_t gates_amount = 1; + + struct input_type { + std::array input_state; + + std::vector> all_vars() { + std::vector> result; + result.insert(result.end(), input_state.begin(), input_state.end()); + return result; + } + }; + + struct result_type { + std::array output_state = {var(0, 0, false), var(0, 0, false), var(0, 0, false)}; + + result_type(const flexible_poseidon &component, std::uint32_t start_row_index) { + std::size_t blocks = rounds_amount + 1; + std::size_t row_capacity = component.witness_amount()/state_size; + std::size_t last_column_id = blocks % row_capacity == 0? row_capacity * state_size: (blocks %row_capacity) * state_size; + last_column_id = last_column_id - 1; + + output_state = { + var(component.W(last_column_id - 2), start_row_index + component.rows_amount - 1, false), + var(component.W(last_column_id - 1), start_row_index + component.rows_amount - 1, false), + var(component.W(last_column_id), start_row_index + component.rows_amount - 1, false) + }; + } + + std::vector> all_vars() { + std::vector> result; + result.insert(result.end(), output_state.begin(), output_state.end()); + return result; + } + }; + + template + explicit flexible_poseidon(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) + {}; + + template + flexible_poseidon(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : component_type(witness, constant, public_input, get_manifest()) + {}; + +/* flexible_poseidon( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs + ) : component_type(witnesses, constants, public_inputs, get_manifest()) + {};*/ + }; + + template + using plonk_flexible_poseidon = + flexible_poseidon, + BlueprintFieldType>; + + template + typename plonk_flexible_poseidon::result_type generate_assignments( + const plonk_flexible_poseidon &component, + assignment> + &assignment, + const typename plonk_flexible_poseidon::input_type + &instance_input, + const std::uint32_t start_row_index + ) { + using component_type = plonk_flexible_poseidon; + + constexpr static const std::uint32_t state_size = component_type::state_size; + + std::array state = { + var_value(assignment, instance_input.input_state[0]), + var_value(assignment, instance_input.input_state[1]), + var_value(assignment, instance_input.input_state[2])}; + std::array next_state; + + assignment.witness(component.W(0), start_row_index) = state[0]; + assignment.witness(component.W(1), start_row_index) = state[1]; + assignment.witness(component.W(2), start_row_index) = state[2]; + + static_assert(state_size == 3); + std::size_t row = 0; + std::size_t column = 0; + + for (std::size_t i = 0; i < component.rounds_amount; i++) { + for (std::size_t j = 0; j < state_size; j++) { + next_state[j] = state[0].pow(component_type::sbox_alpha) * component_type::mds[j][0] + + state[1].pow(component_type::sbox_alpha) * component_type::mds[j][1] + + state[2].pow(component_type::sbox_alpha) * component_type::mds[j][2] + + component_type::round_constant[i][j]; + } + column += 3; + if( column + 3 > component.witness_amount() ){ + row++; + column = 0; + } + assignment.witness(component.W(column), start_row_index + row) = next_state[0]; + assignment.witness(component.W(column+1), start_row_index + row) = next_state[1]; + assignment.witness(component.W(column+2), start_row_index + row) = next_state[2]; + state = next_state; + } + return typename component_type::result_type(component, start_row_index); + } + + template + std::vector + generate_gates( + const plonk_flexible_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_poseidon::input_type + &instance_input) { + + using component_type = plonk_flexible_poseidon; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector selectors; + + std::size_t start_column = 0; + std::vector> constraints; + std::size_t gate_id = 0; + for (std::size_t round = 0; round < component_type::rounds_amount; round++) { + if(start_column == 0) constraints.push_back(std::vector()); + var input_var1 = var(component.W(start_column), 0); + var input_var2 = var(component.W(start_column+1), 0); + var input_var3 = var(component.W(start_column+2), 0); + var output_var1; + var output_var2; + var output_var3; + if( start_column + 5 < component.witness_amount() ){ + output_var1 = var(component.W(start_column+3), 0); + output_var2 = var(component.W(start_column+4), 0); + output_var3 = var(component.W(start_column+5), 0); + } else { + output_var1 = var(component.W(0), 1); + output_var2 = var(component.W(1), 1); + output_var3 = var(component.W(2), 1); + } + auto constraint1 = + output_var1 - + (input_var1.pow(component_type::sbox_alpha) * component_type::mds[0][0] + + input_var2.pow(component_type::sbox_alpha) * component_type::mds[0][1] + + input_var3.pow(component_type::sbox_alpha) * component_type::mds[0][2] + + component_type::round_constant[round][0]); + auto constraint2 = + output_var2 - + (input_var1.pow(component_type::sbox_alpha) * component_type::mds[1][0] + + input_var2.pow(component_type::sbox_alpha) * component_type::mds[1][1] + + input_var3.pow(component_type::sbox_alpha) * component_type::mds[1][2] + + component_type::round_constant[round][1]); + auto constraint3 = + output_var3 - + (input_var1.pow(component_type::sbox_alpha) * component_type::mds[2][0] + + input_var2.pow(component_type::sbox_alpha) * component_type::mds[2][1] + + input_var3.pow(component_type::sbox_alpha) * component_type::mds[2][2] + + component_type::round_constant[round][2]); + constraints[gate_id].push_back(constraint1); + constraints[gate_id].push_back(constraint2); + constraints[gate_id].push_back(constraint3); + if( start_column + 5 > component.witness_amount() ){ + selectors.push_back(bp.add_gate(constraints[gate_id])); + gate_id++; + start_column = 0; + } else { + start_column+= 3; + } + } + if(selectors.size() != constraints.size()){ + selectors.push_back(bp.add_gate(constraints[gate_id])); + } + return selectors; + } + + template + void generate_copy_constraints( + const plonk_flexible_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_poseidon::input_type + &instance_input, + const std::size_t start_row_index) { + + // CRITICAL: these copy constraints might not be sufficient, but are definitely required. + // I've added copy constraints for the inputs, but internal ones might be missing + // Proceed with care + using var = typename plonk_flexible_poseidon::var; + for (std::size_t i = 0; i < 3; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.input_state[i]}); + } + } + + template + typename plonk_flexible_poseidon::result_type generate_circuit( + const plonk_flexible_poseidon &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_poseidon::input_type + &instance_input, + const std::size_t start_row_index) { + using component_type = plonk_flexible_poseidon; + + auto selector_indices = generate_gates(component, bp, assignment, instance_input); + for( std::size_t i = 0; i < selector_indices.size(); i++){ + assignment.enable_selector(i, start_row_index+i); + } + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_POSEIDON_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/pow_factor.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/pow_factor.hpp new file mode 100644 index 000000000..a9e69d94d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/pow_factor.hpp @@ -0,0 +1,357 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + class pow_factor; + + // Efficiently calculating a polynomial of a single variable + // layout is made of repeated blocks like this (shown for witness_amount = 18) + // -------------------------------------------------------------------------------- + // |theta|c_14|c_13|c_12|c_11|c_10|c_9|c_8|c_7|r_0|c_6|c_5|c_4|c_3|c_2|c_1|c_0|r_1| + // -------------------------------------------------------------------------------- + // Calculating polynomials of 7-th degree at a time + // Carrying of the results between rows is done via copy constrainting into the first variable (after theta) + // of the next row; the amount of coefficients is padded at the beginning with zeroes to the + template + class pow_factor, + BlueprintFieldType> + : public plonk_component { + public: + + static std::size_t row_capacity(std::size_t witness_amount) { + return witness_amount == 10 ? 8 : 8 + (witness_amount - 10) / 8 * 7; + } + + static std::size_t total_rows_amount( + std::size_t witness_amount, std::size_t curr_vars, std::size_t rows = 0) { + const std::size_t row_capacity = pow_factor::row_capacity(witness_amount); + if (curr_vars <= row_capacity) { + return rows + 1; + } + rows += curr_vars / row_capacity; + curr_vars = curr_vars % row_capacity + curr_vars / row_capacity; + return total_rows_amount(witness_amount, curr_vars, rows); + } + + static std::size_t calculate_padding(std::size_t witness_amount, std::size_t power) { + const std::size_t row_capacity = pow_factor::row_capacity(witness_amount); + const std::size_t total_rows = total_rows_amount(witness_amount, power + 1); + const std::size_t total_capacity = total_rows * row_capacity; + const std::size_t bonus_vars = total_rows - 1; + return total_capacity - bonus_vars - (power + 1); + } + + static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t power) { + return total_rows_amount(witness_amount, power + 1); + } + + std::size_t power; + + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t witness_amount; + + public: + gate_manifest_type(std::size_t witness_amount_) : + witness_amount((witness_amount_ - 10) / 8){}; + + bool operator<(gate_manifest_type const& other) const { + return witness_amount < other.witness_amount || + (witness_amount == other.witness_amount); + } + + std::uint32_t gates_amount() const override { + return pow_factor::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t power) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest( + std::size_t power) { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(10, 10 + power / 6 + 1, 8)), + true + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t power) { + return rows_amount_internal(witness_amount, power); + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), power); + const std::string component_name = "fri array swap component"; + + struct input_type { + var theta; + std::vector coefficients; // coefficients; highest power first + + std::vector> all_vars() { + std::vector> result; + result.reserve(1 + coefficients.size()); + result.push_back(theta); + result.insert(result.end(), coefficients.begin(), coefficients.end()); + return result; + } + }; + + struct result_type { + var output; + + result_type(const pow_factor &component, std::size_t start_row_index) { + const std::size_t end_row_index = start_row_index + component.rows_amount - 1; + output = var(component.W(component.witness_amount() - 1), end_row_index, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit pow_factor(ContainerType witness, std::size_t power_) : + component_type(witness, {}, {}, get_manifest(power_)), + power(power_) {}; + + template + pow_factor(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t power_) : + component_type(witness, constant, public_input, get_manifest(power_)), + power(power_) {}; + + pow_factor( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t power_) : + component_type(witnesses, constants, public_inputs, get_manifest(power_)), + power(power_) {}; + + inline std::vector pad_input(const std::vector &input, + std::size_t start_row_index, std::size_t padding) const { + std::vector padded_arr; + padded_arr.reserve(input.size() + padding); + var zero = var(this->C(0), start_row_index, false, var::column_type::constant); + for (std::size_t i = 0; i < padding; i++) { + padded_arr.push_back(zero); + } + padded_arr.insert(padded_arr.end(), input.begin(), input.end()); + BOOST_ASSERT(padded_arr.size() == input.size() + padding); + return padded_arr; + } + }; + + template + using plonk_pow_factor = + pow_factor, + BlueprintFieldType>; + + template + typename plonk_pow_factor::result_type generate_assignments( + const plonk_pow_factor &component, + assignment> + &assignment, + const typename plonk_pow_factor::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_pow_factor; + using value_type = typename BlueprintFieldType::value_type; + using var = typename component_type::var; + + BOOST_ASSERT(component.power + 1 == instance_input.coefficients.size()); + // copy this here because we use the zero constant value to pad the input + generate_assignments_constant(component, assignment, start_row_index); + const std::size_t padding = component_type::calculate_padding(component.witness_amount(), component.power); + std::vector padded_arr = component.pad_input(instance_input.coefficients, start_row_index, padding); + std::size_t row = start_row_index; + std::size_t var_index = 0; + value_type poly_value = var_value(assignment, padded_arr[var_index++]); + value_type theta = var_value(assignment, instance_input.theta); + while (var_index < padded_arr.size()) { + assignment.witness(component.W(0), row) = theta; + assignment.witness(component.W(1), row) = poly_value; + for (std::size_t i = 1; i < component.witness_amount() - 1; i += 8) { + for (std::size_t j = i + 1; j < i + 8; j++) { + value_type coeff_value = var_value(assignment, padded_arr[var_index++]); + assignment.witness(component.W(j), row) = coeff_value; + poly_value = poly_value * theta + coeff_value; + } + assignment.witness(component.W(i + 8), row) = poly_value; + } + row++; + } + BOOST_ASSERT(row == start_row_index + component.rows_amount); + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_pow_factor &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_pow_factor::input_type + &instance_input) { + + using component_type = plonk_pow_factor; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector constraints; + var theta = var(component.W(0), 0); + for (std::size_t start_index = 1; start_index < component.witness_amount() - 1; start_index += 8) { + std::array coefficients; + for (std::size_t i = start_index; i < start_index + 8; i++) { + coefficients[i - start_index] = var(component.W(i), 0, true, var::column_type::witness); + } + constraint_type new_constraint = coefficients[0]; + for (std::size_t i = 1; i < 8; i++) { + new_constraint = new_constraint * theta + coefficients[i]; + } + new_constraint -= var(component.W(start_index + 8), 0, true, var::column_type::witness); + constraints.push_back(new_constraint); + } + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_pow_factor &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_pow_factor::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_pow_factor; + using var = typename component_type::var; + + const std::size_t padding = component_type::calculate_padding(component.witness_amount(), component.power); + std::vector padded_arr = component.pad_input(instance_input.coefficients, start_row_index, padding); + var zero = var(component.C(0), start_row_index, false, var::column_type::constant); + std::size_t slot_index = 1; + std::size_t var_index = 0; + std::size_t row = start_row_index; + bp.add_copy_constraint( + {instance_input.theta, + var(component.W(0), start_row_index, false, var::column_type::witness)}); + while (var_index < padded_arr.size()) { + if ((slot_index - 9) % 8 == 0 && slot_index >= 9) { + slot_index++; + } + if (slot_index >= component.witness_amount() - 1) { + slot_index = 2; + if (row + 1 < start_row_index + component.rows_amount) { + bp.add_copy_constraint( + {var(component.W(component.witness_amount() - 1), row, false, var::column_type::witness), + var(component.W(1), row + 1, false, var::column_type::witness)}); + bp.add_copy_constraint( + {instance_input.theta, + var(component.W(0), row + 1, false, var::column_type::witness)}); + } + row++; + } + if (var_index < padding) { + bp.add_copy_constraint( + {zero, + var(component.W(slot_index), row, false, var::column_type::witness)}); + } else { + bp.add_copy_constraint( + {padded_arr[var_index], + var(component.W(slot_index), row, false, var::column_type::witness)}); + } + slot_index++; + var_index++; + } + row++; + BOOST_ASSERT(row == start_row_index + component.rows_amount); + } + + template + void generate_assignments_constant( + const plonk_pow_factor &component, + assignment> + &assignment, + const std::size_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = BlueprintFieldType::value_type::zero(); + } + + template + typename plonk_pow_factor::result_type generate_circuit( + const plonk_pow_factor &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_pow_factor::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_pow_factor; + + BOOST_ASSERT(component.power + 1 == instance_input.coefficients.size()); + + generate_assignments_constant(component, assignment, start_row_index); + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/swap.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/swap.hpp new file mode 100644 index 000000000..6df5bba59 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/swap.hpp @@ -0,0 +1,258 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_swap_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_swap_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: array of triples < + // Output: array of pairs where if b_i == 0 => , else + // b1, c1, d1, output1_0, output1_1, b2, c2, d2, output2_0, output2_1, ... + template + class flexible_swap; + + template + class flexible_swap, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return flexible_swap::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(5)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount + ) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + std::array inp; // the array of pairs of elements + + std::vector> all_vars() { + std::vector> result; + result.push_back(inp[0]); + result.push_back(inp[1]); + result.push_back(inp[2]); + return result; + } + }; + + struct result_type { + std::array output; // the array with possibly swapped elements + + result_type(const flexible_swap &component, std::size_t start_row_index) { + output[0] = var(component.W(3), start_row_index, false); + output[1] = var(component.W(4), start_row_index, false); + } + + std::vector> all_vars() { + std::vector> result; + result.push_back(output[0]); + result.push_back(output[1]); + return result; + } + }; + + template + explicit flexible_swap(ContainerType witness, std::size_t _n) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + flexible_swap(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()){}; + + flexible_swap( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) + {}; + }; + + template + using plonk_flexible_swap = + flexible_swap, + BlueprintFieldType>; + + template + typename plonk_flexible_swap::result_type generate_assignments( + const plonk_flexible_swap &component, + assignment> + &assignment, + const typename plonk_flexible_swap::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_swap; + using value_type = typename BlueprintFieldType::value_type; + auto b = var_value(assignment, instance_input.inp[0]); + auto c = var_value(assignment, instance_input.inp[1]); + auto d = var_value(assignment, instance_input.inp[2]); + + assignment.witness(component.W(0), start_row_index) = b; + assignment.witness(component.W(1), start_row_index) = c; + assignment.witness(component.W(2), start_row_index) = d; + assignment.witness(component.W(3), start_row_index) = b == 0? c: d; + assignment.witness(component.W(4), start_row_index) = b == 0? d: c; + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_flexible_swap::result_type generate_empty_assignments( + const plonk_flexible_swap &component, + assignment> + &assignment, + const typename plonk_flexible_swap::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_swap; + using value_type = typename BlueprintFieldType::value_type; + auto b = var_value(assignment, instance_input.inp[0]); + auto c = var_value(assignment, instance_input.inp[1]); + auto d = var_value(assignment, instance_input.inp[2]); + + assignment.witness(component.W(0), start_row_index) = b; + assignment.witness(component.W(1), start_row_index) = c; + assignment.witness(component.W(2), start_row_index) = d; + assignment.witness(component.W(3), start_row_index) = b == 0? c: d; + assignment.witness(component.W(4), start_row_index) = b == 0? d: c; + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_flexible_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_swap::input_type + &instance_input + ) { + using component_type = plonk_flexible_swap; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::vector constraints; + + var input_b_var = var(component.W(0), 0, true), + input_c_var = var(component.W(1), 0, true), + input_d_var = var(component.W(2), 0, true), + output0_var = var(component.W(3), 0, true), + output1_var = var(component.W(4), 0, true); + + constraints.emplace_back(input_b_var * (input_b_var - 1)); + constraints.emplace_back(output0_var - ((1-input_b_var) * input_c_var + input_b_var * input_d_var)); + constraints.emplace_back(output1_var - ((1-input_b_var) * input_d_var + input_b_var * input_c_var)); + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_flexible_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_swap::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_swap; + using var = typename component_type::var; + + bp.add_copy_constraint( + {std::get<0>(instance_input.inp), var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint( + {std::get<1>(instance_input.inp), var(component.W(1), start_row_index, false)}); + bp.add_copy_constraint( + {std::get<2>(instance_input.inp), var(component.W(2), start_row_index, false)}); + } + + template + typename plonk_flexible_swap::result_type generate_circuit( + const plonk_flexible_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_swap::input_type + &instance_input, + const std::size_t start_row_index + ) { + using component_type = plonk_flexible_swap; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_swap_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/x_index.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/x_index.hpp new file mode 100644 index 000000000..095df0e5e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/flexible/x_index.hpp @@ -0,0 +1,309 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_x_index_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_x_index_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: field value x, bit-array + // Constant: omega + // Output: bit b0 + // Configuration: + // 1, b_0, w_0^2 * [(1-b_0)+\omega * b_0], b1, w_1^2 * [(1-b_1)+\omega * b_1] ... + template + class flexible_x_index; + + template + class flexible_x_index, BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using value_type = typename BlueprintFieldType::value_type; + + std::size_t n; + value_type omega; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return flexible_x_index::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t n + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(3, 300, 3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t n) { + std::size_t cells = 2 * n; + std::size_t one_row_cells = ((witness_amount-1) / 2)*2; + return cells%one_row_cells == 0? cells/one_row_cells: cells/one_row_cells + 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), n); + + struct input_type { + var x; + std::vector b; // the array of pairs of elements + + std::vector> all_vars() { + std::vector> result; + result.push_back(x); + for( std::size_t i = 0; i < b.size(); i++ ){ + result.push_back(b[i]); + } + return result; + } + }; + + struct result_type { + var b0; + + result_type(const flexible_x_index &component, std::size_t start_row_index) { +// Think carefully! + b0 = var(component.W(1), start_row_index, false); + } + + std::vector> all_vars() { + std::vector> result; + result.push_back(b0); + return result; + } + }; + + template + explicit flexible_x_index(ContainerType witness, std::size_t _n, value_type _omega) : + component_type(witness, {}, {}, get_manifest()), + n(_n), omega(_omega) {}; + + template + flexible_x_index(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t _n, value_type _omega) : + component_type(witness, constant, public_input, get_manifest()), + n(_n), omega(_omega) {}; + + flexible_x_index( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _n, value_type _omega) : + component_type(witnesses, constants, public_inputs, get_manifest()), + n(_n), omega(_omega) {}; + }; + + template + using plonk_flexible_x_index = + flexible_x_index, + BlueprintFieldType>; + + template + typename plonk_flexible_x_index::result_type generate_assignments( + const plonk_flexible_x_index &component, + assignment> + &assignment, + const typename plonk_flexible_x_index::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_flexible_x_index; + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t n = instance_input.b.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + value_type x = var_value(assignment, instance_input.x); + value_type b0; + value_type tmp = 1; + value_type omega = component.omega; + value_type x_index = 0; + for( std::size_t i = 0; i < n; i++){ + value_type b = var_value(assignment, instance_input.b[n - 1 -i]); + tmp = tmp * tmp * (b + omega *(1-b)); + x_index *= 2; + x_index += (1-b); + } + BOOST_ASSERT(tmp == x || tmp == -x); + if( tmp == x ) b0 = 1; else b0 =0; + + std::vector all_ordered_bits; + all_ordered_bits.push_back(b0); + for( std::size_t i = 0; i < n; i++){ + all_ordered_bits.push_back(var_value(assignment, instance_input.b[n - 1 -i])); + } + + tmp = 1; + for (std::size_t row = 0; row < rows_amount; row++) { + assignment.constant(component.C(0), start_row_index + row) = omega; + assignment.witness(component.W(0), start_row_index + row) = tmp; + for (std::size_t block = 0; block < (witness_amount-1)/2; block++, cur++) { + if (cur < all_ordered_bits.size()) { + assignment.witness(component.W(block*2 + 1), start_row_index + row) = all_ordered_bits[cur]; + tmp = tmp * tmp * (all_ordered_bits[cur] + (1-all_ordered_bits[cur]) * omega); + assignment.witness(component.W(block*2 + 2), start_row_index + row) = tmp; + } else { + assignment.witness(component.W(block*2 + 1), start_row_index + row) = 1; + tmp = tmp * tmp; + assignment.witness(component.W(block*2 + 2), start_row_index + row) = tmp; + cur ++; + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_flexible_x_index &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_x_index::input_type + &instance_input) { + + using component_type = plonk_flexible_x_index; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + BOOST_ASSERT(component.n == instance_input.b.size()); + + std::vector constraints; + var omega = var(component.C(0), 0, true, var::column_type::constant); + for( std::size_t block = 0; block < (component.witness_amount()-1)/2; block++){ + var prev = var(component.W(block*2), 0, true); + var b = var(component.W(block*2 + 1), 0, true); + var next = var(component.W(block*2 + 2), 0, true); + constraints.push_back( next - prev * prev *(b + omega - omega * b)); + } + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_flexible_x_index &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_x_index::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_x_index; + using var = typename component_type::var; + + std::size_t cur = 0; + for( std::size_t row = 0; row < component.rows_amount; row++){ + if( row != 0) bp.add_copy_constraint({ + var(component.W((component.witness_amount() - 1) / 2 * 2), start_row_index+row - 1, false), + var(component.W(0), start_row_index+row, false), + }); + for( std::size_t block = 0; block < (component.witness_amount() - 1) / 2; block++ ){ + if( cur != 0){ + bp.add_copy_constraint({instance_input.b[component.n - cur], var(component.W(block * 2 + 1), start_row_index + row, false)}); + } + cur++; + if( cur == component.n + 1 ){ + bp.add_copy_constraint({instance_input.x, var(component.W(block * 2 + 2), start_row_index + row, false)}); + break; + } + } + } + +/* + BOOST_ASSERT(component.n == instance_input.arr.size()); + std::size_t n = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + std::size_t cur = 0; + for (std::size_t row = 0; row < rows_amount; row++) { + if(cur >= n) break; + for (std::size_t block = 0; block < witness_amount/3; block++, cur++) { + if(cur >= n) break; + bp.add_copy_constraint( + {instance_input.arr[cur].first, var(component.W(3*block), start_row_index + row, false)}); + bp.add_copy_constraint( + {instance_input.arr[cur].second, var(component.W(3*block+1), start_row_index + row, false)}); + } + }*/ + } + + template + typename plonk_flexible_x_index::result_type generate_circuit( + const plonk_flexible_x_index &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_flexible_x_index::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_flexible_x_index; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FLEXIBLE_x_index_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/base_details/batch_dlog_accumulator_check_base.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/base_details/batch_dlog_accumulator_check_base.hpp new file mode 100644 index 000000000..a3c9c56e7 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/base_details/batch_dlog_accumulator_check_base.hpp @@ -0,0 +1,221 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_BASE_DETAILS_BATCH_DGLOG_ACCUMULATOR_CHECK_BASE_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_BASE_DETAILS_BATCH_DGLOG_ACCUMULATOR_CHECK_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // https://github.com/MinaProtocol/mina/blob/f01d3925a273ded939a80e1de9afcd9f913a7c17/src/lib/crypto/kimchi_bindings/stubs/src/urs_utils.rs#L10 + template + class batch_dlog_accumulator_check_base; + + template + class batch_dlog_accumulator_check_base, + CurveType, KimchiParamsType, W0, + W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + using sub_component = zk::components::subtraction; + + using kimchi_constants = zk::components::kimchi_inner_constants; + using KimchiCommitmentParamsType = typename KimchiParamsType::commitment_params_type; + + constexpr static const std::size_t padding_size = kimchi_constants::srs_padding_size(); + + constexpr static const std::size_t final_msm_size = kimchi_constants::final_msm_size(BatchSize); + + using msm_component = + zk::components::element_g1_multi_scalar_mul; + + using to_group_component = zk::components::to_group; + + using var_ec_point = typename zk::components::var_ec_point; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + + using batch_proof_type = typename zk::components::batch_evaluation_proof_base< + BlueprintFieldType, ArithmetizationType, KimchiParamsType, KimchiCommitmentParamsType>; + + using verifier_index_type = kimchi_verifier_index_base; + + using proof_binding = + typename zk::components::binding; + + using transcript_type = kimchi_transcript_fq; + + public: + constexpr static const std::size_t rows_amount = msm_component::rows_amount; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector comms; + verifier_index_type verifier_index; + typename proof_binding::template fr_data fr_output; + }; + + struct result_type { + + result_type(std::size_t start_row_index) { + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + var zero(0, start_row_index, false, var::column_type::constant); + std::array bases; + std::size_t bases_idx = 0; + + var_ec_point point_at_infinity = {zero, zero}; + + bases[bases_idx++] = params.verifier_index.H; + + for (std::size_t i = 0; i < KimchiCommitmentParamsType::srs_len; i++) { + bases[bases_idx++] = params.verifier_index.G[i]; + } + for (std::size_t i = 0; i < padding_size; i++) { + bases[bases_idx++] = point_at_infinity; + } + + for (std::size_t i = 0; i < params.comms.size(); i++) { + basses[bases_idx++] = params.comms[i]; + } + + assert(bases_idx == final_msm_size); + + auto res = + msm_component::generate_assignments(assignment, {params.fr_output.scalars, bases}, row) + .output; + row += msm_component::rows_amount; + + assert(row == start_row_index + rows_amount); + return result_type(start_row_index); + } + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(bp, assignment, params, start_row_index); + + std::size_t row = start_row_index; + var two_pow_255(0, row, false, var::column_type::constant); + var zero(0, start_row_index + 1, false, var::column_type::constant); + + var_ec_point point_at_infinity = {zero, zero}; + + std::array bases; + std::size_t bases_idx = 0; + + bases[bases_idx++] = params.verifier_index.H; + for (std::size_t i = 0; i < KimchiCommitmentParamsType::srs_len; i++) { + bases[bases_idx++] = params.verifier_index.G[i]; + } + for (std::size_t i = 0; i < padding_size; i++) { + bases[bases_idx++] = point_at_infinity; + } + + for (std::size_t i = 0; i < params.comms.size(); i++) { + basses[bases_idx++] = params.comms[i]; + } + + assert(bases_idx == final_msm_size); + + auto res = + msm_component::generate_circuit(bp, assignment, {params.fr_output.scalars, bases}, row) + .output; + row += msm_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void + generate_gates(blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = 0; + } + }; + + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_BASE_DETAILS_BATCH_DGLOG_ACCUMULATOR_CHECK_BASE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_base_field.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_base_field.hpp new file mode 100644 index 000000000..14984f482 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_base_field.hpp @@ -0,0 +1,326 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the BATCH_VERIFY_BASE_FIELD component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BATCH_VERIFY_BASE_FIELD_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BATCH_VERIFY_BASE_FIELD_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + // batched polynomial commitment verification (base field) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L610 + // Input: list of batch evaluation proofs + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L881-L888 + // Output: - + template + class batch_verify_base_field; + + template + class batch_verify_base_field, + CurveType, + KimchiParamsType, + KimchiCommitmentParamsType, + BatchSize, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14 > { + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + + using var = crypto3::zk::snark::plonk_variable; + using sub_component = subtraction; + + using kimchi_constants = kimchi_inner_constants; + + constexpr static const std::size_t padding_size = kimchi_constants::srs_padding_size(); + + constexpr static const std::size_t final_msm_size = kimchi_constants::final_msm_size(BatchSize); + + using msm_component = element_g1_multi_scalar_mul< ArithmetizationType, CurveType, final_msm_size, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14>; + + using to_group_component = to_group; + + using var_ec_point = typename var_ec_point; + + using opening_proof_type = typename + kimchi_opening_proof_base; + + using batch_proof_type = typename + batch_evaluation_proof_base; + + using verifier_index_type = kimchi_verifier_index_base; + + using proof_binding = typename binding; + + using transcript_type = kimchi_transcript_fq; + + constexpr static const std::size_t selector_seed = 0xff91; + + public: + constexpr static const std::size_t rows_amount = transcript_type::absorb_fr_rows + + transcript_type::challenge_rows + + 1 + msm_component::rows_amount; + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array proofs; + verifier_index_type verifier_index; + typename proof_binding::template fr_data fr_output; + }; + + struct result_type { + + result_type(std::size_t start_row_index) { + } + }; + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + var two_pow_255(0, start_row_index, false, var::column_type::constant); + var zero(0, start_row_index + 1, false, var::column_type::constant); + std::array bases; + std::size_t bases_idx = 0; + + var_ec_point point_at_infinity = {zero, zero}; + + bases[bases_idx++] = params.verifier_index.H; + + for(std::size_t i = 0; i < KimchiCommitmentParamsType::srs_len; i++) { + bases[bases_idx++] = params.verifier_index.G[i]; + } + for (std::size_t i = 0; i < padding_size; i++) { + bases[bases_idx++] = point_at_infinity; + } + + for (std::size_t i = 0; i < params.proofs.size(); i++) { + transcript_type transcript = params.proofs[i].transcript; + transcript.absorb_fr_assignment(assignment, {{params.fr_output.cip_shifted[i]}}, row); + row += transcript_type::absorb_fr_rows; + var t = transcript.challenge_fq_assignment(assignment, row); + row += transcript_type::challenge_rows; + + //var_ec_point U = to_group_component:: + + //U = transcript.squeeze.to_group() + typename CurveType::template g1_type::value_type U_value = + algebra::random_element>(); + assignment.witness(W0)[row] = U_value.X; + assignment.witness(W1)[row] = U_value.Y; + var_ec_point U = {var(0, row), var(1, row)}; + row++; + + //params.proofs[i].transcript.absorb_assignment(assignment, params.proofs[i].o.delta.x, row); + //params.proofs[i].transcript.absorb_assignment(assignment, params.proofs[i].o.delta.y, row); + bases[bases_idx++] = params.proofs[i].opening_proof.G; + bases[bases_idx++] = U; + for (std::size_t j = 0 ; j < params.proofs[i].opening_proof.L.size(); j++) { + bases[bases_idx++] = params.proofs[i].opening_proof.L[j]; + bases[bases_idx++] = params.proofs[i].opening_proof.R[j]; + } + std::size_t unshifted_size = 0; + + for (std::size_t j = 0 ; j < params.proofs[i].comm.size(); j++) { + unshifted_size = params.proofs[i].comm[j].parts.size(); + for (std::size_t k =0; k< unshifted_size; k++){ + bases[bases_idx++] = params.proofs[i].comm[j].parts[k]; + } + } + bases[bases_idx++] = U; + bases[bases_idx++] = params.proofs[i].opening_proof.delta; + } + + assert(bases_idx == final_msm_size); + + auto res = msm_component::generate_assignments(assignment, {params.fr_output.scalars, bases}, row); + row += msm_component::rows_amount; + + assert(row == start_row_index + rows_amount); + return result_type(start_row_index); + } + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + generate_assignments_constant(bp, assignment, + params, start_row_index); + + std::size_t row = start_row_index; + var two_pow_255(0, row, false, var::column_type::constant); + var zero(0, start_row_index + 1, false, var::column_type::constant); + + var_ec_point point_at_infinity = {zero, zero}; + + std::array bases; + std::size_t bases_idx = 0; + + bases[bases_idx++] = params.verifier_index.H; + for(std::size_t i = 0; i < KimchiCommitmentParamsType::srs_len; i ++) { + bases[bases_idx++] = params.verifier_index.G[i]; + } + for (std::size_t i = 0; i < padding_size; i++) { + bases[bases_idx++] = point_at_infinity; + } + + for (std::size_t i = 0; i < params.proofs.size(); i++) { + transcript_type transcript = params.proofs[i].transcript; + transcript.absorb_fr_circuit(bp, assignment, {{params.fr_output.cip_shifted[i]}}, row); + row += transcript_type::absorb_fr_rows; + var t = transcript.challenge_fq_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + //U = transcript.squeeze.to_group() + var_ec_point U = {var(0, row), var(1, row)}; + + row++; + + //params.proofs[i].transcript.absorb_assignment(assignment, params.proofs[i].o.delta.x, row); + //params.proofs[i].transcript.absorb_assignment(assignment, params.proofs[i].o.delta.y, row); + bases[bases_idx++] = params.proofs[i].opening_proof.G; + bases[bases_idx++] = U; + for (std::size_t j = 0 ; j < params.proofs[i].opening_proof.L.size(); j++) { + bases[bases_idx++] = params.proofs[i].opening_proof.L[j]; + bases[bases_idx++] = params.proofs[i].opening_proof.R[j]; + } + std::size_t unshifted_size = 0; + + for (std::size_t j = 0 ; j < params.proofs[i].comm.size(); j++) { + unshifted_size = params.proofs[i].comm[j].parts.size(); + for (std::size_t k =0; k < unshifted_size; k++){ + bases[bases_idx++] = params.proofs[i].comm[j].parts[k]; + } + } + bases[bases_idx++] = U; + bases[bases_idx++] = params.proofs[i].opening_proof.delta; + } + + assert(bases_idx == final_msm_size); + + auto res = msm_component::generate_circuit(bp, assignment, {params.fr_output.scalars, bases}, row); + row += msm_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + } + + static void generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + } + + static void + generate_assignments_constant(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + typename BlueprintFieldType::integral_type tmp = 1; + assignment.constant(0)[row] = (tmp << 255); + row++; + assignment.constant(0)[row] = 0; + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VARIABLE_BASE_MULTIPLICATION_EDWARD25519_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_scalar_field.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_scalar_field.hpp new file mode 100644 index 000000000..b956a30ee --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/batch_verify_scalar_field.hpp @@ -0,0 +1,689 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the BATCH_VERIFY_SCALAR_FIELD component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BATCH_VERIFY_SCALAR_FIELD_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BATCH_VERIFY_SCALAR_FIELD_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // batched polynomial commitment verification (scalar field) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L610 + // Input: list of batch evaluation proofs + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L881-L888 + // Output: list of scalars for MSM in batch verify base + template + class batch_verify_scalar_field; + + template + class batch_verify_scalar_field, + CurveType, + KimchiParamsType, + KimchiCommitmentParamsType, + BatchSize, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14 > { + + typedef crypto3::zk::snark::plonk_constraint_system ArithmetizationType; + + + using var = crypto3::zk::snark::plonk_variable; + + using mul_component = multiplication; + using sub_component = subtraction; + using add_component = addition; + using mul_by_const_component = mul_by_constant; + + using random_component = random; + + using endo_scalar_component = + endo_scalar; + + using b_poly_component = b_poly; + using b_poly_coeff_component = b_poly_coefficients; + + using kimchi_constants = kimchi_inner_constants; + + constexpr static std::size_t scalars_len() { + return kimchi_constants::final_msm_size(BatchSize); + } + + using prepare_scalars_component = + prepare_scalars; + + using batch_proof = batch_evaluation_proof_scalar; + + constexpr static const std::size_t selector_seed = 0x0f28; + + constexpr static const std::size_t srs_len = KimchiCommitmentParamsType::srs_len; + constexpr static const std::size_t eval_rounds = KimchiCommitmentParamsType::eval_rounds; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + row += random_component::rows_amount; + row += random_component::rows_amount; + + for (std::size_t batch_id = 0; batch_id < BatchSize; batch_id++) { + for (std::size_t j = 0; j < eval_rounds; j++) { + row += endo_scalar_component::rows_amount; + + row += sub_component::rows_amount; + } + + row += endo_scalar_component::rows_amount; + + for (std::size_t i = 0; i < KimchiParamsType::eval_points_amount; i++) { + row += b_poly_component::rows_amount; + + row += mul_component::rows_amount; + + row += add_component::rows_amount; + + row += mul_component::rows_amount; + } + + row += b_poly_coeff_component::rows_amount; + + row += mul_by_const_component::rows_amount; + + row += mul_component::rows_amount; + + row += sub_component::rows_amount; + + for (std::size_t i = 0; i < b_poly_coeff_component::polynomial_len; i++) { + row += mul_component::rows_amount; + row += add_component::rows_amount; + } + + row += mul_component::rows_amount; + + row += sub_component::rows_amount; + + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + for (std::size_t i = 0; i < eval_rounds; i++) { + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + } + + for (std::size_t i = 0; i < kimchi_constants::evaluations_in_batch_size; i++) { + for (std::size_t j = 0; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split + 1; + j++) { + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + } + } + + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + } + + row += prepare_scalars_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array batches; + }; + + struct result_type { + std::array output; + + result_type(std::size_t start_row_index) { + } + }; + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + generate_assignments_constant(bp, assignment, params, start_row_index); + + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + + std::array scalars; + std::size_t scalar_idx = KimchiCommitmentParamsType::srs_len + + kimchi_constants::srs_padding_size(); + + for (std::size_t i = 0; + i < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + i++) { + scalars[i] = zero; + } + + var rand_base = random_component::generate_circuit( + bp, assignment, {params.batches}, row).output; + row += random_component::rows_amount; + var sg_rand_base = random_component::generate_circuit( + bp, assignment, {params.batches}, row).output; + row += random_component::rows_amount; + + var rand_base_i = one; + var sg_rand_base_i = one; + + for (std::size_t batch_id = 0; batch_id < params.batches.size(); batch_id++) { + var cip = params.batches[batch_id].cip; + + std::array, 2> challenges; + for (std::size_t j = 0; j < eval_rounds; j++) { + challenges[0][j] = endo_scalar_component::generate_circuit( + bp, assignment, + {params.batches[batch_id].fq_output.challenges[j]}, + row).output; + row += endo_scalar_component::rows_amount; + + challenges[1][j] = generate_circuit( + bp, assignment, {zero, challenges[0][j]}, row).output; + row += sub_component::rows_amount; + } + + var c = endo_scalar_component::generate_circuit( + bp, assignment, + {params.batches[batch_id].fq_output.c}, + row).output; + row += endo_scalar_component::rows_amount; + + var b0_scale = one; + var b0 = zero; + + for (std::size_t i = 0; i < KimchiParamsType::eval_points_amount; i++) { + var term = b_poly_component::generate_circuit( + bp, assignment, + {challenges[0], params.batches[batch_id].eval_points[i], + one}, row).output; + row += b_poly_component::rows_amount; + + var tmp = generate_circuit( + bp, assignment, {b0_scale, term}, row).output; + row += mul_component::rows_amount; + + b0 = generate_circuit( + bp, assignment, {b0, tmp}, row).output; + row += add_component::rows_amount; + + b0_scale = generate_circuit( + bp, assignment, {b0_scale, params.batches[batch_id].r}, row).output; + row += mul_component::rows_amount; + } + + auto s = b_poly_coeff_component::generate_circuit( + bp, assignment, {challenges[0], one}, row).output; + row += b_poly_coeff_component::rows_amount; + + var neg_rand_base_i = + generate_circuit( + bp, assignment, {rand_base_i, -1}, row).output; + row += mul_by_const_component::rows_amount; + + // neg_rand_base_i * opening.z1 - sg_rand_base_i + var tmp = generate_circuit(bp, + assignment, {neg_rand_base_i, params.batches[batch_id].opening.z1}, + row).output; + row += mul_component::rows_amount; + + tmp = generate_circuit(bp, + assignment, {tmp, sg_rand_base_i}, row).output; + row += sub_component::rows_amount; + scalars[scalar_idx++] = tmp; + + for (std::size_t i = 0; i < s.size(); i++) { + var sg_s = generate_circuit( + bp, assignment, {sg_rand_base_i, s[i]}, + row).output; + row += mul_component::rows_amount; + + scalars[i] = generate_circuit( + bp, assignment, {scalars[i], sg_s}, row).output; + row += add_component::rows_amount; + } + + var rand_base_z2 = generate_circuit( + bp, assignment, {rand_base_i, + params.batches[batch_id].opening.z2}, + row).output; + row += mul_component::rows_amount; + + scalars[0] = generate_circuit( + bp, assignment, {scalars[0], rand_base_z2}, + row).output; + row += sub_component::rows_amount; + + // neg_rand_base_i * (opening.z1 * b0) + var z1_b0 = generate_circuit( + bp, assignment, {b0, + params.batches[batch_id].opening.z1}, + row).output; + row += mul_component::rows_amount; + scalars[scalar_idx++] = generate_circuit( + bp, assignment, {z1_b0, + neg_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + var c_rand_base_i = generate_circuit( + bp, assignment, {c, + rand_base_i}, + row).output; + row += mul_component::rows_amount; + for (std::size_t i = 0; i < eval_rounds; i++) { + // rand_base_i_c_i * u_inv + scalars[scalar_idx++] = generate_circuit( + bp, assignment, {challenges[1][i], + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + // rand_base_i_c_i * u + scalars[scalar_idx++] = generate_circuit( + bp, assignment, {challenges[0][i], + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + } + + var xi_i = one; + for (std::size_t i = 0; i < kimchi_constants::evaluations_in_batch_size; i++) { + // iterating over the polynomial segments + shifted part + for (std::size_t j = 0; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split + 1; + j++) { + + // rand_base_i_c_i * xi_i + scalars[scalar_idx++] = generate_circuit( + bp, assignment, {xi_i, + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + xi_i = generate_circuit( + bp, assignment, {xi_i, + params.batches[batch_id].xi}, + row).output; + row += mul_component::rows_amount; + } + } + + // rand_base_i_c_i * combined_inner_product0 + scalars[scalar_idx++] = generate_circuit( + bp, assignment, {cip, + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + scalars[scalar_idx++] = rand_base_i; + + rand_base_i = generate_circuit( + bp, assignment, + {rand_base_i, rand_base}, row).output; + row += mul_component::rows_amount; + + sg_rand_base_i = generate_circuit( + bp, assignment, + {sg_rand_base_i, sg_rand_base}, row).output; + row += mul_component::rows_amount; + } + + scalars = prepare_scalars_component::generate_circuit(bp, assignment, + {scalars}, row).output; + row += prepare_scalars_component::rows_amount; + + assert(row == start_row_index + rows_amount); + assert(scalar_idx == kimchi_constants::final_msm_size(BatchSize) - 1); + + result_type res(start_row_index); + res.output = scalars; + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + + typename BlueprintFieldType::value_type endo_factor = + 0x12CCCA834ACDBA712CAAD5DC57AAB1B01D1F8BD237AD31491DAD5EBDFDFE4AB9_cppui_modular255; + std::size_t endo_num_bits = 128; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + + std::array scalars; + std::size_t scalar_idx = KimchiCommitmentParamsType::srs_len + + kimchi_constants::srs_padding_size(); + + for (std::size_t i = 0; + i < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + i++) { + scalars[i] = zero; + } + + var rand_base = random_component::generate_assignments( + assignment, {params.batches}, row).output; + row += random_component::rows_amount; + var sg_rand_base = random_component::generate_assignments( + assignment, {params.batches}, row).output; + row += random_component::rows_amount; + + var rand_base_i = one; + var sg_rand_base_i = one; + + for (std::size_t batch_id = 0; batch_id < params.batches.size(); batch_id++) { + var cip = params.batches[batch_id].cip; + + std::array, 2> challenges; + for (std::size_t j = 0; j < eval_rounds; j++) { + challenges[0][j] = endo_scalar_component::generate_assignments( + assignment, + {params.batches[batch_id].fq_output.challenges[j]}, + row).output; + row += endo_scalar_component::rows_amount; + + challenges[1][j] = sub_component::generate_assignments( + assignment, {zero, challenges[0][j]}, row).output; + row += sub_component::rows_amount; + } + + var c = endo_scalar_component::generate_assignments( + assignment, + {params.batches[batch_id].fq_output.c}, + row).output; + row += endo_scalar_component::rows_amount; + + var b0_scale = one; + var b0 = zero; + + for (std::size_t i = 0; i < KimchiParamsType::eval_points_amount; i++) { + var term = b_poly_component::generate_assignments( + assignment, + {challenges[0], params.batches[batch_id].eval_points[i], + one}, row).output; + row += b_poly_component::rows_amount; + + var tmp = mul_component::generate_assignments( + assignment, {b0_scale, term}, row).output; + row += mul_component::rows_amount; + + b0 = add_component::generate_assignments( + assignment, {b0, tmp}, row).output; + row += add_component::rows_amount; + + b0_scale = mul_component::generate_assignments( + assignment, {b0_scale, params.batches[batch_id].r}, row).output; + row += mul_component::rows_amount; + } + + auto s = b_poly_coeff_component::generate_assignments( + assignment, {challenges[0], one}, row).output; + row += b_poly_coeff_component::rows_amount; + + var neg_rand_base_i = mul_by_const_component::generate_assignments( + assignment, {rand_base_i, -1}, row).output; + row += mul_by_const_component::rows_amount; + + // neg_rand_base_i * opening.z1 - sg_rand_base_i + var tmp = mul_component::generate_assignments( + assignment, {neg_rand_base_i, params.batches[batch_id].opening.z1}, + row).output; + row += mul_component::rows_amount; + + tmp = sub_component::generate_assignments( + assignment, {tmp, sg_rand_base_i}, row).output; + row += sub_component::rows_amount; + scalars[scalar_idx++] = tmp; + + for (std::size_t i = 0; i < s.size(); i++) { + var sg_s = mul_component::generate_assignments( + assignment, {sg_rand_base_i, s[i]}, + row).output; + row += mul_component::rows_amount; + + scalars[i] = add_component::generate_assignments( + assignment, {scalars[i], sg_s}, row).output; + row += add_component::rows_amount; + } + + var rand_base_z2 = mul_component::generate_assignments( + assignment, {rand_base_i, + params.batches[batch_id].opening.z2}, + row).output; + row += mul_component::rows_amount; + + scalars[0] = sub_component::generate_assignments( + assignment, {scalars[0], rand_base_z2}, + row).output; + row += sub_component::rows_amount; + + // neg_rand_base_i * (opening.z1 * b0) + var z1_b0 = mul_component::generate_assignments( + assignment, {b0, + params.batches[batch_id].opening.z1}, + row).output; + row += mul_component::rows_amount; + scalars[scalar_idx++] = mul_component::generate_assignments( + assignment, {z1_b0, + neg_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + var c_rand_base_i = mul_component::generate_assignments( + assignment, {c, + rand_base_i}, + row).output; + row += mul_component::rows_amount; + for (std::size_t i = 0; i < eval_rounds; i++) { + // rand_base_i_c_i * u_inv + scalars[scalar_idx++] = mul_component::generate_assignments( + assignment, {challenges[1][i], + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + // rand_base_i_c_i * u + scalars[scalar_idx++] = mul_component::generate_assignments( + assignment, {challenges[0][i], + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + } + + var xi_i = one; + for (std::size_t i = 0; i < kimchi_constants::evaluations_in_batch_size; i++) { + // iterating over the polynomial segments + shifted part + for (std::size_t j = 0; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split + 1; + j++) { + + // rand_base_i_c_i * xi_i + scalars[scalar_idx++] = mul_component::generate_assignments( + assignment, {xi_i, + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + xi_i = mul_component::generate_assignments( + assignment, {xi_i, + params.batches[batch_id].xi}, + row).output; + row += mul_component::rows_amount; + } + } + + // rand_base_i_c_i * combined_inner_product0 + scalars[scalar_idx++] = mul_component::generate_assignments( + assignment, {cip, + c_rand_base_i}, + row).output; + row += mul_component::rows_amount; + + scalars[scalar_idx++] = rand_base_i; + + rand_base_i = mul_component::generate_assignments(assignment, + {rand_base_i, rand_base}, row).output; + row += mul_component::rows_amount; + + sg_rand_base_i = mul_component::generate_assignments(assignment, + {sg_rand_base_i, sg_rand_base}, row).output; + row += mul_component::rows_amount; + } + + scalars = prepare_scalars_component::generate_assignments(assignment, + {scalars}, row).output; + row += prepare_scalars_component::rows_amount; + + assert(row == start_row_index + rows_amount); + assert(scalar_idx == kimchi_constants::final_msm_size(BatchSize) - 1); + + result_type res(start_row_index); + res.output = scalars; + return res; + } + + private: + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &public_assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + + } + + static void generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + } + + static void + generate_assignments_constant(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + } + }; + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BATCH_VERIFY_SCALAR_FIELD_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/prepare_scalars.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/prepare_scalars.hpp new file mode 100644 index 000000000..dcdce99e3 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/prepare_scalars.hpp @@ -0,0 +1,184 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_HPP + +#include + +#include +#include + +// #include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // shift scalars for scalar multiplication input + // f(X) = X -> X - 2^255 when the scalar field is larger than the base field and // TODO: "larger scalar + // field is depricated case" f(X) = X -> (X - 2^255 - 1) / 2 otherwise Input: [x_0, ..., x_InputSize] + // Output: [f(x_0), ..., f(x_InputSize)] + template + class prepare_scalars; + + template + class prepare_scalars, + CurveType, InputSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + + constexpr static const std::size_t selector_seed = 0x0f2C; + + constexpr static bool scalar_larger() { + using ScalarField = typename CurveType::scalar_field_type; + using BaseField = typename CurveType::base_field_type; + + auto n1 = ScalarField::modulus; + auto n2 = BaseField::modulus; + + return n1 > n2; + } + + public: + constexpr static const std::size_t rows_amount = + InputSize * (add_component::rows_amount + mul_component::rows_amount); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array scalars; + }; + + struct result_type { + std::array output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + var shift = var(0, start_row_index, false, var::column_type::constant); + var coef = var(0, start_row_index + 1, false, var::column_type::constant); + + std::size_t row = start_row_index; + + std::array shifted; + result_type result; + + for (std::size_t i = 0; i < InputSize; ++i) { + shifted[i] = zk::components::generate_circuit( + bp, assignment, {params.scalars[i], shift}, row) + .output; + row += add_component::rows_amount; + result.output[i] = + zk::components::generate_circuit(bp, assignment, {shifted[i], coef}, row) + .output; + row += mul_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + + return result; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var shift = var(0, start_row_index, false, var::column_type::constant); + var coef = var(0, start_row_index + 1, false, var::column_type::constant); + + std::size_t row = start_row_index; + + std::array shifted; + result_type result; + + for (std::size_t i = 0; i < InputSize; ++i) { + shifted[i] = + add_component::generate_assignments(assignment, {params.scalars[i], shift}, row).output; + row += add_component::rows_amount; + result.output[i] = + mul_component::generate_assignments(assignment, {shifted[i], coef}, row).output; + row += mul_component::rows_amount; + } + + return result; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type base = 2; + if (scalar_larger()) { + assignment.constant(0)[row] = -base.pow(255); + row++; + assignment.constant(0)[row] = 1; + } else { + assignment.constant(0)[row] = -base.pow(255) - 1; + row++; + assignment.constant(0)[row] = 1 / base; + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/random.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/random.hpp new file mode 100644 index 000000000..85fe30921 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/batch_scalar/random.hpp @@ -0,0 +1,194 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_RANDOM_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_RANDOM_HPP + +#include + +#include +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // pseudo-random element generation + // it's used for randomization here: + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L656 + // Input: - + // Output: x \in F_r + template + class random; + + template + class random, + KimchiParamsType, BatchSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, + W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using transcript_type = kimchi_transcript_fr; + + using batch_proof = + batch_evaluation_proof_scalar; + + constexpr static const std::size_t selector_seed = 0x0f29; + + public: + constexpr static const std::size_t rows_amount = + transcript_type::init_rows + + BatchSize * (transcript_type::state_size * transcript_type::absorb_rows + + 3 * transcript_type::absorb_rows) + + transcript_type::challenge_rows; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array batches; + }; + + struct result_type { + var output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + + transcript_type transcript; + transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + + for (auto batched_proof : params.batches) { + // the most part of the data that influences the results is accumulated in the transcript + auto state = batched_proof.transcript.state(); + for (std::size_t i = 0; i < state.size(); i++) { + transcript.absorb_circuit(bp, assignment, state[i], row); + row += transcript_type::absorb_rows; + } + + transcript.absorb_circuit(bp, assignment, batched_proof.cip, row); + row += transcript_type::absorb_rows; + + transcript.absorb_circuit(bp, assignment, batched_proof.opening.z1, row); + row += transcript_type::absorb_rows; + transcript.absorb_circuit(bp, assignment, batched_proof.opening.z2, row); + row += transcript_type::absorb_rows; + } + + var output = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + assert(row == start_row_index + rows_amount); + + generate_assignments_constants(assignment, params, start_row_index); + + result_type res = {output}; + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + + transcript_type transcript; + transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + + for (auto batched_proof : params.batches) { + // the most part of the data that influences the results is accumulated in the transcript + auto state = batched_proof.transcript.state(); + for (std::size_t i = 0; i < state.size(); i++) { + transcript.absorb_assignment(assignment, state[i], row); + row += transcript_type::absorb_rows; + } + + transcript.absorb_assignment(assignment, batched_proof.cip, row); + row += transcript_type::absorb_rows; + + transcript.absorb_assignment(assignment, batched_proof.opening.z1, row); + row += transcript_type::absorb_rows; + transcript.absorb_assignment(assignment, batched_proof.opening.z2, row); + row += transcript_type::absorb_rows; + } + + var output = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + assert(row == start_row_index + rows_amount); + + result_type res = {output}; + return res; + } + + private: + static void generate_assignments_constants( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = 0; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_RANDOM_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/binding.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/binding.hpp new file mode 100644 index 000000000..0a714b099 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/binding.hpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP + +#include + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct binding { + using var = snark::plonk_variable; + using commitment_parms_type = typename KimchiParamsType::commitment_params_type; + using kimchi_constants = zk::components::kimchi_inner_constants; + + template + struct fr_data { + private: + constexpr static const std::size_t f_comm_msm_size = kimchi_constants::f_comm_msm_size; + constexpr static const std::size_t lookup_columns = KimchiParamsType::circuit_params::lookup_columns; + + public: + std::array scalars; + std::array, BatchSize> f_comm_scalars; + std::array cip_shifted; + + std::array neg_pub; + std::array zeta_to_srs_len; + var zeta_to_domain_size_minus_1; + + std::array joint_combiner_powers_prepared; + }; + + template + struct fq_data { }; + + struct fq_sponge_output { + var joint_combiner; + var beta; // beta and gamma can be combined from limbs in the base circuit + var gamma; + var alpha; + var zeta; + var fq_digest; // TODO overflow check + std::array challenges; + var c; + }; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/compare.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/compare.hpp new file mode 100644 index 000000000..e2ab6aade --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/compare.hpp @@ -0,0 +1,390 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_COMPARE_WITH_CONSTANT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_COMPARE_WITH_CONSTANT_HPP + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + ///////////////// Compare Value with Constant //////////////////////////////// + // Constant is pallas base field modulus + // Return 0, if value >= constant + // Return 1 otherwise + template + class compare_with_const; + + template + class compare_with_const, + CurveType, + W0, + W1, + W2> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using sub_component = zk::components::subtraction; + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using div_component = zk::components::division; + using mul_by_const_component = zk::components::mul_by_constant; + + using var = snark::plonk_variable; + + constexpr static const std::size_t selector_seed = 0x0ff8; + + public: + constexpr static const std::size_t rows_amount = 15 + 8 * 87; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var value; + params_type(var val) : value(val) { + } + }; + + struct result_type { + var output; + + result_type(std::size_t row) { + output = var(W2, static_cast(row), false, var::column_type::witness); + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t component_start_row) { + + generate_assignments_constants(bp, assignment, params, component_start_row); + + std::size_t row = component_start_row; + + var k = var(0, component_start_row, false, var::column_type::constant); + var power87 = var(0, component_start_row + 1, false, var::column_type::constant); + var zero = var(0, component_start_row + 2, false, var::column_type::constant); + var one = var(0, component_start_row + 3, false, var::column_type::constant); + + var c_var = + zk::components::generate_circuit(bp, assignment, {zero, params.value}, row) + .output; + row++; + + var b_var = + zk::components::generate_circuit(bp, assignment, {power87, k}, row).output; + row++; + b_var = + zk::components::generate_circuit(bp, assignment, {b_var, one}, row).output; + row++; + b_var = + zk::components::generate_circuit(bp, assignment, {b_var, c_var}, row).output; + row++; + var c1_var = zero; + var b1_var = zero; + var bit_var; + typename BlueprintFieldType::value_type times = 1; + + for (int i = 0; i < 87; ++i) { + bit_var = var(W1, row, false); + var bit_check_c = + zk::components::generate_circuit(bp, assignment, {one, bit_var}, row) + .output; + row++; + bit_check_c = zk::components::generate_circuit( + bp, assignment, {bit_var, bit_check_c}, row) + .output; + row++; + bit_var = zk::components::generate_circuit( + bp, assignment, {bit_var, times}, row) + .output; + row++; + c1_var = + zk::components::generate_circuit(bp, assignment, {bit_var, c1_var}, row) + .output; + row++; + bit_var = var(W1, row, false); + var bit_check_b = + zk::components::generate_circuit(bp, assignment, {one, bit_var}, row) + .output; + row++; + bit_check_b = zk::components::generate_circuit( + bp, assignment, {bit_var, bit_check_b}, row) + .output; + row++; + bit_var = zk::components::generate_circuit( + bp, assignment, {bit_var, times}, row) + .output; + row++; + b1_var = + zk::components::generate_circuit(bp, assignment, {bit_var, b1_var}, row) + .output; + row++; + times *= 2; + } + + var delta_b_var = + zk::components::generate_circuit(bp, assignment, {b_var, b1_var}, row) + .output; + row++; + var b1_inv_var = var(W1, row, false); + var inv_check_b1 = zk::components::generate_circuit( + bp, assignment, {delta_b_var, b1_inv_var}, row) + .output; + row++; + var inv_check_one = + zk::components::generate_circuit(bp, assignment, {one, inv_check_b1}, row) + .output; + row++; + var inv_check = zk::components::generate_circuit( + bp, assignment, {inv_check_one, delta_b_var}, row) + .output; + row++; + + var delta_c_var = + zk::components::generate_circuit(bp, assignment, {c_var, c1_var}, row) + .output; + row++; + var c1_inv_var = var(W1, row, false); + var inv_check_c1 = zk::components::generate_circuit( + bp, assignment, {delta_c_var, c1_inv_var}, row) + .output; + row++; + inv_check_one = + zk::components::generate_circuit(bp, assignment, {one, inv_check_c1}, row) + .output; + row++; + inv_check = zk::components::generate_circuit( + bp, assignment, {inv_check_one, delta_c_var}, row) + .output; + row++; + + var result_check_mul = zk::components::generate_circuit( + bp, assignment, {inv_check_c1, inv_check_b1}, row) + .output; + row++; + var result_check_sum = zk::components::generate_circuit( + bp, assignment, {inv_check_c1, inv_check_b1}, row) + .output; + row++; + var result_check = zk::components::generate_circuit( + bp, assignment, {result_check_sum, result_check_mul}, row) + .output; + return result_type(row); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t component_start_row) { + std::size_t row = component_start_row; + + var k = var(0, component_start_row, false, var::column_type::constant); + var power87 = var(0, component_start_row + 1, false, var::column_type::constant); + var zero = var(0, component_start_row + 2, false, var::column_type::constant); + var one = var(0, component_start_row + 3, false, var::column_type::constant); + + var c_var = sub_component::generate_assignments(assignment, {zero, params.value}, row).output; + row++; + + var b_var = sub_component::generate_assignments(assignment, {power87, k}, row).output; + row++; + b_var = sub_component::generate_assignments(assignment, {b_var, one}, row).output; + row++; + b_var = add_component::generate_assignments(assignment, {b_var, c_var}, row).output; + row++; + + auto b_for_bits = assignment.var_value(b_var).data; + auto c_for_bits = assignment.var_value(c_var).data; + typename BlueprintFieldType::value_type bit; + + typename BlueprintFieldType::value_type times = 1; + var c1_var = zero; + var b1_var = zero; + var bit_var; + + for (int i = 0; i < 87; ++i) { + bit.data = c_for_bits - (c_for_bits >> 1 << 1); + assignment.witness(W1)[row] = bit; + bit_var = var(W1, row, false); + var bit_check_c = + sub_component::generate_assignments(assignment, {one, bit_var}, row).output; + row++; + bit_check_c = + mul_component::generate_assignments(assignment, {bit_var, bit_check_c}, row).output; + row++; + c_for_bits = c_for_bits >> 1; + bit_var = + mul_by_const_component::generate_assignments(assignment, {bit_var, times}, row).output; + row++; + c1_var = add_component::generate_assignments(assignment, {bit_var, c1_var}, row).output; + row++; + + bit.data = b_for_bits - (b_for_bits >> 1 << 1); + assignment.witness(W1)[row] = bit; + bit_var = var(W1, row, false); + var bit_check_b = + sub_component::generate_assignments(assignment, {one, bit_var}, row).output; + row++; + bit_check_b = + mul_component::generate_assignments(assignment, {bit_var, bit_check_b}, row).output; + row++; + b_for_bits = b_for_bits >> 1; + bit_var = + mul_by_const_component::generate_assignments(assignment, {bit_var, times}, row).output; + row++; + b1_var = add_component::generate_assignments(assignment, {bit_var, b1_var}, row).output; + row++; + times *= 2; + } + + var delta_b_var = sub_component::generate_assignments(assignment, {b_var, b1_var}, row).output; + row++; + typename BlueprintFieldType::value_type b1_inv; + if (assignment.var_value(delta_b_var) != 0) { + b1_inv = assignment.var_value(delta_b_var).inversed(); + } else { + b1_inv = 0; + } + assignment.witness(W1)[row] = b1_inv; + var b1_inv_var = var(W1, row, false); + var inv_check_b1 = + mul_component::generate_assignments(assignment, {delta_b_var, b1_inv_var}, row).output; + row++; + var inv_check_one = + sub_component::generate_assignments(assignment, {one, inv_check_b1}, row).output; + row++; + var inv_check = + mul_component::generate_assignments(assignment, {inv_check_one, delta_b_var}, row).output; + row++; + + var delta_c_var = sub_component::generate_assignments(assignment, {c_var, c1_var}, row).output; + row++; + typename BlueprintFieldType::value_type c1_inv; + if (assignment.var_value(delta_c_var) != 0) { + c1_inv = assignment.var_value(delta_c_var).inversed(); + } else { + c1_inv = 0; + } + assignment.witness(W1)[row] = c1_inv; + var c1_inv_var = var(W1, row, false); + var inv_check_c1 = + mul_component::generate_assignments(assignment, {delta_c_var, c1_inv_var}, row).output; + row++; + inv_check_one = + sub_component::generate_assignments(assignment, {one, inv_check_c1}, row).output; + row++; + inv_check = + mul_component::generate_assignments(assignment, {inv_check_one, delta_c_var}, row).output; + row++; + + var result_check_mul = + mul_component::generate_assignments(assignment, {inv_check_c1, inv_check_b1}, row).output; + row++; + var result_check_sum = + add_component::generate_assignments(assignment, {inv_check_c1, inv_check_b1}, row).output; + row++; + var result_check = + sub_component::generate_assignments(assignment, {result_check_sum, result_check_mul}, row) + .output; + return result_type(row); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + + var zero = var(0, component_start_row + 2, false, var::column_type::constant); + + std::size_t row = component_start_row + 5; + var bit_check; + for (int i = 0; i < 87; ++i) { + bit_check = typename mul_component::result_type(row).output; + bp.add_copy_constraint({bit_check, zero}); + row += 4; + bit_check = typename mul_component::result_type(row).output; + bp.add_copy_constraint({bit_check, zero}); + row += 4; + } + row += 2; + var inv_check = typename mul_component::result_type(row).output; + bp.add_copy_constraint({inv_check, zero}); + row += 4; + inv_check = typename mul_component::result_type(row).output; + bp.add_copy_constraint({inv_check, zero}); + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t component_start_row) { + std::size_t row = component_start_row; + typename BlueprintFieldType::value_type base = 2; + assignment.constant(0)[row] = + 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001_cppui_modular255 - + 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001_cppui_modular255; + row++; + assignment.constant(0)[row] = base.pow(87); + row++; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ORACLES_DETAIL_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/generic_scalars.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/generic_scalars.hpp new file mode 100644 index 000000000..ec6e66575 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/generic_scalars.hpp @@ -0,0 +1,243 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_GENERIC_SCALARS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_GENERIC_SCALARS_HPP + +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // generic constraint scalars + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/polynomials/generic.rs#L242 + // Input: + // Output: generic-gate-related scalar x for linearization + template + class generic_scalars; + + template + class generic_scalars, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + + constexpr static const std::size_t selector_seed = 0x0f26; + + constexpr static const std::size_t generic_registers = 3; + + public: + constexpr static const std::size_t rows_amount = 12 * mul_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + constexpr static const std::size_t output_size = 10; + + struct params_type { + std::array, + KimchiParamsType::eval_points_amount> + evals; + std::array alphas; + std::size_t start_idx; + }; + + struct result_type { + std::array output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + + constexpr std::size_t parts = 2; + + for (std::size_t i = 0; i < parts; i++) { + var alpha_generic = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + + // addition part + // alpha_generic * w_zeta[register_offset + j] + for (std::size_t j = 0; j < 3; j++) { + output[5 * i + j] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + + // multiplication + var tmp = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + output[5 * i + 3] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + + // constant + output[5 * i + 4] = alpha_generic; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array output; + + constexpr std::size_t parts = 2; + + std::array alpha_pows = { + params.alphas[params.start_idx], + params.alphas[params.start_idx + 1], + }; + std::array offsets = {0, generic_registers}; + + for (std::size_t i = 0; i < parts; i++) { + var alpha_generic = + zk::components::generate_circuit( + bp, assignment, {alpha_pows[i], params.evals[0].generic_selector}, row) + .output; + row += mul_component::rows_amount; + + // addition part + // alpha_generic * w_zeta[register_offset + j] + for (std::size_t j = 0; j < 3; j++) { + output[5 * i + j] = + zk::components::generate_circuit( + bp, assignment, {alpha_generic, params.evals[0].w[offsets[i] + j]}, row) + .output; + row += mul_component::rows_amount; + } + + // multiplication + var tmp = zk::components::generate_circuit( + bp, assignment, + {params.evals[0].w[offsets[i]], params.evals[0].w[offsets[i] + 1]}, row) + .output; + row += mul_component::rows_amount; + output[5 * i + 3] = zk::components::generate_circuit( + bp, assignment, {alpha_generic, tmp}, row) + .output; + row += mul_component::rows_amount; + + // constant + output[5 * i + 4] = alpha_generic; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + std::array output; + + constexpr std::size_t parts = 2; + + std::array alpha_pows = { + params.alphas[params.start_idx], + params.alphas[params.start_idx + 1], + }; + std::array offsets = {0, generic_registers}; + + for (std::size_t i = 0; i < parts; i++) { + var alpha_generic = mul_component::generate_assignments( + assignment, {alpha_pows[i], params.evals[0].generic_selector}, row) + .output; + row += mul_component::rows_amount; + + // addition part + // alpha_generic * w_zeta[register_offset + j] + for (std::size_t j = 0; j < 3; j++) { + output[5 * i + j] = + mul_component::generate_assignments( + assignment, {alpha_generic, params.evals[0].w[offsets[i] + j]}, row) + .output; + row += mul_component::rows_amount; + } + + // multiplication + var tmp = + mul_component::generate_assignments( + assignment, {params.evals[0].w[offsets[i]], params.evals[0].w[offsets[i] + 1]}, row) + .output; + row += mul_component::rows_amount; + output[5 * i + 3] = + mul_component::generate_assignments(assignment, {alpha_generic, tmp}, row).output; + row += mul_component::rows_amount; + + // constant + output[5 * i + 4] = alpha_generic; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_GENERIC_SCALARS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/index_terms_scalars.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/index_terms_scalars.hpp new file mode 100644 index 000000000..a3a51dca8 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/index_terms_scalars.hpp @@ -0,0 +1,231 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_INDEX_TERMS_SCALARS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_INDEX_TERMS_SCALARS_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + constexpr void constexpr_for(F &&f) { + if constexpr (Start < End) { + f(std::integral_constant()); + constexpr_for(f); + } + } + + // constraints scalars (exluding generic constraint) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L568-L673 + // Input: constraint + // Output: constraint-related scalar x for linearization + template + class index_terms_scalars; + + template + class index_terms_scalars, + KimchiParamsType, W0, W1, W2, W3, + W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + + using evaluations_type = + typename zk::components::kimchi_proof_evaluations; + + constexpr static const std::size_t selector_seed = 0x0f27; + + using index_terms_list = typename KimchiParamsType::circuit_params::index_terms_list; + + constexpr static std::size_t rows() { + std::size_t n = 0; + + for (std::size_t i = 0; i < index_terms_list::size; i++) { + n += index_terms_list::terms[i].rows_amount; + } + + return n; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var eval_point; // zeta + + var alpha; + var beta; + var gamma; + var joint_combiner; + + std::array evaluations; + + var group_gen; + std::size_t domain_size; + }; + + struct result_type { + std::array output; + + result_type(std::size_t start_row_index) { + } + + result_type() { + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array output; + + std::size_t output_idx = 0; + + constexpr static const std::size_t end = index_terms_list::size; + + constexpr_for<0, end, 1>([&row, &output_idx, &output, ¶ms, &bp, &assignment](auto i) { + using component_type = + zk::components::rpn_expression; + output[output_idx++] = component_type::generate_circuit( + bp, + assignment, + {index_terms_list::terms[i].str_repr, params.eval_point, + params.alpha, params.beta, params.gamma, params.joint_combiner, + params.evaluations, params.group_gen, params.domain_size}, + row) + .output; + row += component_type::rows_amount; + }); + + assert(output_idx == KimchiParamsType::index_term_size()); + assert(row == start_row_index + rows_amount); + + result_type res; + res.output = output; + + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array output; + + std::size_t output_idx = 0; + + constexpr static const std::size_t end = index_terms_list::size; + constexpr_for<0, end, 1>([&row, &output_idx, &output, ¶ms, &assignment](auto i) { + using component_type = + zk::components::rpn_expression; + output[output_idx++] = component_type::generate_assignments( + assignment, + {index_terms_list::terms[i].str_repr, params.eval_point, + params.alpha, params.beta, params.gamma, params.joint_combiner, + params.evaluations, params.group_gen, params.domain_size}, + row) + .output; + row += component_type::rows_amount; + }); + + assert(output_idx == KimchiParamsType::index_term_size()); + assert(row == start_row_index + rows_amount); + + result_type res; + res.output = output; + + return res; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_INDEX_TERMS_SCALARS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/perm_scalars.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/perm_scalars.hpp new file mode 100644 index 000000000..758f3d0a7 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/perm_scalars.hpp @@ -0,0 +1,195 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PERM_SCALAR_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PERM_SCALAR_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // permutation argument scalars + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/polynomials/permutation.rs#L325 + // Input: + // Output: permutation-related scalar x for linearization + template + class perm_scalars; + + template + class perm_scalars, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using mul_const_component = zk::components::mul_by_constant; + using add_component = zk::components::addition; + + constexpr static const std::size_t selector_seed = 0x0f2E; + + constexpr static const std::size_t scalar_rows = KimchiParamsType::permut_size - 1; + + public: + constexpr static const std::size_t rows_amount = + 3 * mul_component::rows_amount + + scalar_rows * (2 * mul_component::rows_amount + 2 * add_component::rows_amount) + + mul_const_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array, + KimchiParamsType::eval_points_amount> + evals; + std::array alphas; + std::size_t start_idx; + + var beta; + var gamma; + var zkp_zeta; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + output = {var(W1, start_row_index + rows_amount - 1, false)}; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array w; + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + w[i] = params.evals[0].w[i]; + } + std::array s; + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + s[i] = params.evals[0].s[i]; + } + var z = params.evals[1].z; + std::size_t size = KimchiParamsType::permut_size - 1; + + zk::components::generate_circuit(bp, assignment, {z, params.beta}, row); + auto res = typename mul_component::result_type({z, params.beta}, row); + row += mul_component::rows_amount; + zk::components::generate_circuit( + bp, assignment, {res.output, params.alphas[params.start_idx]}, row); + res = typename mul_component::result_type({res.output, params.alphas[params.start_idx]}, row); + row += mul_component::rows_amount; + zk::components::generate_circuit( + bp, assignment, {res.output, params.zkp_zeta}, row); + res = typename mul_component::result_type({res.output, params.zkp_zeta}, row); + row += mul_component::rows_amount; + + for (std::size_t i = 0; i < size; i++) { + zk::components::generate_circuit(bp, assignment, {s[i], params.beta}, row); + auto tmp = typename mul_component::result_type({s[i], params.beta}, row); + row += mul_component::rows_amount; + zk::components::generate_circuit( + bp, assignment, {tmp.output, params.gamma}, row); + auto add_tmp = typename add_component::result_type({tmp.output, params.gamma}, row); + row += add_component::rows_amount; + zk::components::generate_circuit( + bp, assignment, {add_tmp.output, w[i]}, row); + add_tmp = typename add_component::result_type({add_tmp.output, w[i]}, row); + row += add_component::rows_amount; + zk::components::generate_circuit( + bp, assignment, {add_tmp.output, res.output}, row); + res = typename mul_component::result_type({add_tmp.output, res.output}, row); + row += mul_component::rows_amount; + } + zk::components::generate_circuit(bp, assignment, {res.output, -1}, row); + auto const_res = typename mul_const_component::result_type({res.output, -1}, row); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array w; + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + w[i] = params.evals[0].w[i]; + } + std::array s; + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + s[i] = params.evals[0].s[i]; + } + var z = params.evals[1].z; + std::size_t size = KimchiParamsType::permut_size - 1; + + auto res = mul_component::generate_assignments(assignment, {z, params.beta}, row); + row += mul_component::rows_amount; + res = mul_component::generate_assignments( + assignment, {res.output, params.alphas[params.start_idx]}, row); + row += mul_component::rows_amount; + res = mul_component::generate_assignments(assignment, {res.output, params.zkp_zeta}, row); + row += mul_component::rows_amount; + + for (std::size_t i = 0; i < size; i++) { + auto tmp = mul_component::generate_assignments(assignment, {s[i], params.beta}, row); + row += mul_component::rows_amount; + auto add_tmp = + add_component::generate_assignments(assignment, {tmp.output, params.gamma}, row); + row += add_component::rows_amount; + add_tmp = add_component::generate_assignments(assignment, {add_tmp.output, w[i]}, row); + row += add_component::rows_amount; + res = mul_component::generate_assignments(assignment, {add_tmp.output, res.output}, row); + row += add_component::rows_amount; + } + auto const_res = mul_const_component::generate_assignments(assignment, {res.output, -1}, row); + return result_type(start_row_index); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PERM_SCALAR_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_expression.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_expression.hpp new file mode 100644 index 000000000..9b1360a52 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_expression.hpp @@ -0,0 +1,786 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_RPN_EXPRESSION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_RPN_EXPRESSION_HPP + +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Evaluate an RPN expression + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/expr.rs#L467 + // Input: RPN expression E, variables values V + // Output: E(V) \in F_r + template + class rpn_expression; + + template + class rpn_expression, + KimchiParamsType, NRows, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, + W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using sub_component = zk::components::subtraction; + using endo_scalar_component = zk::components::endo_scalar; + + using poseidon_component = zk::components::poseidon; + + using exponentiation_component = zk::components::exponentiation; + + using vanishes_on_last_4_rows_component = + zk::components::vanishes_on_last_4_rows; + + using unnormalized_lagrange_basis_component = + zk::components::unnormalized_lagrange_basis; + + using evaluations_type = + typename zk::components::kimchi_proof_evaluations; + + constexpr static const std::size_t selector_seed = 0x0f31; + + constexpr static const std::size_t mds_size = 3; + + static std::array, mds_size> mds_vars(const std::size_t start_row) { + std::array, mds_size> result; + std::size_t mds_start_row = start_row; + + for (std::size_t i = 0; i < mds_size; ++i) { + for (std::size_t j = 0; j < mds_size; ++j) { + result[i][j] = + var(0, mds_start_row + i * mds_size + j, false, var::column_type::constant); + } + } + return result; + } + + static var var_from_evals(const std::array + evaluations, + const std::size_t var_column, + const std::size_t var_row) { + auto evals = evaluations[var_row]; + + /// 0 - witness_columns: witnesses + /// witness_columns + 1: z + /// witness_columns + 2: PoseidonSelector + /// witness_columns + 3: GenericSelector + /// witness_columns + 4: LookupAggreg + /// witness_columns + 5: LookupTable + /// witness_columns + 6: LookupRuntimeTable + /// witness_columns + 7+: LookupSorted + + if (var_column < KimchiParamsType::witness_columns) { + return evals.w[var_column]; + } + + switch (var_column) { + case KimchiParamsType::witness_columns + 1: + return evals.z; + case KimchiParamsType::witness_columns + 2: + return evals.poseidon_selector; + case KimchiParamsType::witness_columns + 3: + return evals.generic_selector; + case KimchiParamsType::witness_columns + 4: + return evals.lookup.aggreg; + case KimchiParamsType::witness_columns + 5: + return evals.lookup.table; + case KimchiParamsType::witness_columns + 6: + return evals.lookup.runtime; + default: + break; + } + + assert(var_column <= KimchiParamsType::witness_columns + 6 + + KimchiParamsType::circuit_params::lookup_columns); + + return evals.lookup.sorted[var_column - KimchiParamsType::witness_columns - 7]; + } + + enum token_type { + alpha, + beta, + gamma, + joint_combiner, + endo_coefficient, + mds, + literal, + cell, + dup, + pow, + add, + mul, + sub, + vanishes_on_last_4_rows, + unnormalized_lagrange_basis, + store, + load + }; + + struct token_value_type { + token_type type; + std::pair + value; + int int_data; + }; + + static std::vector rpn_from_string(const std::string_view expression) { + + std::vector tokens_str; + boost::split(tokens_str, expression, boost::is_any_of(";")); + for (std::size_t i = 0; i < tokens_str.size(); i++) { + boost::trim(tokens_str[i]); + } + + std::vector tokens; + for (std::size_t i = 0; i < tokens_str.size(); i++) { + + std::string token_str = tokens_str[i]; + if (token_str.empty()) { + continue; + } + + token_value_type token; + + if (token_str.find("Alpha") != std::string::npos) { + token.type = token_type::alpha; + } else if (token_str.find("Beta") != std::string::npos) { + token.type = token_type::beta; + } else if (token_str.find("Gamma") != std::string::npos) { + token.type = token_type::gamma; + } else if (token_str.find("JointCombiner") != std::string::npos) { + token.type = token_type::joint_combiner; + } else if (token_str.find("EndoCoefficient") != std::string::npos) { + token.type = token_type::endo_coefficient; + } else if (token_str.find("Mds") != std::string::npos) { + token.type = token_type::mds; + std::size_t row_pos = token_str.find("row"); + row_pos += 5; + std::size_t row_end_pos = token_str.find(" ", row_pos); + std::string row_str = token_str.substr(row_pos, row_end_pos - row_pos); + token.value.first = std::stoi(row_str); + + std::size_t col_pos = token_str.find("col"); + col_pos += 5; + std::size_t col_end_pos = token_str.find(" ", col_pos); + std::string col_str = token_str.substr(col_pos, col_end_pos - col_pos); + token.value.second = std::stoi(col_str); + } else if (token_str.find("Literal") != std::string::npos) { + token.type = token_type::literal; + std::size_t value_start_pos = token_str.find("Literal") + 8; + std::size_t value_end_pos = token_str.find(";", value_start_pos); + std::string value_str = + token_str.substr(value_start_pos, value_end_pos - value_start_pos); + token.value.first = multiprecision::cpp_int("0x" + value_str); + } else if (token_str.find("Cell") != std::string::npos) { + token.type = token_type::cell; + + std::size_t row_pos = token_str.find("row"); + std::size_t row; + if (token_str.find("Curr", row_pos) != std::string::npos) { + row = 0; + } else { // Next + row = 1; + } + + std::size_t col_pos = token_str.find("col"); + std::size_t col; + if (token_str.find("Witness", col_pos) != std::string::npos) { + // Witness(col) + std::size_t witness_pos = token_str.find("Witness", col_pos); + std::size_t col_start_pow = witness_pos + 8; + std::size_t col_end_pow = token_str.find(")", col_start_pow); + std::string col_str = token_str.substr(col_start_pow, col_end_pow - col_start_pow); + col = std::stoi(col_str); + } else { + std::array column_types = {"Z", "Poseidon", + "Generic", "LookupAggreg", + "LookupTable", "LookupRuntimeTable"}; + for (std::size_t i = 0; i < column_types.size(); i++) { + if (token_str.find(column_types[i]) != std::string::npos) { + col = KimchiParamsType::witness_columns + i + 1; + break; + } + } + + // lookup_sorted + if (token_str.find("LookupSorted") != std::string::npos) { + std::size_t col_start_pos = token_str.find("LookupSorted", col_pos) + 14; + std::size_t col_end_pos = token_str.find(")", col_start_pos); + std::string col_str = + token_str.substr(col_start_pos, col_end_pos - col_start_pos); + col = KimchiParamsType::witness_columns + 7 + std::stoi(col_str); + } + } + + token.value.first = col; + token.value.second = row; + } else if (token_str.find("Dup") != std::string::npos) { + token.type = token_type::dup; + } else if (token_str.find("Pow") != std::string::npos) { + token.type = token_type::pow; + + std::size_t exp_start_pos = token_str.find("Pow") + 4; + std::size_t exp_end_pos = token_str.find(")", exp_start_pos); + std::string exp_str = token_str.substr(exp_start_pos, exp_end_pos - exp_start_pos); + token.value.first = std::stoi(exp_str); + } else if (token_str.find("Add") != std::string::npos) { + token.type = token_type::add; + } else if (token_str.find("Mul") != std::string::npos) { + token.type = token_type::mul; + } else if (token_str.find("Sub") != std::string::npos) { + token.type = token_type::sub; + } else if (token_str.find("VanishesOnLast4Rows") != std::string::npos) { + token.type = token_type::vanishes_on_last_4_rows; + } else if (token_str.find("UnnormalizedLagrangeBasis") != std::string::npos) { + token.type = token_type::unnormalized_lagrange_basis; + + std::size_t exp_start_pos = token_str.find("UnnormalizedLagrangeBasis"); + exp_start_pos = token_str.find("(", exp_start_pos) + 1; + std::size_t exp_end_pos = token_str.find(")", exp_start_pos); + std::string exp_str = token_str.substr(exp_start_pos, exp_end_pos - exp_start_pos); + token.int_data = std::stoi(exp_str); + } else if (token_str.find("Store") != std::string::npos) { + token.type = token_type::store; + } else if (token_str.find("Load") != std::string::npos) { + token.type = token_type::load; + + std::size_t idx_start_pos = token_str.find("Load") + 5; + std::size_t idx_end_pos = token_str.find(")", idx_start_pos); + std::string idx_str = token_str.substr(idx_start_pos, idx_end_pos - idx_start_pos); + token.value.first = std::stoi(idx_str); + } else { + throw std::runtime_error("Unknown token type"); + } + + tokens.push_back(token); + } + + return tokens; + } + + public: + constexpr static const std::size_t rows_amount = NRows; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::string_view expression; + + var eval_point; // zeta + + var alpha; + var beta; + var gamma; + var joint_combiner; + + std::array evaluations; + + var group_gen; + std::size_t domain_size; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + } + + result_type() { + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + generate_assignments_constants(assignment, params, start_row_index); + + std::vector tokens = rpn_from_string(params.expression); + + std::vector stack; + std::vector cache; + + var endo_factor(0, row, false, var::column_type::constant); + row++; + + auto mds = mds_vars(row); + row += mds_size * mds_size; + + for (token_value_type t : tokens) { + switch (t.type) { + case token_type::alpha: + stack.emplace_back(params.alpha); + break; + case token_type::beta: + stack.emplace_back(params.beta); + break; + case token_type::gamma: + stack.emplace_back(params.gamma); + break; + case token_type::joint_combiner: + stack.emplace_back(params.joint_combiner); + break; + case token_type::endo_coefficient: + stack.emplace_back(endo_factor); + break; + case token_type::mds: { + std::size_t mds_row = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.first.data); + std::size_t mds_col = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.second.data); + stack.emplace_back(mds[mds_row][mds_col]); + break; + } + case token_type::literal: { + var literal(0, row, false, var::column_type::constant); + stack.emplace_back(literal); + row++; + break; + } + case token_type::cell: { + std::size_t cell_col = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.first.data); + std::size_t cell_row = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.second.data); + var cell_val = var_from_evals(params.evaluations, cell_col, cell_row); + stack.emplace_back(cell_val); + break; + } + case token_type::dup: + stack.emplace_back(stack.back()); + break; + case token_type::pow: { + var exponent(0, row, false, var::column_type::constant); + row++; + + var res = zk::components::generate_circuit( + bp, assignment, {stack.back(), exponent}, row) + .output; + row += exponentiation_component::rows_amount; + stack[stack.size() - 1] = res; + break; + } + case token_type::add: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = + zk::components::generate_circuit(bp, assignment, {x, y}, row) + .output; + row += add_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::mul: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = + zk::components::generate_circuit(bp, assignment, {x, y}, row) + .output; + row += mul_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::sub: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = + zk::components::generate_circuit(bp, assignment, {y, x}, row) + .output; + row += sub_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::vanishes_on_last_4_rows: { + var res = vanishes_on_last_4_rows_component::generate_circuit( + bp, + assignment, + {params.group_gen, params.domain_size, params.eval_point}, + row) + .output; + row += vanishes_on_last_4_rows_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::unnormalized_lagrange_basis: { + var res = unnormalized_lagrange_basis_component::generate_circuit( + bp, + assignment, + {params.group_gen, params.domain_size, params.eval_point, t.int_data}, + row) + .output; + row += unnormalized_lagrange_basis_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::store: { + var x = stack.back(); + cache.emplace_back(x); + break; + } + case token_type::load: { + std::size_t idx = (std::size_t) typename BlueprintFieldType::integral_type(t.value.first.data); + stack.push_back(cache[idx]); + break; + } + } + } + + assert(row == start_row_index + rows_amount); + + result_type res; + res.output = stack[stack.size() - 1]; + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::vector tokens = rpn_from_string(params.expression); + + std::vector stack; + std::vector cache; + + var endo_factor(0, row, false, var::column_type::constant); + row += 1; + + auto mds = mds_vars(row); + row += mds_size * mds_size; + + for (token_value_type t : tokens) { + switch (t.type) { + case token_type::alpha: + stack.emplace_back(params.alpha); + break; + case token_type::beta: + stack.emplace_back(params.beta); + break; + case token_type::gamma: + stack.emplace_back(params.gamma); + break; + case token_type::joint_combiner: + stack.emplace_back(params.joint_combiner); + break; + case token_type::endo_coefficient: + stack.emplace_back(endo_factor); + break; + case token_type::mds: { + std::size_t mds_row = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.first.data); + std::size_t mds_col = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.second.data); + stack.emplace_back(mds[mds_row][mds_col]); + break; + } + case token_type::literal: { + var literal(0, row, false, var::column_type::constant); + stack.emplace_back(literal); + row++; + break; + } + case token_type::cell: { + std::size_t cell_col = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.first.data); + std::size_t cell_row = (std::size_t) + typename BlueprintFieldType::integral_type(t.value.second.data); + var cell_val = var_from_evals(params.evaluations, cell_col, cell_row); + stack.emplace_back(cell_val); + break; + } + case token_type::dup: + stack.emplace_back(stack.back()); + break; + case token_type::pow: { + var exponent(0, row, false, var::column_type::constant); + row++; + + var res = exponentiation_component::generate_assignments( + assignment, {stack.back(), exponent}, row) + .output; + row += exponentiation_component::rows_amount; + + stack[stack.size() - 1] = res; + break; + } + case token_type::add: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = add_component::generate_assignments(assignment, {x, y}, row).output; + row += add_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::mul: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = mul_component::generate_assignments(assignment, {x, y}, row).output; + row += mul_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::sub: { + var x = stack.back(); + stack.pop_back(); + var y = stack.back(); + stack.pop_back(); + var res = sub_component::generate_assignments(assignment, {y, x}, row).output; + row += sub_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::vanishes_on_last_4_rows: { + var res = + vanishes_on_last_4_rows_component::generate_assignments( + assignment, {params.group_gen, params.domain_size, params.eval_point}, row) + .output; + row += vanishes_on_last_4_rows_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::unnormalized_lagrange_basis: { + var res = unnormalized_lagrange_basis_component::generate_assignments( + assignment, + {params.group_gen, params.domain_size, params.eval_point, t.int_data}, + row) + .output; + row += unnormalized_lagrange_basis_component::rows_amount; + stack.push_back(res); + break; + } + case token_type::store: { + var x = stack.back(); + cache.emplace_back(x); + break; + } + case token_type::load: { + std::size_t idx = (std::size_t) typename BlueprintFieldType::integral_type(t.value.first.data); + stack.push_back(cache[idx]); + break; + } + } + } + + assert(row == start_row_index + rows_amount); + + result_type res; + res.output = stack[stack.size() - 1]; + return res; + } + + private: + static void generate_assignments_constants( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + typename KimchiParamsType::curve_type::base_field_type::integral_type endo_integral_base = + typename KimchiParamsType::curve_type::base_field_type::integral_type( + endo_scalar_component::endo_q.data); + typename BlueprintFieldType::integral_type endo_integral = endo_integral_base; + assignment.constant(0)[row] = typename BlueprintFieldType::value_type(endo_integral); + row++; + + std::array, mds_size> mds = + poseidon_component::mds_constants(); + for (std::size_t i = 0; i < mds_size; i++) { + for (std::size_t j = 0; j < mds_size; j++) { + assignment.constant(0)[row] = mds[i][j]; + row++; + } + } + + std::vector tokens = rpn_from_string(params.expression); + + for (token_value_type t : tokens) { + switch (t.type) { + case token_type::literal: { + assignment.constant(0)[row] = t.value.first; + row++; + break; + } + case token_type::pow: { + assignment.constant(0)[row] = t.value.first; + row++; + row += exponentiation_component::rows_amount; + break; + } + case token_type::add: { + row += add_component::rows_amount; + break; + } + case token_type::mul: { + row += mul_component::rows_amount; + break; + } + case token_type::sub: { + row += sub_component::rows_amount; + break; + } + case token_type::vanishes_on_last_4_rows: { + row += vanishes_on_last_4_rows_component::rows_amount; + break; + } + case token_type::unnormalized_lagrange_basis: { + row += unnormalized_lagrange_basis_component::rows_amount; + break; + } + } + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_RPN_EXPRESSION_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_string_literal.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_string_literal.hpp new file mode 100644 index 000000000..7b2368c71 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/rpn_string_literal.hpp @@ -0,0 +1,162 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_RPN_STRING_LITERAL_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_RPN_STRING_LITERAL_HPP + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + constexpr const std::size_t count_delimiters(const char *expression) { + size_t i = 0; + size_t cnt = 0; + for (; expression[i] != '\0'; i++) { + if (expression[i] == ';') { + cnt++; + } + } + return cnt; + } + + constexpr const std::size_t str_len(const char *expression) { + size_t size = 0; + for (; expression[size] != '\0'; size++) { + } + return size; + } + + constexpr std::size_t find_str(const char *expression, const char *str, std::size_t n, std::size_t start_pos, + std::size_t end_pos) { + size_t j = 0; + size_t i = start_pos; + for (; i < end_pos; i++) { + for (j = 0; j < n && expression[i + j] == str[j]; j++) + ; + if (j == n) { + return i; + } + } + return std::string::npos; + } + + template + constexpr size_t rpn_component_rows(const char *expression) { + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using sub_component = zk::components::subtraction; + + using exponentiation_component = zk::components::exponentiation; + + using vanishes_on_last_4_rows_component = zk::components::vanishes_on_last_4_rows< + ArithmetizationType, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14>; + + using unnormalized_lagrange_basis_component = zk::components::unnormalized_lagrange_basis; + + const std::size_t literal_string_size = str_len(expression); + + const size_t mds_size = 3; + std::array str_start = {}; + + std::array str_end = {}; + str_start[0] = 0; + str_end[tokens_array_size - 1] = literal_string_size; + size_t i = 0; + const char *alpha_c = "Alpha"; + const char *beta_c = "Beta"; + const char *gamma_c = "Gamma"; + const char *joint_combiner_c = "JointCombiner"; + const char *endo_coefficient_c = "EndoCoefficient"; + const char *mds_c = "Mds"; + const char *literal_c = "Literal"; + const char *cell_c = "Cell"; + const char *dup_c = "Dup"; + const char *pow_c = "Pow"; + const char *add_c = "Add"; + const char *mul_c = "Mul"; + const char *sub_c = "Sub"; + const char *vanishes_on_last_4_rows_c = "VanishesOnLast4Rows"; + const char *unnormalized_lagrange_basis_c = "UnnormalizedLagrangeBasis"; + const char *store_c = "Store"; + const char *load_c = "Load"; + const char *del = ";"; + for (i = 0; i < tokens_array_size - 1; i++) { + size_t pos = find_str(expression, del, 1, str_start[i], literal_string_size); + str_end[i] = pos; + str_start[i + 1] = pos + 1; + } + size_t rows = 1 + mds_size * mds_size; + for (i = 0; i < tokens_array_size; i++) { + if (find_str(expression, literal_c, 7, str_start[i], str_end[i]) != std::string::npos) { + rows++; + } else if (find_str(expression, pow_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows++; + rows += exponentiation_component::rows_amount; + } else if (find_str(expression, add_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows += add_component::rows_amount; + } else if (find_str(expression, mul_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows += mul_component::rows_amount; + } else if (find_str(expression, sub_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows += sub_component::rows_amount; + } else if (find_str(expression, vanishes_on_last_4_rows_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows += vanishes_on_last_4_rows_component::rows_amount; + } + else if (find_str(expression, unnormalized_lagrange_basis_c, 3, str_start[i], str_end[i]) != std::string::npos) { + rows += unnormalized_lagrange_basis_component::rows_amount; + } + } + + return rows; + } + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_RPN_STRING_LITERAL_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/unnormalized_lagrange_basis.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/unnormalized_lagrange_basis.hpp new file mode 100644 index 000000000..93ac36e81 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/unnormalized_lagrange_basis.hpp @@ -0,0 +1,195 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_UNNORMALIZED_LAGRANGE_BASIS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_UNNORMALIZED_LAGRANGE_BASIS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Compute the ith unnormalized lagrange basis + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/expr.rs#L150 + // Input: group generator (w), + // i, + // domain_size, + // evaluation point (x) + // Output: (x^domain_size - 1) / (x - w^i) + template + class unnormalized_lagrange_basis; + + template + class unnormalized_lagrange_basis< + snark::plonk_constraint_system, W0, W1, W2, W3, W4, W5, + W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using sub_component = zk::components::subtraction; + using exp_component = zk::components::exponentiation; + using div_component = zk::components::division; + + constexpr static const std::size_t selector_seed = 0x0f25; + + constexpr static const std::size_t zk_rows = 3; + + public: + constexpr static const std::size_t rows_amount = 3 + 2 * exp_component::rows_amount + + 2 * sub_component::rows_amount + + div_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var group_gen; + std::size_t domain_size; + var x; + int i; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index + rows_amount - div_component::rows_amount; + output = typename div_component::result_type(row).output; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + var domain_size(0, start_row_index, false, var::column_type::constant); + var basis_element(0, start_row_index + 1, false, var::column_type::constant); + var one(0, start_row_index + 2, false, var::column_type::constant); + + std::size_t row = start_row_index; + row += 3; // skip row for constants in exp_component + + var denominator = + exp_component::generate_circuit(bp, assignment, {params.group_gen, basis_element}, row) + .output; + row += exp_component::rows_amount; + + denominator = zk::components::generate_circuit(bp, assignment, + {params.x, denominator}, row) + .output; + row += sub_component::rows_amount; + + var numerator = + exp_component::generate_circuit(bp, assignment, {params.x, domain_size}, row).output; + row += exp_component::rows_amount; + numerator = + zk::components::generate_circuit(bp, assignment, {numerator, one}, row) + .output; + row += sub_component::rows_amount; + + var res = zk::components::generate_circuit(bp, assignment, + {numerator, denominator}, row) + .output; + row += div_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var domain_size(0, start_row_index, false, var::column_type::constant); + var basis_element(0, start_row_index + 1, false, var::column_type::constant); + var one(0, start_row_index + 2, false, var::column_type::constant); + + std::size_t row = start_row_index; + row += 3; // skip row for constants in exp_component + + var denominator = + exp_component::generate_assignments(assignment, {params.group_gen, basis_element}, row) + .output; + row += exp_component::rows_amount; + + denominator = + sub_component::generate_assignments(assignment, {params.x, denominator}, row).output; + row += sub_component::rows_amount; + + var numerator = + exp_component::generate_assignments(assignment, {params.x, domain_size}, row).output; + row += exp_component::rows_amount; + numerator = sub_component::generate_assignments(assignment, {numerator, one}, row).output; + row += sub_component::rows_amount; + + var res = div_component::generate_assignments(assignment, {numerator, denominator}, row).output; + row += div_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = params.domain_size; + row++; + assignment.constant(0)[row] = + params.i >= 0 ? params.i : params.domain_size - std::size_t(-params.i); + row++; + assignment.constant(0)[row] = 1; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_UNNORMALIZED_LAGRANGE_BASIS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/vanishes_on_last_4_rows.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/vanishes_on_last_4_rows.hpp new file mode 100644 index 000000000..a110dd8a0 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/constraints/vanishes_on_last_4_rows.hpp @@ -0,0 +1,214 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_VANISHES_ON_LAST_4_ROWS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_VANISHES_ON_LAST_4_ROWS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // (x - w^{n - 4}) (x - w^{n - 3}) * (x - w^{n - 2}) * (x - w^{n - 1}) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/polynomials/permutation.rs#L64 + // Input: group generator (w), + // domain size (n), + // evaluation point (x) + // Output: (x - w^{n - 4}) (x - w^{n - 3}) * (x - w^{n - 2}) * (x - w^{n - 1}) + template + class vanishes_on_last_4_rows; + + template + class vanishes_on_last_4_rows, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using exp_component = zk::components::exponentiation; + using sub_component = zk::components::subtraction; + + constexpr static const std::size_t selector_seed = 0x0f25; + + constexpr static const std::size_t zk_rows = 3; + + public: + constexpr static const std::size_t rows_amount = 1 + exp_component::rows_amount + + 6 * mul_component::rows_amount + + 4 * sub_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var group_gen; + std::size_t domain_size; + var x; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + var domain_size = var(0, start_row_index, false, var::column_type::constant); + + std::size_t row = start_row_index; + row++; // skip row for constants in exp_component + + result_type result(row); + + var w1 = exp_component::generate_circuit(bp, assignment, {params.group_gen, domain_size}, row) + .output; + row += exp_component::rows_amount; + var w2 = + zk::components::generate_circuit(bp, assignment, {w1, params.group_gen}, row) + .output; + row += mul_component::rows_amount; + var w3 = + zk::components::generate_circuit(bp, assignment, {w2, params.group_gen}, row) + .output; + row += mul_component::rows_amount; + var w4 = + zk::components::generate_circuit(bp, assignment, {w3, params.group_gen}, row) + .output; + row += mul_component::rows_amount; + + var a1 = + zk::components::generate_circuit(bp, assignment, {params.x, w1}, row).output; + row += sub_component::rows_amount; + var a2 = + zk::components::generate_circuit(bp, assignment, {params.x, w2}, row).output; + row += sub_component::rows_amount; + var a3 = + zk::components::generate_circuit(bp, assignment, {params.x, w3}, row).output; + row += sub_component::rows_amount; + var a4 = + zk::components::generate_circuit(bp, assignment, {params.x, w4}, row).output; + row += sub_component::rows_amount; + + var ans1 = + zk::components::generate_circuit(bp, assignment, {a1, a2}, row).output; + row += mul_component::rows_amount; + var ans2 = + zk::components::generate_circuit(bp, assignment, {ans1, a3}, row).output; + row += mul_component::rows_amount; + result.output = + zk::components::generate_circuit(bp, assignment, {ans2, a4}, row).output; + row += mul_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var domain_size = var(0, start_row_index, false, var::column_type::constant); + + std::size_t row = start_row_index; + row++; // skip row for constants in exp_component + + result_type result(row); + + var w1 = exp_component::generate_assignments(assignment, {params.group_gen, domain_size}, row) + .output; + row += exp_component::rows_amount; + var w2 = mul_component::generate_assignments(assignment, {w1, params.group_gen}, row).output; + row += mul_component::rows_amount; + var w3 = mul_component::generate_assignments(assignment, {w2, params.group_gen}, row).output; + row += mul_component::rows_amount; + var w4 = mul_component::generate_assignments(assignment, {w3, params.group_gen}, row).output; + row += mul_component::rows_amount; + + var a1 = sub_component::generate_assignments(assignment, {params.x, w1}, row).output; + row += sub_component::rows_amount; + var a2 = sub_component::generate_assignments(assignment, {params.x, w2}, row).output; + row += sub_component::rows_amount; + var a3 = sub_component::generate_assignments(assignment, {params.x, w3}, row).output; + row += sub_component::rows_amount; + var a4 = sub_component::generate_assignments(assignment, {params.x, w4}, row).output; + row += sub_component::rows_amount; + + var ans1 = mul_component::generate_assignments(assignment, {a1, a2}, row).output; + row += mul_component::rows_amount; + var ans2 = mul_component::generate_assignments(assignment, {ans1, a3}, row).output; + row += mul_component::rows_amount; + result.output = mul_component::generate_assignments(assignment, {ans2, a4}, row).output; + row += mul_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result; + } + + private: + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = params.domain_size - zk_rows - 1; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_VANISHES_ON_LAST_4_ROWS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/inner_constants.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/inner_constants.hpp new file mode 100644 index 000000000..c3689c8f5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/inner_constants.hpp @@ -0,0 +1,88 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_INNER_CONSTANTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_INNER_CONSTANTS_HPP + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct kimchi_inner_constants { + + public: + using commitment_params_type = typename KimchiParamsType::commitment_params_type; + + constexpr static std::size_t ft_generic_size = 2 * 5; + constexpr static std::size_t permutation_constraints = 3; + + constexpr static std::size_t evaluations_in_batch_size = + KimchiParamsType::prev_challenges_size // recursion + + 1 // p_comm + + 1 // ft_comm + + 1 // z_comm + + 1 // generic_comm + + 1 // psm_comm + + KimchiParamsType::witness_columns // w_comm + + KimchiParamsType::permut_size - 1 + KimchiParamsType::lookup_comm_size; + + constexpr static std::size_t srs_padding_size() { + std::size_t srs_two_power = 1 << (boost::static_log2::value); + std::size_t padding_size = srs_two_power == commitment_params_type::srs_len ? + 0 : + srs_two_power * 2 - commitment_params_type::srs_len; + return padding_size; + } + + constexpr static std::size_t final_msm_size(const std::size_t batch_size) { + return 1 // H + + commitment_params_type::srs_len // G + + srs_padding_size() + + (1 // opening.G + + 1 // U + + 2 * commitment_params_type::eval_rounds + + evaluations_in_batch_size * commitment_params_type::shifted_commitment_split + 1 // U + + 1) // opening.delta + * batch_size; + } + + constexpr static std::size_t f_comm_msm_size = + 1 + ft_generic_size + KimchiParamsType::circuit_params::index_terms_list::size; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_INNER_CONSTANTS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/limbs.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/limbs.hpp new file mode 100644 index 000000000..41ecf18bc --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/limbs.hpp @@ -0,0 +1,583 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LIMBS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LIMBS_HPP + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + ///////////////// From Limbs //////////////////////////////// + // Recalculate field element from two 64-bit chunks + // It's a part of transcript functionality + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/sponge.rs#L87 + // Input: x1 = [a_0, ..., a_63], x2 = [b_0, ..., b_63] + // Output: y = [a_0, ...., a_63, b_0, ..., b_63] + template + class from_limbs; + + template + class from_limbs>: + public plonk_component { + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return from_limbs::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(3)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + static constexpr const std::size_t gates_amount = 1; + + struct input_type { + var first_limb = var(0, 0, false); + var second_limb = var(0, 0, false); + input_type(std::array input) : first_limb(input[0]), second_limb(input[1]) { + } + input_type(var first, var second) : first_limb(first), second_limb(second) { + } + + std::vector> all_vars() { + return {first_limb, second_limb}; + } + }; + + struct result_type { + var result = var(0, 0); + + result_type(const from_limbs &component, std::size_t start_row_index) { + result = var(component.W(2), static_cast(start_row_index), false, var::column_type::witness); + } + + std::vector> all_vars() { + return {result}; + } + }; + + template + from_limbs(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + from_limbs(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + from_limbs( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + + }; + + template + using plonk_from_limbs = from_limbs>; + + template + typename plonk_from_limbs::result_type + generate_circuit( + const plonk_from_limbs &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_from_limbs::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_from_limbs::result_type(component, start_row_index); + } + + template + typename plonk_from_limbs::result_type + generate_assignments( + const plonk_from_limbs &component, + assignment> &assignment, + const typename plonk_from_limbs::input_type &instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type first_limb = var_value(assignment, instance_input.first_limb); + typename BlueprintFieldType::value_type second_limb = var_value(assignment, instance_input.second_limb); + assignment.witness(component.W(0), row) = first_limb; + assignment.witness(component.W(1), row) = second_limb; + typename BlueprintFieldType::value_type scalar = 2; + scalar = scalar.pow(64) * second_limb + first_limb; + assignment.witness(component.W(2), row) = scalar; + + return typename plonk_from_limbs::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_from_limbs &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_from_limbs::input_type + &instance_input) { + + using var = typename plonk_from_limbs::var; + + typename BlueprintFieldType::value_type scalar = 2; + auto constraint_1 = + var(component.W(0), 0) + var(component.W(1), 0) * scalar.pow(64) - var(component.W(2), 0); + + return bp.add_gate({constraint_1}); + } + + template + void generate_copy_constraints( + const plonk_from_limbs &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_from_limbs::input_type &instance_input, + const std::uint32_t start_row_index) { + + bp.add_copy_constraint( + {{component.W(0), static_cast(start_row_index), false}, + {instance_input.first_limb.index, instance_input.first_limb.rotation, false, instance_input.first_limb.type}}); + bp.add_copy_constraint( + {{component.W(1), static_cast(start_row_index), false}, + {instance_input.second_limb.index, instance_input.second_limb.rotation, false, instance_input.second_limb.type}}); + } + + /////////////// To Limbs //////////////////////////////// + // Split field element into four 64-bit chunks + // It's a part of transcript functionality + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/sponge.rs#L110 + // Input: x = [a_0, ...., a255] + // Output: y0 = [a_0, ..., a_63], y1 = [a_64, ..., a_127], y2 = [a_128, ..., a_191], y3 = [a_192, ..., + // a_255] + template + class to_limbs; + + template + class to_limbs>: + public plonk_component { + + + constexpr static const std::size_t chunk_size = 64; + using range_check_component = nil::blueprint::components::range_check< + crypto3::zk::snark::plonk_constraint_system>; + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return to_limbs::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(range_check_component::get_gate_manifest(witness_amount, chunk_size)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(15)), + false + ).merge_with(range_check_component::get_manifest(chunk_size_public)); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1 + 2 * chunk_amount * + range_check_component::get_rows_amount(witness_amount, chunk_size); + } + + constexpr static const std::size_t chunk_size_public = chunk_size; + constexpr static const std::size_t chunk_amount = 4; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + constexpr static const std::size_t gates_amount = 1; + + struct input_type { + var param; + + input_type(var value) : param(value) { + } + + std::vector> all_vars() { + return {param}; + } + }; + + struct result_type { + std::array result; + + result_type(const to_limbs &component, std::size_t start_row_index) { + result = {var(component.W(1), static_cast(start_row_index), false, var::column_type::witness), + var(component.W(2), static_cast(start_row_index), false, var::column_type::witness), + var(component.W(3), static_cast(start_row_index), false, var::column_type::witness), + var(component.W(4), static_cast(start_row_index), false, var::column_type::witness)}; + } + + std::vector> all_vars() { + return {result[0], result[1], result[2], result[3]}; + } + }; + + template + to_limbs(ContainerType witness): + component_type(witness, {}, {}, get_manifest()){}; + + template + to_limbs(WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input): + component_type(witness, constant, public_input, get_manifest()){}; + + to_limbs( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs): + component_type(witnesses, constants, public_inputs, get_manifest()){}; + }; + + template + using plonk_to_limbs = to_limbs>; + + template + typename plonk_to_limbs::result_type + generate_circuit( + const plonk_to_limbs &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_to_limbs::input_type &instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_to_limbs::var; + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = plonk_to_limbs; + range_check range_check_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8), component.W(9), + component.W(10), component.W(11), component.W(12), component.W(13), component.W(14)},{component.C(0)},{},component_type::chunk_size_public); + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + std::size_t row = start_row_index; + std::array chunks = { + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false), + var(component.W(4), row, false) + }; + std::array b_chunks_vars = { + var(component.W(5), row, false), + var(component.W(6), row, false), + var(component.W(7), row, false), + var(component.W(8), row, false) + }; + + row++; + + for (std::size_t i = 0; i < component_type::chunk_amount; i++) { + generate_circuit(range_check_instance, bp, assignment, {chunks[i]}, row); + row += range_check_instance.rows_amount; + generate_circuit(range_check_instance, bp, assignment, {b_chunks_vars[i]}, row); + row += range_check_instance.rows_amount; + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_to_limbs::result_type(component, start_row_index); + + } + + template + typename plonk_to_limbs::result_type + generate_assignments( + const plonk_to_limbs &component, + assignment> &assignment, + const typename plonk_to_limbs::input_type instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_to_limbs::var; + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = plonk_to_limbs; + range_check range_check_instance( + {component.W(0), component.W(1), component.W(2), component.W(3), component.W(4), + component.W(5), component.W(6), component.W(7), component.W(8), component.W(9), + component.W(10), component.W(11), component.W(12), component.W(13), component.W(14)},{component.C(0)},{},component_type::chunk_size_public); + + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type value = var_value(assignment, instance_input.param); + auto value_data = value.data; + auto shifted_data = value_data >> 64 << 64; + assignment.witness(component.W(0), row) = value_data; + assignment.witness(component.W(1), row) = value_data - shifted_data; + value_data = value_data >> 64; + shifted_data = shifted_data >> 64 >> 64 << 64; + assignment.witness(component.W(2), row) = value_data - shifted_data; + value_data = value_data >> 64; + shifted_data = shifted_data >> 64 >> 64 << 64; + assignment.witness(component.W(3), row) = value_data - shifted_data; + value_data = value_data >> 64; + assignment.witness(component.W(4), row) = value_data; + + typename BlueprintFieldType::extended_integral_type modulus_p = BlueprintFieldType::modulus; + typename BlueprintFieldType::extended_integral_type one = 1; + typename BlueprintFieldType::extended_integral_type power = (one << 256); + typename BlueprintFieldType::extended_integral_type c = power - modulus_p; + typename BlueprintFieldType::extended_integral_type mask = (one << 64) - 1; + std::array c_chunks = { + c & mask, (c >> 64) & mask, (c >> 128) & mask, (c >> 192) & mask}; + + typename BlueprintFieldType::extended_integral_type b = + typename BlueprintFieldType::extended_integral_type(typename BlueprintFieldType::integral_type(value.data)) + c; + std::array b_chunks = { + b & mask, (b >> 64) & mask, (b >> 128) & mask, (b >> 192) & mask}; + assignment.witness(component.W(5), row) = b_chunks[0]; + assignment.witness(component.W(6), row) = b_chunks[1]; + assignment.witness(component.W(7), row) = b_chunks[2]; + assignment.witness(component.W(8), row) = b_chunks[3]; + + // We must be careful here not to have negative values. + typename BlueprintFieldType::extended_integral_type W9_part = typename BlueprintFieldType::integral_type( + assignment.witness(component.W(1), row).data); + W9_part += c_chunks[0]; + if (W9_part > b_chunks[0]) { + assignment.witness(component.W(9), row) = (W9_part - b_chunks[0]) >> 64; + } else { + assignment.witness(component.W(9), row) = BlueprintFieldType::modulus - typename BlueprintFieldType::integral_type((b_chunks[0] - W9_part) >> 64); + } + + typename BlueprintFieldType::extended_integral_type W10_part = typename BlueprintFieldType::integral_type( + assignment.witness(component.W(2), row).data); + W10_part += typename BlueprintFieldType::integral_type(assignment.witness(component.W(9), row).data); + W10_part += c_chunks[1]; + if (W10_part > b_chunks[1]) { + assignment.witness(component.W(10), row) = (W10_part - b_chunks[1]) >> 64; + } else { + assignment.witness(component.W(10), row) = BlueprintFieldType::modulus - typename BlueprintFieldType::integral_type((b_chunks[1] - W10_part) >> 64); + } + + typename BlueprintFieldType::extended_integral_type W11_part = typename BlueprintFieldType::integral_type( + assignment.witness(component.W(3), row).data); + W11_part += typename BlueprintFieldType::integral_type(assignment.witness(component.W(10), row).data); + W11_part += c_chunks[2]; + if (W11_part > b_chunks[2]) { + assignment.witness(component.W(11), row) = (W11_part - b_chunks[2]) >> 64; + } else { + assignment.witness(component.W(11), row) = BlueprintFieldType::modulus - typename BlueprintFieldType::integral_type((b_chunks[2] - W11_part) >> 64); + } + + std::array chunks = { + var(component.W(1), row, false), + var(component.W(2), row, false), + var(component.W(3), row, false), + var(component.W(4), row, false)}; + + std::array b_chunks_vars = { + var(component.W(5), row, false), + var(component.W(6), row, false), + var(component.W(7), row, false), + var(component.W(8), row, false)}; + + row++; + + for (std::size_t i = 0; i < component_type::chunk_amount; i++) { + generate_assignments(range_check_instance, assignment, {chunks[i]}, row); + row += range_check_instance.rows_amount; + generate_assignments(range_check_instance, assignment, {b_chunks_vars[i]}, row); + row += range_check_instance.rows_amount; + } + + return typename plonk_to_limbs::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_to_limbs &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_to_limbs::input_type + &instance_input) { + + using var = typename plonk_to_limbs::var; + + typename BlueprintFieldType::value_type scalar = 2; + typename BlueprintFieldType::extended_integral_type modulus_p = BlueprintFieldType::modulus; + typename BlueprintFieldType::extended_integral_type one = 1; + typename BlueprintFieldType::extended_integral_type power = (one << 256); + typename BlueprintFieldType::extended_integral_type c = power - modulus_p; + typename BlueprintFieldType::extended_integral_type mask = (one << 64) - 1; + std::array c_chunks = { + c & mask, (c >> 64) & mask, (c >> 128) & mask, (c >> 192) & mask}; + auto constraint_1 = + var(component.W(1), 0) + var(component.W(2), 0) * scalar.pow(64) + + var(component.W(3), 0) * scalar.pow(128) + var(component.W(4), 0) * scalar.pow(192) - + var(component.W(0), 0); + auto constraint_2 = + -var(component.W(1), 0) - typename BlueprintFieldType::value_type(c_chunks[0]) + + var(component.W(5), 0) + var(component.W(9), 0) * (one << 64); + auto constraint_3 = + -var(component.W(2), 0) - typename BlueprintFieldType::value_type(c_chunks[1]) - + var(component.W(9), 0) + var(component.W(6), 0) + var(component.W(10), 0) * (one << 64); + auto constraint_4 = + -var(component.W(3), 0) - typename BlueprintFieldType::value_type(c_chunks[2]) - + var(component.W(10), 0) + var(component.W(7), 0) + var(component.W(11), 0) * (one << 64); + auto constraint_5 = + -var(component.W(4), 0) - typename BlueprintFieldType::value_type(c_chunks[3]) - + var(component.W(11), 0) + var(component.W(8), 0); + + auto constraint_6 = var(component.W(9), 0) * (var(component.W(9), 0) - 1); + auto constraint_7 = var(component.W(10), 0) * (var(component.W(10), 0) - 1); + auto constraint_8 = var(component.W(11), 0) * (var(component.W(11), 0) - 1); + + return bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, + constraint_7, constraint_8}); + } + + template + void generate_copy_constraints( + const plonk_to_limbs &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_to_limbs::input_type &instance_input, + const std::uint32_t start_row_index) { + + bp.add_copy_constraint({{component.W(0), static_cast(start_row_index), false}, + {instance_input.param.index, instance_input.param.rotation, false, instance_input.param.type}}); + } + + template + class input_type_converter; + + template + class result_type_converter; + + template + class input_type_converter> { + + using component_type = plonk_to_limbs; + using input_type = typename component_type::input_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + public: + static input_type convert( + const input_type &input, + nil::blueprint::assignment> + &assignment, + nil::blueprint::assignment> + &tmp_assignment) { + + input_type new_input(var(0, 0, false, var::column_type::public_input)); + tmp_assignment.public_input(0, 0) = var_value(assignment, input.param); + return new_input; + } + + static var deconvert_var(const input_type &input, + var variable) { + BOOST_ASSERT(variable.type == var::column_type::public_input); + return input.param; + } + }; + + template + class result_type_converter> { + + using component_type = plonk_to_limbs; + using input_type = typename component_type::input_type; + using result_type = typename component_type::result_type; + using stretcher_type = component_stretcher; + public: + static result_type convert(const stretcher_type &component, const result_type old_result, + const input_type &instance_input, std::size_t start_row_index) { + result_type new_result(component.component, start_row_index); + + for (std::size_t i = 0; i < 4; i++) { + new_result.result[i] = component.move_var( + old_result.result[i], + start_row_index + component.line_mapping[old_result.result[i].rotation], + instance_input + ); + } + + return new_result; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ORACLES_DETAIL_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fq.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fq.hpp new file mode 100644 index 000000000..d9dcf17be --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fq.hpp @@ -0,0 +1,145 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FQ_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FQ_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // map_fq set copy constraints between input fq_data (which is input for scalar field components) and + // recalculated fq_data (base field components output) + // Input: common data (generated by the base part of the veridier) for scalar and base verifiers + // Output: - + template + class map_fq; + + template + class map_fq, + CurveType, KimchiParamsType, KimchiCommitmentParamsType, BatchSize, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::base_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using proof_binding = + typename zk::components::binding; + + using fq_data = typename proof_binding::template fq_data; + + constexpr static const std::size_t selector_seed = 0x0f2D; + + public: + constexpr static const std::size_t rows_amount = 0; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + fq_data data_public; + fq_data data_recalculated; + }; + + struct result_type { }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + generate_assignments_constant(bp, assignment, params, start_row_index); + + return result_type(); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + return result_type(); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FQ_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fr.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fr.hpp new file mode 100644 index 000000000..133ced02e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/map_fr.hpp @@ -0,0 +1,149 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FR_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FR_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // map_fr set copy constraints between input fr_data (which is input for base field components) and + // recalculated fr_data (scalar field components output) + // Input: common data (generated by the scalar part of the veridier) for scalar and base verifiers + // Output: - + template + class map_fr; + + template< + typename CurveType, typename KimchiParamsType, + typename KimchiCommitmentParamsType, std::size_t BatchSize, + std::size_t W0, std::size_t W1, std::size_t W2, std::size_t W3, std::size_t W4, std::size_t W5, + std::size_t W6, std::size_t W7, std::size_t W8, std::size_t W9, std::size_t W10, std::size_t W11, + std::size_t W12, std::size_t W13, std::size_t W14> + class map_fr< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, KimchiCommitmentParamsType, BatchSize, W0, W1, W2, W3, W4, W5, + W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using proof_binding = + typename zk::components::binding; + + using fr_data = typename proof_binding::template fr_data; + + constexpr static const std::size_t selector_seed = 0x0f2B; + + public: + constexpr static const std::size_t rows_amount = 0; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + fr_data data_public; + fr_data data_recalculated; + }; + + struct result_type { }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + generate_assignments_constant(bp, assignment, params, start_row_index); + + return result_type(); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + return result_type(); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_MAP_FR_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly.hpp new file mode 100644 index 000000000..cac1cc843 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly.hpp @@ -0,0 +1,195 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Univariate polynomial at point + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L239 + // Input: challenges, x + // Output: (1 + challenges[-1] x)(1 + challenges[-2] x^2)(1 + challenges[-3] x^4)... + template + class b_poly; + + template + class b_poly, EvalRounds, W0, + W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + + constexpr static const std::size_t selector_seed = 0xf20; + + public: + constexpr static const std::size_t rows_amount = (EvalRounds - 1) * mul_component::rows_amount + + EvalRounds * ( + mul_component::rows_amount + add_component::rows_amount + mul_component::rows_amount + ); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array &challenges; + var eval_point; + var one; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + + for (std::size_t i = 1; i < EvalRounds; i++) { + row += mul_component::rows_amount; + } + var res; + for (std::size_t i = 0; i < EvalRounds; i++) { + row += mul_component::rows_amount; + + row += add_component::rows_amount; + + res = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + + output = res; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array pow_twos; + pow_twos[0] = params.eval_point; + for (std::size_t i = 1; i < EvalRounds; i++) { + pow_twos[i] = zk::components::generate_circuit( + bp, assignment, {pow_twos[i - 1], pow_twos[i - 1]}, row) + .output; + row += mul_component::rows_amount; + } + var res = params.one; + for (std::size_t i = 0; i < EvalRounds; i++) { + var mul_result = + zk::components::generate_circuit( + bp, assignment, {params.challenges[i], pow_twos[EvalRounds - 1 - i]}, row) + .output; + row += mul_component::rows_amount; + + var sum_result = zk::components::generate_circuit( + bp, assignment, {params.one, mul_result}, row) + .output; + row += add_component::rows_amount; + + res = + zk::components::generate_circuit(bp, assignment, {res, sum_result}, row) + .output; + row += mul_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array pow_twos; + pow_twos[0] = params.eval_point; + for (std::size_t i = 1; i < EvalRounds; i++) { + pow_twos[i] = + mul_component::generate_assignments(assignment, {pow_twos[i - 1], pow_twos[i - 1]}, row) + .output; + row += mul_component::rows_amount; + } + var res = params.one; + for (std::size_t i = 0; i < EvalRounds; i++) { + var mul_result = mul_component::generate_assignments( + assignment, {params.challenges[i], pow_twos[EvalRounds - 1 - i]}, row) + .output; + row += mul_component::rows_amount; + + var sum_result = + add_component::generate_assignments(assignment, {params.one, mul_result}, row).output; + row += add_component::rows_amount; + + res = mul_component::generate_assignments(assignment, {res, sum_result}, row).output; + row += mul_component::rows_amount; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly_coefficients.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly_coefficients.hpp new file mode 100644 index 000000000..2dd05990f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/b_poly_coefficients.hpp @@ -0,0 +1,146 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_COEFFICIENTS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_COEFFICIENTS_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Coefficients of univariate polynomial + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L251 + // Input: challenges + // Output: f = [c0, c1, ...], where f = (1 + challenges[-1] * X)(1 + challenges[-2] * X^2)(1 + + // challenges[-3] * X^4)... + template + class b_poly_coefficients; + + template + class b_poly_coefficients, + EvalRounds, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + + constexpr static const std::size_t selector_seed = 0x0f21; + + public: + constexpr static const std::size_t polynomial_len = 1 << EvalRounds; + + constexpr static const std::size_t rows_amount = mul_component::rows_amount * polynomial_len; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array &challenges; + var one; + }; + + struct result_type { + std::array output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array output; + output[0] = params.one; + std::size_t k = 0; + std::size_t pow = 1; + + for (std::size_t i = 1; i < polynomial_len; i++) { + std::size_t shift = i == pow ? 1 : 0; + k += shift; + pow <<= shift; + output[i] = zk::components::generate_circuit( + bp, assignment, + {output[i - (pow >> 1)], params.challenges[EvalRounds - 1 - (k - 1)]}, row) + .output; + row += mul_component::rows_amount; + } + + result_type res; + res.output = output; + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array output; + output[0] = params.one; + std::size_t k = 0; + std::size_t pow = 1; + + for (std::size_t i = 1; i < polynomial_len; i++) { + std::size_t shift = i == pow ? 1 : 0; + k += shift; + pow <<= shift; + output[i] = mul_component::generate_assignments( + assignment, + {output[i - (pow >> 1)], params.challenges[EvalRounds - 1 - (k - 1)]}, + row) + .output; + row += mul_component::rows_amount; + } + + result_type res; + res.output = output; + return res; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_B_POLY_COEFFICIENTS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/combine_proof_evals.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/combine_proof_evals.hpp new file mode 100644 index 000000000..e92cf835d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/combine_proof_evals.hpp @@ -0,0 +1,265 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMBINE_PROOF_EVALS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMBINE_PROOF_EVALS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Proof evals are element of the finite field, so combine works just as scalar multiplication + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/proof.rs#L105 + // Input: x, proof_evaluations (see kimchi_proof_evaluations): {w_0, ... w_14, z, ..., + // poseidon_selector} Output: proof_evaluations: {x * w_0, ... x * w_14, x * z, ..., x * + // poseidon_selector} + template + class combine_proof_evals; + + template + class combine_proof_evals, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + + constexpr static const std::size_t selector_seed = 0x0f23; + + constexpr static const std::size_t lookup_rows() { + std::size_t rows = 0; + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + rows += KimchiParamsType::circuit_params::lookup_columns * mul_component::rows_amount; + + rows += 2 * mul_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + rows += mul_component::rows_amount; + } + } + + return rows; + } + + public: + constexpr static const std::size_t rows_amount = + KimchiParamsType::witness_columns * mul_component::rows_amount // w + + mul_component::rows_amount // z + + (KimchiParamsType::permut_size - 1) * mul_component::rows_amount // s + + lookup_rows() + mul_component::rows_amount // generic + + mul_component::rows_amount; // poseidon + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + kimchi_proof_evaluations evals; + var x; + }; + + struct result_type { + kimchi_proof_evaluations output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + output.w[i] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + // z + output.z = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + output.s[i] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + // lookup + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + for (std::size_t i = 0; i < KimchiParamsType::circuit_params::lookup_columns; i++) { + output.lookup.sorted[i] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + + output.lookup.aggreg = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + + output.lookup.table = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + output.lookup.runtime = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + } + // generic_selector + output.generic_selector = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + // poseidon_selector + output.poseidon_selector = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + zk::components::generate_circuit(bp, assignment, + {params.evals.w[i], params.x}, row); + row += mul_component::rows_amount; + } + // z + zk::components::generate_circuit(bp, assignment, {params.evals.z, params.x}, + row); + row += mul_component::rows_amount; + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + zk::components::generate_circuit(bp, assignment, + {params.evals.s[i], params.x}, row); + row += mul_component::rows_amount; + } + // lookup + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + for (std::size_t i = 0; i < KimchiParamsType::circuit_params::lookup_columns; i++) { + zk::components::generate_circuit( + bp, assignment, {params.evals.lookup.sorted[i], params.x}, row); + row += mul_component::rows_amount; + } + + zk::components::generate_circuit( + bp, assignment, {params.evals.lookup.aggreg, params.x}, row); + row += mul_component::rows_amount; + + zk::components::generate_circuit(bp, assignment, + {params.evals.lookup.table, params.x}, row); + row += mul_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + zk::components::generate_circuit( + bp, assignment, {params.evals.lookup.runtime, params.x}, row); + row += mul_component::rows_amount; + } + } + // generic_selector + zk::components::generate_circuit(bp, assignment, + {params.evals.generic_selector, params.x}, row); + row += mul_component::rows_amount; + // poseidon_selector + zk::components::generate_circuit( + bp, assignment, {params.evals.poseidon_selector, params.x}, row); + row += mul_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + mul_component::generate_assignments(assignment, {params.evals.w[i], params.x}, row); + row += mul_component::rows_amount; + } + // z + mul_component::generate_assignments(assignment, {params.evals.z, params.x}, row); + row += mul_component::rows_amount; + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + mul_component::generate_assignments(assignment, {params.evals.s[i], params.x}, row); + row += mul_component::rows_amount; + } + // lookup + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + for (std::size_t i = 0; i < KimchiParamsType::circuit_params::lookup_columns; i++) { + mul_component::generate_assignments(assignment, + {params.evals.lookup.sorted[i], params.x}, row); + row += mul_component::rows_amount; + } + + mul_component::generate_assignments(assignment, {params.evals.lookup.aggreg, params.x}, + row); + row += mul_component::rows_amount; + + mul_component::generate_assignments(assignment, {params.evals.lookup.table, params.x}, row); + row += mul_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + mul_component::generate_assignments(assignment, {params.evals.lookup.runtime, params.x}, + row); + row += mul_component::rows_amount; + } + } + // generic_selector + mul_component::generate_assignments(assignment, {params.evals.generic_selector, params.x}, row); + row += mul_component::rows_amount; + // poseidon_selector + mul_component::generate_assignments(assignment, {params.evals.poseidon_selector, params.x}, + row); + row += mul_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMBINE_PROOF_EVALS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/element_powers.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/element_powers.hpp new file mode 100644 index 000000000..9f0384389 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/element_powers.hpp @@ -0,0 +1,155 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ELEMENT_POWERS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ELEMENT_POWERS_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // for (base, n) calculates [base^0, base^1, ..., base^n] + // n >= 0 + // Input: base, n + // Output: [base^0, base^1, ..., base^n] + template + class element_powers; + + template + class element_powers, n, W0, + W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + + constexpr static const std::size_t selector_seed = 0x0f0c; + + public: + constexpr static const std::size_t rows_amount = + n <= 1 ? 1 : (n - 2) * mul_component::rows_amount + 1; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var base; + var one; + }; + + struct result_type { + std::array output; + + result_type(std::size_t component_start_row) { + output[0] = var(W0, component_start_row, false); + if (n > 0) { + output[1] = var(W1, component_start_row, false); + } + for (std::size_t i = 2; i < n; i++) { + output[i] = typename mul_component::result_type(component_start_row + i - 1).output; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var base(W1, start_row_index, false); + var last_result(W1, start_row_index, false); + std::size_t row = start_row_index + 1; + for (std::size_t i = 2; i < n; i++) { + last_result = zk::components::generate_circuit(bp, assignment, + {base, last_result}, row) + .output; + row += mul_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + assignment.witness(W0)[start_row_index] = 1; + assignment.witness(W1)[start_row_index] = assignment.var_value(params.base); + var base(W1, start_row_index, false); + var last_result(W1, start_row_index, false); + row++; + + for (std::size_t i = 2; i < n; i++) { + last_result = + mul_component::generate_assignments(assignment, {base, last_result}, row).output; + row += mul_component::rows_amount; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + bp.add_copy_constraint({{W0, static_cast(start_row_index), false}, params.one}); + bp.add_copy_constraint({{W1, static_cast(start_row_index), false}, params.base}); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ELEMENT_POWERS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/ft_eval.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/ft_eval.hpp new file mode 100644 index 000000000..d04b81495 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/ft_eval.hpp @@ -0,0 +1,624 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_FT_EVAL_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_FT_EVAL_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // ft polynomial at zeta + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L320-L384 + // Input: + // Output: ft(zeta) + template + class ft_eval; + + template + class ft_eval, CurveType, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using sub_component = zk::components::subtraction; + using div_component = zk::components::division; + + using zkpm_eval_component = + zk::components::zkpm_evaluate; + + using zk_w3_component = zk::components::zk_w3; + + using verifier_index_type = kimchi_verifier_index_scalar; + + using index_terms_list = typename KimchiParamsType::circuit_params::index_terms_list; + using constant_term_component = + zk::components::rpn_expression; + + constexpr static const std::size_t selector_seed = 0x0f22; + constexpr static const std::size_t eval_points_amount = 2; + + constexpr static std::size_t rows() { + std::size_t row = 0; + row += 2; // skip rows for constant in zkpm + row += zkpm_eval_component::rows_amount; + row += sub_component::rows_amount; + + row += add_component::rows_amount; + row += 3 * mul_component::rows_amount; + + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + row += 2 * mul_component::rows_amount; + row += 2 * add_component::rows_amount; + } + + if (KimchiParamsType::public_input_size > + 0) { // if public input isn't present, then public_eval is empty + row += sub_component::rows_amount; + } + + row += 2 * mul_component::rows_amount; + + for (std::size_t i = 0; i < KimchiParamsType::permut_size; i++) { + row += 3 * mul_component::rows_amount; + row += 2 * add_component::rows_amount; + } + row += sub_component::rows_amount; + + // numerator calculation + row += zk_w3_component::rows_amount; + row += 3 * sub_component::rows_amount; + row += 5 * mul_component::rows_amount; + row += add_component::rows_amount; + + // denominator + row += mul_component::rows_amount; + row += div_component::rows_amount; + + row += mul_component::rows_amount; + row += add_component::rows_amount; + + row += constant_term_component::rows_amount; + row += sub_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + verifier_index_type &verifier_index; + std::array alpha_powers; + std::array, eval_points_amount> + combined_evals; + var gamma; + var beta; + var zeta; + var zeta_pow_n; + var joint_combiner; + std::array public_eval; + }; + + struct result_type { + var output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row + rows_amount - sub_component::rows_amount; + output = typename sub_component::result_type(row).output; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + var one(0, start_row_index + 1, false, var::column_type::constant); + + row += 2; // skip rows for constant in zkpm + + // zkp = index.zkpm().evaluate(&zeta); + var zkp = + zkpm_eval_component::generate_circuit( + bp, assignment, + {params.verifier_index.omega, params.verifier_index.domain_size, params.zeta}, row) + .output; + row += zkpm_eval_component::rows_amount; + + // zeta1m1 = zeta_pow_n - ScalarField::::one(); + var zeta1m1 = zk::components::generate_circuit(bp, + assignment, {params.zeta_pow_n, one}, row).output; + row += sub_component::rows_amount; + + // get alpha0, alpha1, alpha2 + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + assert(alpha_idxs.second >= 3); + var alpha0 = params.alpha_powers[alpha_idxs.first]; + var alpha1 = params.alpha_powers[alpha_idxs.first + 1]; + var alpha2 = params.alpha_powers[alpha_idxs.first + 2]; + + // let init = (evals[0].w[PERMUTS - 1] + gamma) * evals[1].z * alpha0 * zkp; + var init = + zk::components::generate_circuit( + bp, assignment, + {params.combined_evals[0].w[KimchiParamsType::permut_size - 1], params.gamma}, row) + .output; + row += add_component::rows_amount; + init = zk::components::generate_circuit(bp, + assignment, {init, params.combined_evals[1].z}, row).output; + row += mul_component::rows_amount; + init = + zk::components::generate_circuit(bp, assignment, {init, alpha0}, row).output; + row += mul_component::rows_amount; + init = zk::components::generate_circuit(bp, assignment, {init, zkp}, row).output; + row += mul_component::rows_amount; + + // let mut ft_eval0 = evals[0] + // .w + // .iter() + // .zip(evals[0].s.iter()) + // .map(|(w, s)| (beta * s) + w + gamma) + // .fold(init, |x, y| x * y); + var ft_eval0 = init; + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + var w = params.combined_evals[0].w[i]; + var s = params.combined_evals[0].s[i]; + var beta_s = + zk::components::generate_circuit(bp, assignment, {params.beta, s}, row) + .output; + row += mul_component::rows_amount; + var w_beta_s = + zk::components::generate_circuit(bp, assignment, {w, beta_s}, row) + .output; + row += add_component::rows_amount; + var w_beta_s_gamma = zk::components::generate_circuit( + bp, assignment, {w_beta_s, params.gamma}, row) + .output; + row += add_component::rows_amount; + ft_eval0 = zk::components::generate_circuit(bp, assignment, + {ft_eval0, w_beta_s_gamma}, row) + .output; + row += mul_component::rows_amount; + } + + // ft_eval0 - p_eval[0] + if (KimchiParamsType::public_input_size > 0) { + var ft_eval0 = zk::components::generate_circuit(bp, + assignment, {ft_eval0, params.public_eval[0]}, row).output; + row += sub_component::rows_amount; + } + + // ft_eval0 -= evals[0] + // .w + // .iter() + // .zip(index.shift.iter()) + // .map(|(w, s)| gamma + (beta * zeta * s) + w) + // .fold(alpha0 * zkp * evals[0].z, |x, y| x * y); + var ft_eval0_sub = + zk::components::generate_circuit(bp, assignment, {alpha0, zkp}, row).output; + row += mul_component::rows_amount; + ft_eval0_sub = zk::components::generate_circuit( + bp, assignment, {ft_eval0_sub, params.combined_evals[0].z}, row) + .output; + row += mul_component::rows_amount; + + for (std::size_t i = 0; i < KimchiParamsType::permut_size; i++) { + var w = params.combined_evals[0].w[i]; + var s = params.verifier_index.shift[i]; + var beta_s = + zk::components::generate_circuit(bp, assignment, {params.beta, s}, row) + .output; + row += mul_component::rows_amount; + var beta_zeta_s = zk::components::generate_circuit( + bp, assignment, {params.zeta, beta_s}, row) + .output; + row += mul_component::rows_amount; + var gamma_beta_zeta_s = zk::components::generate_circuit( + bp, assignment, {params.gamma, beta_zeta_s}, row) + .output; + row += add_component::rows_amount; + var w_gamma_beta_zeta_s = zk::components::generate_circuit( + bp, assignment, {w, gamma_beta_zeta_s}, row) + .output; + row += add_component::rows_amount; + + ft_eval0_sub = zk::components::generate_circuit( + bp, assignment, {ft_eval0_sub, w_gamma_beta_zeta_s}, row) + .output; + row += mul_component::rows_amount; + } + ft_eval0 = zk::components::generate_circuit(bp, assignment, + {ft_eval0, ft_eval0_sub}, row) + .output; + row += sub_component::rows_amount; + + // numerator calculation + + var domain_offset_for_zk = + zk_w3_component::generate_circuit(bp, // index.w() + assignment, {params.verifier_index}, row) + .output; + row += zk_w3_component::rows_amount; + + // zeta - index.w() + var zeta_minus_w = zk::components::generate_circuit( + bp, assignment, {params.zeta, domain_offset_for_zk}, row) + .output; + row += sub_component::rows_amount; + + // (zeta - ScalarField::::one()) + var zeta_minus_one = + zk::components::generate_circuit(bp, assignment, {params.zeta, one}, row) + .output; + row += sub_component::rows_amount; + + // (ScalarField::::one() - evals[0].z) + var one_minus_z = zk::components::generate_circuit( + bp, assignment, {one, params.combined_evals[0].z}, row) + .output; + row += sub_component::rows_amount; + + // let numerator = ((zeta1m1 * alpha1 * (zeta - index.w())) + // + (zeta1m1 * alpha2 * (zeta - ScalarField::::one()))) + // * (ScalarField::::one() - evals[0].z); + var numerator = + zk::components::generate_circuit(bp, assignment, {zeta1m1, alpha1}, row) + .output; + row += mul_component::rows_amount; + + numerator = zk::components::generate_circuit(bp, assignment, + {numerator, zeta_minus_w}, row) + .output; + row += mul_component::rows_amount; + + var numerator_term = + zk::components::generate_circuit(bp, assignment, {zeta1m1, alpha2}, row) + .output; + row += mul_component::rows_amount; + numerator_term = zk::components::generate_circuit( + bp, assignment, {numerator_term, zeta_minus_one}, row) + .output; + row += mul_component::rows_amount; + + numerator = zk::components::generate_circuit(bp, assignment, + {numerator, numerator_term}, row) + .output; + row += add_component::rows_amount; + + numerator = zk::components::generate_circuit(bp, assignment, + {numerator, one_minus_z}, row) + .output; + row += mul_component::rows_amount; + + // let denominator = (zeta - index.w()) * (zeta - ScalarField::::one()); + // let denominator = denominator.inverse().expect("negligible probability"); + var denominator = zk::components::generate_circuit( + bp, assignment, {zeta_minus_w, zeta_minus_one}, row) + .output; + row += mul_component::rows_amount; + + denominator = + zk::components::generate_circuit(bp, assignment, {one, denominator}, row) + .output; + row += div_component::rows_amount; + + // ft_eval0 += numerator * denominator; + var numerator_denominator = zk::components::generate_circuit( + bp, assignment, {numerator, denominator}, row) + .output; + row += mul_component::rows_amount; + ft_eval0 = zk::components::generate_circuit( + bp, assignment, {ft_eval0, numerator_denominator}, row) + .output; + row += add_component::rows_amount; + + // evaluate constant term expression + var pt = constant_term_component::generate_circuit( + bp, assignment, + {index_terms_list::constant_term_str, params.zeta, params.alpha_powers[1], + params.beta, params.gamma, params.joint_combiner, params.combined_evals, + params.verifier_index.omega, params.verifier_index.domain_size}, + row) + .output; + row += constant_term_component::rows_amount; + + ft_eval0 = + zk::components::generate_circuit(bp, assignment, {ft_eval0, pt}, row).output; + row += sub_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + generate_copy_constraints(bp, assignment, params, start_row_index); + generate_assignments_constants(assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + var one(0, start_row_index + 1, false, var::column_type::constant); + + row += 2; // skip rows for constant in zkpm + + // zkp = index.zkpm().evaluate(&zeta); + var zkp = + zkpm_eval_component::generate_assignments( + assignment, + {params.verifier_index.omega, params.verifier_index.domain_size, params.zeta}, row) + .output; + row += zkpm_eval_component::rows_amount; + + // zeta1m1 = zeta_pow_n - ScalarField::::one(); + var zeta1m1 = sub_component::generate_assignments( + assignment, {params.zeta_pow_n, one}, row).output; + row += sub_component::rows_amount; + + // get alpha0, alpha1, alpha2 + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + assert(alpha_idxs.second >= 3); + var alpha0 = params.alpha_powers[alpha_idxs.first]; + var alpha1 = params.alpha_powers[alpha_idxs.first + 1]; + var alpha2 = params.alpha_powers[alpha_idxs.first + 2]; + + // let init = (evals[0].w[PERMUTS - 1] + gamma) * evals[1].z * alpha0 * zkp; + var init = + add_component::generate_assignments( + assignment, + {params.combined_evals[0].w[KimchiParamsType::permut_size - 1], params.gamma}, row) + .output; + row += add_component::rows_amount; + init = mul_component::generate_assignments( + assignment, {init, params.combined_evals[1].z}, row).output; + row += mul_component::rows_amount; + init = mul_component::generate_assignments(assignment, {init, alpha0}, row).output; + row += mul_component::rows_amount; + init = mul_component::generate_assignments(assignment, {init, zkp}, row).output; + row += mul_component::rows_amount; + + // let mut ft_eval0 = evals[0] + // .w + // .iter() + // .zip(evals[0].s.iter()) + // .map(|(w, s)| (beta * s) + w + gamma) + // .fold(init, |x, y| x * y); + var ft_eval0 = init; + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + var w = params.combined_evals[0].w[i]; + var s = params.combined_evals[0].s[i]; + var beta_s = mul_component::generate_assignments(assignment, {params.beta, s}, row).output; + row += mul_component::rows_amount; + var w_beta_s = add_component::generate_assignments(assignment, {w, beta_s}, row).output; + row += add_component::rows_amount; + var w_beta_s_gamma = + add_component::generate_assignments(assignment, {w_beta_s, params.gamma}, row).output; + row += add_component::rows_amount; + ft_eval0 = + mul_component::generate_assignments(assignment, {ft_eval0, w_beta_s_gamma}, row).output; + row += mul_component::rows_amount; + } + + // ft_eval0 - p_eval[0] + if (KimchiParamsType::public_input_size > 0) { + var ft_eval0 = sub_component::generate_assignments( + assignment, {ft_eval0, params.public_eval[0]}, row).output; + row += sub_component::rows_amount; + } + + // ft_eval0 -= evals[0] + // .w + // .iter() + // .zip(index.shift.iter()) + // .map(|(w, s)| gamma + (beta * zeta * s) + w) + // .fold(alpha0 * zkp * evals[0].z, |x, y| x * y); + var ft_eval0_sub = mul_component::generate_assignments(assignment, {alpha0, zkp}, row).output; + row += mul_component::rows_amount; + ft_eval0_sub = mul_component::generate_assignments( + assignment, {ft_eval0_sub, params.combined_evals[0].z}, row) + .output; + row += mul_component::rows_amount; + + for (std::size_t i = 0; i < KimchiParamsType::permut_size; i++) { + var w = params.combined_evals[0].w[i]; + var s = params.verifier_index.shift[i]; + var beta_s = mul_component::generate_assignments(assignment, {params.beta, s}, row).output; + row += mul_component::rows_amount; + var beta_zeta_s = + mul_component::generate_assignments(assignment, {params.zeta, beta_s}, row).output; + row += mul_component::rows_amount; + var gamma_beta_zeta_s = + add_component::generate_assignments(assignment, {params.gamma, beta_zeta_s}, row) + .output; + row += add_component::rows_amount; + var w_gamma_beta_zeta_s = + add_component::generate_assignments(assignment, {w, gamma_beta_zeta_s}, row).output; + row += add_component::rows_amount; + + ft_eval0_sub = mul_component::generate_assignments(assignment, + {ft_eval0_sub, w_gamma_beta_zeta_s}, row) + .output; + row += mul_component::rows_amount; + } + ft_eval0 = + sub_component::generate_assignments(assignment, {ft_eval0, ft_eval0_sub}, row).output; + row += sub_component::rows_amount; + + // numerator calculation + + var domain_offset_for_zk = zk_w3_component::generate_assignments( // index.w() + assignment, {params.verifier_index}, row) + .output; + row += zk_w3_component::rows_amount; + + // zeta - index.w() + var zeta_minus_w = + sub_component::generate_assignments(assignment, {params.zeta, domain_offset_for_zk}, row) + .output; + row += sub_component::rows_amount; + + // (zeta - ScalarField::::one()) + var zeta_minus_one = + sub_component::generate_assignments(assignment, {params.zeta, one}, row).output; + row += sub_component::rows_amount; + + // (ScalarField::::one() - evals[0].z) + var one_minus_z = + sub_component::generate_assignments(assignment, {one, params.combined_evals[0].z}, row) + .output; + row += sub_component::rows_amount; + + // let numerator = ((zeta1m1 * alpha1 * (zeta - index.w())) + // + (zeta1m1 * alpha2 * (zeta - ScalarField::::one()))) + // * (ScalarField::::one() - evals[0].z); + var numerator = mul_component::generate_assignments(assignment, {zeta1m1, alpha1}, row).output; + row += mul_component::rows_amount; + + numerator = + mul_component::generate_assignments(assignment, {numerator, zeta_minus_w}, row).output; + row += mul_component::rows_amount; + + var numerator_term = + mul_component::generate_assignments(assignment, {zeta1m1, alpha2}, row).output; + row += mul_component::rows_amount; + numerator_term = + mul_component::generate_assignments(assignment, {numerator_term, zeta_minus_one}, row) + .output; + row += mul_component::rows_amount; + + numerator = + add_component::generate_assignments(assignment, {numerator, numerator_term}, row).output; + row += add_component::rows_amount; + + numerator = + mul_component::generate_assignments(assignment, {numerator, one_minus_z}, row).output; + row += mul_component::rows_amount; + + // let denominator = (zeta - index.w()) * (zeta - ScalarField::::one()); + // let denominator = denominator.inverse().expect("negligible probability"); + var denominator = + mul_component::generate_assignments(assignment, {zeta_minus_w, zeta_minus_one}, row).output; + row += mul_component::rows_amount; + + denominator = div_component::generate_assignments(assignment, {one, denominator}, row).output; + row += div_component::rows_amount; + + // ft_eval0 += numerator * denominator; + var numerator_denominator = + mul_component::generate_assignments(assignment, {numerator, denominator}, row).output; + row += mul_component::rows_amount; + ft_eval0 = + add_component::generate_assignments(assignment, {ft_eval0, numerator_denominator}, row) + .output; + row += add_component::rows_amount; + + // evaluate constant term expression + var pt = constant_term_component::generate_assignments( + assignment, + {index_terms_list::constant_term_str, params.zeta, params.alpha_powers[1], + params.beta, params.gamma, params.joint_combiner, params.combined_evals, + params.verifier_index.omega, params.verifier_index.domain_size}, + row) + .output; + row += constant_term_component::rows_amount; + + ft_eval0 = sub_component::generate_assignments(assignment, {ft_eval0, pt}, row).output; + row += sub_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_FT_EVAL_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/lagrange_denominators.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/lagrange_denominators.hpp new file mode 100644 index 000000000..092624e62 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/lagrange_denominators.hpp @@ -0,0 +1,187 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_LAGRANGE_DENOMINATORS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_LAGRANGE_DENOMINATORS_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // result = [(zeta - omega^(i))^(-1)] concat. [(zeta_omega - omega^(i))^(-1)] for i in + // (0..public_input_size) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L231-L240 + // Input: eval_point_0, eval_point_1, [omega^0, omega^1, ..., omega^public_input_size] + // Output: [(eval_point_0 - omega^(i))^(-1), (eval_point_1 - omega^(i))^(-1) for i in + // (0..public_input_size)] + template + class lagrange_denominators; + + template + class lagrange_denominators, + PublicInputSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using sub_component = zk::components::subtraction; + using div_component = zk::components::division; + + constexpr static const std::size_t selector_seed = 0x0f0d; + + public: + constexpr static const std::size_t rows_amount = + (sub_component::rows_amount + div_component::rows_amount) * PublicInputSize * 2; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var zeta; + var zeta_omega; + std::array omega_powers; + var one; + }; + + struct result_type { + std::array output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + for (std::size_t i = 0; i < PublicInputSize; i++) { + row += sub_component::rows_amount; + var div_res = typename div_component::result_type(row).output; + row += div_component::rows_amount; + output[i] = div_res; + } + + for (std::size_t i = 0; i < PublicInputSize; i++) { + row += sub_component::rows_amount; + var div_res = typename div_component::result_type(row).output; + row += div_component::rows_amount; + output[PublicInputSize + i] = div_res; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + for (std::size_t i = 0; i < PublicInputSize; i++) { + var sub_res = zk::components::generate_circuit( + bp, assignment, {params.zeta, params.omega_powers[i]}, row) + .output; + row += sub_component::rows_amount; + var div_res = zk::components::generate_circuit(bp, assignment, + {params.one, sub_res}, row) + .output; + row += div_component::rows_amount; + } + + for (std::size_t i = 0; i < PublicInputSize; i++) { + var sub_res = zk::components::generate_circuit( + bp, assignment, {params.zeta_omega, params.omega_powers[i]}, row) + .output; + row += sub_component::rows_amount; + var div_res = zk::components::generate_circuit(bp, assignment, + {params.one, sub_res}, row) + .output; + row += div_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + for (std::size_t i = 0; i < PublicInputSize; i++) { + var sub_res = sub_component::generate_assignments( + assignment, {params.zeta, params.omega_powers[i]}, row) + .output; + row += sub_component::rows_amount; + var div_res = + div_component::generate_assignments(assignment, {params.one, sub_res}, row).output; + row += div_component::rows_amount; + } + + for (std::size_t i = 0; i < PublicInputSize; i++) { + var sub_res = sub_component::generate_assignments( + assignment, {params.zeta_omega, params.omega_powers[i]}, row) + .output; + row += sub_component::rows_amount; + var div_res = + div_component::generate_assignments(assignment, {params.one, sub_res}, row).output; + row += div_component::rows_amount; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_LAGRANGE_DENOMINATORS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/oracles_cip.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/oracles_cip.hpp new file mode 100644 index 000000000..25ee6f605 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/oracles_cip.hpp @@ -0,0 +1,262 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ORACLES_CIP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ORACLES_CIP_HPP + +#include + +#include + +#include +#include + +#include +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // combined inner product from oracles data + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L386-L441 + // Input: + // Output: + template + class oracles_cip; + + template + class oracles_cip, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + constexpr static std::size_t poseidon_selector_size() { + if (KimchiParamsType::circuit_params::poseidon_gate == true) { + return 1; + } + return 0; + } + constexpr static std::size_t generic_selector_size() { + if (KimchiParamsType::circuit_params::ec_arithmetic_gates == true) { + return 1; + } + return 0; + } + constexpr static std::size_t p_eval_size() { + if (KimchiParamsType::public_input_size > 0) { + return 1; + } + return 0; + } + constexpr static const std::size_t cip_size = + KimchiParamsType::prev_challenges_size + p_eval_size() // p_eval + + 1 // ft_eval + + 1 // z + + generic_selector_size() // generic_selector + + poseidon_selector_size() // poseidon_selector + + KimchiParamsType::witness_columns + KimchiParamsType::permut_size - 1; + + constexpr static const std::size_t eval_points_amount = 2; + + using cip_component = + zk::components::combined_inner_product; + + constexpr static const std::size_t selector_seed = 0xf2e; + + public: + constexpr static const std::size_t rows_amount = cip_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var v; + var u; + + var ft_eval0; + var ft_eval1; + std::array< + std::array, + eval_points_amount>, + KimchiParamsType::prev_challenges_size> + polys; + std::array p_eval; + std::array, eval_points_amount> + evals; + }; + + struct result_type { + var output; + result_type() { + } + result_type(std::size_t start_row_index) { + } + + result_type(const params_type ¶ms, std::size_t start_row_index) { + output = params.ft_eval0; + } + }; + + private: + static std::array, eval_points_amount> + prepare_cip_input(const params_type ¶ms) { + std::array, eval_points_amount> es; + // std::cout< 0) { + for (std::size_t i = 0; i < KimchiParamsType::prev_challenges_size; ++i) { + for (std::size_t j = 0; + j < KimchiParamsType::commitment_params_type::split_poly_eval_size; + ++j) { + for (std::size_t k = 0; k < eval_points_amount; ++k) { + es[k][i] = params.polys[i][k][j]; + } + } + } + es_idx += KimchiParamsType::prev_challenges_size; + } + if (KimchiParamsType::public_input_size > 0) { + for (std::size_t i = 0; i < eval_points_amount; ++i) { + es[i][es_idx] = params.p_eval[i]; + } + es_idx++; + } + + es[0][es_idx] = params.ft_eval0; + es[1][es_idx] = params.ft_eval1; + es_idx++; + + for (std::size_t i = 0; i < eval_points_amount; ++i) { + es[i][es_idx] = params.evals[i].z; + } + es_idx++; + if (KimchiParamsType::circuit_params::ec_arithmetic_gates == true) { + for (std::size_t i = 0; i < eval_points_amount; ++i) { + es[i][es_idx] = params.evals[i].generic_selector; + } + es_idx++; + } + + if (KimchiParamsType::circuit_params::poseidon_gate == true) { + for (std::size_t i = 0; i < eval_points_amount; ++i) { + es[i][es_idx] = params.evals[i].poseidon_selector; + } + + es_idx++; + } + for (std::size_t i = 0; i < eval_points_amount; ++i) { + // std::size_t es_idx_tmp = es_idx; + for (std::size_t j = 0, es_idx_tmp = es_idx; j < KimchiParamsType::witness_columns; + ++j, ++es_idx_tmp) { + es[i][es_idx_tmp] = params.evals[i].w[j]; + } + } + es_idx += KimchiParamsType::witness_columns; + for (std::size_t i = 0; i < eval_points_amount; ++i) { + for (std::size_t j = 0, es_idx_tmp = es_idx; j < KimchiParamsType::permut_size - 1; + ++j, ++es_idx_tmp) { + es[i][es_idx_tmp] = params.evals[i].s[j]; + } + // es_idx++; + } + es_idx += KimchiParamsType::permut_size - 1; + + assert(es_idx <= cip_size); + + return es; + } + + public: + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + auto es = prepare_cip_input(params); + + var res = + cip_component::generate_circuit(bp, assignment, {es[0], es[1], params.v, params.u}, row) + .output; + row += cip_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + generate_copy_constraints(bp, assignment, params, start_row_index); + result_type result; + result.output = res; + return result; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + auto es = prepare_cip_input(params); + + var res = + cip_component::generate_assignments(assignment, {es[0], es[1], params.v, params.u}, row) + .output; + row += cip_component::rows_amount; + + assert(row == start_row_index + rows_amount); + result_type result; + result.output = res; + return result; + } + + private: + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ORACLES_CIP_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/prev_chal_evals.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/prev_chal_evals.hpp new file mode 100644 index 000000000..482791cf4 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/prev_chal_evals.hpp @@ -0,0 +1,275 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PREV_CHAL_EVALS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PREV_CHAL_EVALS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // evaluate univariate polynomial at points + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L67 + // Input: prev_challenges, zeta, zeta * omega, zeta^n, (zeta * omega)^n, + // Output: (1 + prev_challenges[-1] x)(1 + prev_challenges[-2] x^2)(1 + prev_challenges[-3] x^4)... + template + class prev_chal_evals; + + template + class prev_chal_evals, + KimchiCommitmentParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, + W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using sub_component = zk::components::subtraction; + + using b_poly_component = + zk::components::b_poly; + using b_poly_coeff_component = + zk::components::b_poly_coefficients; + + constexpr static const std::size_t selector_seed = 0x0f0f; + constexpr static const std::size_t eval_points_amount = 2; + constexpr static const std::size_t eval_rounds = KimchiCommitmentParamsType::eval_rounds; + constexpr static const std::size_t split_poly_eval_size = + KimchiCommitmentParamsType::split_poly_eval_size; + constexpr static const std::size_t max_poly_size = KimchiCommitmentParamsType::max_poly_size; + constexpr static const std::size_t b_len = 1 << eval_rounds; + + public: + constexpr static const std::size_t rows_amount = + split_poly_eval_size == 1 ? + eval_points_amount * b_poly_component::rows_amount : + b_poly_coeff_component::rows_amount + + eval_points_amount * (b_poly_component::rows_amount + + (b_len - max_poly_size) * + (mul_component::rows_amount + mul_component::rows_amount + + add_component::rows_amount) + + mul_component::rows_amount + sub_component::rows_amount); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array &prev_challenges; + std::array eval_points; + std::array powers_of_eval_points_for_chunks; + var one; + var zero; + }; + + struct result_type { + std::array, eval_points_amount> output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + for (std::size_t i = 0; i < eval_points_amount; i++) { + var full = typename b_poly_component::result_type(row).output; + row += b_poly_component::rows_amount; + if (split_poly_eval_size == 1) { + output[i][0] = full; + continue; + } + + var diff; + for (std::size_t j = max_poly_size; j < b_len; j++) { + if (i == 0 && j == max_poly_size) { + row += b_poly_coeff_component::rows_amount; + } + row += mul_component::rows_amount; + row += mul_component::rows_amount; + + diff = typename add_component::result_type(row).output; + row += add_component::rows_amount; + } + + row += mul_component::rows_amount; + var res_0 = typename sub_component::result_type(row).output; + row += sub_component::rows_amount; + output[i][0] = res_0; + output[i][1] = diff; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array b; + std::array, eval_points_amount> res; + for (std::size_t i = 0; i < eval_points_amount; i++) { + var full = + b_poly_component::generate_circuit( + bp, assignment, {params.prev_challenges, params.eval_points[i], params.one}, row) + .output; + row += b_poly_component::rows_amount; + if (split_poly_eval_size == 1) { + res[i][0] = full; + continue; + } + + var betaacc = params.one; + var diff = params.zero; + for (std::size_t j = max_poly_size; j < b_len; j++) { + if (i == 0 && j == max_poly_size) { + b = b_poly_coeff_component::generate_circuit( + bp, assignment, {params.prev_challenges, params.one}, row) + .output; + row += b_poly_coeff_component::rows_amount; + } + var b_j = b[j]; + var ret = + zk::components::generate_circuit(bp, assignment, {betaacc, b_j}, row) + .output; + row += mul_component::rows_amount; + + betaacc = zk::components::generate_circuit( + bp, assignment, {betaacc, params.eval_points[i]}, row) + .output; + row += mul_component::rows_amount; + + diff = zk::components::generate_circuit(bp, assignment, {diff, ret}, row) + .output; + row += add_component::rows_amount; + } + + // [full - (diff * powers_of_eval_points_for_chunks[i]), diff] + var res_0 = zk::components::generate_circuit( + bp, assignment, {diff, params.powers_of_eval_points_for_chunks[i]}, row) + .output; + row += mul_component::rows_amount; + res_0 = zk::components::generate_circuit(bp, assignment, {full, res_0}, row) + .output; + row += sub_component::rows_amount; + res[i][0] = res_0; + res[i][1] = diff; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array b; + std::array, eval_points_amount> res; + for (std::size_t i = 0; i < eval_points_amount; i++) { + var full = b_poly_component::generate_assignments( + assignment, {params.prev_challenges, params.eval_points[i], params.one}, row) + .output; + row += b_poly_component::rows_amount; + if (split_poly_eval_size == 1) { + res[i][0] = full; + continue; + } + + var betaacc = params.one; + var diff = params.zero; + for (std::size_t j = max_poly_size; j < b_len; j++) { + if (i == 0 && j == max_poly_size) { + b = b_poly_coeff_component::generate_assignments( + assignment, {params.prev_challenges, params.one}, row) + .output; + row += b_poly_coeff_component::rows_amount; + } + var b_j = b[j]; + var ret = mul_component::generate_assignments(assignment, {betaacc, b_j}, row).output; + row += mul_component::rows_amount; + + betaacc = mul_component::generate_assignments(assignment, + {betaacc, params.eval_points[i]}, row) + .output; + row += mul_component::rows_amount; + + diff = add_component::generate_assignments(assignment, {diff, ret}, row).output; + row += add_component::rows_amount; + } + + // [full - (diff * powers_of_eval_points_for_chunks[i]), diff] + var res_0 = mul_component::generate_assignments( + assignment, {diff, params.powers_of_eval_points_for_chunks[i]}, row) + .output; + row += mul_component::rows_amount; + res_0 = sub_component::generate_assignments(assignment, {full, res_0}, row).output; + row += sub_component::rows_amount; + res[i][0] = res_0; + res[i][1] = diff; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PREV_CHAL_EVALS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/public_evaluations.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/public_evaluations.hpp new file mode 100644 index 000000000..37c7e627d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/oracles_scalar/public_evaluations.hpp @@ -0,0 +1,242 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PUBLIC_EVALUATIONS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PUBLIC_EVALUATIONS_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // evaluate negated public polynomials at evaluation points + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L245-L269 + // Input: zeta^n, (zeta * omega)^n, lagrange_denominators, public_input, omega_powers + // Output: r = {r_0, r_1} + // r_0 = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange_denominators, p from public_input, w from omega_powers for l from + // 0 to PulicInputSize + // r_1 = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange_denominators, p from public_input, w from omega_powers for l from + // PulicInputSize to 2 * PulicInputSize + template + class public_evaluations; + + template + class public_evaluations, + PublicInputSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using sub_component = zk::components::subtraction; + using div_component = zk::components::division; + using add_component = zk::components::addition; + using mul_component = zk::components::multiplication; + using mul_by_const_component = zk::components::mul_by_constant; + + constexpr static const std::size_t selector_seed = 0x0f0e; + + public: + constexpr static const std::size_t rows_amount = + 1 + ((mul_by_const_component::rows_amount + mul_component::rows_amount + + mul_component::rows_amount + add_component::rows_amount) * + PublicInputSize + + sub_component::rows_amount + div_component::rows_amount + mul_component::rows_amount) * + 2; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var zeta_pow_n; + var zeta_omega_pow_n; + std::array &public_input; + std::array &lagrange_base; + std::array &omega_powers; + var domain_size; + var one; + var zero; + }; + + struct result_type { + std::array output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + row++; + for (std::size_t j = 0; j < 2; j++) { + for (std::size_t i = 0; i < PublicInputSize; i++) { + row += mul_by_const_component::rows_amount; + row += mul_component::rows_amount; + row += mul_component::rows_amount; + row += add_component::rows_amount; + } + + row += sub_component::rows_amount; + row += div_component::rows_amount; + output[j] = typename mul_component::result_type(row).output; + row += mul_component::rows_amount; + } + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + std::array res = {var(W0, row, false), var(W1, row, false)}; + row++; + + for (std::size_t j = 0; j < 2; j++) { + for (std::size_t i = 0; i < PublicInputSize; i++) { + var term = zk::components::generate_circuit( + bp, assignment, {params.lagrange_base[j * PublicInputSize + i], -1}, row) + .output; + row += mul_by_const_component::rows_amount; + term = zk::components::generate_circuit( + bp, assignment, {term, params.public_input[i]}, row) + .output; + row += mul_component::rows_amount; + term = zk::components::generate_circuit( + bp, assignment, {term, params.omega_powers[i]}, row) + .output; + row += mul_component::rows_amount; + res[j] = + zk::components::generate_circuit(bp, assignment, {res[j], term}, row) + .output; + row += add_component::rows_amount; + } + + var tmp = j == 0 ? params.zeta_pow_n : params.zeta_omega_pow_n; + var res_multiplier = + zk::components::generate_circuit(bp, assignment, {tmp, params.one}, row) + .output; + row += sub_component::rows_amount; + res_multiplier = zk::components::generate_circuit( + bp, assignment, {res_multiplier, params.domain_size}, row) + .output; + row += div_component::rows_amount; + res[j] = zk::components::generate_circuit(bp, assignment, + {res[j], res_multiplier}, row) + .output; + row += mul_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + // r[0] = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from 0 to PulicInputSize + // r[2] = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from PulicInputSize to 2 * + // PulicInputSize + assignment.witness(W0)[row] = 0; + assignment.witness(W1)[row] = 0; + std::array res = {var(W0, row, false), var(W1, row, false)}; + row++; + + for (std::size_t j = 0; j < 2; j++) { + for (std::size_t i = 0; i < PublicInputSize; i++) { + var term = mul_by_const_component::generate_assignments( + assignment, {params.lagrange_base[j * PublicInputSize + i], -1}, row) + .output; + row += mul_by_const_component::rows_amount; + term = + mul_component::generate_assignments(assignment, {term, params.public_input[i]}, row) + .output; + row += mul_component::rows_amount; + term = + mul_component::generate_assignments(assignment, {term, params.omega_powers[i]}, row) + .output; + row += mul_component::rows_amount; + res[j] = add_component::generate_assignments(assignment, {res[j], term}, row).output; + row += add_component::rows_amount; + } + + var tmp = j == 0 ? params.zeta_pow_n : params.zeta_omega_pow_n; + var res_multiplier = + sub_component::generate_assignments(assignment, {tmp, params.one}, row).output; + row += sub_component::rows_amount; + res_multiplier = div_component::generate_assignments( + assignment, {res_multiplier, params.domain_size}, row) + .output; + row += div_component::rows_amount; + res[j] = + mul_component::generate_assignments(assignment, {res[j], res_multiplier}, row).output; + row += mul_component::rows_amount; + } + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + bp.add_copy_constraint({var(W0, start_row_index, false), params.zero}); + bp.add_copy_constraint({var(W1, start_row_index, false), params.zero}); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_PUBLIC_EVALUATIONS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/sponge.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/sponge.hpp new file mode 100644 index 000000000..4c0b87294 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/sponge.hpp @@ -0,0 +1,253 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_SPONGE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_SPONGE_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Poseidon sponge construction + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/poseidon.rs#L64 + template + class kimchi_sponge; + + template + class kimchi_sponge, + CurveType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + using poseidon_component = + typename zk::components::poseidon; + using add_component = typename zk::components::addition; + std::size_t state_count = 0; + bool state_absorbed = true; + + std::array state = {var(W0, 0), var(W1, 0), var(W2, 0)}; + + void permute_assignment(blueprint_assignment_table &assignment, + const std::size_t &component_start_row) { + + typename poseidon_component::result_type poseidon_res = + poseidon_component::generate_assignments(assignment, {state}, component_start_row); + + for (std::size_t i = 0; i < poseidon_component::state_size; i++) { + state[i] = poseidon_res.output_state[i]; + } + } + + void add_input_assignment(blueprint_assignment_table &assignment, + var &input, + std::size_t state_index, + const std::size_t component_start_row) { + + auto addition_result = add_component::generate_assignments( + assignment, {input, state[state_index]}, component_start_row); + state[state_index] = addition_result.output; + } + + void permute_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const std::size_t component_start_row) { + + typename poseidon_component::result_type poseidon_res = + poseidon_component::generate_circuit(bp, assignment, {state}, component_start_row); + + for (std::size_t i = 0; i < poseidon_component::state_size; i++) { + state[i] = poseidon_res.output_state[i]; + } + } + + void add_input_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var &input, + std::size_t state_index, + const std::size_t component_start_row) { + + auto addition_result = zk::components::generate_circuit( + bp, assignment, {input, state[state_index]}, component_start_row); + state[state_index] = addition_result.output; + } + + constexpr static const std::size_t permute_rows = poseidon_component::rows_amount; + constexpr static const std::size_t add_input_rows = add_component::rows_amount; + + public: + constexpr static const std::size_t init_rows = 0; + constexpr static const std::size_t absorb_rows = permute_rows + add_input_rows; + constexpr static const std::size_t squeeze_rows = permute_rows; + constexpr static const std::size_t gates_amount = 0; + + constexpr static const std::size_t state_size = poseidon_component::state_size; + + std::array _inner_state() { + return state; + } + + void init_assignment(blueprint_assignment_table &assignment, + var zero, + const std::size_t component_start_row) { + + for (std::size_t i = 0; i < poseidon_component::state_size; i++) { + state[i] = zero; + } + } + + void init_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var zero, + const std::size_t component_start_row) { + + for (std::size_t i = 0; i < poseidon_component::state_size; i++) { + state[i] = zero; + } + } + + void absorb_assignment(blueprint_assignment_table &assignment, + var absorbing_value, + const std::size_t component_start_row) { + + std::size_t row = component_start_row; + + if (this->state_absorbed) { + if (this->state_count == poseidon_component::rate) { + permute_assignment(assignment, component_start_row); + row += permute_rows; + + add_input_assignment(assignment, absorbing_value, 0, row); + + this->state_count = 1; + } else { + add_input_assignment(assignment, absorbing_value, this->state_count, + component_start_row); + + this->state_count++; + } + } else { + add_input_assignment(assignment, absorbing_value, 0, component_start_row); + + this->state_absorbed = true; + this->state_count = 1; + } + } + + void absorb_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var &absorbing_value, + const std::size_t component_start_row) { + + std::size_t row = component_start_row; + + if (this->state_absorbed) { + if (this->state_count == poseidon_component::rate) { + permute_circuit(bp, assignment, component_start_row); + + row += permute_rows; + + add_input_circuit(bp, assignment, absorbing_value, 0, row); + + this->state_count = 1; + } else { + add_input_circuit(bp, assignment, absorbing_value, this->state_count, + component_start_row); + + this->state_count++; + } + } else { + add_input_circuit(bp, assignment, absorbing_value, 0, component_start_row); + + this->state_absorbed = true; + this->state_count = 1; + } + } + + var squeeze_assignment(blueprint_assignment_table &assignment, + const std::size_t component_start_row) { + if (!this->state_absorbed) { // state = squeezed + if (this->state_count == poseidon_component::rate) { + permute_assignment(assignment, component_start_row); + this->state_count = 1; + return this->state[0]; + } else { + return this->state[this->state_count++]; + } + } else { + permute_assignment(assignment, component_start_row); + + this->state_absorbed = false; + this->state_count = 1; + + return this->state[0]; + } + } + + var squeeze_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const std::size_t component_start_row) { + + if (!this->state_absorbed) { // state = squeezed + if (this->state_count == poseidon_component::rate) { + permute_circuit(bp, assignment, component_start_row); + this->state_count = 1; + return this->state[0]; + } else { + return this->state[this->state_count++]; + } + } else { + permute_circuit(bp, assignment, component_start_row); + + this->state_absorbed = false; + this->state_count = 1; + + return this->state[0]; + } + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_SPONGE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/table_commitment.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/table_commitment.hpp new file mode 100644 index 000000000..fe635c6d2 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/table_commitment.hpp @@ -0,0 +1,219 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_TABLE_COMMITMENT_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_TABLE_COMMITMENT_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // Compute Lookup Table commitment + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L830 + // Input: + // Output: + template + class table_commitment; + + template + class table_commitment< + snark::plonk_constraint_system, + KimchiParamsType, + CurveType, + W0, + W1, + W2, + W3, + W4, + W5, + W6, + W7, + W8, + W9, + W10, + W11, + W12, + W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + + constexpr static const std::size_t lookup_columns = KimchiParamsType::circuit_params::lookup_columns; + + constexpr static const std::size_t use_lookup_runtime = KimchiParamsType::circuit_params::lookup_runtime ? 1 : 0; + + constexpr static const std::size_t msm_size = (lookup_columns + use_lookup_runtime) + * KimchiParamsType::commitment_params_type::shifted_commitment_split; + + using commitment_type = typename + zk::components::kimchi_commitment_type; + using msm_component = zk::components::element_g1_multi_scalar_mul; + + public: + constexpr static const std::size_t rows_amount = msm_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector table; + std::array joint_combiner; + commitment_type runtime; + }; + + struct result_type { + commitment_type output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + std::size_t row = start_row_index; + std::array commitments; + std::array scalars; + std::size_t j = 0; + std::size_t comm_size = params.table[0].parts.size(); + for(std::size_t i = j; i < commitments.size(); i++) { + for (std::size_t k = 0; k < comm_size; k++) { + commitments[i*comm_size + k] = params.table[i].parts[k]; + scalars[i*comm_size + k] = params.joint_combiner[i]; + j++; + } + } + if (KimchiParamsType::circuit_params::lookup_runtime) { + for (std::size_t k = 0; k < comm_size; k++) { + commitments[j] = params.runtime.parts[k]; + scalars[j] = params.joint_combiner[1]; + j++; + } + } + msm_component::generate_circuit(bp, assignment, {scalars, commitments}, row); + return result_type(row); + + return result_type(row); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::array commitments; + std::array scalars; + std::size_t j = 0; + std::size_t comm_size = params.table[0].parts.size(); + for(std::size_t i = j; i < commitments.size(); i++) { + for (std::size_t k = 0; k < comm_size; k++) { + commitments[i*comm_size + k] = params.table[i].parts[k]; + scalars[i*comm_size + k] = params.joint_combiner[i]; + j++; + } + } + if (KimchiParamsType::circuit_params::lookup_runtime) { + for (std::size_t k = 0; k < comm_size; k++) { + commitments[j] = params.runtime.parts[k]; + scalars[j] = params.joint_combiner[1]; + j++; + } + } + msm_component::generate_assignments(assignment, {scalars, commitments}, row); + return result_type(row); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_TABLE_COMMITMENT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/to_group.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/to_group.hpp new file mode 100644 index 000000000..32657610d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/to_group.hpp @@ -0,0 +1,639 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_TO_GROUP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_TO_GROUP_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // generate elliptic curve point from a field element + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/poly-commitment/src/commitment.rs#L370 + // Input: x \in F_q + // Output: U \in E(F_q) + template + class to_group; + + template + class to_group, W0, W1, W2, + W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + using div_component = zk::components::division_or_zero; + using sub_component = zk::components::subtraction; + + constexpr static const std::size_t selector_seed = 0x0f30; + + struct curve_params { + var u; + var fu; + var inv_three_u_squared; + var sqrt_neg_three_u_squared; + var sqrt_neg_three_u_squared_minus_u_over_2; + + var b; + + curve_params(std::size_t start_row_index) { + u = var(0, start_row_index + 3, false, var::column_type::constant); + fu = var(0, start_row_index + 4, false, var::column_type::constant); + inv_three_u_squared = var(0, start_row_index + 5, false, var::column_type::constant); + sqrt_neg_three_u_squared = var(0, start_row_index + 6, false, var::column_type::constant); + sqrt_neg_three_u_squared_minus_u_over_2 = + var(0, start_row_index + 7, false, var::column_type::constant); + b = var(0, start_row_index + 8, false, var::column_type::constant); + } + }; + + constexpr static std::size_t potential_xs_rows = + mul_component::rows_amount * 9 + add_component::rows_amount * 2 + div_component::rows_amount + + sub_component::rows_amount * 4; + + static std::array + potential_xs_assignment(blueprint_assignment_table &assignment, var t, + curve_params params, var one, var zero, std::size_t row) { + var t2 = mul_component::generate_assignments(assignment, {t, t}, row).output; + row += mul_component::rows_amount; + + var alpha = add_component::generate_assignments(assignment, {t2, params.fu}, row).output; + row += add_component::rows_amount; + alpha = mul_component::generate_assignments(assignment, {alpha, t2}, row).output; + row += mul_component::rows_amount; + alpha = div_component::generate_assignments(assignment, {one, alpha}, row).output; + row += div_component::rows_amount; + + var x1 = t2; + x1 = mul_component::generate_assignments(assignment, {x1, x1}, row).output; // t2^2 + row += mul_component::rows_amount; + x1 = + mul_component::generate_assignments(assignment, {x1, alpha}, row).output; // t2^2 * alpha + row += mul_component::rows_amount; + x1 = mul_component::generate_assignments(assignment, {x1, params.sqrt_neg_three_u_squared}, row) + .output; // t2^2 * alpha * sqrt(-3u^2) + row += mul_component::rows_amount; + x1 = sub_component::generate_assignments( + assignment, {params.sqrt_neg_three_u_squared_minus_u_over_2, x1}, row) + .output; // sqrt(-3u^2-u/2) - t2^2 * alpha * sqrt(-3u^2) + row += sub_component::rows_amount; + + var minus_u = sub_component::generate_assignments(assignment, {zero, params.u}, row).output; + row += sub_component::rows_amount; + + var x2 = sub_component::generate_assignments(assignment, {minus_u, x1}, row).output; + row += sub_component::rows_amount; + + var t2_plus_fu = add_component::generate_assignments(assignment, {t2, params.fu}, row).output; + row += add_component::rows_amount; + var t2_inv = mul_component::generate_assignments(assignment, {t2_plus_fu, alpha}, row).output; + row += mul_component::rows_amount; + + var x3 = mul_component::generate_assignments(assignment, {t2_plus_fu, t2_plus_fu}, row).output; + row += mul_component::rows_amount; + x3 = mul_component::generate_assignments(assignment, {x3, t2_inv}, row).output; + row += mul_component::rows_amount; + x3 = mul_component::generate_assignments(assignment, {x3, params.inv_three_u_squared}, row) + .output; + row += mul_component::rows_amount; + x3 = sub_component::generate_assignments(assignment, {params.u, x3}, row).output; + row += sub_component::rows_amount; + + return {x1, x2, x3}; + } + + static std::array + potential_xs_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, var t, + curve_params params, var one, var zero, std::size_t row) { + var t2 = zk::components::generate_circuit(bp, assignment, {t, t}, row).output; + row += mul_component::rows_amount; + + var alpha = + zk::components::generate_circuit(bp, assignment, {t2, params.fu}, row) + .output; + row += add_component::rows_amount; + alpha = + zk::components::generate_circuit(bp, assignment, {alpha, t2}, row).output; + row += mul_component::rows_amount; + alpha = + zk::components::generate_circuit(bp, assignment, {one, alpha}, row).output; + row += div_component::rows_amount; + + var x1 = t2; + x1 = zk::components::generate_circuit(bp, assignment, {x1, x1}, row) + .output; // t2^2 + row += mul_component::rows_amount; + x1 = zk::components::generate_circuit(bp, assignment, {x1, alpha}, row) + .output; // t2^2 * alpha + row += mul_component::rows_amount; + x1 = zk::components::generate_circuit(bp, assignment, + {x1, params.sqrt_neg_three_u_squared}, row) + .output; // t2^2 * alpha * sqrt(-3u^2) + row += mul_component::rows_amount; + x1 = zk::components::generate_circuit( + bp, assignment, {params.sqrt_neg_three_u_squared_minus_u_over_2, x1}, row) + .output; // sqrt(-3u^2-u/2) - t2^2 * alpha * sqrt(-3u^2) + row += sub_component::rows_amount; + + var minus_u = + zk::components::generate_circuit(bp, assignment, {zero, params.u}, row) + .output; + row += sub_component::rows_amount; + + var x2 = + zk::components::generate_circuit(bp, assignment, {minus_u, x1}, row).output; + row += sub_component::rows_amount; + + var t2_plus_fu = + zk::components::generate_circuit(bp, assignment, {t2, params.fu}, row) + .output; + row += add_component::rows_amount; + var t2_inv = + zk::components::generate_circuit(bp, assignment, {t2_plus_fu, alpha}, row) + .output; + row += mul_component::rows_amount; + + var x3 = zk::components::generate_circuit(bp, assignment, + {t2_plus_fu, t2_plus_fu}, row) + .output; + row += mul_component::rows_amount; + x3 = zk::components::generate_circuit(bp, assignment, {x3, t2_inv}, row).output; + row += mul_component::rows_amount; + x3 = zk::components::generate_circuit(bp, assignment, + {x3, params.inv_three_u_squared}, row) + .output; + row += mul_component::rows_amount; + x3 = + zk::components::generate_circuit(bp, assignment, {params.u, x3}, row).output; + row += sub_component::rows_amount; + + return {x1, x2, x3}; + } + + constexpr static std::size_t get_y_rows = + mul_component::rows_amount * 3 + add_component::rows_amount; + + static var get_y_assignments(blueprint_assignment_table &assignment, var x, + curve_params params, std::size_t row) { + // curve_eq + var y_squared = mul_component::generate_assignments(assignment, {x, x}, row) + .output; // x^2 + A (A = 0 for pasta curves) + row += mul_component::rows_amount; + + y_squared = + mul_component::generate_assignments(assignment, {y_squared, x}, row).output; // x^3 + A x + row += mul_component::rows_amount; + + y_squared = add_component::generate_assignments(assignment, {y_squared, params.b}, row) + .output; // x^3 + A x + B + row += add_component::rows_amount; + + // sqrt + typename BlueprintFieldType::value_type y_val = assignment.var_value(y_squared).sqrt(); + assignment.witness(0)[row] = y_val; + var y(0, row); + var y_squared_recalculated = + mul_component::generate_assignments(assignment, {y, y}, row).output; + row += mul_component::rows_amount; + + // copy constraint + + return y; + } + + static var get_y_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, var x, + curve_params params, std::size_t row) { + // curve_eq + var y_squared = zk::components::generate_circuit(bp, assignment, {x, x}, row) + .output; // x^2 + A (A = 0 for pasta curves) + row += mul_component::rows_amount; + + y_squared = zk::components::generate_circuit(bp, assignment, {y_squared, x}, row) + .output; // x^3 + A x + row += mul_component::rows_amount; + + y_squared = + zk::components::generate_circuit(bp, assignment, {y_squared, params.b}, row) + .output; // x^3 + A x + B + row += add_component::rows_amount; + + // sqrt + var y(0, row); + var y_squared_recalculated = + zk::components::generate_circuit(bp, assignment, {y, y}, row).output; + row += mul_component::rows_amount; + + // copy constraint + + return y; + } + + constexpr static std::size_t rows() { + std::size_t row = 0; + row += potential_xs_rows; + + const std::size_t points_size = 3; + for (std::size_t i = 0; i < points_size; ++i) { + row += get_y_rows; + } + + for (std::size_t i = 0; i < points_size; ++i) { + row += sub_component::rows_amount; + row += div_component::rows_amount; + + row += mul_component::rows_amount; + row += sub_component::rows_amount; + + row += add_component::rows_amount; + + if (i == 0) { + continue; + } + + row += div_component::rows_amount; + + row += mul_component::rows_amount; + + row += sub_component::rows_amount; + + row += mul_component::rows_amount; + + row += mul_component::rows_amount; + } + + for (std::size_t i = 0; i < points_size; ++i) { + row += mul_component::rows_amount; + row += add_component::rows_amount; + + row += mul_component::rows_amount; + row += add_component::rows_amount; + } + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var t; + }; + + struct result_type { + var_ec_point output; + + result_type(std::size_t start_row_index) { + const std::size_t points_size = 3; + + std::size_t row = rows_amount - points_size * (2 * mul_component::rows_amount + + 2 * add_component::rows_amount); + + var x; + var y; + + for (std::size_t i = 0; i < points_size; ++i) { + row += mul_component::rows_amount; + x = typename add_component::result_type(row).output; + row += add_component::rows_amount; + + row += mul_component::rows_amount; + y = typename add_component::result_type(row).output; + row += add_component::rows_amount; + } + + output = {x, y}; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(assignment, params, start_row_index); + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + var one(0, start_row_index + 1, false, var::column_type::constant); + var minus_one(0, start_row_index + 2, false, var::column_type::constant); + + curve_params params_curve(start_row_index); + + std::array xs = + potential_xs_circuit(bp, assignment, params.t, params_curve, one, zero, row); + row += potential_xs_rows; + + std::array ys; + for (std::size_t i = 0; i < xs.size(); ++i) { + ys[i] = get_y_circuit(bp, assignment, xs[i], params_curve, row); + row += get_y_rows; + } + + std::array nulifiers; + // nulifiers[i] = 1 if ys[i] != -1 AND nulifiers[i - 1] == 0, 0 otherwise + // E1: (ys[i] - (-1)) * (ys[i] - (-1))**(-1) -1 = 0 if ys[i] != -1, -1 otherwise + // E2: E1 + 1 = 1 if ys[i] != -1, 0 otherwise + // E3: nulifiers[i - 1] * nulifiers[i - 1]**(-1) -1 = 0 if nulifiers[i - 1] != 0, -1 otherwise + // E4: E3 * (-1) = 0 if nulifiers[i - 1] != 0, 1 otherwise + // E5: E2 * E4 = 1 if ys[i] != -1 AND nulifiers[i - 1] = 0, 0 otherwise + + for (std::size_t i = 0; i < ys.size(); ++i) { + var y1 = + zk::components::generate_circuit(bp, assignment, {ys[i], minus_one}, row) + .output; + row += sub_component::rows_amount; + var y1_inversed = + zk::components::generate_circuit(bp, assignment, {one, y1}, row).output; + row += div_component::rows_amount; + + var e1 = + zk::components::generate_circuit(bp, assignment, {y1, y1_inversed}, row) + .output; + row += mul_component::rows_amount; + e1 = zk::components::generate_circuit(bp, assignment, {e1, one}, row).output; + row += sub_component::rows_amount; + + var e2 = + zk::components::generate_circuit(bp, assignment, {e1, one}, row).output; + row += add_component::rows_amount; + + if (i == 0) { + nulifiers[i] = e2; + continue; + } + + var n_inversed = zk::components::generate_circuit( + bp, assignment, {one, nulifiers[i - 1]}, row) + .output; + row += div_component::rows_amount; + + var e3 = zk::components::generate_circuit( + bp, assignment, {nulifiers[i - 1], n_inversed}, row) + .output; + row += mul_component::rows_amount; + + e3 = zk::components::generate_circuit(bp, assignment, {e3, one}, row).output; + row += sub_component::rows_amount; + + var e4 = + zk::components::generate_circuit(bp, assignment, {e3, minus_one}, row) + .output; + row += mul_component::rows_amount; + + var e5 = + zk::components::generate_circuit(bp, assignment, {e2, e4}, row).output; + row += mul_component::rows_amount; + + nulifiers[i] = e5; + } + + var x = zero; + var y = zero; + + // res = (xs[0] * nulifiers[0] + xs[1] * nulifiers[1] + xs[2] * nulifiers[2], + // ys[0] * nulifiers[0] + ys[1] * nulifiers[1] + ys[2] * nulifiers[2]) + for (std::size_t i = 0; i < xs.size(); ++i) { + var tmp = zk::components::generate_circuit(bp, assignment, + {xs[i], nulifiers[i]}, row) + .output; + row += mul_component::rows_amount; + x = zk::components::generate_circuit(bp, assignment, {x, tmp}, row).output; + row += add_component::rows_amount; + + var tmp_y = zk::components::generate_circuit(bp, assignment, + {ys[i], nulifiers[i]}, row) + .output; + row += mul_component::rows_amount; + y = zk::components::generate_circuit(bp, assignment, {y, tmp_y}, row).output; + row += add_component::rows_amount; + } + + assert(row == start_row_index + rows_amount); + + generate_copy_constraints(bp, assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + var zero(0, start_row_index, false, var::column_type::constant); + var one(0, start_row_index + 1, false, var::column_type::constant); + var minus_one(0, start_row_index + 2, false, var::column_type::constant); + + curve_params params_curve(start_row_index); + + std::array xs = + potential_xs_assignment(assignment, params.t, params_curve, one, zero, row); + row += potential_xs_rows; + + std::array ys; + for (std::size_t i = 0; i < xs.size(); ++i) { + ys[i] = get_y_assignments(assignment, xs[i], params_curve, row); + row += get_y_rows; + } + + std::array nulifiers; + // nulifiers[i] = 1 if ys[i] != -1 AND nulifiers[i - 1] == 0, 0 otherwise + // E1: (ys[i] - (-1)) * (ys[i] - (-1))**(-1) -1 = 0 if ys[i] != -1, -1 otherwise + // E2: E1 + 1 = 1 if ys[i] != -1, 0 otherwise + // E3: nulifiers[i - 1] * nulifiers[i - 1]**(-1) -1 = 0 if nulifiers[i - 1] != 0, -1 otherwise + // E4: E3 * (-1) = 0 if nulifiers[i - 1] != 0, 1 otherwise + // E5: E2 * E4 = 1 if ys[i] != -1 AND nulifiers[i - 1] = 0, 0 otherwise + + for (std::size_t i = 0; i < ys.size(); ++i) { + var y1 = sub_component::generate_assignments(assignment, {ys[i], minus_one}, row).output; + row += sub_component::rows_amount; + var y1_inversed = div_component::generate_assignments(assignment, {one, y1}, row).output; + row += div_component::rows_amount; + + var e1 = mul_component::generate_assignments(assignment, {y1, y1_inversed}, row).output; + row += mul_component::rows_amount; + e1 = sub_component::generate_assignments(assignment, {e1, one}, row).output; + row += sub_component::rows_amount; + + var e2 = add_component::generate_assignments(assignment, {e1, one}, row).output; + row += add_component::rows_amount; + + if (i == 0) { + nulifiers[i] = e2; + continue; + } + + var n_inversed = + div_component::generate_assignments(assignment, {one, nulifiers[i - 1]}, row).output; + row += div_component::rows_amount; + + var e3 = + mul_component::generate_assignments(assignment, {nulifiers[i - 1], n_inversed}, row) + .output; + row += mul_component::rows_amount; + + e3 = sub_component::generate_assignments(assignment, {e3, one}, row).output; + row += sub_component::rows_amount; + + var e4 = mul_component::generate_assignments(assignment, {e3, minus_one}, row).output; + row += mul_component::rows_amount; + + var e5 = mul_component::generate_assignments(assignment, {e2, e4}, row).output; + row += mul_component::rows_amount; + + nulifiers[i] = e5; + } + + var x = zero; + var y = zero; + + // res = (xs[0] * nulifiers[0] + xs[1] * nulifiers[1] + xs[2] * nulifiers[2], + // ys[0] * nulifiers[0] + ys[1] * nulifiers[1] + ys[2] * nulifiers[2]) + for (std::size_t i = 0; i < xs.size(); ++i) { + var tmp = + mul_component::generate_assignments(assignment, {xs[i], nulifiers[i]}, row).output; + row += mul_component::rows_amount; + x = add_component::generate_assignments(assignment, {x, tmp}, row).output; + row += add_component::rows_amount; + + var tmp_y = + mul_component::generate_assignments(assignment, {ys[i], nulifiers[i]}, row).output; + row += mul_component::rows_amount; + y = add_component::generate_assignments(assignment, {y, tmp_y}, row).output; + row += add_component::rows_amount; + } + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + assignment.constant(0)[row] = -1; + row++; + + // curve_params: + typename BlueprintFieldType::value_type u_val; + typename BlueprintFieldType::value_type fu_val; + typename BlueprintFieldType::value_type inv_three_u_squared_val; + typename BlueprintFieldType::value_type sqrt_neg_three_u_squared_val; + typename BlueprintFieldType::value_type sqrt_neg_three_u_squared_minus_u_over_2_val; + typename BlueprintFieldType::value_type b_val; + + if (std::is_same::value) { + u_val = 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular255; + fu_val = 0x0000000000000000000000000000000000000000000000000000000000000006_cppui_modular255; + inv_three_u_squared_val = + 0x2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC18465FD5B88A612661E209E00000001_cppui_modular255; + sqrt_neg_three_u_squared_val = + 0x25999506959B74E25955ABB8AF5563603A3F17A46F5A62923B5ABD7BFBFC9573_cppui_modular255; + sqrt_neg_three_u_squared_minus_u_over_2_val = + 0x12CCCA834ACDBA712CAAD5DC57AAB1B01D1F8BD237AD31491DAD5EBDFDFE4AB9_cppui_modular255; + b_val = 0x0000000000000000000000000000000000000000000000000000000000000005_cppui_modular255; + } else { // vesta + u_val = 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular255; + fu_val = 0x0000000000000000000000000000000000000000000000000000000000000006_cppui_modular255; + inv_three_u_squared_val = + 0x2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC18465FD5BB87093B2D9F21600000001_cppui_modular255; + sqrt_neg_three_u_squared_val = + 0x0D0334B0507CA51CA23B69B039EE1EB41FDA8CFA8F80675E5553A5C0A1541C9F_cppui_modular255; + sqrt_neg_three_u_squared_minus_u_over_2_val = + 0x06819A58283E528E511DB4D81CF70F5A0FED467D47C033AF2AA9D2E050AA0E4F_cppui_modular255; + b_val = 0x0000000000000000000000000000000000000000000000000000000000000005_cppui_modular255; + } + // var u; + assignment.constant(0)[row] = u_val; + row++; + // var fu; + assignment.constant(0)[row] = fu_val; + row++; + // var inv_three_u_squared; + assignment.constant(0)[row] = inv_three_u_squared_val; + row++; + // var sqrt_neg_three_u_squared; + assignment.constant(0)[row] = sqrt_neg_three_u_squared_val; + row++; + // var sqrt_neg_three_u_squared_minus_u_over_2; + assignment.constant(0)[row] = sqrt_neg_three_u_squared_minus_u_over_2_val; + row++; + // var b; + assignment.constant(0)[row] = b_val; + row++; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_TO_GROUP_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fq.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fq.hpp new file mode 100644 index 000000000..6928adf71 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fq.hpp @@ -0,0 +1,291 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_FQ_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_FQ_HPP + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Fiat-Shamir transfotmation (base field part) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/sponge.rs#L98 + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/sponge.rs#L128 + template + class kimchi_transcript_fq; + + template + class kimchi_transcript_fq, + CurveType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using group_value = typename zk::components::var_ec_point; + + constexpr static bool scalar_larger() { + using ScalarField = typename CurveType::scalar_field_type; + using BaseField = typename CurveType::base_field_type; + + auto n1 = ScalarField::modulus; + auto n2 = BaseField::modulus; + return (n1 > n2); + } + + static const std::size_t fr_value_size = scalar_larger() ? 2 : 1; + + struct fr_value { + std::array value; + }; + + static const std::size_t CHALLENGE_LENGTH_IN_LIMBS = 2; + static const std::size_t HIGH_ENTROPY_LIMBS = 2; + + using sponge_component = kimchi_sponge; + sponge_component sponge; + + using sub_component = zk::components::subtraction; + using mul_component = zk::components::multiplication; + + using pack = from_limbs; + using unpack = + to_limbs; + using compare = compare_with_const; + + std::vector last_squeezed; + + std::array + squeeze_limbs_assignment(blueprint_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + if (last_squeezed.size() >= CHALLENGE_LENGTH_IN_LIMBS) { + std::array limbs = {last_squeezed[0], last_squeezed[1]}; + std::vector remaining = {last_squeezed.begin() + CHALLENGE_LENGTH_IN_LIMBS, + last_squeezed.end()}; + last_squeezed = remaining; + return limbs; + } + var sq = sponge.squeeze_assignment(assignment, row); + row += sponge_component::squeeze_rows; + auto x = unpack::generate_assignments(assignment, {sq}, row).result; + row += unpack::rows_amount; + for (int i = 0; i < HIGH_ENTROPY_LIMBS; ++i) { + last_squeezed.push_back(x[i]); + } + return squeeze_limbs_assignment(assignment, row); + } + + std::array + squeeze_limbs_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + if (last_squeezed.size() >= CHALLENGE_LENGTH_IN_LIMBS) { + std::array limbs = {last_squeezed[0], last_squeezed[1]}; + std::vector remaining = {last_squeezed.begin() + CHALLENGE_LENGTH_IN_LIMBS, + last_squeezed.end()}; + last_squeezed = remaining; + return limbs; + } + var sq = sponge.squeeze_circuit(bp, assignment, row); + row += sponge_component::squeeze_rows; + auto x = unpack::generate_circuit(bp, assignment, {sq}, row).result; + row += unpack::rows_amount; + for (int i = 0; i < HIGH_ENTROPY_LIMBS; ++i) { + last_squeezed.push_back(x[i]); + } + return squeeze_limbs_circuit(bp, assignment, row); + } + + public: + constexpr static const std::size_t rows_amount = 0; + constexpr static const std::size_t init_rows = sponge_component::init_rows; + constexpr static const std::size_t absorb_group_rows = 2 * sponge_component::absorb_rows; + constexpr static const std::size_t absorb_fr_rows = fr_value_size * sponge_component::absorb_rows; + constexpr static const std::size_t challenge_rows = + sponge_component::squeeze_rows + unpack::rows_amount + pack::rows_amount; + constexpr static const std::size_t challenge_fq_rows = sponge_component::squeeze_rows; + constexpr static const std::size_t digest_rows = + sponge_component::squeeze_rows + compare::rows_amount + mul_component::rows_amount; + + void init_assignment(blueprint_assignment_table &assignment, + var zero, + const std::size_t component_start_row) { + sponge.init_assignment(assignment, zero, component_start_row); + last_squeezed = {}; + } + + void init_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var &zero, + const std::size_t component_start_row) { + sponge.init_circuit(bp, assignment, zero, component_start_row); + last_squeezed = {}; + } + + void absorb_g_assignment(blueprint_assignment_table &assignment, + group_value g, + std::size_t component_start_row) { + // accepts {g.X, g.Y} + std::size_t row = component_start_row; + last_squeezed = {}; + sponge.absorb_assignment(assignment, g.X, row); + row += sponge_component::absorb_rows; + sponge.absorb_assignment(assignment, g.Y, row); + } + + void absorb_g_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + group_value g, + std::size_t component_start_row) { + // accepts {g.X, g.Y} + std::size_t row = component_start_row; + last_squeezed = {}; + sponge.absorb_circuit(bp, assignment, g.X, row); + row += sponge_component::absorb_rows; + sponge.absorb_circuit(bp, assignment, g.Y, row); + } + + void absorb_fr_assignment(blueprint_assignment_table &assignment, + fr_value absorbing_value, + std::size_t component_start_row) { + std::size_t row = component_start_row; + last_squeezed = {}; + for (std::size_t i = 0; i < fr_value_size; i++) { + sponge.absorb_assignment(assignment, absorbing_value.value[i], row); + row += sponge_component::absorb_rows; + } + } + + void absorb_fr_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + fr_value absorbing_value, + std::size_t &component_start_row) { + std::size_t row = component_start_row; + last_squeezed = {}; + for (std::size_t i = 0; i < fr_value_size; i++) { + sponge.absorb_circuit(bp, assignment, absorbing_value.value[i], row); + row += sponge_component::absorb_rows; + } + } + + var challenge_assignment(blueprint_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + auto limbs = squeeze_limbs_assignment(assignment, row); + row += sponge_component::squeeze_rows; + row += unpack::rows_amount; + return pack::generate_assignments(assignment, limbs, row).result; + } + + var challenge_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + auto limbs = squeeze_limbs_circuit(bp, assignment, row); + row += sponge_component::squeeze_rows; + row += unpack::rows_amount; + return pack::generate_circuit(bp, assignment, limbs, row).result; + } + + var challenge_fq_assignment(blueprint_assignment_table &assignment, + std::size_t component_start_row) { + last_squeezed = {}; + return sponge.squeeze_assignment(assignment, component_start_row); + } + + var challenge_fq_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + std::size_t component_start_row) { + last_squeezed = {}; + return sponge.squeeze_circuit(bp, assignment, component_start_row); + } + + var digest_assignment(blueprint_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + last_squeezed = {}; + var sq = sponge.squeeze_assignment(assignment, row); + row += sponge_component::squeeze_rows; + if (scalar_larger()) { + return sq; + } + var compare_result = compare::generate_assignments(assignment, sq, row).output; + row += compare::rows_amount; + return mul_component::generate_assignments(assignment, {compare_result, sq}, row).output; + } + + var digest_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + std::size_t component_start_row) { + std::size_t row = component_start_row; + last_squeezed = {}; + var sq = sponge.squeeze_circuit(bp, assignment, row); + row += sponge_component::squeeze_rows; + if (scalar_larger()) { + return sq; + } + var compare_result = compare::generate_circuit(bp, assignment, sq, row).output; + row += compare::rows_amount; + return zk::components::generate_circuit( + bp, assignment, {compare_result, sq}, row) + .output; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_FQ_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fr.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fr.hpp new file mode 100644 index 000000000..4f3361d8e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/transcript_fr.hpp @@ -0,0 +1,286 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_HPP + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Fiat-Shamir transfotmation (scalar field part) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/oracle/src/sponge.rs#L81 + template + class kimchi_transcript_fr; + + template + class kimchi_transcript_fr, + CurveType, + KimchiParamsType, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + static const std::size_t CHALLENGE_LENGTH_IN_LIMBS = 2; + static const std::size_t HIGH_ENTROPY_LIMBS = 2; + + using sponge_component = kimchi_sponge; + + sponge_component sponge; + using pack = from_limbs; + using unpack = + to_limbs; + + std::vector last_squeezed; + + var pack_assignment(blueprint_assignment_table &assignment, + const std::size_t component_start_row, + std::array + limbs) { + auto pack_res = pack::generate_assignments(assignment, limbs, component_start_row); + return pack_res.result; + } + + var pack_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const std::size_t component_start_row, + std::array + limbs) { + auto pack_res = pack::generate_circuit(bp, assignment, limbs, component_start_row); + return pack_res.result; + } + + std::array unpack_assignment(blueprint_assignment_table &assignment, + const std::size_t component_start_row, + var elem) { + auto unpack_res = unpack::generate_assignments(assignment, {elem}, component_start_row); + return unpack_res.result; + } + + std::array + unpack_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const std::size_t component_start_row, + var elem) { + auto unpack_res = unpack::generate_circuit(bp, assignment, {elem}, component_start_row); + return unpack_res.result; + } + + public: + constexpr static const std::size_t rows_amount = 0; + constexpr static const std::size_t init_rows = sponge_component::init_rows; + constexpr static const std::size_t absorb_rows = sponge_component::absorb_rows; + constexpr static const std::size_t challenge_rows = + sponge_component::squeeze_rows + unpack::rows_amount + pack::rows_amount; + constexpr static const std::size_t absorb_evaluations_rows = 25 * absorb_rows; + + constexpr static const std::size_t state_size = sponge_component::state_size; + + std::array state() { + return sponge._inner_state(); + } + + void init_assignment(blueprint_assignment_table &assignment, + var zero, + const std::size_t component_start_row) { + sponge.init_assignment(assignment, zero, component_start_row); + last_squeezed = {}; + } + + void init_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var &zero, + const std::size_t component_start_row) { + sponge.init_circuit(bp, assignment, zero, component_start_row); + last_squeezed = {}; + } + + void absorb_assignment(blueprint_assignment_table &assignment, + var absorbing_value, + const std::size_t component_start_row) { + last_squeezed = {}; + sponge.absorb_assignment(assignment, absorbing_value, component_start_row); + } + + void absorb_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const var &input, + const std::size_t component_start_row) { + last_squeezed = {}; + sponge.absorb_circuit(bp, assignment, input, component_start_row); + } + + void absorb_evaluations_assignment(blueprint_assignment_table &assignment, + var public_eval, + kimchi_proof_evaluations + private_eval, + const std::size_t component_start_row) { + last_squeezed = {}; + std::size_t row = component_start_row; + std::vector points = {public_eval, private_eval.z, private_eval.generic_selector, + private_eval.poseidon_selector, + private_eval.w[0], + private_eval.w[1], + private_eval.w[2], + private_eval.w[3], + private_eval.w[4], + private_eval.w[5], + private_eval.w[6], + private_eval.w[7], + private_eval.w[8], + private_eval.w[9], + private_eval.w[10], + private_eval.w[11], + private_eval.w[12], + private_eval.w[13], + private_eval.w[14], + private_eval.s[0], + private_eval.s[1], + private_eval.s[2], + private_eval.s[3], + private_eval.s[4], + private_eval.s[5]}; + for (auto p : points) { + sponge.absorb_assignment(assignment, p, row); + row += sponge_component::absorb_rows; + } + } + + void absorb_evaluations_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + var public_eval, + kimchi_proof_evaluations + private_eval, + const std::size_t component_start_row) { + last_squeezed = {}; + std::size_t row = component_start_row; + std::vector points = {public_eval, + private_eval.z, + private_eval.generic_selector, + private_eval.poseidon_selector, + private_eval.w[0], + private_eval.w[1], + private_eval.w[2], + private_eval.w[3], + private_eval.w[4], + private_eval.w[5], + private_eval.w[6], + private_eval.w[7], + private_eval.w[8], + private_eval.w[9], + private_eval.w[10], + private_eval.w[11], + private_eval.w[12], + private_eval.w[13], + private_eval.w[14], + private_eval.s[0], + private_eval.s[1], + private_eval.s[2], + private_eval.s[3], + private_eval.s[4], + private_eval.s[5]}; + for (auto p : points) { + sponge.absorb_circuit(bp, assignment, p, row); + row += sponge_component::absorb_rows; + } + } + + var challenge_assignment(blueprint_assignment_table &assignment, + const std::size_t component_start_row) { + std::size_t row = component_start_row; + if (last_squeezed.size() >= CHALLENGE_LENGTH_IN_LIMBS) { + std::array limbs = {last_squeezed[0], last_squeezed[1]}; + std::vector remaining = {last_squeezed.begin() + CHALLENGE_LENGTH_IN_LIMBS, + last_squeezed.end()}; + last_squeezed = remaining; + return pack_assignment(assignment, row, limbs); + } + var sq = sponge.squeeze_assignment(assignment, row); + row += sponge_component::squeeze_rows; + auto x = unpack_assignment(assignment, row, sq); + row += unpack::rows_amount; + for (int i = 0; i < HIGH_ENTROPY_LIMBS; ++i) { + last_squeezed.push_back(x[i]); + } + return challenge_assignment(assignment, row); + } + + var challenge_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const std::size_t component_start_row) { + std::size_t row = component_start_row; + if (last_squeezed.size() >= CHALLENGE_LENGTH_IN_LIMBS) { + std::array limbs = {last_squeezed[0], last_squeezed[1]}; + std::vector remaining = {last_squeezed.begin() + CHALLENGE_LENGTH_IN_LIMBS, + last_squeezed.end()}; + last_squeezed = remaining; + return pack_circuit(bp, assignment, row, limbs); + } + var sq = sponge.squeeze_circuit(bp, assignment, row); + row += sponge_component::squeeze_rows; + auto x = unpack_circuit(bp, assignment, row, sq); + row += unpack::rows_amount; + for (int i = 0; i < HIGH_ENTROPY_LIMBS; ++i) { + last_squeezed.push_back(x[i]); + } + return challenge_circuit(bp, assignment, row); + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TRANSCRIPT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zk_w3.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zk_w3.hpp new file mode 100644 index 000000000..4acf58387 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zk_w3.hpp @@ -0,0 +1,149 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZK_W3_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZK_W3_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // Returns the end of the circuit, which is used for introducing zero-knowledge in the permutation + // polynomial + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/polynomials/permutation.rs#L85 + // Input: verifier_index + // Output: g**(domain_size - zk_rows) + template + class zk_w3; + + template + class zk_w3, W0, W1, W2, W3, + W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + constexpr static std::size_t exp_size = 64; + using exp_component = + zk::components::exponentiation; + + using verifier_index_type = kimchi_verifier_index_scalar; + + constexpr static const std::size_t zk_rows = 3; + + constexpr static const std::size_t selector_seed = 0xf21; + + public: + constexpr static const std::size_t rows_amount = exp_component::rows_amount + 1; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + verifier_index_type verifier_index; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + output = typename exp_component::result_type(start_row_index + 1).output; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + // domain.group_gen.pow(&[domain.size - (ZK_ROWS)]) + var exponent(0, start_row_index, false, var::column_type::constant); + row++; // exponent component also uses constant column + var res = exp_component::generate_circuit(bp, assignment, + {params.verifier_index.omega, exponent}, row) + .output; + row += exp_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + generate_assignments_constants(assignment, params, start_row_index); + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + // domain.group_gen.pow(&[domain.size - (ZK_ROWS)]) + var exponent(0, start_row_index, false, var::column_type::constant); + row++; // exponent component also uses constant column + var res = exp_component::generate_assignments(assignment, + {params.verifier_index.omega, exponent}, row) + .output; + row += exp_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void generate_assignments_constants( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = params.verifier_index.domain_size - zk_rows; + row++; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZK_W3_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zkpm_evaluate.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zkpm_evaluate.hpp new file mode 100644 index 000000000..21f774084 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/detail/zkpm_evaluate.hpp @@ -0,0 +1,208 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZKPM_EVALUATE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZKPM_EVALUATE_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // (x - w^{n - 3}) * (x - w^{n - 2}) * (x - w^{n - 1}) + // zk-polynomial evaluation + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/circuits/polynomials/permutation.rs#L91 + // Input: group generator (w), + // domain size (n), + // evaluation point (x) + // Output: (x - w^{n - 3}) * (x - w^{n - 2}) * (x - w^{n - 1}) + template + class zkpm_evaluate; + + template + class zkpm_evaluate, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using exp_component = zk::components::exponentiation; + using sub_component = zk::components::subtraction; + + constexpr static const std::size_t selector_seed = 0x0f25; + + constexpr static const std::size_t zk_rows = 3; + + public: + constexpr static const std::size_t rows_amount = 1 + exp_component::rows_amount + + 4 * mul_component::rows_amount + + 3 * sub_component::rows_amount; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var group_gen; + std::size_t domain_size; + var x; + }; + + struct result_type { + var output; + + result_type(std::size_t start_row_index) { + std::size_t row = start_row_index; + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + var domain_size = var(0, start_row_index, false, var::column_type::constant); + + std::size_t row = start_row_index; + row++; // skip row for constants in exp_component + + result_type result(row); + + var w1 = exp_component::generate_circuit(bp, assignment, {params.group_gen, domain_size}, row) + .output; + row += exp_component::rows_amount; + var w2 = + zk::components::generate_circuit(bp, assignment, {w1, params.group_gen}, row) + .output; + row += mul_component::rows_amount; + var w3 = + zk::components::generate_circuit(bp, assignment, {w2, params.group_gen}, row) + .output; + row += mul_component::rows_amount; + + var a1 = + zk::components::generate_circuit(bp, assignment, {params.x, w1}, row).output; + row += sub_component::rows_amount; + var a2 = + zk::components::generate_circuit(bp, assignment, {params.x, w2}, row).output; + row += sub_component::rows_amount; + var a3 = + zk::components::generate_circuit(bp, assignment, {params.x, w3}, row).output; + row += sub_component::rows_amount; + + var ans1 = + zk::components::generate_circuit(bp, assignment, {a1, a2}, row).output; + row += mul_component::rows_amount; + result.output = + zk::components::generate_circuit(bp, assignment, {ans1, a3}, row).output; + row += mul_component::rows_amount; + + return result; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var domain_size = var(0, start_row_index, false, var::column_type::constant); + + std::size_t row = start_row_index; + row++; // skip row for constants in exp_component + + result_type result(row); + + var w1 = exp_component::generate_assignments(assignment, {params.group_gen, domain_size}, row) + .output; + row += exp_component::rows_amount; + var w2 = mul_component::generate_assignments(assignment, {w1, params.group_gen}, row).output; + row += mul_component::rows_amount; + var w3 = mul_component::generate_assignments(assignment, {w2, params.group_gen}, row).output; + row += mul_component::rows_amount; + + var a1 = sub_component::generate_assignments(assignment, {params.x, w1}, row).output; + row += sub_component::rows_amount; + var a2 = sub_component::generate_assignments(assignment, {params.x, w2}, row).output; + row += sub_component::rows_amount; + var a3 = sub_component::generate_assignments(assignment, {params.x, w3}, row).output; + row += sub_component::rows_amount; + + var ans1 = mul_component::generate_assignments(assignment, {a1, a2}, row).output; + row += mul_component::rows_amount; + result.output = mul_component::generate_assignments(assignment, {ans1, a3}, row).output; + row += mul_component::rows_amount; + + return result; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = params.domain_size - zk_rows; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_ZKPM_EVALUATE_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/oracles_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/oracles_scalar.hpp new file mode 100644 index 000000000..f1ed3a989 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/oracles_scalar.hpp @@ -0,0 +1,696 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_ORACLES_SCALAR_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_ORACLES_SCALAR_COMPONENT_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // random oracles argument (scalar field part) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L113 + // Input: kimchi proof (scalar field part), + // challenges generated by SpongeFq, + // verifier index (public data) + // Output: oracles result, scalar field part + // (https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L457-L468) + template + class oracles_scalar; + + template + class oracles_scalar< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, KimchiCommitmentParamsType, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + constexpr static const std::size_t selector_seed = 0x0f08; + + using endo_scalar_component = + zk::components::endo_scalar; + + using exponentiation_component = + zk::components::exponentiation; + using mul_component = zk::components::multiplication; + + using alpha_powers_component = zk::components::element_powers; + + using pi_powers_component = zk::components::element_powers; + + using lagrange_denominators_component = + zk::components::lagrange_denominators; + + using public_eval_component = + zk::components::public_evaluations; + + using prev_chal_evals_component = + zk::components::prev_chal_evals; + + using combined_proof_evals_component = + zk::components::combine_proof_evals; + + using ft_eval_component = + zk::components::ft_eval; + + using cip_component = zk::components::oracles_cip; + + using transcript_type = kimchi_transcript; + + + + using proof_binding = typename zk::components::binding; + + constexpr static const std::size_t eval_points_amount = 2; + using prev_chal_output = + std::array, eval_points_amount>; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + if (KimchiParamsType::circuit_params::use_lookup && + KimchiParamsType::circuit_params::joint_lookup) { + row += endo_scalar_component::rows_amount; + } + + // alpha + row += endo_scalar_component::rows_amount; + // zeta + row += endo_scalar_component::rows_amount; + + //transcript.init_assignment(assignment, row); + row += transcript_type::init_rows; + row += transcript_type::absorb_rows; + + // zeta_pow_n + row += exponentiation_component::rows_amount; + + // zeta_omega + row += mul_component::rows_amount; + + // zeta_omega_pow_n + row += exponentiation_component::rows_amount; + + // alpha_powers + row += alpha_powers_component::rows_amount; + + // omega_powers + row += pi_powers_component::rows_amount; + + // lagrange_denominators + row += lagrange_denominators_component::rows_amount; + + // TODO: check on empty public_input + // public_eval + row += public_eval_component::rows_amount; + + row += transcript_type::absorb_evaluations_rows; + row += transcript_type::absorb_evaluations_rows; + + row += transcript_type::absorb_rows; + + // v_challenge + row += transcript_type::challenge_rows; + + row += endo_scalar_component::rows_amount; + + // u_challenge + row += transcript_type::challenge_rows; + + row += endo_scalar_component::rows_amount; + + // powers_of_eval_points_for_chunks + row += 2 * exponentiation_component::rows_amount; + + // prev_challenges_evals + row += prev_chal_evals_component::rows_amount; + + // combined_evals + for (std::size_t i = 0; i < eval_points_amount; i++) { + row += combined_proof_evals_component::rows_amount; + } + + // ft_eval0 + row += ft_eval_component::rows_amount; + + //cip + row += cip_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + + + kimchi_verifier_index_scalar &verifier_index; + kimchi_proof_scalar &proof; + typename proof_binding::fq_sponge_output &fq_output; + + params_type(kimchi_verifier_index_scalar &_verifier_index, + kimchi_proof_scalar &_proof, + typename proof_binding::fq_sponge_output &_fq_output) : + verifier_index(_verifier_index), + proof(_proof), + fq_output(_fq_output) {} + }; + + struct result_type { + struct random_oracles { + var alpha; + var zeta; + var v; + var u; + var v_chal; + var u_chal; + }; + + transcript_type transcript; + random_oracles oracles; + std::array alpha_powers; + std::array p_eval; + std::array powers_of_eval_points_for_chunks; + std::array + prev_challenges_evals; + var zeta_pow_n; + var ft_eval0; + std::array, + eval_points_amount> combined_evals; + var cip; + std::array eval_points; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + auto selector_iterator = assignment.find_selector(selector_seed); + std::size_t first_selector_index; + + if (selector_iterator == assignment.selectors_end()) { + first_selector_index = assignment.allocate_selector(selector_seed, gates_amount); + generate_gates(bp, assignment, params, first_selector_index); + } else { + first_selector_index = selector_iterator->second; + } + + generate_assignments_constant(bp, assignment, params, start_row_index); + + var zero = var(0, start_row_index + 4, false, var::column_type::constant); + var one = var(0, start_row_index + 5, false, var::column_type::constant); + var domain_size = var(0, start_row_index + 6, false, var::column_type::constant); + var max_poly_size = var(0, start_row_index + 7, false, var::column_type::constant); + + std::size_t row = start_row_index; + + var beta = params.fq_output.beta; + var gamma = params.fq_output.gamma; + + + var joint_combiner; + if (KimchiParamsType::circuit_params::use_lookup && + KimchiParamsType::circuit_params::joint_lookup) { + joint_combiner = endo_scalar_component::generate_circuit(bp, assignment, + {params.fq_output.joint_combiner}, row).output; + row += endo_scalar_component::rows_amount; + } + + // alpha = phi(alpha_challenge) + var alpha = endo_scalar_component::generate_circuit( + bp, assignment, {params.fq_output.alpha}, row).output; + row += endo_scalar_component::rows_amount; + // zeta = phi(zeta_challenge) + var zeta = endo_scalar_component::generate_circuit( + bp, assignment, {params.fq_output.zeta}, row).output; + row += endo_scalar_component::rows_amount; + + // fr_transcript.absorb(fq_digest) + transcript_type transcript; + transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + transcript.absorb_circuit(bp, assignment, params.fq_output.fq_digest, row); + row += transcript_type::absorb_rows; + + // zeta_pow_n = zeta**n + var zeta_pow_n = exponentiation_component::generate_circuit( + bp, assignment, + {zeta, domain_size}, row) + .output; + row += exponentiation_component::rows_amount; + + var zeta_omega = zk::components::generate_circuit(bp, assignment, + {zeta, params.verifier_index.omega}, row).output; + row += mul_component::rows_amount; + + var zeta_omega_pow_n = + exponentiation_component::generate_circuit(bp, assignment, + {zeta_omega, domain_size}, row).output; + row += exponentiation_component::rows_amount; + + std::array alpha_powers = + alpha_powers_component::generate_circuit(bp, assignment, + {alpha, one}, row).output; + row += alpha_powers_component::rows_amount; + + std::array omega_powers = + pi_powers_component::generate_circuit(bp, assignment, + {params.verifier_index.omega, one}, row).output; + row += pi_powers_component::rows_amount; + + std::array lagrange_denominators = + lagrange_denominators_component::generate_circuit(bp, assignment, + {zeta, zeta_omega, omega_powers, one}, row).output; + row += lagrange_denominators_component::rows_amount; + + // TODO: check on empty public_input + std::array pi = params.proof.public_input; + std::array public_eval = public_eval_component::generate_circuit(bp, + assignment, {zeta_pow_n, zeta_omega_pow_n, + pi, + lagrange_denominators, + omega_powers, + domain_size, one, zero}, row).output; + row += public_eval_component::rows_amount; + + transcript.absorb_evaluations_circuit( + bp, assignment, public_eval[0], params.proof.proof_evals[0], row); + row += transcript_type::absorb_evaluations_rows; + transcript.absorb_evaluations_circuit( + bp, assignment, public_eval[1], params.proof.proof_evals[1], row); + row += transcript_type::absorb_evaluations_rows; + + transcript.absorb_circuit(bp, assignment, params.proof.ft_eval, row); + row += transcript_type::absorb_rows; + + var v_challenge = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + var v = endo_scalar_component::generate_circuit( + bp, assignment, {v_challenge}, row).output; + row += endo_scalar_component::rows_amount; + + var u_challenge = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + var u = endo_scalar_component::generate_circuit( + bp, assignment, {u_challenge}, row).output; + row += endo_scalar_component::rows_amount; + + + std::array powers_of_eval_points_for_chunks; + powers_of_eval_points_for_chunks[0] = exponentiation_component::generate_circuit( + bp, assignment, + {zeta, max_poly_size}, row) + .output; + row += exponentiation_component::rows_amount; + powers_of_eval_points_for_chunks[1] = exponentiation_component::generate_circuit( + bp, assignment, + {zeta_omega, max_poly_size}, row) + .output; + row += exponentiation_component::rows_amount; + + + std::array prev_challenges_evals; + + for (std::size_t i = 0; i < KimchiParamsType::prev_challenges_size; i++) { + std::array prev_challenges = + params.proof.prev_challenges[i]; + prev_challenges_evals[i] = + prev_chal_evals_component::generate_circuit(bp, assignment, + {prev_challenges, + {{zeta, zeta_omega}}, + powers_of_eval_points_for_chunks, + one, zero}, row).output; + row += prev_chal_evals_component::rows_amount; + } + + std::array, + eval_points_amount> combined_evals; + for (std::size_t i = 0; i < eval_points_amount; i++) { + combined_evals[i] = combined_proof_evals_component::generate_circuit( + bp, assignment, {params.proof.proof_evals[i], + powers_of_eval_points_for_chunks[i]}, row).output; + row += combined_proof_evals_component::rows_amount; + } + + std::array, + eval_points_amount> evals = params.proof.proof_evals; + var ft_eval0 = ft_eval_component::generate_circuit( + bp, + assignment, + {params.verifier_index, + zeta_pow_n, + alpha_powers, + combined_evals, + gamma, + beta, + public_eval, + zeta, + joint_combiner}, + row + ).output; + row += ft_eval_component::rows_amount; + + //cip + var cip = cip_component::generate_circuit(bp, + assignment, + {v, + u, + ft_eval0, + params.proof.ft_eval, + prev_challenges_evals, + public_eval, + params.proof.proof_evals}, + row).output; + row += cip_component::rows_amount; + + generate_copy_constraints(bp, assignment, params, start_row_index); + + typename result_type::random_oracles random_oracles = { + alpha, + zeta, + v, + u, + v_challenge, + u_challenge + }; + + return { + transcript, + random_oracles, + alpha_powers, + public_eval, + powers_of_eval_points_for_chunks, + prev_challenges_evals, + zeta_pow_n, + ft_eval0, + combined_evals, + cip, + {zeta, zeta_omega} + }; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + typename BlueprintFieldType::value_type endo_factor = + 0x12CCCA834ACDBA712CAAD5DC57AAB1B01D1F8BD237AD31491DAD5EBDFDFE4AB9_cppui_modular255; + std::size_t num_bits = 128; + + var fq_digest = params.fq_output.fq_digest; + var beta = params.fq_output.beta; + var gamma = params.fq_output.gamma; + + var joint_combiner; + if (KimchiParamsType::circuit_params::use_lookup && + KimchiParamsType::circuit_params::joint_lookup) { + joint_combiner = endo_scalar_component::generate_assignments(assignment, + {params.fq_output.joint_combiner}, row).output; + row += endo_scalar_component::rows_amount; + } + + var alpha = endo_scalar_component::generate_assignments(assignment, + {params.fq_output.alpha}, row).output; + row += endo_scalar_component::rows_amount; + + var zeta = endo_scalar_component::generate_assignments(assignment, + {params.fq_output.zeta}, row).output; + row += endo_scalar_component::rows_amount; + + var zero = var(0, start_row_index + 4, false, var::column_type::constant); + var one = var(0, start_row_index + 5, false, var::column_type::constant); + var domain_size = var(0, start_row_index + 6, false, var::column_type::constant); + var max_poly_size = var(0, start_row_index + 7, false, var::column_type::constant); + + transcript_type transcript; + transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + transcript.absorb_assignment(assignment, fq_digest, row); + row += transcript_type::absorb_rows; + + var n = domain_size; + var zeta_pow_n = exponentiation_component::generate_assignments( + assignment, {zeta, n}, row).output; + row += exponentiation_component::rows_amount; + + var zeta_omega = mul_component::generate_assignments(assignment, {zeta, + params.verifier_index.omega}, row).output; + row += mul_component::rows_amount; + + var zeta_omega_pow_n = exponentiation_component::generate_assignments( + assignment, {zeta_omega, n}, row).output; + row += exponentiation_component::rows_amount; + + std::array alpha_powers = alpha_powers_component::generate_assignments( + assignment, {alpha, one}, row).output; + row += alpha_powers_component::rows_amount; + + std::array omega_powers = + pi_powers_component::generate_assignments(assignment, + {params.verifier_index.omega, one}, row).output; + row += pi_powers_component::rows_amount; + + std::array lagrange_denominators = + lagrange_denominators_component::generate_assignments(assignment, + {zeta, zeta_omega, omega_powers, one}, row).output; + row += lagrange_denominators_component::rows_amount; + + std::array pi = params.proof.public_input; + std::array public_eval = public_eval_component::generate_assignments( + assignment, {zeta_pow_n, zeta_omega_pow_n, + pi, + lagrange_denominators, + omega_powers, + n, one, zero}, row).output; + row += public_eval_component::rows_amount; + + transcript.absorb_evaluations_assignment( + assignment, public_eval[0], params.proof.proof_evals[0], row); + row += transcript_type::absorb_evaluations_rows; + transcript.absorb_evaluations_assignment( + assignment, public_eval[1], params.proof.proof_evals[1], row); + row += transcript_type::absorb_evaluations_rows; + + transcript.absorb_assignment(assignment, params.proof.ft_eval, row); + row += transcript_type::absorb_rows; + + var v_challenge = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + var v = endo_scalar_component::generate_assignments(assignment, + {v_challenge}, row).output; + row += endo_scalar_component::rows_amount; + + var u_challenge = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + var u = endo_scalar_component::generate_assignments(assignment, + {u_challenge}, row).output; + row += endo_scalar_component::rows_amount; + + std::array powers_of_eval_points_for_chunks = { + exponentiation_component::generate_assignments( + assignment, {zeta, max_poly_size}, row).output, + exponentiation_component::generate_assignments( + assignment, {zeta_omega, max_poly_size}, + row + exponentiation_component::rows_amount).output + }; + row += 2 * exponentiation_component::rows_amount; + + std::array prev_challenges_evals; + + for (std::size_t i = 0; i < KimchiParamsType::prev_challenges_size; i++) { + std::array prev_challenges = + params.proof.prev_challenges[i]; + prev_challenges_evals[i] = + prev_chal_evals_component::generate_assignments(assignment, + {prev_challenges, + {{zeta, zeta_omega}}, + powers_of_eval_points_for_chunks, + one, zero}, row).output; + row += prev_chal_evals_component::rows_amount; + } + + std::array, + eval_points_amount> combined_evals; + for (std::size_t i = 0; i < eval_points_amount; i++) { + combined_evals[i] = combined_proof_evals_component::generate_assignments( + assignment, {params.proof.proof_evals[i], + powers_of_eval_points_for_chunks[i]}, row).output; + row += combined_proof_evals_component::rows_amount; + } + + std::array, + eval_points_amount> evals = params.proof.proof_evals; + var ft_eval0 = ft_eval_component::generate_assignments( + assignment, + {params.verifier_index, + zeta_pow_n, + alpha_powers, + combined_evals, + gamma, + beta, + public_eval, + zeta, + joint_combiner}, + row + ).output; + row += ft_eval_component::rows_amount; + + //cip + var cip = cip_component::generate_assignments( + assignment, + {v, + u, + ft_eval0, + params.proof.ft_eval, + prev_challenges_evals, + public_eval, + params.proof.proof_evals}, + row).output; + row += cip_component::rows_amount; + + typename result_type::random_oracles random_oracles = { + alpha, + zeta, + v, + u, + v_challenge, + u_challenge + }; + + return { + transcript, + random_oracles, + alpha_powers, + public_eval, + powers_of_eval_points_for_chunks, + prev_challenges_evals, + zeta_pow_n, + ft_eval0, + combined_evals, + cip, + {zeta, zeta_omega} + }; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + + } + + static void + generate_assignments_constant(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row + 4; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + + assignment.constant(0)[row] = params.verifier_index.domain_size; + row++; + assignment.constant(0)[row] = KimchiCommitmentParamsType::max_poly_size; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_ORACLES_SCALAR_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/prepare_batch_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/prepare_batch_scalar.hpp new file mode 100644 index 000000000..2b1565e34 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/prepare_batch_scalar.hpp @@ -0,0 +1,372 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PREPARE_BATCH_SCALAR_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PREPARE_BATCH_SCALAR_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // partial verification of the proof before batched verification (scalar field part) + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L472 + // Input: kimchi proof (scalar field part), + // challenges generated by SpongeFq, + // verifier index (public data) + // Output: batch evaluation proof, scalar field part + // (https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L881-L888) + template + class prepare_batch_scalar; + + template + class prepare_batch_scalar< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, KimchiCommitmentParamsType, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using sub_component = zk::components::subtraction; + + using oracles_component = oracles_scalar; + + using zkpm_evaluate_component = zkpm_evaluate; + + using perm_scalars_component = perm_scalars; + + using generic_scalars_component = generic_scalars; + + using index_terms_scalars_component = index_terms_scalars; + + using proof_binding = typename zk::components::binding; + + using batch_proof = batch_evaluation_proof_scalar; + + using index_terms_list = typename KimchiParamsType::circuit_params::index_terms_list; + + using kimchi_constants = zk::components::kimchi_inner_constants; + + using verifier_index_type = kimchi_verifier_index_scalar; + + constexpr static const std::size_t selector_seed = 0x0f24; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + row += oracles_component::rows_amount; + + row += zkpm_evaluate_component::rows_amount; + + row += perm_scalars_component::rows_amount; + + row += generic_scalars_component::rows_amount; + + row += sub_component::rows_amount; + + row += index_terms_scalars_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + constexpr static const std::size_t f_comm_msm_size = + kimchi_constants::f_comm_msm_size; + + struct params_type { + verifier_index_type &verifier_index; + kimchi_proof_scalar &proof; + typename proof_binding::fq_sponge_output &fq_output; + }; + + struct result_type { + batch_proof prepared_proof; + var zeta_to_srs_len; + std::array f_comm_scalars; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + generate_assignments_constant(bp, assignment, params, start_row_index); + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + var domain_size = var(0, start_row_index + 2, false, var::column_type::constant); + var max_poly_size = var(0, start_row_index + 3, false, var::column_type::constant); + + typename oracles_component::params_type oracles_params( + params.verifier_index, params.proof, params.fq_output + ); + auto oracles_output = oracles_component::generate_circuit(bp, assignment, + oracles_params, row); + row += oracles_component::rows_amount; + + std::array f_comm_scalars; + std::size_t f_comm_idx = 0; + + var zkp = zkpm_evaluate_component::generate_circuit(bp, assignment, + {params.verifier_index.omega, params.verifier_index.domain_size, + oracles_output.oracles.zeta}, row).output; + row += zkpm_evaluate_component::rows_amount; + + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + f_comm_scalars[f_comm_idx] = perm_scalars_component::generate_circuit(bp, + assignment, {oracles_output.combined_evals, oracles_output.alpha_powers, + alpha_idxs.first, + params.fq_output.beta, params.fq_output.gamma, + zkp}, row).output; + f_comm_idx += 1; + row += perm_scalars_component::rows_amount; + + alpha_idxs = + index_terms_list::alpha_map(argument_type::Generic); + std::array generic_scalars = + generic_scalars_component::generate_circuit(bp, + assignment, {oracles_output.combined_evals, oracles_output.alpha_powers, + alpha_idxs.first}, row).output; + std::copy(std::begin(generic_scalars), std::end(generic_scalars), + std::begin(f_comm_scalars) + f_comm_idx); + f_comm_idx += generic_scalars_component::output_size; + row += generic_scalars_component::rows_amount; + + // xi^n - 1 + var vanishing_eval = zk::components::generate_circuit(bp, + assignment, {oracles_output.zeta_pow_n, one}, row + ).output; + row += sub_component::rows_amount; + + auto index_scalars = index_terms_scalars_component::generate_circuit( + bp, assignment, { + oracles_output.oracles.zeta, + oracles_output.oracles.alpha, + params.fq_output.beta, params.fq_output.gamma, + params.fq_output.joint_combiner, + oracles_output.combined_evals, + params.verifier_index.omega, + params.verifier_index.domain_size}, row + ).output; + row += index_terms_scalars_component::rows_amount; + + for(std::size_t i = 0; i < index_scalars.size(); i++) { + f_comm_scalars[f_comm_idx++] = index_scalars[i]; + } + + var zeta_to_srs_len = oracles_output.powers_of_eval_points_for_chunks[0]; + + assert(row == start_row_index + rows_amount); + + result_type res = { + {oracles_output.cip, + params.fq_output, + oracles_output.eval_points, + oracles_output.oracles.u, + oracles_output.oracles.v, + params.proof.opening, + oracles_output.transcript}, + zeta_to_srs_len, + f_comm_scalars + }; + + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + var domain_size = var(0, start_row_index + 2, false, var::column_type::constant); + var max_poly_size = var(0, start_row_index + 3, false, var::column_type::constant); + + typename oracles_component::params_type oracles_params( + params.verifier_index, params.proof, params.fq_output + ); + auto oracles_output = oracles_component::generate_assignments(assignment, + oracles_params, row); + row += oracles_component::rows_amount; + + std::array f_comm_scalars; + std::size_t f_comm_idx = 0; + var zkp = zkpm_evaluate_component::generate_assignments(assignment, + {params.verifier_index.omega, params.verifier_index.domain_size, + oracles_output.oracles.zeta}, row).output; + row += zkpm_evaluate_component::rows_amount; + + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + f_comm_scalars[f_comm_idx] = perm_scalars_component::generate_assignments( + assignment, {oracles_output.combined_evals, oracles_output.alpha_powers, + alpha_idxs.first, + params.fq_output.beta, params.fq_output.gamma, + zkp}, row).output; + f_comm_idx += 1; + row += perm_scalars_component::rows_amount; + + alpha_idxs = + index_terms_list::alpha_map(argument_type::Generic); + std::array generic_scalars = + generic_scalars_component::generate_assignments( + assignment, {oracles_output.combined_evals, oracles_output.alpha_powers, + alpha_idxs.first}, row).output; + std::copy(std::begin(generic_scalars), std::end(generic_scalars), + std::begin(f_comm_scalars) + f_comm_idx); + f_comm_idx += generic_scalars_component::output_size; + row += generic_scalars_component::rows_amount; + + // xi^n - 1 + var vanishing_eval = sub_component::generate_assignments( + assignment, {oracles_output.zeta_pow_n, one}, row + ).output; + row += sub_component::rows_amount; + + auto index_scalars = index_terms_scalars_component::generate_assignments( + assignment, { + oracles_output.oracles.zeta, + oracles_output.oracles.alpha, + params.fq_output.beta, params.fq_output.gamma, + params.fq_output.joint_combiner, + oracles_output.combined_evals, + params.verifier_index.omega, + params.verifier_index.domain_size}, row + ).output; + row += index_terms_scalars_component::rows_amount; + for(std::size_t i = 0; i < index_scalars.size(); i++) { + f_comm_scalars[f_comm_idx] = index_scalars[i]; + } + + var zeta_to_srs_len = oracles_output.powers_of_eval_points_for_chunks[0]; + + assert(row == start_row_index + rows_amount); + + result_type res = { + {oracles_output.cip, + params.fq_output, + oracles_output.eval_points, + oracles_output.oracles.u, + oracles_output.oracles.v, + params.proof.opening, + oracles_output.transcript}, + zeta_to_srs_len, + f_comm_scalars + }; + + return res; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + + } + + static void + generate_assignments_constant(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + + assignment.constant(0)[row] = params.verifier_index.domain_size; + row++; + assignment.constant(0)[row] = KimchiCommitmentParamsType::max_poly_size; + } + }; + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PREPARE_BATCH_SCALAR_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/circuit_description.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/circuit_description.hpp new file mode 100644 index 000000000..46c75e053 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/circuit_description.hpp @@ -0,0 +1,64 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PROOF_SYSTEM_CIRCUIT_DESCRIPTION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PROOF_SYSTEM_CIRCUIT_DESCRIPTION_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct kimchi_circuit_description { + using index_terms_list = IndexTermsList; + + static const std::size_t witness_columns = WitnessColumns; + static const std::size_t permut_size = PermutSize; + + static const std::size_t alpha_powers_n = index_terms_list::alpha_powers_n; + + static const bool poseidon_gate = index_terms_list::poseidon_gate; + static const bool ec_arithmetic_gates = index_terms_list::ec_arithmetic_gates; + static const bool chacha_gate = index_terms_list::chacha_gate; + static const bool generic_gate = index_terms_list::generic_gate; + + static const std::size_t poseidon_gates_count = index_terms_list::poseidon_gates_count; + static const std::size_t ec_arithmetic_gates_count = index_terms_list::ec_arithmetic_gates_count; + + static const bool use_lookup = index_terms_list::lookup_columns > 0; + static const bool joint_lookup = index_terms_list::joint_lookup; + static const std::size_t lookup_columns = index_terms_list::lookup_columns; + static const bool lookup_runtime = index_terms_list::lookup_runtime; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PROOF_SYSTEM_CIRCUIT_DESCRIPTION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_commitment_params.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_commitment_params.hpp new file mode 100644 index 000000000..0de543501 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_commitment_params.hpp @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_SYSTEM_KIMCHI_COMMITMENT_PARAMS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_SYSTEM_KIMCHI_COMMITMENT_PARAMS_HPP + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct kimchi_commitment_params_type { + constexpr static std::size_t max_poly_size = MaxPolySize; + constexpr static std::size_t eval_rounds = EvalRounds; + constexpr static std::size_t split_poly_eval_size = max_poly_size == (1 << eval_rounds) ? 1 : 2; + constexpr static std::size_t srs_len = SrsLen; + + // TODO we can set commitments size values from template but for now it looks like we can just fix + // it + constexpr static std::size_t shifted_commitment_split = 1; + constexpr static std::size_t max_comm_size = 1; + constexpr static std::size_t w_comm_size = 1; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_SYSTEM_KIMCHI_COMMITMENT_PARAMS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_params.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_params.hpp new file mode 100644 index 000000000..2546e2b92 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/proof_system/kimchi_params.hpp @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PARAMS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PARAMS_HPP + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template + struct kimchi_params_type { + using commitment_params_type = CommitmentParamsType; + using curve_type = CurveType; + using circuit_params = CircuitDescriptionType; + + constexpr static std::size_t alpha_powers_n = CircuitDescriptionType::alpha_powers_n; + constexpr static std::size_t public_input_size = PublicInputSize; + constexpr static std::size_t witness_columns = CircuitDescriptionType::witness_columns; + constexpr static std::size_t permut_size = CircuitDescriptionType::permut_size; + + constexpr static bool use_lookup = CircuitDescriptionType::use_lookup; + + constexpr static std::size_t eval_points_amount = 2; + constexpr static std::size_t scalar_challenge_size = 128; + + constexpr static std::size_t prev_challenges_size = PrevChalSize; + + constexpr static std::size_t lookup_comm_size = 0; // TODO: + constexpr static std::size_t index_term_size() { + return circuit_params::index_terms_list::size; + } + + constexpr static std::size_t witness_commitment_size = 1; + constexpr static std::size_t z_commitment_size = 1; + constexpr static std::size_t t_commitment_size = 1; + constexpr static std::size_t lookup_runtime_commitment_size = 1; + constexpr static std::size_t lookup_sorted_commitment_size = 1; + constexpr static std::size_t lookup_aggregated_commitment_size = 1; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PARAMS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/batch_dlog_accumulator_check_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/batch_dlog_accumulator_check_scalar.hpp new file mode 100644 index 000000000..3a3981625 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/batch_dlog_accumulator_check_scalar.hpp @@ -0,0 +1,337 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the BATCH_VERIFY_SCALAR_FIELD component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_BATCH_VERIFY_SCALAR_FIELD_HPP +#define CRYPTO3_ZK_BLUEPRINT_BATCH_VERIFY_SCALAR_FIELD_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // https://github.com/MinaProtocol/mina/blob/f01d3925a273ded939a80e1de9afcd9f913a7c17/src/lib/crypto/kimchi_bindings/stubs/src/urs_utils.rs#L10 + template + class batch_dlog_accumulator_check_scalar; + + template + class batch_dlog_accumulator_check_scalar< + snark::plonk_constraint_system, CurveType, + KimchiParamsType, CommsLen, Rounds, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, + W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using KimchiCommitmentParamsType = typename KimchiParamsType::commitment_params_type; + + using mul_component = zk::components::multiplication; + using sub_component = zk::components::subtraction; + using div_component = zk::components::division; + + using random_component = + zk::components::random; + + using b_poly_coeff_component = + zk::components::b_poly_coefficients; + + using kimchi_constants = zk::components::kimchi_inner_constants; + + constexpr static std::size_t scalars_len() { + return kimchi_constants::final_msm_size(BatchSize); + } + + using prepare_scalars_component = + zk::components::prepare_scalars; + + using batch_proof = batch_evaluation_proof_scalar; + + constexpr static const std::size_t selector_seed = 0x0f28; + + constexpr static const std::size_t srs_len = KimchiCommitmentParamsType::srs_len; + constexpr static const std::size_t eval_rounds = KimchiCommitmentParamsType::eval_rounds; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + row += random_component::rows_amount; + + for (std::size_t i = 0; i < CommsLen; i++) { + row += mul_component::rows_amount; + } + + for (std::size_t i = 0; i < params.challenges.size(); i++) { + row += div_component::rows_amount; + } + + for (std::size_t i = 0; i < Rounds; i++) { + for (std::size_t j = 0; j < CommsLen; j++) { + row += b_poly_coeff_component::rows_amount; + + for (std::size_t k = 0; k < s.size(); k++) { + row += mul_component::rows_amount; + } + } + } + + for (std::size_t i = 0; i < termss.size(); i++) { + for (std::size_t j = 0; + j < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + j++) { + + row += sub_component::rows_amount; + } + } + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector challenges; + }; + + struct result_type { + std::array output; + + result_type(std::size_t start_row_index) { + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(bp, assignment, params, start_row_index); + + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + + std::array scalars; + std::size_t scalar_idx = + KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + + for (std::size_t i = 0; + i < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + i++) { + scalars[i] = zero; + } + + var rand_base = + random_component::generate_circuit(bp, assignment, {params.batches}, row).output; + row += random_component::rows_amount; + + var rand_base_i = one; + + std::array rs; + + for (std::size_t i = 0; i < CommsLen; i++) { + rs[i] = rand_base_i; + scalars[scalar_idx++] = rand_base_i; + + rand_base_i = + zk::components::generate_circuit(bp, assignment, {rand_base_i, rand_base}, row).output; + row += mul_component::rows_amount; + } + + std::vector challenges_inv(params.challenges.size()); + + for (std::size_t i = 0; i < params.challenges.size(); i++) { + challenges_inv[i] = zk::components::generate_circuit(bp, assignment, + {one, params.challenges[i]}, row) + .output; + row += div_component::rows_amount; + } + + std::array, Rounds> termss; + + for (std::size_t i = 0; i < Rounds; i++) { + for (std::size_t j = 0; j < CommsLen; j++) { + auto s = b_poly_coeff_component::generate_circuit(bp, assignment, {challenges[0], one}, row) + .output; + row += b_poly_coeff_component::rows_amount; + + for (std::size_t k = 0; k < s.size(); k++) { + s[k] = zk::components::generate_circuit(bp, assignment, {s[k], rs[j]}, row) + .output; + row += mul_component::rows_amount; + + termss[i][k] = s[k]; + } + } + } + + for (std::size_t i = 0; i < termss.size(); i++) { + for (std::size_t j = 0; + j < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + j++) { + + scalars[j] = zk::components::generate_circuit(bp, assignment, {scalars[i], termss[i][j]}, row).output; + row += sub_component::rows_amount; + } + } + + result_type res(start_row_index); + res.output = scalars; + return res; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + + std::array scalars; + std::size_t scalar_idx = + KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + + for (std::size_t i = 0; + i < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + i++) { + scalars[i] = zero; + } + + var rand_base = + random_component::generate_assignments(assignment, {params.batches}, row).output; + row += random_component::rows_amount; + + var rand_base_i = one; + + std::array rs; + + for (std::size_t i = 0; i < CommsLen; i++) { + rs[i] = rand_base_i; + scalars[scalar_idx++] = rand_base_i; + + rand_base_i = + mul_component::generate_assignments(assignment, {rand_base_i, rand_base}, row).output; + row += mul_component::rows_amount; + } + + std::vector challenges_inv(params.challenges.size()); + + for (std::size_t i = 0; i < params.challenges.size(); i++) { + challenges_inv[i] = div_component::generate_assignments(assignment, + {one, params.challenges[i]}, row) + .output; + row += div_component::rows_amount; + } + + std::array, Rounds> termss; + + for (std::size_t i = 0; i < Rounds; i++) { + for (std::size_t j = 0; j < CommsLen; j++) { + auto s = b_poly_coeff_component::generate_assignments(assignment, {challenges[0], one}, row) + .output; + row += b_poly_coeff_component::rows_amount; + + for (std::size_t k = 0; k < s.size(); k++) { + s[k] = mul_component::generate_assignments(assignment, {s[k], rs[j]}, row) + .output; + row += mul_component::rows_amount; + + termss[i][k] = s[k]; + } + } + } + + for (std::size_t i = 0; i < termss.size(); i++) { + for (std::size_t j = 0; + j < KimchiCommitmentParamsType::srs_len + kimchi_constants::srs_padding_size(); + j++) { + + scalars[j] = sub_component::generate_assignments(assignment, {scalars[i], termss[i][j]}, row).output; + row += sub_component::rows_amount; + } + } + + result_type res(start_row_index); + res.output = scalars; + return res; + } + + private: + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + row++; + } + }; + + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_BATCH_VERIFY_SCALAR_FIELD_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/derive_plonk.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/derive_plonk.hpp new file mode 100644 index 000000000..2a1fcad04 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/derive_plonk.hpp @@ -0,0 +1,277 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // https://github.com/MinaProtocol/mina/blob/a76a550bc2724f53be8ebaf681c3b35686a7f080/src/lib/pickles/plonk_checks/plonk_checks.ml#L380 + template + class derive_plonk; + + template + class derive_plonk, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using zkpm_evaluate_component = zkpm_evaluate; + + using perm_scalars_component = perm_scalars; + + using generic_scalars_component = + generic_scalars; + + using index_terms_scalars_component = + index_terms_scalars; + + using alpha_powers_component = zk::components::element_powers; + + using plonk_map_fields_component = + plobk_map_fields; + + using verifier_index_type = kimchi_verifier_index_scalar; + using index_terms_list = typename KimchiParamsType::circuit_params::index_terms_list; + + constexpr static const std::size_t lookup_rows() { + std::size_t rows = 0; + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + + if (KimchiParamsType::circuit_params::lookup_runtime) { + } + } + + return rows; + } + + constexpr static const std::size_t rows() { + std::size_t row = 0; + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + verifier_index_type verifier_index; + var zeta; + var alpha; + var beta; + var gamma; + var joint_combiner; + std::array, + KimchiParamsType::eval_points_amount> combined_evals; + }; + + struct result_type { + var output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + + + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(assignment, params, start_row_index); + + std::size_t row = start_row_index; + + var one(0, start_row_index, false, var::column_type::public_input); + + std::array alpha_powers = + alpha_powers_component::generate_circuit(bp, assignment, {params.alpha, one}, row).output; + row += alpha_powers_component::rows_amount; + + var zkp = zkpm_evaluate_component::generate_circuit(bp, assignment, + {params.verifier_index.omega, + params.verifier_index.domain_size, + params.zeta}, + row) + .output; + row += zkpm_evaluate_component::rows_amount; + + + auto index_scalars = + index_terms_scalars_component::generate_circuit(bp, + assignment, + {params.zeta, params.alpha, params.beta, + params.gamma, params.joint_combiner, params.combined_evals, + params.verifier_index.omega, params.verifier_index.domain_size}, + row) + .output; + row += index_terms_scalars_component::rows_amount; + + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + var perm_scalar = + perm_scalars_component::generate_circuit(bp, + assignment, + {params.combined_evals, alpha_powers, alpha_idxs.first, + params.fq_output.beta, params.fq_output.gamma, zkp}, + row) + .output; + row += perm_scalars_component::rows_amount; + + alpha_idxs = index_terms_list::alpha_map(argument_type::Generic); + std::array generic_scalars = + generic_scalars_component::generate_circuit(bp, + assignment, + {params.combined_evals, oracles_output.alpha_powers, alpha_idxs.first}, row) + .output; + row += generic_scalars_component::rows_amount; + + var output = plonk_map_fields_component::generate_circuit(bp, + assignment, + {params.alpha, params.beta, params.gamma, params.joint_combiner, + index_scalars, perm_scalar, generic_scalars}, + row).output; + row += plonk_map_fields_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var one(0, start_row_index, false, var::column_type::public_input); + + std::array alpha_powers = + alpha_powers_component::generate_circuit(bp, assignment, {params.alpha, one}, row).output; + row += alpha_powers_component::rows_amount; + + var zkp = zkpm_evaluate_component::generate_assignments(assignment, + {params.verifier_index.omega, + params.verifier_index.domain_size, + params.zeta}, + row) + .output; + row += zkpm_evaluate_component::rows_amount; + + + auto index_scalars = + index_terms_scalars_component::generate_assignments( + assignment, + {params.zeta, params.alpha, params.beta, + params.gamma, params.joint_combiner, params.combined_evals, + params.verifier_index.omega, params.verifier_index.domain_size}, + row) + .output; + row += index_terms_scalars_component::rows_amount; + + std::pair alpha_idxs = + index_terms_list::alpha_map(argument_type::Permutation); + var perm_scalar = + perm_scalars_component::generate_assignments( + assignment, + {params.combined_evals, alpha_powers, alpha_idxs.first, + params.fq_output.beta, params.fq_output.gamma, zkp}, + row) + .output; + row += perm_scalars_component::rows_amount; + + alpha_idxs = index_terms_list::alpha_map(argument_type::Generic); + std::array generic_scalars = + generic_scalars_component::generate_assignments( + assignment, + {params.combined_evals, oracles_output.alpha_powers, alpha_idxs.first}, row) + .output; + row += generic_scalars_component::rows_amount; + + var output = plonk_map_fields_component::generate_assignments( + assignment, + {params.alpha, params.beta, params.gamma, params.joint_combiner, + index_scalars, perm_scalar, generic_scalars}, + row).output; + row += plonk_map_fields_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + + static void generate_assignments_constant( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 1; + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/evals_of_split_evals.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/evals_of_split_evals.hpp new file mode 100644 index 000000000..920150566 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/evals_of_split_evals.hpp @@ -0,0 +1,297 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // https://github.com/MinaProtocol/mina/blob/a76a550bc2724f53be8ebaf681c3b35686a7f080/src/lib/pickles/plonk_checks/plonk_checks.ml#L83 + template + class evals_of_split_evals; + + template + class evals_of_split_evals, + KimchiParamsType, SplitSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using add_component = zk::components::addition; + using exponentiation_component = + zk::components::exponentiation; + using combined_proof_evals_component = + zk::components::combine_proof_evals; + + constexpr static const std::size_t lookup_rows() { + std::size_t rows = 0; + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + + if (KimchiParamsType::circuit_params::lookup_runtime) { + } + } + + return rows; + } + + constexpr static const std::size_t rows() { + std::size_t row = 0; + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array, SplitSize> split_evals; + std::array points; + }; + + struct result_type { + kimchi_proof_evaluations output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + + + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(assignment, params, start_row_index); + + std::size_t row = start_row_index; + + var exponent(0, start_row_index, false, var::column_type::public_input); + + for (std::size_t i = 0; i < KimchiParamsType::eval_points_amount; i++) { + var point_exp = + exponentiation_component::generate_circuit(bp, assignment, {params.points[i], exponent}, row).output; + row += exponentiation_component::rows_amount; + + kimchi_proof_evaluations evals_acc; + + // init + for (std::size_t j = 0; j < evals_acc.w.size(); j++) { + evals_acc.w[j] = params.split_evals[SplitSize - 1].w[j]; + } + evals_acc.z = params.split_evals[SplitSize - 1].z; + for (std::size_t j = 0; j < evals_acc.s.size(); j++) { + evals_acc.s[j] = params.split_evals[SplitSize - 1].s[j]; + } + for (std::size_t j = 0; j < evals_acc.lookup.sorted.size(); j++) { + evals_acc.lookup.sorted[j] = params.split_evals[SplitSize - 1].lookup.sorted[j]; + } + evals_acc.lookup.aggreg = params.split_evals[SplitSize - 1].lookup.aggreg; + evals_acc.lookup.table = params.split_evals[SplitSize - 1].lookup.table; + evals_acc.lookup.runtime = params.split_evals[SplitSize - 1].lookup.runtime; + evals_acc.generic_selector = params.split_evals[SplitSize - 1].generic_selector; + evals_acc.poseidon_selector = params.split_evals[SplitSize - 1].poseidon_selector; + + // accumulation + for (std::size_t j = SplitSize - 2; j >= 0; j--) { + evals_acc = + combined_proof_evals_component::generate_circuit(bp, + assignment, {evals_acc, point_exp}, + row) + .output; + row += combined_proof_evals_component::rows_amount; + + for (std::size_t k = 0; k < evals_acc.w.size(); k++) { + evals_acc.w[k] = zk::components::generate_circuit(bp, assignment, {evals_acc.w[k], params.split_evals[j].w[k]}, row).output; + row += add_component::rows_amount; + } + + evals_acc.z = zk::components::generate_circuit(bp, assignment, {evals_acc.z, params.split_evals[j].z}, row).output; + row += add_component::rows_amount; + + for (std::size_t k = 0; k < evals_acc.s.size(); k++) { + evals_acc.s[k] = zk::components::generate_circuit(bp, assignment, {evals_acc.s[k], params.split_evals[j].s[k]}, row).output; + row += add_component::rows_amount; + } + + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + for (std::size_t k = 0; k < evals_acc.lookup.sorted.size(); k++) { + evals_acc.lookup.sorted[k] = zk::components::generate_circuit(bp, assignment, {evals_acc.lookup.sorted[k], params.split_evals[j].lookup.sorted[k]}, row).output; + row += add_component::rows_amount; + } + + evals_acc.lookup.aggreg = zk::components::generate_circuit(bp, assignment, {evals_acc.lookup.aggreg, params.split_evals[j].lookup.aggreg}, row).output; + row += add_component::rows_amount; + + evals_acc.lookup.table = zk::components::generate_circuit(bp, assignment, {evals_acc.lookup.table, params.split_evals[j].lookup.table}, row).output; + row += add_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + evals_acc.lookup.runtime = zk::components::generate_circuit(bp, assignment, {evals_acc.lookup.runtime, params.split_evals[j].lookup.runtime}, row).output; + row += add_component::rows_amount; + } + } + + evals_acc.generic_selector = zk::components::generate_circuit(bp, assignment, {evals_acc.generic_selector, params.split_evals[j].generic_selector}, row).output; + row += add_component::rows_amount; + + evals_acc.poseidon_selector = zk::components::generate_circuit(bp, assignment, {evals_acc.poseidon_selector, params.split_evals[j].poseidon_selector}, row).output; + row += add_component::rows_amount; + } + } + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + var exponent(0, start_row_index, false, var::column_type::public_input); + + for (std::size_t i = 0; i < KimchiParamsType::eval_points_amount; i++) { + var point_exp = + exponentiation_component::generate_assignments(assignment, {params.points[i], exponent}, row).output; + row += exponentiation_component::rows_amount; + + kimchi_proof_evaluations evals_acc; + + // init + for (std::size_t j = 0; j < evals_acc.w.size(); j++) { + evals_acc.w[j] = params.split_evals[SplitSize - 1].w[j]; + } + evals_acc.z = params.split_evals[SplitSize - 1].z; + for (std::size_t j = 0; j < evals_acc.s.size(); j++) { + evals_acc.s[j] = params.split_evals[SplitSize - 1].s[j]; + } + for (std::size_t j = 0; j < evals_acc.lookup.sorted.size(); j++) { + evals_acc.lookup.sorted[j] = params.split_evals[SplitSize - 1].lookup.sorted[j]; + } + evals_acc.lookup.aggreg = params.split_evals[SplitSize - 1].lookup.aggreg; + evals_acc.lookup.table = params.split_evals[SplitSize - 1].lookup.table; + evals_acc.lookup.runtime = params.split_evals[SplitSize - 1].lookup.runtime; + evals_acc.generic_selector = params.split_evals[SplitSize - 1].generic_selector; + evals_acc.poseidon_selector = params.split_evals[SplitSize - 1].poseidon_selector; + + // accumulation + for (std::size_t j = SplitSize - 2; j >= 0; j--) { + evals_acc = + combined_proof_evals_component::generate_assignments( + assignment, {evals_acc, point_exp}, + row) + .output; + row += combined_proof_evals_component::rows_amount; + + for (std::size_t k = 0; k < evals_acc.w.size(); k++) { + evals_acc.w[k] = add_component::generate_assignments(assignment, {evals_acc.w[k], params.split_evals[j].w[k]}, row).output; + row += add_component::rows_amount; + } + + evals_acc.z = add_component::generate_assignments(assignment, {evals_acc.z, params.split_evals[j].z}, row).output; + row += add_component::rows_amount; + + for (std::size_t k = 0; k < evals_acc.s.size(); k++) { + evals_acc.s[k] = add_component::generate_assignments(assignment, {evals_acc.s[k], params.split_evals[j].s[k]}, row).output; + row += add_component::rows_amount; + } + + if (KimchiParamsType::circuit_params::lookup_columns > 0) { + for (std::size_t k = 0; k < evals_acc.lookup.sorted.size(); k++) { + evals_acc.lookup.sorted[k] = add_component::generate_assignments(assignment, {evals_acc.lookup.sorted[k], params.split_evals[j].lookup.sorted[k]}, row).output; + row += add_component::rows_amount; + } + + evals_acc.lookup.aggreg = add_component::generate_assignments(assignment, {evals_acc.lookup.aggreg, params.split_evals[j].lookup.aggreg}, row).output; + row += add_component::rows_amount; + + evals_acc.lookup.table = add_component::generate_assignments(assignment, {evals_acc.lookup.table, params.split_evals[j].lookup.table}, row).output; + row += add_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + evals_acc.lookup.runtime = add_component::generate_assignments(assignment, {evals_acc.lookup.runtime, params.split_evals[j].lookup.runtime}, row).output; + row += add_component::rows_amount; + } + } + + evals_acc.generic_selector = add_component::generate_assignments(assignment, {evals_acc.generic_selector, params.split_evals[j].generic_selector}, row).output; + row += add_component::rows_amount; + + evals_acc.poseidon_selector = add_component::generate_assignments(assignment, {evals_acc.poseidon_selector, params.split_evals[j].poseidon_selector}, row).output; + row += add_component::rows_amount; + } + } + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + + static void generate_assignments_constant( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 1 << KimchiParamsType::commitment_params_type::eval_rounds; + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/plonk_map_fields.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/plonk_map_fields.hpp new file mode 100644 index 000000000..320d1d18e --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/plonk_map_fields.hpp @@ -0,0 +1,122 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_PLONK_MAP_FIELDS_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_PLONK_MAP_FIELDS_HPP + +#include + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // https://github.com/MinaProtocol/mina/blob/a76a550bc2724f53be8ebaf681c3b35686a7f080/src/lib/pickles/plonk_checks/plonk_checks.ml#L409 + template + class plobk_map_fields; + + template + class plobk_map_fields, + KimchiParamsType, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, + W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + constexpr static const std::size_t rows() { + std::size_t row = 0; + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + var zeta; + var alpha; + var beta; + var gamma; + var joint_combiner; + std::array index_terms; + var perm; + std::array generic; + }; + + struct result_type { + var output; + + result_type(std::size_t component_start_row) { + std::size_t row = component_start_row; + + + } + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(assignment, params, start_row_index); + + std::size_t row = start_row_index; + + + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_SCALAR_DETAILS_EVALS_OF_SPLIT_EVALS_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/prepare_scalars_inversion.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/prepare_scalars_inversion.hpp new file mode 100644 index 000000000..b0c90b593 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/scalar_details/prepare_scalars_inversion.hpp @@ -0,0 +1,180 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_INVERSION_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_INVERSION_HPP + +#include + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // inverse function for prepare scalars + // https://github.com/MinaProtocol/mina/blob/f01d3925a273ded939a80e1de9afcd9f913a7c17/src/lib/pickles_types/shifted_value.ml#L129 + template + class prepare_scalars_inversion; + + template + class prepare_scalars_inversion, + CurveType, InputSize, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using mul_component = zk::components::multiplication; + using add_component = zk::components::addition; + + constexpr static const std::size_t selector_seed = 0x0f2C; + + constexpr static bool scalar_larger() { + using ScalarField = typename CurveType::scalar_field_type; + using BaseField = typename CurveType::base_field_type; + + auto n1 = ScalarField::modulus; + auto n2 = BaseField::modulus; + + return n1 > n2; + } + + public: + constexpr static const std::size_t rows_amount = + InputSize * (add_component::rows_amount + mul_component::rows_amount); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array scalars; + }; + + struct result_type { + std::array output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constants(bp, assignment, params, start_row_index); + + var shift = var(0, start_row_index, false, var::column_type::constant); + var coef = var(0, start_row_index + 1, false, var::column_type::constant); + + std::size_t row = start_row_index; + + std::array shifted; + result_type result; + + for (std::size_t i = 0; i < InputSize; ++i) { + shifted[i] = zk::components::generate_circuit( + bp, assignment, {params.scalars[i], shift}, row) + .output; + row += add_component::rows_amount; + result.output[i] = + zk::components::generate_circuit(bp, assignment, {shifted[i], coef}, row) + .output; + row += mul_component::rows_amount; + } + + generate_copy_constraints(bp, assignment, params, start_row_index); + + return result; + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + var shift = var(0, start_row_index, false, var::column_type::constant); + var coef = var(0, start_row_index + 1, false, var::column_type::constant); + + std::size_t row = start_row_index; + + std::array shifted; + result_type result; + + for (std::size_t i = 0; i < InputSize; ++i) { + shifted[i] = + add_component::generate_assignments(assignment, {params.scalars[i], shift}, row).output; + row += add_component::rows_amount; + result.output[i] = + mul_component::generate_assignments(assignment, {shifted[i], coef}, row).output; + row += mul_component::rows_amount; + } + + return result; + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + } + + static void generate_assignments_constants( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + typename BlueprintFieldType::value_type base = 2; + if (scalar_larger()) { + assignment.constant(0)[row] = -base.pow(255); + row++; + assignment.constant(0)[row] = 1; + } else { + assignment.constant(0)[row] = -base.pow(255) - 1; + row++; + assignment.constant(0)[row] = 1 / base; + } + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_BATCH_SCALAR_PREPARE_SCALARS_INVERSION_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/alpha_argument_type.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/alpha_argument_type.hpp new file mode 100644 index 000000000..2932a5863 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/alpha_argument_type.hpp @@ -0,0 +1,46 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_ALPHA_ARGUMENT_TYPE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_ALPHA_ARGUMENT_TYPE_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + enum argument_type { + Permutation, + Generic, + Zero, + Lookup, + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_ALPHA_ARGUMENT_TYPE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/binding.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/binding.hpp new file mode 100644 index 000000000..0a714b099 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/binding.hpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP + +#include + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct binding { + using var = snark::plonk_variable; + using commitment_parms_type = typename KimchiParamsType::commitment_params_type; + using kimchi_constants = zk::components::kimchi_inner_constants; + + template + struct fr_data { + private: + constexpr static const std::size_t f_comm_msm_size = kimchi_constants::f_comm_msm_size; + constexpr static const std::size_t lookup_columns = KimchiParamsType::circuit_params::lookup_columns; + + public: + std::array scalars; + std::array, BatchSize> f_comm_scalars; + std::array cip_shifted; + + std::array neg_pub; + std::array zeta_to_srs_len; + var zeta_to_domain_size_minus_1; + + std::array joint_combiner_powers_prepared; + }; + + template + struct fq_data { }; + + struct fq_sponge_output { + var joint_combiner; + var beta; // beta and gamma can be combined from limbs in the base circuit + var gamma; + var alpha; + var zeta; + var fq_digest; // TODO overflow check + std::array challenges; + var c; + }; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_BINDING_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/column_type.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/column_type.hpp new file mode 100644 index 000000000..96e32364d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/column_type.hpp @@ -0,0 +1,62 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_COLUMN_TYPE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_COLUMN_TYPE_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + enum column_type { + Witness, + Coefficient, + Z, + LookupSorted, + LookupAggreg, + LookupKindIndex, // ChaCha = 0, ChaChaFinal = 1, LookupGate = 2, RangeCheckGate = 3 + LookupTable, + LookupRuntimeSelector, + LookupRuntimeTable, + CompleteAdd, + VarBaseMul, + EndoMul, + EndoMulScalar, + Poseidon, + ChaCha0, + ChaCha1, + ChaCha2, + ChaChaFinal, + RangeCheck0, + RangeCheck1 + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_COLUMN_TYPE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/commitment.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/commitment.hpp new file mode 100644 index 000000000..7aa9ab249 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/commitment.hpp @@ -0,0 +1,53 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMMITMENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMMITMENT_HPP + +#include + +#include + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct kimchi_commitment_type { + using var_ec_point = typename zk::components::var_ec_point; + std::array parts; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_COMMITMENT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/evaluation_proof.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/evaluation_proof.hpp new file mode 100644 index 000000000..2c8e7c76a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/evaluation_proof.hpp @@ -0,0 +1,80 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_EVALUATION_PROOF_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_EVALUATION_PROOF_HPP + +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct kimchi_lookup_evaluations { + using var = snark::plonk_variable; + + std::array sorted; + + var aggreg; + var table; + + var runtime; + + kimchi_lookup_evaluations() { + } + }; + + template + struct kimchi_proof_evaluations { + using var = snark::plonk_variable; + // witness polynomials + std::array w; + // permutation polynomial + var z; + // permutation polynomials + // (PERMUTS-1 evaluations because the last permutation is only used in commitment form) + std::array s; + // /// lookup-related evaluations + kimchi_lookup_evaluations lookup; + // /// evaluation of the generic selector polynomial + var generic_selector; + // /// evaluation of the poseidon selector polynomial + var poseidon_selector; + + kimchi_proof_evaluations() { + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_TYPES_EVALUATION_PROOF_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/index_term_type.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/index_term_type.hpp new file mode 100644 index 000000000..6749b8530 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/index_term_type.hpp @@ -0,0 +1,48 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERM_TYPE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERM_TYPE_HPP + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + struct index_term_type { + const column_type type; + const std::size_t index; + const char *str_repr; + const std::size_t rows_amount; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERM_TYPE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/proof.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/proof.hpp new file mode 100644 index 000000000..1f7c7c184 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/proof.hpp @@ -0,0 +1,169 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_HPP + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + struct kimchi_opening_proof_scalar { + using var = snark::plonk_variable; + + var z1; + var z2; + }; + + template + struct kimchi_proof_scalar { + using var = snark::plonk_variable; + + std::array, 2> proof_evals; + var ft_eval; + std::array public_input; + std::array, KimchiParamsType::prev_challenges_size> prev_challenges; + + kimchi_opening_proof_scalar opening; + }; + + template + struct batch_evaluation_proof_scalar { + using proof_binding = + typename zk::components::binding; + using var = snark::plonk_variable; + + var cip; + typename proof_binding::fq_sponge_output fq_output; + std::array eval_points; + // scaling factor for polynomials + var r; + // scaling factor for evaluation point powers + var xi; + + kimchi_opening_proof_scalar opening; + + using transcript_type = + kimchi_transcript_fr; + transcript_type transcript; + }; + + template + struct kimchi_opening_proof_base { + using var = snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + + std::array L; + std::array R; + var_ec_point delta; + var_ec_point G; + }; + + template + struct kimchi_proof_base { + using var = snark::plonk_variable; + using commitment_params_type = typename KimchiParamsType::commitment_params_type; + + using commitment_type = typename zk::components::kimchi_commitment_type< + BlueprintFieldType, commitment_params_type::shifted_commitment_split>; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + + using kimchi_constants = zk::components::kimchi_inner_constants; + + struct commitments_type { + std::array witness; + commitment_type lookup_runtime; + commitment_type table; + std::vector lookup_sorted; + commitment_type lookup_agg; + commitment_type z; + commitment_type t; + std::array + prev_challenges; // to-do: get in the component from oracles + }; + + commitments_type comm; + opening_proof_type o; + std::array scalars; + }; + + template + struct batch_evaluation_proof_base { + using proof_binding = + typename zk::components::binding; + using var = snark::plonk_variable; + + using commitment_type = typename zk::components::kimchi_commitment_type< + BlueprintFieldType, KimchiCommitmentParamsType::shifted_commitment_split>; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + + using kimchi_constants = zk::components::kimchi_inner_constants; + + using transcript_type = + typename zk::components::kimchi_transcript_fq; + + // typename proof_binding::fq_sponge_output fq_output; + std::array comm; + opening_proof_type opening_proof; + + transcript_type transcript; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_PROOF_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/verifier_index.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/verifier_index.hpp new file mode 100644 index 000000000..04801d632 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/types/verifier_index.hpp @@ -0,0 +1,100 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFIER_INDEX_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFIER_INDEX_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + typedef std::array kimchi_scalar_limbs; + + template + struct kimchi_verifier_index_scalar { + using var = snark::plonk_variable; + + // nil::crypto3::math::evaluation_domain domain; + std::size_t max_quot_size; + std::size_t domain_size; + std::array shift; + + var omega; + }; + + template + struct kimchi_verifier_index_base { + using FieldType = typename CurveType::base_field_type; + using commitment_params_type = typename KimchiParamsType::commitment_params_type; + + using commitment_type = typename zk::components::kimchi_commitment_type< + FieldType, commitment_params_type::shifted_commitment_split>; + + using var = snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + + static constexpr const std::size_t chacha_size = 4; + static constexpr const std::size_t range_check_size = 2; + + struct commitments_type { + std::array sigma; + std::array coefficient; + commitment_type generic; + commitment_type psm; + std::vector selectors; + std::vector lookup_selectors; + commitment_type runtime_tables_selector; + std::vector lookup_table; + commitment_type complete_add; + commitment_type var_base_mul; + commitment_type endo_mul; + commitment_type endo_mul_scalar; + std::array chacha; + std::array range_check; + }; + + var_ec_point H; + std::array G; + std::array lagrange_bases; + commitments_type comm; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFIER_INDEX_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verifier_base_field.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verifier_base_field.hpp new file mode 100644 index 000000000..bb3120ef8 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verifier_base_field.hpp @@ -0,0 +1,871 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the BASE_FIELD component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_BASE_FIELD_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_BASE_FIELD_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // base field part of batch_verify + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L911 + // Input: list of mina-proofs (base field part), + // precalculated fq_data and fr_data (the data that used both by scalar and base verifiers) + // verifier index (public data) + // Output: - + template + class base_field; + + template + class base_field, CurveType, + KimchiParamsType, KimchiCommitmentParamsType, BatchSize, W0, W1, W2, W3, W4, W5, W6, + W7, W8, W9, W10, W11, W12, W13, W14> { + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using var = crypto3::zk::snark::plonk_variable; + using var_ec_point = typename zk::components::var_ec_point; + using sub_component = zk::components::subtraction; + using mul_component = zk::components::multiplication; + using const_mul_component = zk::components::mul_by_constant; + using table_comm_component = + zk::components::table_commitment; + + using proof_type = kimchi_proof_base; + using kimchi_constants = zk::components::kimchi_inner_constants; + + constexpr static const std::size_t f_comm_base_size = kimchi_constants::f_comm_msm_size; + + using msm_component = + zk::components::element_g1_multi_scalar_mul; + using lagrange_msm_component = + zk::components::element_g1_multi_scalar_mul; + + using scalar_mul_component = + zk::components::curve_element_variable_base_scalar_mul; + using add_component = + zk::components::curve_element_unified_addition; + + using proof_binding = + typename zk::components::binding; + + using map_fq_component = zk::components::map_fq; + + using batch_proof_type = typename zk::components::batch_evaluation_proof_base< + BlueprintFieldType, ArithmetizationType, KimchiParamsType, KimchiCommitmentParamsType>; + + using verifier_index_type = kimchi_verifier_index_base; + + using index_terms_list = typename KimchiParamsType::circuit_params::index_terms_list; + + using commitment_type = typename zk::components::kimchi_commitment_type< + BlueprintFieldType, KimchiCommitmentParamsType::shifted_commitment_split>; + + using batch_verify_component = + zk::components::batch_verify_base_field; + + using transcript_type = kimchi_transcript_fq; + + constexpr static const std::size_t selector_seed = 0xff91; + + constexpr static const std::size_t rows() { + std::size_t row = 0; + + row++; + + for (std::size_t i = 0; i < BatchSize; i++) { + row = row + lagrange_msm_component::rows_amount; + + // Oracles + row += transcript_type::init_rows; + + row += transcript_type::absorb_group_rows; + + row += KimchiParamsType::circuit_params::witness_columns * + KimchiParamsType::witness_commitment_size * transcript_type::absorb_group_rows; + + if (KimchiParamsType::circuit_params::use_lookup) { + if (KimchiParamsType::circuit_params::lookup_runtime) { + row += KimchiParamsType::lookup_runtime_commitment_size * + transcript_type::absorb_group_rows; + } + + if (KimchiParamsType::circuit_params::joint_lookup) { + row += transcript_type::challenge_rows; + } + + row += KimchiParamsType::circuit_params::lookup_columns * + KimchiParamsType::lookup_sorted_commitment_size * + transcript_type::absorb_group_rows; + } + + row += transcript_type::challenge_rows; + row += transcript_type::challenge_rows; + + if (KimchiParamsType::circuit_params::use_lookup) { + row += KimchiParamsType::lookup_aggregated_commitment_size * + transcript_type::absorb_group_rows; + } + + row += KimchiParamsType::z_commitment_size * transcript_type::absorb_group_rows; + + row += transcript_type::challenge_rows; + + row += KimchiParamsType::t_commitment_size * transcript_type::absorb_group_rows; + + row += transcript_type::challenge_rows; + + row += transcript_type::digest_rows; + + // Oracles end + + for (std::size_t j = 0; j < KimchiCommitmentParamsType::max_comm_size; j++) { + row += msm_component::rows_amount; + } + + for (std::size_t j = 0; j < KimchiCommitmentParamsType::max_comm_size; j++) { + row += scalar_mul_component::rows_amount; + row += add_component::rows_amount; + } + + for (std::size_t j = 0; j < KimchiParamsType::t_commitment_size; j++) { + row += scalar_mul_component::rows_amount; + row += add_component::rows_amount; + } + row += scalar_mul_component::rows_amount; + row += const_mul_component::rows_amount; + + row += add_component::rows_amount; + + if (KimchiParamsType::circuit_params::use_lookup) { + row += table_comm_component::rows_amount; + } + } + + row += batch_verify_component::rows_amount; + + row += map_fq_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array proofs; + verifier_index_type verifier_index; + + typename proof_binding::template fr_data fr_data; + typename proof_binding::template fq_data fq_data; + }; + + struct result_type { + + result_type(std::size_t start_row_index) { + } + }; + + private: + template + static void parse_commitments( + std::array, f_comm_base_size> &unshifted_commitments, + CommitmentType comm, + std::size_t &comm_idx) { + + for (std::size_t k = 0; k < comm.parts.size(); k++) { + unshifted_commitments[comm_idx].push_back(comm.parts[k]); + } + comm_idx++; + } + + static std::array, f_comm_base_size> + prepare_f_comm(const params_type ¶ms, std::size_t batch_idx) { + + std::array, f_comm_base_size> unshifted_commitments; + std::size_t comm_idx = 0; + + typename proof_type::commitments_type comm = params.proofs[batch_idx].comm; + typename verifier_index_type::commitments_type index_comm = params.verifier_index.comm; + + parse_commitments(unshifted_commitments, + params.verifier_index.comm.sigma[KimchiParamsType::permut_size - 1], + comm_idx); + + // take generic_size coeff_comm + std::array generic_comm; + for (std::size_t i = 0; i < generic_comm.size(); i++) { + generic_comm[i] = params.verifier_index.comm.coefficient[i]; + } + + for (std::size_t i = 0; i < kimchi_constants::ft_generic_size; i++) { + parse_commitments(unshifted_commitments, generic_comm[i], comm_idx); + } + + for (std::size_t i = 0; i < index_terms_list::size; i++) { + index_term_type term = index_terms_list::terms[i]; + switch (term.type) { + case column_type::Witness: + parse_commitments(unshifted_commitments, comm.witness[term.index], comm_idx); + break; + case column_type::Coefficient: + parse_commitments(unshifted_commitments, index_comm.coefficient[term.index], + comm_idx); + break; + case column_type::Z: + parse_commitments(unshifted_commitments, comm.z, comm_idx); + break; + case column_type::LookupSorted: + parse_commitments(unshifted_commitments, comm.lookup_sorted[term.index], comm_idx); + break; + case column_type::LookupAggreg: { + parse_commitments(unshifted_commitments, comm.lookup_agg, comm_idx); + break; + } + case column_type::LookupKindIndex: { + parse_commitments(unshifted_commitments, index_comm.lookup_selectors[term.index], + comm_idx); + break; + } + case column_type::LookupRuntimeSelector: { + parse_commitments(unshifted_commitments, index_comm.runtime_tables_selector, + comm_idx); + break; + } + case column_type::CompleteAdd: { + parse_commitments(unshifted_commitments, index_comm.complete_add, comm_idx); + break; + } + case column_type::VarBaseMul: { + parse_commitments(unshifted_commitments, index_comm.var_base_mul, comm_idx); + break; + } + case column_type::EndoMul: { + parse_commitments(unshifted_commitments, index_comm.endo_mul, comm_idx); + break; + } + case column_type::EndoMulScalar: { + parse_commitments(unshifted_commitments, index_comm.endo_mul_scalar, comm_idx); + break; + } + case column_type::Poseidon: { + parse_commitments(unshifted_commitments, index_comm.psm, comm_idx); + break; + } + case column_type::ChaCha0: { + parse_commitments(unshifted_commitments, index_comm.chacha[0], comm_idx); + break; + } + case column_type::ChaCha1: { + parse_commitments(unshifted_commitments, index_comm.chacha[1], comm_idx); + break; + } + case column_type::ChaCha2: { + parse_commitments(unshifted_commitments, index_comm.chacha[2], comm_idx); + break; + } + case column_type::ChaChaFinal: { + parse_commitments(unshifted_commitments, index_comm.chacha[3], comm_idx); + break; + } + case column_type::RangeCheck0: { + parse_commitments(unshifted_commitments, index_comm.range_check[0], comm_idx); + break; + } + case column_type::RangeCheck1: { + parse_commitments(unshifted_commitments, index_comm.range_check[1], comm_idx); + break; + } + case column_type::LookupTable: + break; + case column_type::LookupRuntimeTable: + break; + } + } + + assert(comm_idx == f_comm_base_size); + + return unshifted_commitments; + } + + public: + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + std::array batch_proofs; + var zero(0, row, false, var::column_type::constant); + row++; + + for (std::size_t i = 0; i < BatchSize; i++) { + + // p_comm is always the commitment of size 1 + auto p_comm_unshifted = + lagrange_msm_component::generate_assignments( + assignment, {params.fr_data.neg_pub, params.verifier_index.lagrange_bases}, row) + .output; + row = row + lagrange_msm_component::rows_amount; + + // Oracles + transcript_type transcript; + transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + + transcript.absorb_g_assignment(assignment, p_comm_unshifted, row); + row += transcript_type::absorb_group_rows; + + for (std::size_t j = 0; j < params.proofs[i].comm.witness.size(); j++) { + for (std::size_t k = 0; k < params.proofs[i].comm.witness[j].parts.size(); k++) { + transcript.absorb_g_assignment(assignment, + params.proofs[i].comm.witness[j].parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + var joint_combiner; + + if (KimchiParamsType::circuit_params::use_lookup) { + if (KimchiParamsType::circuit_params::lookup_runtime) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_runtime.parts.size(); + k++) { + transcript.absorb_g_assignment( + assignment, params.proofs[i].comm.lookup_runtime.parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + if (KimchiParamsType::circuit_params::joint_lookup) { + joint_combiner = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + } else { + joint_combiner = zero; + } + + for (std::size_t j = 0; j < params.proofs[i].comm.lookup_sorted.size(); j++) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_sorted[j].parts.size(); + k++) { + transcript.absorb_g_assignment( + assignment, params.proofs[i].comm.lookup_sorted[j].parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + } + + var beta = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + var gamma = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + if (KimchiParamsType::circuit_params::use_lookup) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_agg.parts.size(); k++) { + transcript.absorb_g_assignment(assignment, + params.proofs[i].comm.lookup_agg.parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + for (std::size_t k = 0; k < params.proofs[i].comm.z.parts.size(); k++) { + transcript.absorb_g_assignment(assignment, params.proofs[i].comm.z.parts[k], row); + row += transcript_type::absorb_group_rows; + } + + var alpha = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + for (std::size_t k = 0; k < params.proofs[i].comm.t.parts.size(); k++) { + transcript.absorb_g_assignment(assignment, params.proofs[i].comm.t.parts[k], row); + row += transcript_type::absorb_group_rows; + } + + var zeta = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + var digest = transcript.digest_assignment(assignment, row); + row += transcript_type::digest_rows; + + // Oracles end + + // f_comm + std::array, f_comm_base_size> f_comm_bases = + prepare_f_comm(params, i); + + std::array f_comm; + for (std::size_t j = 0; j < KimchiCommitmentParamsType::max_comm_size; j++) { + std::array bases; + std::array scalars; + for (std::size_t k = 0; k < f_comm_base_size; k++) { + if (j < f_comm_bases[k].size()) { + bases[k] = f_comm_bases[k][j]; + scalars[k] = params.proofs[i].scalars[k]; + } else { + bases[k] = {zero, zero}; + scalars[k] = zero; + } + } + auto res = msm_component::generate_assignments(assignment, {scalars, bases}, row); + f_comm[j] = {res.output.X, res.output.Y}; + row += msm_component::rows_amount; + } + + // chuncked_f_comm + var_ec_point chuncked_f_comm = {zero, zero}; + + for (std::size_t j = 0; j < f_comm.size(); j++) { + auto res0 = scalar_mul_component::generate_assignments( + assignment, + {{chuncked_f_comm.X, chuncked_f_comm.Y}, params.fr_data.zeta_to_srs_len[i]}, row); + row += scalar_mul_component::rows_amount; + auto res1 = add_component::generate_assignments( + assignment, {{res0.X, res0.Y}, {f_comm[j].X, f_comm[j].Y}}, row); + row += add_component::rows_amount; + chuncked_f_comm = {res1.X, res1.Y}; + } + + // chunked_t_comm + var_ec_point chunked_t_comm = {zero, zero}; + ; + for (std::size_t j = 0; j < params.proofs[i].comm.t.parts.size(); j++) { + auto res0 = scalar_mul_component::generate_assignments( + assignment, + {{chunked_t_comm.X, chunked_t_comm.Y}, params.fr_data.zeta_to_srs_len[i]}, row); + row += scalar_mul_component::rows_amount; + + auto res1 = add_component::generate_assignments( + assignment, + {{res0.X, res0.Y}, + {params.proofs[i].comm.t.parts[j].X, params.proofs[i].comm.t.parts[j].Y}}, + row); + row += add_component::rows_amount; + chunked_t_comm = {res1.X, res1.Y}; + } + + // ft_comm + + auto scaled_t_comm = scalar_mul_component::generate_assignments( + assignment, + {{chunked_t_comm.X, chunked_t_comm.Y}, params.fr_data.zeta_to_domain_size_minus_1}, + row); + row += scalar_mul_component::rows_amount; + + typename BlueprintFieldType::value_type minus_1 = -1; + var const_res_unshifted = + const_mul_component::generate_assignments(assignment, {scaled_t_comm.Y, minus_1}, row) + .output; + row += const_mul_component::rows_amount; + + var_ec_point neg_scaled_t_comm = {scaled_t_comm.X, const_res_unshifted}; + + auto ft_comm_part = add_component::generate_assignments( + assignment, + {{neg_scaled_t_comm.X, neg_scaled_t_comm.Y}, {chuncked_f_comm.X, chuncked_f_comm.Y}}, + row); + row += add_component::rows_amount; + commitment_type ft_comm = {{{ft_comm_part.X, ft_comm_part.Y}}}; + for (std::size_t j = 1; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split; + j++) { + ft_comm.parts[j] = {zero, zero}; + } + + // evaluations + + std::array evaluations; + std::size_t eval_idx = 0; + + for (auto chal : params.proofs[i].comm.prev_challenges) { + evaluations[eval_idx++] = chal; + } + + commitment_type p_comm = {{{p_comm_unshifted.X, p_comm_unshifted.Y}}}; + for (std::size_t j = 1; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split; + j++) { + ft_comm.parts[j] = {zero, zero}; + } + evaluations[eval_idx++] = p_comm; + evaluations[eval_idx++] = ft_comm; + evaluations[eval_idx++] = params.proofs[i].comm.z; + evaluations[eval_idx++] = params.verifier_index.comm.generic; + evaluations[eval_idx++] = params.verifier_index.comm.psm; + + for (std::size_t j = 0; j < params.proofs[i].comm.witness.size(); j++) { + evaluations[eval_idx++] = params.proofs[i].comm.witness[j]; + } + for (std::size_t j = 0; j < params.verifier_index.comm.sigma.size() - 1; j++) { + evaluations[eval_idx++] = params.verifier_index.comm.sigma[j]; + } + + if (KimchiParamsType::circuit_params::use_lookup) { + for (std::size_t j = 0; j < params.proofs[i].comm.lookup_sorted.size(); j++) { + evaluations[eval_idx++] = params.proofs[i].comm.lookup_sorted[j]; + } + + evaluations[eval_idx++] = params.proofs[i].comm.lookup_agg; + + evaluations[eval_idx++] = table_comm_component::generate_assignments( + assignment, + {params.verifier_index.comm.lookup_table, params.fr_data.joint_combiner_powers_prepared, + params.proofs[i].comm.lookup_runtime}, + row) + .output; + row += table_comm_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + evaluations[eval_idx++] = params.proofs[i].comm.lookup_runtime; + } + } + + assert(eval_idx == kimchi_constants::evaluations_in_batch_size); + + batch_proof_type p = {{evaluations}, params.proofs[i].o, transcript}; + + batch_proofs[i] = p; + } + typename batch_verify_component::params_type batch_params = { + batch_proofs, params.verifier_index, params.fr_data}; + batch_verify_component::generate_assignments(assignment, batch_params, row); + row += batch_verify_component::rows_amount; + + typename proof_binding::template fq_data fq_data_recalculated; + map_fq_component::generate_assignments(assignment, {params.fq_data, fq_data_recalculated}, row); + row += map_fq_component::rows_amount; + + assert(row == start_row_index + rows_amount); + return result_type(start_row_index); + } + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + generate_assignments_constant(assignment, params, start_row_index); + + std::size_t row = start_row_index; + var zero(0, row, false, var::column_type::constant); + row++; + + std::array batch_proofs; + for (std::size_t i = 0; i < BatchSize; i++) { + auto p_comm_unshifted = + lagrange_msm_component::generate_circuit( + bp, assignment, {params.fr_data.neg_pub, params.verifier_index.lagrange_bases}, row) + .output; + row = row + lagrange_msm_component::rows_amount; + + std::size_t row_tmp = row; + + // Oracles + transcript_type transcript; + transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + + transcript.absorb_g_circuit(bp, assignment, p_comm_unshifted, row); + row += transcript_type::absorb_group_rows; + + for (std::size_t j = 0; j < params.proofs[i].comm.witness.size(); j++) { + for (std::size_t k = 0; k < params.proofs[i].comm.witness[j].parts.size(); k++) { + transcript.absorb_g_circuit(bp, assignment, + params.proofs[i].comm.witness[j].parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + var joint_combiner; + + if (KimchiParamsType::circuit_params::use_lookup) { + if (KimchiParamsType::circuit_params::lookup_runtime) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_runtime.parts.size(); + k++) { + transcript.absorb_g_circuit(bp, assignment, + params.proofs[i].comm.lookup_runtime.parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + if (KimchiParamsType::circuit_params::joint_lookup) { + joint_combiner = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + } else { + joint_combiner = zero; + } + + for (std::size_t j = 0; j < params.proofs[i].comm.lookup_sorted.size(); j++) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_sorted[j].parts.size(); + k++) { + transcript.absorb_g_circuit( + bp, assignment, params.proofs[i].comm.lookup_sorted[j].parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + } + + var beta = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + var gamma = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + if (KimchiParamsType::circuit_params::use_lookup) { + for (std::size_t k = 0; k < params.proofs[i].comm.lookup_agg.parts.size(); k++) { + transcript.absorb_g_circuit(bp, assignment, + params.proofs[i].comm.lookup_agg.parts[k], row); + row += transcript_type::absorb_group_rows; + } + } + + for (std::size_t k = 0; k < params.proofs[i].comm.z.parts.size(); k++) { + transcript.absorb_g_circuit(bp, assignment, params.proofs[i].comm.z.parts[k], row); + row += transcript_type::absorb_group_rows; + } + + var alpha = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + for (std::size_t k = 0; k < params.proofs[i].comm.t.parts.size(); k++) { + transcript.absorb_g_circuit(bp, assignment, params.proofs[i].comm.t.parts[k], row); + row += transcript_type::absorb_group_rows; + } + + var zeta = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + var digest = transcript.digest_circuit(bp, assignment, row); + row += transcript_type::digest_rows; + + // Oracles end + + std::array, f_comm_base_size> f_comm_bases = + prepare_f_comm(params, i); + + std::array f_comm; + for (std::size_t j = 0; j < KimchiCommitmentParamsType::max_comm_size; j++) { + std::array bases; + std::array scalars; + for (std::size_t k = 0; k < f_comm_base_size; k++) { + if (j < f_comm_bases[k].size()) { + bases[k] = f_comm_bases[k][j]; + scalars[k] = params.proofs[i].scalars[k]; + } else { + bases[k] = {zero, zero}; + scalars[k] = zero; + } + } + auto res = msm_component::generate_circuit(bp, assignment, {scalars, bases}, row); + f_comm[j] = {res.output.X, res.output.Y}; + row += msm_component::rows_amount; + } + + // chuncked_f_comm + var_ec_point chuncked_f_comm = {zero, zero}; + + for (std::size_t j = 0; j < f_comm.size(); j++) { + auto res0 = scalar_mul_component::generate_circuit( + bp, assignment, + {{chuncked_f_comm.X, chuncked_f_comm.Y}, params.fr_data.zeta_to_srs_len[i]}, row); + row += scalar_mul_component::rows_amount; + auto res1 = zk::components::generate_circuit( + bp, assignment, {{res0.X, res0.Y}, {f_comm[j].X, f_comm[j].Y}}, row); + row += add_component::rows_amount; + chuncked_f_comm = {res1.X, res1.Y}; + } + + // chunked_t_comm + var_ec_point chunked_t_comm = {zero, zero}; + ; + for (std::size_t j = 0; j < params.proofs[i].comm.t.parts.size(); j++) { + auto res0 = scalar_mul_component::generate_circuit( + bp, assignment, + {{chunked_t_comm.X, chunked_t_comm.Y}, params.fr_data.zeta_to_srs_len[i]}, row); + row += scalar_mul_component::rows_amount; + + auto res1 = zk::components::generate_circuit( + bp, assignment, + {{res0.X, res0.Y}, + {params.proofs[i].comm.t.parts[j].X, params.proofs[i].comm.t.parts[j].Y}}, + row); + row += add_component::rows_amount; + chunked_t_comm = {res1.X, res1.Y}; + } + + // ft_comm + + auto scaled_t_comm = scalar_mul_component::generate_circuit( + bp, assignment, + {{chunked_t_comm.X, chunked_t_comm.Y}, params.fr_data.zeta_to_domain_size_minus_1}, + row); + row += scalar_mul_component::rows_amount; + + typename BlueprintFieldType::value_type minus_1 = -1; + var const_res_unshifted = zk::components::generate_circuit( + bp, assignment, {scaled_t_comm.Y, minus_1}, row) + .output; + row += const_mul_component::rows_amount; + + var_ec_point neg_scaled_t_comm = {scaled_t_comm.X, const_res_unshifted}; + + auto ft_comm_part = zk::components::generate_circuit( + bp, assignment, + {{neg_scaled_t_comm.X, neg_scaled_t_comm.Y}, {chuncked_f_comm.X, chuncked_f_comm.Y}}, + row); + row += add_component::rows_amount; + commitment_type ft_comm = {{{ft_comm_part.X, ft_comm_part.Y}}}; + for (std::size_t j = 1; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split; + j++) { + ft_comm.parts[j] = {zero, zero}; + } + + // evaluations + std::array evaluations; + std::size_t eval_idx = 0; + for (auto chal : params.proofs[i].comm.prev_challenges) { + evaluations[eval_idx++] = chal; + } + + commitment_type p_comm = {{{p_comm_unshifted.X, p_comm_unshifted.Y}}}; + for (std::size_t j = 1; + j < KimchiParamsType::commitment_params_type::shifted_commitment_split; + j++) { + ft_comm.parts[j] = {zero, zero}; + } + evaluations[eval_idx++] = p_comm; + evaluations[eval_idx++] = ft_comm; + evaluations[eval_idx++] = params.proofs[i].comm.z; + evaluations[eval_idx++] = params.verifier_index.comm.generic; + evaluations[eval_idx++] = params.verifier_index.comm.psm; + + for (std::size_t j = 0; j < params.proofs[i].comm.witness.size(); j++) { + evaluations[eval_idx++] = params.proofs[i].comm.witness[j]; + } + for (std::size_t j = 0; j < params.verifier_index.comm.sigma.size() - 1; j++) { + evaluations[eval_idx++] = params.verifier_index.comm.sigma[j]; + } + + if (KimchiParamsType::circuit_params::use_lookup) { + for (std::size_t j = 0; j < params.proofs[i].comm.lookup_sorted.size(); j++) { + evaluations[eval_idx++] = params.proofs[i].comm.lookup_sorted[j]; + } + + evaluations[eval_idx++] = params.proofs[i].comm.lookup_agg; + + evaluations[eval_idx++] = table_comm_component::generate_circuit( + bp, assignment, + {params.verifier_index.comm.lookup_table, params.fr_data.joint_combiner_powers_prepared, + params.proofs[i].comm.lookup_runtime}, + row) + .output; + row += table_comm_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_runtime) { + evaluations[eval_idx++] = params.proofs[i].comm.lookup_runtime; + } + } + + assert(eval_idx == kimchi_constants::evaluations_in_batch_size); + + batch_proof_type p = {{evaluations}, params.proofs[i].o, transcript}; + + batch_proofs[i] = p; + } + typename batch_verify_component::params_type batch_params = { + batch_proofs, params.verifier_index, params.fr_data}; + batch_verify_component::generate_circuit(bp, assignment, batch_params, row); + row += batch_verify_component::rows_amount; + + typename proof_binding::template fq_data fq_data_recalculated; + map_fq_component::generate_circuit(bp, assignment, {params.fq_data, fq_data_recalculated}, row); + row += map_fq_component::rows_amount; + + assert(row == start_row_index + rows_amount); + + return result_type(start_row_index); + } + + private: + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + } + + static void generate_assignments_constant( + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + std::size_t row = start_row_index; + assignment.constant(0)[row] = 0; + } + }; + + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_BASE_FIELD_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_base.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_base.hpp new file mode 100644 index 000000000..d8e7b699c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_base.hpp @@ -0,0 +1,155 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_BASE_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_BASE_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // base field part of verify_generogenous + // https://github.com/MinaProtocol/mina/blob/09348bccf281d54e6fa9dd2d8bbd42e3965e1ff5/src/lib/pickles/verify.ml#L30 + template + class verify_generogenous_base; + + template + class verify_generogenous_base< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, BatchSize, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::base_field_type; + + using ArithmetizationType = snark::plonk_constraint_system; + + using KimchiCommitmentParamsType = typename KimchiParamsType::commitment_params_type; + + using var = snark::plonk_variable; + + using var_ec_point = typename zk::components::var_ec_point; + + using batch_verify_component = + zk::components::batch_dlog_accumulator_check_base; + + using kimchi_verify_component = zk::components::base_field; + + using verifier_index_type = kimchi_verifier_index_base; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + row += batch_verify_component::rows_amount; + + row += kimchi_verify_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector comms; + + std::array proofs; + verifier_index_type verifier_index; + + typename proof_binding::template fr_data fr_data; + typename proof_binding::template fq_data fq_data; + }; + + struct result_type { + var output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + batch_verify_component::generate_circuit(bp, assignmet, + {params.comms, params.verifier_index, params.fr_output}, row); + row += batch_verify_component::rows_amount; + + kimchi_verify_component::generate_circuit(bp, assignmet, + {params.proofs, params.verifier_index, params.ft_data, params.fq_data}, row); + row += kimchi_verify_component::rows_amount; + + return result_type(); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + batch_verify_component::generate_assignments(assignmet, + {params.comms, params.verifier_index, params.fr_output}, row); + row += batch_verify_component::rows_amount; + + kimchi_verify_component::generate_assignments(assignmet, + {params.proofs, params.verifier_index, params.ft_data, params.fq_data}, row); + row += kimchi_verify_component::rows_amount; + + return result_type(); + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_BASE_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_scalar.hpp new file mode 100644 index 000000000..7c7018ece --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_heterogenous_scalar.hpp @@ -0,0 +1,476 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_SCALAR_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_SCALAR_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // scalar field part of verify_generogenous + // https://github.com/MinaProtocol/mina/blob/09348bccf281d54e6fa9dd2d8bbd42e3965e1ff5/src/lib/pickles/verify.ml#L30 + template + class verify_generogenous_scalar; + + template + class verify_generogenous_scalar< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, BatchSize, list_size, evals_size, + W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + constexpr static const std::size_t ScalarSize = 255; + + using ArithmetizationType = snark::plonk_constraint_system; + + using var = snark::plonk_variable; + + using endo_scalar_component = zk::components::endo_scalar; + using mul_component = zk::components::multiplication; + + using add_component = zk::components::multiplication; + + using b_poly_component = + zk::components::b_poly; + + using transcript_type = + kimchi_transcript_fr; + + + using combined_evals_component = zk::components::combined_evals; + + using derive_plonk_component = zk::components::derive_plonk; + + using prepare_scalars_inversion_component = zk::components::prepare_scalars_inversion; + + using cip_component = + zk::components::oracles_cip; + + using batch_verify_component = + zk::components::batch_dlog_accumulator_check_scalar; + + using kimchi_verify_component = + zk::components::verify_scalar; + + using proof_binding = + typename zk::components::binding; + + constexpr static const std::size_t poly_size = 4 + (KimchiParamsType::circuit_params::used_lookup ? 1 : 0); + + constexpr static std::size_t rows() { + std::size_t row = 0; + + for(std::size_t i = 0; i < list_size; i++) { + row += endo_scalar_component::rows_amount; + row += endo_scalar_component::rows_amount; + row += endo_scalar_component::rows_amount; + row += mul_component::rows_amount; + + if (KimchiParamsType::circuit_params::lookup_used) { + row += endo_scalar_component::rows_amount; + } + + row += combined_evals_component::rows_amount; + row += derive_plonk_component::rows_amount; + + for(std::size_t j = 0; j < bulletproofs_size; j++) { + row += endo_scalar_component::rows_amount; + } + + row += transcript_type::init_rows; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + row += transcript_type::absorb_rows; + } + row += transcript_type::challenge_rows; + + row += transcript_type::init_rows; + row += transcript_type::absorb_rows; + row += transcript_type::absorb_rows; + + row += transcript_type::absorb_evaluations_rows; + row += transcript_type::absorb_evaluations_rows; + + row += transcript_type::challenge_rows; + + row += transcript_type::challenge_rows; + + row += cip_component::rows_amount; + + for(std::size_t j = 0; j < bulletproofs_size; j++) { + row += endo_scalar_component::rows_amount; + } + + row += b_poly_component::rows_amount; + + row += b_poly_component::rows_amount; + + row += mul_component::rows_amount; + + row += add_component::rows_amount; + + row += prepare_scalars_inversion_component::rows_amount; + + row += prepare_scalars_inversion_component::rows_amount; + } + + row += batch_dlog_accumulator_check_scalar::rows_amount; + + row += kimchi_verify_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::array def_values; + std::array evals; + std::array messages_for_next_step_proof; + var domain_generator; + kimchi_verifier_index_scalar &verifier_index; + std::array, + BatchSize> &proof; + + typename proof_binding::template fr_data fr_data; + typename proof_binding::template fq_data fq_data; + std::array &fq_output; + }; + + struct result_type { + var output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + for(std::size_t i = 0; i < list_size; i++) { + auto def_values_xi = endo_scalar_component::generate_circuit(bp, assignment, {params.def_values[i].xi}, row).output; + row += endo_scalar_component::rows_amount; + auto zeta = endo_scalar_component::generate_circuit(bp, assignment, {params.def_values[i].plonk.zeta}, row).output; + row += endo_scalar_component::rows_amount; + auto alpha = endo_scalar_component::generate_circuit(bp, assignment, {params.def_values[i].plonk.alpha}, row).output; + row += endo_scalar_component::rows_amount; + auto zetaw = zk::components::generate_circuit(bp, assignment, {zets, params.domain_generator}, row).output; + row += mul_component::rows_amount; + var min_poly_joint_combiner; + if (KimchiParamsType::circuit_params::lookup_used) { + min_poly_joint_combiner = endo_scalar_component::generate_circuit(bp, assignment, {params.def_values[i].plonk.joint_combiner}, row).output; + row += endo_scalar_component::rows_amount; + } + std::array min_poly = {alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, zeta, min_poly_joint_combiner}; + std::array plonk0_poly= {params.def_values[i].plonk.alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, params.def_values[i].plonk.zeta, + params.def_values[i].plonk.joint_combiner}; + auto tick_combined_evals = combined_evals_component::generate_circuit(bp, assignment, {params.evals[i], {zeta, zetaw}}, row).output; + row += combined_evals_component::rows_amount; + auto plonk = derive_plonk_component::generate_circuit(bp, assignment, {kimchi_verifier_index_scalar, + params.def_values[i].plonk.alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, params.def_values[i].plonk.zeta, + params.def_values[i].plonk.joint_combiner, tick_combined_evals}, row).output; + row += derive_plonk_component::rows_amount; + std::size_t bulletproofs_size = params.messages_for_next_step_proof[i].old_bulletproof_challenges.size(); + std::array old_bulletproof_challenges; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + old_bulletproof_challenges[j] = endo_scalar_component::generate_circuit(bp, assignment, + {params.messages_for_next_step_proof[i].old_bulletproof_challenges[j]}, row).output; + row += endo_scalar_component::rows_amount; + } + transcript_type bulletproofs_transcript; + bulletproofs_transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + bulletproofs_transcript.absorb_circuit(bp, assignment, old_bulletproof_challenges[j], row); + row += transcript_type::absorb_rows; + } + var challenges_digest = bulletproofs_transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + transcript_type transcript; + transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + transcript.absorb_circuit(bp, assignment, challenges_digest, row); + row += transcript_type::absorb_rows; + transcript.absorb_circuit(bp, assignment, params.evals.ft_eval1, row); + row += transcript_type::absorb_rows; + + transcript.absorb_evaluations_circuit(bp, assignment, params.evals[i].evals.public_input[0], + evals[i].evals.evals[0], row); + row += transcript_type::absorb_evaluations_rows; + transcript.absorb_evaluations_ciruit(bp, assignment, params.evals[i].evals.public_input[1], + evals[i].evals.evals[1], row); + row += transcript_type::absorb_evaluations_rows; + + var xi_actual_challenge = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + bp.add_copy_constraint({xi_actual_challenge, params.def_values[i].xi}); +s + var r_actual_challenge = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + + var combined_inner_product_actual = cip_component::generate_circuit(bp, assignment, + {r_actual_challenge, min_poly, params.evals[i].ft_eval1, + evals[i].evals}, + row) + .output; + row += cip_component::rows_amount; + + std::array bulletproof_challenges; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + bulletproof_challenges[j] = endo_scalar_component::generate_circuit(bp, assignment, + {params.def_values[i].bulletproof_challenges[j]}, row).output; + row += endo_scalar_component::rows_amount; + } + + auto chal_zeta = b_poly_component::generate_circuit( + bp, assignment, {bulletproof_challenges, zeta, one}, row) + .output; + row += b_poly_component::rows_amount; + + auto chal_zetaw = b_poly_component::generate_circuit( + assignment, {bp, bulletproof_challenges, zetaw, one}, row) + .output; + row += b_poly_component::rows_amount; + + auto t = zk::components::generate_circuit(bp, assignment, {chal_zetaw, r_actual}, row).output; + row += mul_component::rows_amount; + + auto b_actual = zk::components::generate_circuit(bp, assignment, {chal_zeta, t}, row).output; + row += add_component::rows_amount; + + shifted_combined_inner_product = prepare_scalars_inversion_component::generate_circuit(bp, assignment, { + params.def_values[i].combined_inner_product}, row).output; + row += prepare_scalars_inversion_component::rows_amount; + bp.add_copy_constraint({shifted_combined_inner_product, combined_inner_product_actual}); + + shifted_b = prepare_scalars_inversion_component::generate_circuit(bp, assignment, { + params.def_values[i].b}, row).output; + row += prepare_scalars_inversion_component::rows_amount; + bp.add_copy_constraint({shifted_b, b_actual}); + } + + batch_dlog_accumulator_check_scalar::generate_circuit(bp, assignment, + {deferred_values.bulletproof_challenges}, row); + row += batch_dlog_accumulator_check_scalar::rows_amount; + + kimchi_verify_component::generate_circuit(bp, assignment, + {params.fr_data, params.fq_data, verifier_index, params.proof, params.fq_output}, + row); + row += kimchi_verify_component::rows_amount; + + generate_assignments_constant(bp, assignment, params, start_row_index); + + return result_type(); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + var zero = var(0, start_row_index, false, var::column_type::constant); + var one = var(0, start_row_index + 1, false, var::column_type::constant); + for(std::size_t i = 0; i < list_size; i++) { + auto def_values_xi = endo_scalar_component::generate_assignments(assignment, {params.def_values[i].xi}, row).output; + row += endo_scalar_component::rows_amount; + auto zeta = endo_scalar_component::generate_assignments(assignment, {params.def_values[i].plonk.zeta}, row).output; + row += endo_scalar_component::rows_amount; + auto alpha = endo_scalar_component::generate_assignments(assignment, {params.def_values[i].plonk.alpha}, row).output; + row += endo_scalar_component::rows_amount; + auto zetaw = mul_component::generate_assignments(assignment, {zets, params.domain_generator}, row).output; + row += mul_component::rows_amount; + var min_poly_joint_combiner; + if (KimchiParamsType::circuit_params::lookup_used) { + min_poly_joint_combiner = endo_scalar_component::generate_assignments(assignment, {params.def_values[i].plonk.joint_combiner}, row).output; + row += endo_scalar_component::rows_amount; + } + std::array min_poly = {alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, zeta, min_poly_joint_combiner}; + std::array plonk0_poly= {params.def_values[i].plonk.alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, params.def_values[i].plonk.zeta, + params.def_values[i].plonk.joint_combiner}; + auto tick_combined_evals = combined_evals_component::generate_assignments(assignment, {params.evals[i], {zeta, zetaw}}, row).output; + row += combined_evals_component::rows_amount; + auto plonk = derive_plonk_component::generate_assignments(assignment, {kimchi_verifier_index_scalar, + params.def_values[i].plonk.alpha, params.def_values[i].plonk.beta, params.def_values[i].plonk.gamma, params.def_values[i].plonk.zeta, + params.def_values[i].plonk.joint_combiner, tick_combined_evals}, row).output; + row += derive_plonk_component::rows_amount; + std::size_t bulletproofs_size = params.messages_for_next_step_proof[i].old_bulletproof_challenges.size(); + std::array old_bulletproof_challenges; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + old_bulletproof_challenges[j] = endo_scalar_component::generate_assignments(assignment, + {params.messages_for_next_step_proof[i].old_bulletproof_challenges[j]}, row).output; + row += endo_scalar_component::rows_amount; + } + transcript_type bulletproofs_transcript; + bulletproofs_transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + bulletproofs_transcript.absorb_assignment(assignment, old_bulletproof_challenges[j], row); + row += transcript_type::absorb_rows; + } + var challenges_digest = bulletproofs_transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + transcript_type transcript; + transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + transcript.absorb_assignment(assignment, challenges_digest, row); + row += transcript_type::absorb_rows; + transcript.absorb_assignment(assignment, params.evals.ft_eval1, row); + row += transcript_type::absorb_rows; + + transcript.absorb_evaluations_assignment(assignment, params.evals[i].evals.public_input[0], + evals[i].evals.evals[0], row); + row += transcript_type::absorb_evaluations_rows; + transcript.absorb_evaluations_assignment(assignment, params.evals[i].evals.public_input[1], + evals[i].evals.evals[1], row); + row += transcript_type::absorb_evaluations_rows; + + var xi_actual_challenge = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; +s + var r_actual_challenge = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + + var combined_inner_product_actual = cip_component::generate_assignments(assignment, + {r_actual_challenge, min_poly, params.evals[i].ft_eval1, + evals[i].evals}, + row) + .output; + row += cip_component::rows_amount; + + std::array bulletproof_challenges; + for(std::size_t j = 0; j < bulletproofs_size; j++) { + bulletproof_challenges[j] = endo_scalar_component::generate_assignments(assignment, + {params.def_values[i].bulletproof_challenges[j]}, row).output; + row += endo_scalar_component::rows_amount; + } + + auto chal_zeta = b_poly_component::generate_assignments( + assignment, {bulletproof_challenges, zeta, one}, row) + .output; + row += b_poly_component::rows_amount; + + auto chal_zetaw = b_poly_component::generate_assignments( + assignment, {bulletproof_challenges, zetaw, one}, row) + .output; + row += b_poly_component::rows_amount; + + auto t = mul_component::generate_assignments(assignment, {chal_zetaw, r_actual}, row).output; + row += mul_component::rows_amount; + + auto b_actual = add_component::generate_assignments(assignment, {chal_zeta, t}, row).output; + row += add_component::rows_amount; + + shifted_combined_inner_product = prepare_scalars_inversion_component::generate_assignments(assignment, { + params.def_values[i].combined_inner_product}, row).output; + row += prepare_scalars_inversion_component::rows_amount; + + shifted_b = prepare_scalars_inversion_component::generate_assignments(assignment, { + params.def_values[i].b}, row).output; + row += prepare_scalars_inversion_component::rows_amount; + } + + batch_dlog_accumulator_check_scalar::generate_assignments(assignment, + {deferred_values.bulletproof_challenges}, row); + row += batch_dlog_accumulator_check_scalar::rows_amount; + + kimchi_verify_component::generate_assignments(assignment, + {params.fr_data, params.fq_data, verifier_index, params.proof, params.fq_output}, + row); + row += kimchi_verify_component::rows_amount; + return result_type(); + } + + private: + + static void + generate_assignments_constant(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 0; + row++; + assignment.constant(0)[row] = 1; + } + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFY_HETEROGENOUS_SCALAR_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_scalar.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_scalar.hpp new file mode 100644 index 000000000..860e64a95 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/kimchi/verify_scalar.hpp @@ -0,0 +1,240 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFY_SCALAR_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFY_SCALAR_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // scalar field part of batch_verify + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/verifier.rs#L911 + // Input: list of mina-proofs (scalar field part), + // precalculated fq_data and fr_data (the data that used both by scalar and base verifiers) + // verifier index (public data) + // Output: - + template + class verify_scalar; + + template + class verify_scalar< + snark::plonk_constraint_system, + CurveType, KimchiParamsType, KimchiCommitmentParamsType, BatchSize, W0, W1, W2, W3, W4, W5, W6, W7, + W8, W9, W10, W11, W12, W13, W14> { + + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typedef snark::plonk_constraint_system + ArithmetizationType; + + using var = snark::plonk_variable; + + using batch_verify_component = + zk::components::batch_verify_scalar_field; + using prepare_batch_component = + zk::components::prepare_batch_scalar; + using map_fr_component = + zk::components::map_fr; + + using proof_binding = + typename zk::components::binding; + + using batch_proof = batch_evaluation_proof_scalar; + + using prepare_scalars_component = + zk::components::prepare_scalars; + + using verifier_index_type = kimchi_verifier_index_scalar; + + constexpr static const std::size_t selector_seed = 0x0f2A; + + constexpr static std::size_t rows() { + std::size_t row = 0; + + for (std::size_t i = 0; i < BatchSize; i++) { + row += prepare_batch_component::rows_amount; + + row += prepare_scalars_component::rows_amount; + } + + row += batch_verify_component::rows_amount; + + row += map_fr_component::rows_amount; + + return row; + } + + public: + constexpr static const std::size_t rows_amount = rows(); + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + typename proof_binding::template fr_data fr_data; + typename proof_binding::template fq_data fq_data; + + verifier_index_type &verifier_index; + std::array, + BatchSize> &proof; + std::array &fq_output; + }; + + struct result_type { + var output; + }; + + static result_type + generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + std::size_t row = start_row_index; + + generate_assignments_constant(bp, assignment, params, start_row_index); + + typename proof_binding::template fr_data fr_data_recalculated; + + std::array batches; + for (std::size_t i = 0; i < BatchSize; i++) { + auto prepare_output = prepare_batch_component::generate_circuit( + bp, assignment, {params.verifier_index, params.proof[i], params.fq_output[i]}, row); + batches[i] = prepare_output.prepared_proof; + fr_data_recalculated.f_comm_scalars[i] = prepare_output.f_comm_scalars; + fr_data_recalculated.zeta_to_srs_len[i] = prepare_output.zeta_to_srs_len; + row += prepare_batch_component::rows_amount; + + var cip_shifted = prepare_scalars_component::generate_circuit( + bp, assignment, {{prepare_output.prepared_proof.cip}}, row) + .output[0]; + fr_data_recalculated.cip_shifted[i] = cip_shifted; + row += prepare_scalars_component::rows_amount; + } + + auto res = batch_verify_component::generate_circuit(bp, assignment, {batches}, row); + row += batch_verify_component::rows_amount; + + map_fr_component::generate_circuit(bp, assignment, {params.fr_data, fr_data_recalculated}, row); + row += map_fr_component::rows_amount; + + return result_type(); + } + + static result_type generate_assignments(blueprint_assignment_table &assignment, + const params_type ¶ms, + std::size_t start_row_index) { + + std::size_t row = start_row_index; + + typename proof_binding::template fr_data fr_data_recalculated; + + std::array batches; + for (std::size_t i = 0; i < BatchSize; i++) { + auto prepare_output = prepare_batch_component::generate_assignments( + assignment, {params.verifier_index, params.proof[i], params.fq_output[i]}, row); + batches[i] = prepare_output.prepared_proof; + fr_data_recalculated.f_comm_scalars[i] = prepare_output.f_comm_scalars; + fr_data_recalculated.zeta_to_srs_len[i] = prepare_output.zeta_to_srs_len; + row += prepare_batch_component::rows_amount; + + var cip_shifted = prepare_scalars_component::generate_assignments( + assignment, {{prepare_output.prepared_proof.cip}}, row) + .output[0]; + fr_data_recalculated.cip_shifted[i] = cip_shifted; + row += prepare_scalars_component::rows_amount; + } + + auto res = batch_verify_component::generate_assignments(assignment, {batches}, row); + row += batch_verify_component::rows_amount; + + map_fr_component::generate_assignments(assignment, {params.fr_data, fr_data_recalculated}, row); + row += map_fr_component::rows_amount; + + return result_type(); + } + + private: + static void generate_gates(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void + generate_copy_constraints(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row = 0) { + } + + static void generate_assignments_constant( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + std::size_t component_start_row) { + std::size_t row = component_start_row; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_VERIFY_SCALAR_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/expression_evaluation_component.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/expression_evaluation_component.hpp new file mode 100644 index 000000000..4a401df2c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/expression_evaluation_component.hpp @@ -0,0 +1,292 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include "nil/crypto3/zk/snark/arithmetization/plonk/assignment.hpp" +#include "nil/crypto3/zk/snark/arithmetization/plonk/constraint.hpp" +#include "nil/crypto3/zk/snark/arithmetization/plonk/constraint_system.hpp" +#include "nil/crypto3/zk/snark/arithmetization/plonk/variable.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class expression_to_execution_simple : + public boost::static_visitor> { + public: + using value_type = typename BlueprintFieldType::value_type; + using var = nil::crypto3::zk::snark::plonk_variable; + using assignment_type = assignment; + using multiplication_component_type = nil::blueprint::components::multiplication< + ArithmetizationType, BlueprintFieldType, + nil::blueprint::basic_non_native_policy>; + using addition_component_type = nil::blueprint::components::addition< + ArithmetizationType, BlueprintFieldType, + nil::blueprint::basic_non_native_policy>; + using subtraction_component_type = nil::blueprint::components::subtraction< + ArithmetizationType, BlueprintFieldType, + nil::blueprint::basic_non_native_policy>; + + expression_to_execution_simple(assignment_type &_assignment, + const std::unordered_map &_variable_map, + bool _generate_assignment_call) + : assignment_table(_assignment), variable_map(_variable_map), + generate_assignment_call(_generate_assignment_call) + {} + + var visit(const nil::crypto3::math::expression &expr) { + return boost::apply_visitor(*this, expr.get_expr()); + } + + var operator()(const nil::crypto3::math::term& term) { + var result; + const std::size_t term_size = term.get_vars().size(); + if (term_size == 0) { + return assignment_table.add_batch_constant_variable(term.get_coeff()); + } + std::size_t curr_term = 0; + if (term.get_coeff() != value_type::one()) { + auto coeff_var = assignment_table.add_batch_constant_variable(term.get_coeff()); + result = assignment_table.template add_input_to_batch( + {coeff_var, variable_map.at(term.get_vars()[curr_term])}, + generate_assignment_call).output; + } else { + result = variable_map.at(term.get_vars()[curr_term]); + } + curr_term++; + for (; curr_term < term_size; curr_term++) { + result = assignment_table.template add_input_to_batch( + {result, variable_map.at(term.get_vars()[curr_term])}, + generate_assignment_call).output; + } + return result; + } + + var operator()(const nil::crypto3::math::pow_operation& pow) { + int power = pow.get_power(); + BOOST_ASSERT(power > 0); + var expr_res = boost::apply_visitor(*this, pow.get_expr().get_expr()); + if (power == 1) { + return expr_res; + } + var result = assignment_table.add_batch_constant_variable(value_type::one()); + while (power > 1) { + if (power % 2 == 0) { + expr_res = assignment_table.template add_input_to_batch( + {expr_res, expr_res}, + generate_assignment_call).output; + power /= 2; + } else { + result = assignment_table.template add_input_to_batch( + {result, expr_res}, + generate_assignment_call).output; + power -= 1; + } + } + return assignment_table.template add_input_to_batch( + {result, expr_res}, + generate_assignment_call).output; + } + + var operator()(const nil::crypto3::math::binary_arithmetic_operation& op) { + auto res1 = boost::apply_visitor(*this, op.get_expr_left().get_expr()); + auto res2 = boost::apply_visitor(*this, op.get_expr_right().get_expr()); + switch (op.get_op()) { + case crypto3::math::ArithmeticOperator::ADD: + return assignment_table.template add_input_to_batch( + {res1, res2}, + generate_assignment_call).output; + case crypto3::math::ArithmeticOperator::SUB: + return assignment_table.template add_input_to_batch( + {res1, res2}, + generate_assignment_call).output; + case crypto3::math::ArithmeticOperator::MULT: + return assignment_table.template add_input_to_batch( + {res1, res2}, + generate_assignment_call).output; + default: + throw std::runtime_error("Unsupported operation"); + } + } + private: + assignment_type &assignment_table; + const std::unordered_map &variable_map; + bool generate_assignment_call; + }; + + template + class expression_evaluation_component; + + // Brute-force expression evaluation + // Should be relatively easy to repurpose for more opitmised versions + template + class expression_evaluation_component< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using manifest_type = nil::blueprint::plonk_component_manifest; + using expression_evaluator_type = expression_to_execution_simple< + BlueprintFieldType, crypto3::zk::snark::plonk_constraint_system>; + + constraint_type constraint; + + // What do we even do with this if we are batching? + static const std::size_t rows_amount = 0; + static const std::size_t gates_amount = 0; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return expression_evaluation_component::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + constraint_type &constraint) { + static gate_manifest manifest = gate_manifest_type(); + // TODO: should we intersect with batched gates? + return manifest; + } + + static manifest_type get_manifest(constraint_type &constraint) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_single_value_param(3)), true); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + constraint_type &constraint) { + return expression_evaluation_component::rows_amount; + } + + struct input_type { + std::unordered_map variable_mapping; + + std::vector> all_vars() { + std::vector> result; + for (auto &pair : variable_mapping) { + result.push_back(pair.second); + } + return result; + } + }; + + struct result_type { + var output; + + result_type(var output_, std::size_t start_row_index) : output(output_) {} + result_type(var output_) : output(output_) {} + + std::vector> all_vars() { + return {output}; + } + }; + + template + expression_evaluation_component(ContainerType witness, constraint_type constraint_) : + component_type(witness, {}, {}, get_manifest(constraint_)), constraint(constraint_) + {}; + + template + expression_evaluation_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, constraint_type constraint_) : + component_type(witness, constant, public_input, get_manifest(constraint_)), constraint(constraint_) + {}; + + expression_evaluation_component( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + constraint_type constraint_) : + component_type(witnesses, constants, public_inputs, get_manifest(constraint_)), constraint(constraint_) + {}; + }; + } // namespace detail + + template + using plonk_expression_evaluation_component = detail::expression_evaluation_component< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_expression_evaluation_component::result_type generate_assignments( + const plonk_expression_evaluation_component + &component, + assignment> + &assignment_table, + const typename plonk_expression_evaluation_component::input_type + instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_expression_evaluation_component; + using expression_evaluator_type = typename component_type::expression_evaluator_type; + + expression_evaluator_type evaluator(assignment_table, instance_input.variable_mapping, true); + return typename component_type::result_type(evaluator.visit(component.constraint), start_row_index); + } + + template + typename plonk_expression_evaluation_component::result_type generate_circuit( + const plonk_expression_evaluation_component + &component, + circuit> + &bp, + assignment> + &assignment_table, + const typename plonk_expression_evaluation_component::input_type + instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_expression_evaluation_component; + using expression_evaluator_type = typename component_type::expression_evaluator_type; + + expression_evaluator_type evaluator(assignment_table, instance_input.variable_mapping, false); + return typename component_type::result_type(evaluator.visit(component.constraint), start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f1_loop.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f1_loop.hpp new file mode 100644 index 000000000..3690a0c6f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f1_loop.hpp @@ -0,0 +1,606 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the LOOKUP_ARGUMENT_VERIFIER component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F1_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F1_LOOP_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class f1_loop; + + template + class f1_loop> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 0; + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t n) { + + std::size_t r = std::ceil(3.0 * n / (witness_amount - 1)); + if (r < 2) + return 2; + return r; + } + + constexpr static std::size_t gates_amount_internal(std::size_t witness_amount, std::size_t n) { + if (witness_amount % 3 != 1) { + return witness_amount + (witness_amount - 1) / 3; + } + + return witness_amount; + } + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t m) { + return rows_amount_internal(witness_amount, m); + } + + const std::size_t m; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), m); + const std::size_t gates_amount = gates_amount_internal(this->witness_amount(), m); + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + std::size_t degree; + + gate_manifest_type(std::size_t witness_amount_, std::size_t degree_) : + witness_amount(witness_amount_), degree(degree_) { + } + + std::uint32_t gates_amount() const override { + return f1_loop::gates_amount_internal(witness_amount, degree); + } + + bool operator<(const component_gate_manifest *other) const override { + return this->witness_amount < + dynamic_cast(other)->witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t degree) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, degree)); + return manifest; + } + + static manifest_type get_manifest(std::size_t m) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_range_param(4, 15)), false); + return manifest; + } + + struct input_type { + var beta; + var gamma; + std::vector s; + std::vector t; + + std::vector> all_vars() { + std::vector> vars; + vars.push_back(beta); + vars.push_back(gamma); + vars.insert(vars.end(), s.begin(), s.end()); + vars.insert(vars.end(), t.begin(), t.end()); + return vars; + } + }; + + struct result_type { + var output; + + result_type(const f1_loop &component, std::uint32_t start_row_index) { + std::size_t WitnessesAmount = component.witness_amount(); + std::size_t j = (3 * component.m) % (WitnessesAmount - 1); + if (j == 0) { + j = WitnessesAmount - 1; + } + if (3 * component.m + 1 <= WitnessesAmount) { + output = var(component.W(j), start_row_index, false); + } else { + output = var(component.W(j), start_row_index + component.rows_amount - 1, false); + } + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + f1_loop(ContainerType witness, std::size_t m_) : + component_type(witness, {}, {}, get_manifest(m_)), m(m_) {}; + + template + f1_loop(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t m_) : + component_type(witness, constant, public_input, get_manifest(m_)), + m(m_) {}; + + f1_loop(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t m_) : + component_type(witnesses, constants, public_inputs, get_manifest(m_)), + m(m_) {}; + }; + } // namespace detail + + template + using plonk_f1_loop = + detail::f1_loop>; + + template + typename plonk_f1_loop::result_type generate_assignments( + const plonk_f1_loop &component, + assignment> + &assignment, + const typename plonk_f1_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + assert(instance_input.s.size() == instance_input.t.size()); + assert(instance_input.s.size() == component.m); + + typename BlueprintFieldType::value_type beta = var_value(assignment, instance_input.beta); + typename BlueprintFieldType::value_type gamma = var_value(assignment, instance_input.gamma); + typename BlueprintFieldType::value_type one = BlueprintFieldType::value_type::one(); + typename BlueprintFieldType::value_type delta = (one + beta) * gamma; + + typename BlueprintFieldType::value_type h = BlueprintFieldType::value_type::one(); + std::vector assignments; + for (std::size_t i = 0; i < component.m; i++) { + typename BlueprintFieldType::value_type s_i = var_value(assignment, instance_input.s[i]); + typename BlueprintFieldType::value_type t_i = var_value(assignment, instance_input.t[i]); + h = h * (delta + s_i + beta * t_i); + assignments.push_back(s_i); + assignments.push_back(t_i); + assignments.push_back(h); + } + + std::size_t r = 0, j = 0, i = 0; + for (i = 0; i < assignments.size(); i++) { + r = i / (witness_amount - 1); + j = i % (witness_amount - 1) + 1; + assignment.witness(component.W(j), row + r) = assignments[i]; + } + row += r; + if (row - start_row_index < 1) { + row++; + } + for (r = start_row_index; r <= row; r++) { + if (witness_amount % 3 == 1) { + if ((r - start_row_index) % 2 == 0) { + assignment.witness(component.W(0), r) = beta; + } else { + assignment.witness(component.W(0), r) = gamma; + } + } else { + if ((r - start_row_index) % 3 != 1) { + assignment.witness(component.W(0), r) = beta; + } else { + assignment.witness(component.W(0), r) = gamma; + } + } + } + + return typename plonk_f1_loop::result_type(component, + start_row_index); + } + + template + std::vector generate_gates( + const plonk_f1_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f1_loop::input_type instance_input) { + + using var = typename plonk_f1_loop::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + std::vector even_row_constraints; + std::vector odd_row_constraints; + + std::vector selectors; + std::size_t witness_amount = component.witness_amount(); + + auto constraint_1 = + var(component.W(3), 0) - + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(1), 0) + + var(component.W(0), 0) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_1})); + + if (witness_amount % 3 == 1) { + + auto constraint_2 = + var(component.W(3), 0) - + var(component.W(witness_amount - 1), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(1), 0) + + var(component.W(0), 0) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_2})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * var(component.W(j - 1), 0)); + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_3 = + var(component.W(3), 0) - + var(component.W(witness_amount - 1), -1) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + var(component.W(1), 0) + + var(component.W(0), -1) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_3})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + var(component.W(j - 2), 0) + + var(component.W(0), -1) * var(component.W(j - 1), 0)); + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_4 = + var(component.W(3), 0) - + var(component.W(witness_amount - 1), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(1), 0) + + var(component.W(0), 0) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_4})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * var(component.W(j - 1), 0)); + selectors.push_back(bp.add_gate({constraint_})); + } + } else if (witness_amount % 3 == 2) { + + auto constraint_2 = + var(component.W(3), 0) - + var(component.W(witness_amount - 1), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(1), 0) + + var(component.W(0), 0) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_2})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_3 = + var(component.W(2), 0) - + var(component.W(witness_amount - 2), -1) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + + var(component.W(witness_amount - 1), -1) + + var(component.W(0), -1) * var(component.W(1), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_3})); + + for (std::size_t j = 5; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + var(component.W(j - 2), 0) + + var(component.W(0), -1) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_4 = var(component.W(1), 0) - + var(component.W(witness_amount - 3), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + + var(component.W(witness_amount - 2), -1) + + var(component.W(0), 0) * var(component.W(witness_amount - 1), + -1)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_4})); + + for (std::size_t j = 4; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_5 = + var(component.W(3), +1) - + var(component.W(witness_amount - 1), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(1), +1) + + var(component.W(0), 0) * var(component.W(2), +1)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_5})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), +1) - + var(component.W(j - 3), +1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(j - 2), +1) + + var(component.W(0), 0) * + var(component.W(j - 1), + +1)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + } else { + + auto constraint_2 = + var(component.W(3), 0) - + var(component.W(witness_amount - 1), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(1), 0) + + var(component.W(0), 0) * var(component.W(2), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_2})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), +1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_3 = + var(component.W(1), 0) - + var(component.W(witness_amount - 3), -1) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + + var(component.W(witness_amount - 2), -1) + + var(component.W(0), -1) * + var(component.W(witness_amount - 1), -1)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_3})); + + for (std::size_t j = 4; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), -1)) * var(component.W(0), 0) + var(component.W(j - 2), 0) + + var(component.W(0), -1) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_4 = + var(component.W(2), 0) - + var(component.W(witness_amount - 2), -1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + + var(component.W(witness_amount - 1), -1) + + var(component.W(0), 0) * var(component.W(1), 0)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_4})); + + for (std::size_t j = 5; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), 0) - + var(component.W(j - 3), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(j - 2), 0) + + var(component.W(0), 0) * + var(component.W(j - 1), 0)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + + auto constraint_5 = + var(component.W(3), +1) - + var(component.W(witness_amount - 1), 0) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(1), +1) + + var(component.W(0), 0) * var(component.W(2), +1)); // h = (1+beta)gamma + s_0 + beta t_0 + selectors.push_back(bp.add_gate({constraint_5})); + + for (std::size_t j = 6; j < witness_amount; j = j + 3) { + auto constraint_ = + var(component.W(j), +1) - + var(component.W(j - 3), +1) * + ((1 + var(component.W(0), 0)) * var(component.W(0), -1) + var(component.W(j - 2), +1) + + var(component.W(0), 0) * + var(component.W(j - 1), + +1)); // h_new = h_old * ((1+beta)gamma + s_0 + beta t_0) + selectors.push_back(bp.add_gate({constraint_})); + } + } + return selectors; + } + + template + void generate_copy_constraints( + const plonk_f1_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f1_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_f1_loop::var; + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::size_t last_j = (3 * component.m) % (witness_amount - 1); + if (last_j == 0) { + last_j = witness_amount; + } + + for (std::size_t r = 0; r < component.rows_amount; r++) { + if (witness_amount % 3 == 1) { + if (r % 2 == 0) { + bp.add_copy_constraint({var(component.W(0), row + r, false), instance_input.beta}); + } else { + bp.add_copy_constraint({var(component.W(0), row + r, false), instance_input.gamma}); + } + } else { + if (r % 3 != 1) { + bp.add_copy_constraint({var(component.W(0), row + r, false), instance_input.beta}); + } else { + bp.add_copy_constraint({var(component.W(0), row + r, false), instance_input.gamma}); + } + } + + std::size_t j_last = (r == component.rows_amount - 1) ? last_j : witness_amount; + for (std::size_t j = 1; j < j_last; j++) { + auto tmp = (r * (witness_amount - 1) + j); + if (tmp >= 3 * component.m) { + break; + } + if (tmp % 3 == 1) { + bp.add_copy_constraint({var(component.W(j), row + r, false), instance_input.s[tmp / 3]}); + } else if (tmp % 3 == 2) { + bp.add_copy_constraint({var(component.W(j), row + r, false), instance_input.t[tmp / 3]}); + } else { + continue; + } + } + } + } + + template + typename plonk_f1_loop::result_type generate_circuit( + const plonk_f1_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f1_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + assert(instance_input.s.size() == instance_input.t.size()); + assert(instance_input.s.size() == component.m); + + std::vector selectors = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selectors[0], row); + std::size_t last_j = (3 * component.m) % (witness_amount - 1); + if (last_j == 0) { + last_j = witness_amount - 1; + } + + if (3 * component.m + 1 > witness_amount) { + std::size_t r = 0; + if (witness_amount % 3 == 1) { + for (r = 0; r < component.rows_amount - 1; r++) { + for (std::size_t j = 3; j < witness_amount; j = j + 3) { + if (r == 0 && j == 3) + continue; + else { + assignment.enable_selector(selectors[(r & 1) * (witness_amount / 3) + (j / 3)], + row + r); + } + } + } + r = component.rows_amount - 1; + for (std::size_t j = 3; j <= last_j; j = j + 3) { + assignment.enable_selector(selectors[(2 - (r & 1)) * (witness_amount / 3) + (j / 3)], + row + r); + } + } else if (witness_amount % 3 == 2) { + std::size_t pos = 0, start_j = 0; + for (r = 0; r < component.rows_amount - 1; r++) { + start_j = 3 - (r % 3); + for (std::size_t j = start_j; j < witness_amount; j = j + 3) { + if (r == 0 && j == 3) + continue; + pos = (r % 3) * (witness_amount / 3) + ((j + (r % 3)) / 3); + assignment.enable_selector(selectors[pos], row + r); + } + } + r = component.rows_amount - 1; + start_j = 3 - (r % 3); + std::size_t offset = 0; + if (r % 3 == 0) { + offset = witness_amount - 1; + } + for (std::size_t j = start_j; j <= last_j; j = j + 3) { + pos = (r % 3) * (witness_amount / 3) + ((j + (r % 3)) / 3) + offset; + assignment.enable_selector(selectors[pos], row + r - (r % 3 == 0)); + } + } else { + std::size_t pos = 0, start_j = 0; + for (r = 0; r < component.rows_amount - 1; r++) { + start_j = r % 3 + 3 * ((r % 3) == 0); + for (std::size_t j = start_j; j < witness_amount; j = j + 3) { + if (r == 0 && j == 3) + continue; + pos = (r % 3) * (witness_amount / 3) + ((j + (r % 3)) / 3) - (r % 3 == 2); + assignment.enable_selector(selectors[pos], row + r); + } + } + r = component.rows_amount - 1; + start_j = r % 3 + 3 * ((r % 3) == 0); + std::size_t offset = 0; + if (r % 3 == 0) { + offset = witness_amount - 1; + } + for (std::size_t j = start_j; j <= last_j; j = j + 3) { + pos = (r % 3) * (witness_amount / 3) + ((j + (r % 3)) / 3) - (r % 3 == 2) + offset; + assignment.enable_selector(selectors[pos], row + r - (r % 3 == 0)); + } + } + } else { + for (std::size_t j = 6; j <= last_j; j = j + 3) { + assignment.enable_selector(selectors[j / 3], row); + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_f1_loop::result_type(component, + start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F3_LOOP_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f3_loop.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f3_loop.hpp new file mode 100644 index 000000000..21e939d48 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/f3_loop.hpp @@ -0,0 +1,408 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the LOOKUP_ARGUMENT_VERIFIER component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F3_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F3_LOOP_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class f3_loop; + + template + class f3_loop> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 0; + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t n) { + + std::size_t r = std::ceil(4.0 * n / witness_amount); + return r; + } + + constexpr static std::size_t gates_amount_internal(std::size_t witness_amount, std::size_t degree) { + if (witness_amount % 4 == 0) { + return witness_amount / 4 + 1; + } + if (witness_amount % 4 == 2) { + return witness_amount / 2 + 1; + } + return witness_amount + 1; + } + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t m) { + return rows_amount_internal(witness_amount, m); + } + + const std::size_t m; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), m); + const std::size_t gates_amount = gates_amount_internal(this->witness_amount(), m); + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + std::size_t degree; + + gate_manifest_type(std::size_t witness_amount_, std::size_t degree_) : + witness_amount(witness_amount_), degree(degree_) { + } + + std::uint32_t gates_amount() const override { + return f3_loop::gates_amount_internal(witness_amount, degree); + } + + bool operator<(const component_gate_manifest *other) const override { + return this->witness_amount < + dynamic_cast(other)->witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t degree) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, degree)); + return manifest; + } + + static manifest_type get_manifest(std::size_t m) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_range_param(4, 15)), false); + return manifest; + } + + struct input_type { + std::vector alphas; + std::vector s; + std::vector t; + + std::vector> all_vars() { + std::vector> vars; + vars.insert(vars.end(), alphas.begin(), alphas.end()); + vars.insert(vars.end(), s.begin(), s.end()); + vars.insert(vars.end(), t.begin(), t.end()); + return vars; + } + }; + + struct result_type { + var output; + + result_type(const f3_loop &component, std::uint32_t start_row_index) { + std::size_t WitnessesAmount = component.witness_amount(); + std::size_t l = 4 * component.m % WitnessesAmount; + if (l == 0) { + l = WitnessesAmount; + } + output = var(component.W(l - 1), start_row_index + component.rows_amount - 1, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + f3_loop(ContainerType witness, std::size_t m_) : + component_type(witness, {}, {}, get_manifest(m_)), m(m_) {}; + + template + f3_loop(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t m_) : + component_type(witness, constant, public_input, get_manifest(m_)), + m(m_) {}; + + f3_loop(std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t m_) : + component_type(witnesses, constants, public_inputs, get_manifest(m_)), + m(m_) {}; + }; + } // namespace detail + + template + using plonk_f3_loop = + detail::f3_loop>; + + template + typename plonk_f3_loop::result_type generate_assignments( + const plonk_f3_loop &component, + assignment> + &assignment, + const typename plonk_f3_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + assert(instance_input.s.size() == instance_input.t.size()); + assert(instance_input.s.size() == instance_input.alphas.size()); + assert(instance_input.s.size() == component.m); + + typename BlueprintFieldType::value_type f3 = BlueprintFieldType::value_type::zero(); + std::vector assignments; + for (std::size_t i = 0; i < component.m; i++) { + typename BlueprintFieldType::value_type s_i = var_value(assignment, instance_input.s[i]); + typename BlueprintFieldType::value_type t_i = var_value(assignment, instance_input.t[i]); + typename BlueprintFieldType::value_type alpha_i = var_value(assignment, instance_input.alphas[i]); + f3 = f3 + (s_i - t_i) * alpha_i; + assignments.push_back(alpha_i); + assignments.push_back(s_i); + assignments.push_back(t_i); + assignments.push_back(f3); + } + + std::size_t r = 0, j = 0, i = 0; + for (i = 0; i < assignments.size(); i++) { + r = i / (witness_amount); + j = i % witness_amount; + assignment.witness(component.W(j), row + r) = assignments[i]; + } + row += r; + + return typename plonk_f3_loop::result_type(component, + start_row_index); + } + + template + std::vector generate_gates( + const plonk_f3_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f3_loop::input_type instance_input) { + + using var = typename plonk_f3_loop::var; + std::size_t witness_amount = component.witness_amount(); + + std::vector selectors; + + auto constraint_1 = + var(component.W(3), 0) - (var(component.W(1), 0) - var(component.W(2), 0)) * var(component.W(0), 0); + selectors.push_back(bp.add_gate({constraint_1})); + + if (witness_amount % 4 == 0) { + auto constraint_2 = var(component.W(3), 0) - var(component.W(witness_amount - 1), -1) - + (var(component.W(1), 0) - var(component.W(2), 0)) * var(component.W(0), 0); + selectors.push_back(bp.add_gate({constraint_2})); + + for (std::size_t j = 7; j < witness_amount; j = j + 4) { + auto constraint_ = + var(component.W(j), 0) - var(component.W(j - 4), 0) - + (var(component.W(j - 2), 0) - var(component.W(j - 1), 0)) * var(component.W(j - 3), 0); + selectors.push_back(bp.add_gate({constraint_2})); + } + } else if (witness_amount % 4 == 2) { + auto constraint_2 = var(component.W(1), 0) - var(component.W(witness_amount - 3), -1) - + (var(component.W(witness_amount - 1), -1) - var(component.W(0), 0)) * + var(component.W(witness_amount - 2), -1); + selectors.push_back(bp.add_gate({constraint_2})); + + auto constraint_3 = var(component.W(3), 0) - var(component.W(witness_amount - 1), -1) - + (var(component.W(1), 0) - var(component.W(2), 0)) * var(component.W(0), 0); + selectors.push_back(bp.add_gate({constraint_3})); + + for (std::size_t j = 5; j < witness_amount; j = j + 2) { + auto constraint_ = + var(component.W(j), 0) - var(component.W(j - 4), 0) - + (var(component.W(j - 2), 0) - var(component.W(j - 1), 0)) * var(component.W(j - 3), 0); + selectors.push_back(bp.add_gate({constraint_})); + } + + } else { + auto constraint_2 = + var(component.W(0), 0) - var(component.W(witness_amount - 4), -1) - + (var(component.W(witness_amount - 2), -1) - var(component.W(witness_amount - 1), -1)) * + var(component.W(witness_amount - 3), -1); + selectors.push_back(bp.add_gate({constraint_2})); + + auto constraint_3 = var(component.W(1), 0) - var(component.W(witness_amount - 3), -1) - + (var(component.W(witness_amount - 1), -1) - var(component.W(0), 0)) * + var(component.W(witness_amount - 2), -1); + selectors.push_back(bp.add_gate({constraint_3})); + + auto constraint_4 = + var(component.W(2), 0) - var(component.W(witness_amount - 2), -1) - + (var(component.W(0), 0) - var(component.W(1), 0)) * var(component.W(witness_amount - 1), -1); + selectors.push_back(bp.add_gate({constraint_4})); + + auto constraint_5 = var(component.W(3), 0) - var(component.W(witness_amount - 1), -1) - + (var(component.W(1), 0) - var(component.W(2), 0)) * var(component.W(0), 0); + selectors.push_back(bp.add_gate({constraint_5})); + + for (std::size_t j = 4; j < witness_amount; j++) { + auto constraint_ = + var(component.W(j), 0) - var(component.W(j - 4), 0) - + (var(component.W(j - 2), 0) - var(component.W(j - 1), 0)) * var(component.W(j - 3), 0); + selectors.push_back(bp.add_gate({constraint_})); + } + } + + return selectors; + } + + template + void generate_copy_constraints( + const plonk_f3_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f3_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + using var = typename plonk_f3_loop::var; + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::size_t tmp; + + std::size_t last_col = 4 * component.m % witness_amount; + if (last_col == 0) { + last_col = witness_amount; + } + for (std::size_t r = 0; r < component.rows_amount; r++) { + std::size_t last_j = (r == component.rows_amount - 1) ? last_col : witness_amount; + for (std::size_t j = 0; j < last_j; j++) { + tmp = r * witness_amount + j; + if (tmp % 4 == 0) { + bp.add_copy_constraint( + {var(component.W(j), row + r, false), instance_input.alphas[tmp / 4]}); + } else if (tmp % 4 == 1) { + bp.add_copy_constraint({var(component.W(j), row + r, false), instance_input.s[tmp / 4]}); + } else if (tmp % 4 == 2) { + bp.add_copy_constraint({var(component.W(j), row + r, false), instance_input.t[tmp / 4]}); + } + } + } + } + + template + typename plonk_f3_loop::result_type generate_circuit( + const plonk_f3_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_f3_loop::input_type instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + assert(instance_input.s.size() == instance_input.t.size()); + assert(instance_input.s.size() == component.m); + + std::vector selectors = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selectors[0], row); + + std::size_t last_col = 4 * component.m % witness_amount; + if (last_col == 0) { + last_col = witness_amount; + } + + std::size_t start_j, x = witness_amount % 4; + std::size_t r; + if (witness_amount % 4 == 0) { + start_j = 3; + for (r = 0; r < component.rows_amount - 1; r++) { + for (std::size_t j = start_j; j < witness_amount; j = j + 4) { + if (r == 0 && j == 3) + continue; + assignment.enable_selector(selectors[(j / 4 + 1)], row + r); + } + } + r = component.rows_amount - 1; + for (std::size_t j = start_j; j < last_col; j = j + 4) { + assignment.enable_selector(selectors[(j / 4 + 1)], row + r); + } + } else if (witness_amount % 4 == 2) { + for (r = 0; r < component.rows_amount - 1; r++) { + start_j = 3 - 2 * (r % 2); + for (std::size_t j = start_j; j < witness_amount; j = j + 4) { + if (r == 0 && j == 3) + continue; + assignment.enable_selector(selectors[(j / 2 + 1)], row + r); + } + } + + r = component.rows_amount - 1; + start_j = 3 - 2 * (r % 2); + for (std::size_t j = start_j; j < last_col; j = j + 4) { + assignment.enable_selector(selectors[(j / 2 + 1)], row + r); + } + } else { + for (r = 0; r < component.rows_amount - 1; r++) { + if (r % 4 == 0) { + start_j = 3; + } else { + start_j = (3 - x) + (x - 2) * (r % 4 - 1); + } + for (std::size_t j = start_j; j < witness_amount; j = j + 4) { + if (r == 0 && j == 3) + continue; + assignment.enable_selector(selectors[j + 1], row + r); + } + } + + r = component.rows_amount - 1; + if (r % 4 == 0) { + start_j = 3; + } else { + start_j = (3 - x) + (x - 2) * (r % 4 - 1); + } + for (std::size_t j = start_j; j < last_col; j = j + 4) { + assignment.enable_selector(selectors[j + 1], row + r); + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_f3_loop::result_type(component, + start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_F3_LOOP_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/gate_component.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/gate_component.hpp new file mode 100644 index 000000000..de5853310 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/detail/gate_component.hpp @@ -0,0 +1,416 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the GATE_COMPONENT component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_COMPONENT_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + /** + * Description: Polynomial evaluation component for non-constant polynomials using Horner's methods + * Input: theta, C_0, C_1, ..., C_{d-1}, q. + * Output: G = q*(C_0 + theta * C_1 + theta^2 * C_2 + ... + theta^{d-1} * C_{d-1}) % p + */ + template + class gate_component; + + template + class gate_component< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 0; + + constexpr static std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t degree) { + + assert(degree != 0); + std::size_t r = std::ceil(2.0 * degree / (witness_amount - 1)); + if ((2 * degree - 1) % (witness_amount - 1) + 1 >= witness_amount - 3) { + r++; + } + return r; + } + + static std::size_t gates_amount_internal(std::size_t witness_amount) { + return 2 * witness_amount; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t degree) { + return rows_amount_internal(witness_amount, degree); + } + + const std::size_t _d; + bool need_extra_row = false; + + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), _d); + const std::size_t gates_amount = gates_amount_internal(this->witness_amount()); + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) { + } + + std::uint32_t gates_amount() const override { + return gate_component::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + std::size_t other_witness_amount = + dynamic_cast(other)->witness_amount; + return witness_amount < other_witness_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t degree) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t degree) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_range_param(3, 15)), false); + return manifest; + } + + struct input_type { + var theta; + std::vector constraints; + var selector; + + std::vector> all_vars() { + std::vector> vars; + vars.push_back(theta); + vars.insert(vars.begin() + 1, constraints.begin(), constraints.end()); + vars.push_back(selector); + return vars; + } + }; + + struct result_type { + var output; + + result_type(const gate_component &component, std::uint32_t start_row_index) { + output = var(component.W(component.witness_amount() - 1), + start_row_index + component.rows_amount - 1, false); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + gate_component(ContainerType witness, std::size_t _d_) : + component_type(witness, {}, {}, get_manifest(_d_)), _d(_d_) { + std::size_t WitnessesAmount = this->witness_amount(); + if ((2 * _d - 1) % (WitnessesAmount - 1) + 1 >= WitnessesAmount - 3) { + need_extra_row = true; + } + }; + + template + gate_component(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t _d_) : + component_type(witness, constant, public_input, get_manifest(_d_)), + _d(_d_) { + std::size_t WitnessesAmount = this->witness_amount(); + if ((2 * _d - 1) % (WitnessesAmount - 1) + 1 >= WitnessesAmount - 3) { + need_extra_row = true; + } + }; + + gate_component( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _d_) : + component_type(witnesses, constants, public_inputs, get_manifest(_d_)), + _d(_d_) { + std::size_t WitnessesAmount = this->witness_amount(); + if ((2 * _d - 1) % (WitnessesAmount - 1) + 1 >= WitnessesAmount - 3) { + need_extra_row = true; + } + }; + }; + + } // namespace detail + + template + using plonk_gate_component = detail::gate_component< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_gate_component::result_type generate_assignments( + const plonk_gate_component &component, + assignment> + &assignment, + const typename plonk_gate_component::input_type + instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + typename BlueprintFieldType::value_type q = var_value(assignment, instance_input.selector); + typename BlueprintFieldType::value_type theta = var_value(assignment, instance_input.theta); + + std::vector assignments; + typename BlueprintFieldType::value_type G = BlueprintFieldType::value_type::zero(); + + typename BlueprintFieldType::value_type tmp; + for (std::size_t i = 1; i <= component._d; i++) { + tmp = var_value(assignment, instance_input.constraints[component._d - i + 1]); + assignments.push_back(tmp); + G = theta * (G + tmp); + assignments.push_back(G); + } + G = q * (G + var_value(assignment, instance_input.constraints[0])); + + std::size_t r = 0, j = 0, i = 0; + for (i = 0; i < assignments.size(); i++) { + r = i / (witness_amount - 1); + j = i % (witness_amount - 1) + 1; + assignment.witness(component.W(j), row + r) = assignments[i]; + } + row += r; + + for (r = start_row_index; r <= row; r++) { + assignment.witness(component.W(0), r) = theta; + } + j = (assignments.size() % (witness_amount - 1)) + 1; + if (component.need_extra_row) { + j = 0; + row++; + } + + assignment.witness(component.W(j), row) = var_value(assignment, instance_input.constraints[0]); + assignment.witness(component.W(j + 1), row) = q; + assignment.witness(component.W(witness_amount - 1), row) = G; + + return typename plonk_gate_component::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_gate_component &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_gate_component::input_type + instance_input) { + + std::size_t witness_amount = component.witness_amount(); + using var = typename plonk_gate_component::var; + + std::vector selectors; + + auto constraint_1 = + var(component.W(2), 0) - var(component.W(1), 0) * var(component.W(0), 0); // G = theta * C_d + selectors.push_back(bp.add_gate({constraint_1})); + + auto constraint_2 = + var(component.W(1), 0) - var(component.W(0), 0) * (var(component.W(witness_amount - 1), -1) + + var(component.W(witness_amount - 2), -1)); + selectors.push_back(bp.add_gate({constraint_2})); + + auto constraint_3 = + var(component.W(2), 0) - + var(component.W(0), 0) * (var(component.W(1), 0) + var(component.W(witness_amount - 1), -1)); + selectors.push_back(bp.add_gate({constraint_3})); + + for (std::size_t i = 3; i < witness_amount; i++) { + auto constraint_i = var(component.W(i), 0) - var(component.W(0), 0) * (var(component.W(i - 1), 0) + + var(component.W(i - 2), 0)); + selectors.push_back(bp.add_gate({constraint_i})); + } + + auto constraint_5 = + var(component.W(witness_amount - 1), 0) - + var(component.W(1), 0) * (var(component.W(0), 0) + var(component.W(witness_amount - 3), -1)); + selectors.push_back(bp.add_gate({constraint_5})); + + auto constraint_6 = + var(component.W(witness_amount - 1), 0) - + var(component.W(1), 0) * (var(component.W(0), 0) + var(component.W(witness_amount - 2), -1)); + selectors.push_back(bp.add_gate({constraint_6})); + + auto constraint_7 = + var(component.W(witness_amount - 1), 0) - + var(component.W(1), 0) * (var(component.W(0), 0) + var(component.W(witness_amount - 1), -1)); + selectors.push_back(bp.add_gate({constraint_7})); + + for (std::size_t i = 2; i < witness_amount - 1; i++) { + auto constraint_i = + var(component.W(witness_amount - 1), 0) - + var(component.W(i), 0) * (var(component.W(i - 1), 0) + var(component.W(i - 2), 0)); + selectors.push_back(bp.add_gate({constraint_i})); + } + + return selectors; + } + + template + void generate_copy_constraints( + const plonk_gate_component &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_gate_component::input_type + instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_gate_component::var; + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::size_t r = 0, j = 0; + + for (std::size_t i = 0; i < component.rows_amount - 1; i++) { + bp.add_copy_constraint({var(component.W(0), row + i, false), instance_input.theta}); + } + if (!component.need_extra_row) { + bp.add_copy_constraint( + {var(component.W(0), row + component.rows_amount - 1, false), instance_input.theta}); + } + + for (std::size_t i = 0; i < component._d; i++) { + r = (2 * i) / (witness_amount - 1); + j = (2 * i) % (witness_amount - 1) + 1; + bp.add_copy_constraint( + {var(component.W(j), row + r, false), instance_input.constraints[component._d - i]}); + } + row = start_row_index + component.rows_amount - 1; + j = 2 * component._d % (witness_amount - 1) + 1; + if (component.need_extra_row) { + j = 0; + } + + bp.add_copy_constraint({var(component.W(j), row, false), instance_input.constraints[0]}); + bp.add_copy_constraint({var(component.W(j + 1), row, false), instance_input.selector}); + } + + template + typename plonk_gate_component::result_type generate_circuit( + const plonk_gate_component &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_gate_component::input_type + instance_input, + const std::size_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + std::vector selector_indices = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_indices[0], row); + + // first row gates + std::size_t last_gate = (2 * component._d - 1) % (witness_amount - 1) + 1; + std::size_t first_row_last_gate = witness_amount - 1; + + if (component.rows_amount == 1 || (component.rows_amount == 2 && component.need_extra_row)) { + first_row_last_gate = last_gate; + } + + for (std::size_t i = 4; i <= first_row_last_gate; i = i + 2) { + assignment.enable_selector(selector_indices[i], row); + } + + if (component.rows_amount > 1) { + // middle row gates + std::size_t r; + std::size_t tmp = 2; + for (r = 1; r < component.rows_amount - 2; r++) { + tmp = 2 - ((witness_amount - 1) % 2) * (r % 2); + for (std::size_t i = tmp; i < witness_amount; i = i + 2) { + assignment.enable_selector(selector_indices[i], row + r); + } + } + + tmp = 2 - ((witness_amount - 1) % 2) * (r % 2); + if (component.need_extra_row && r == component.rows_amount - 2) { + for (std::size_t i = tmp; i <= last_gate; i = i + 2) { + assignment.enable_selector(selector_indices[i], row + r); + } + r++; + } + if( !component.need_extra_row && r == component.rows_amount - 2) { + for (std::size_t i = tmp; i < witness_amount; i = i + 2) { + assignment.enable_selector(selector_indices[i], row + r); + } + r++; + } + + // last row gates + tmp = 2 - (r % 2) * ((witness_amount - 1) % 2); + if (component.need_extra_row) { + assignment.enable_selector(selector_indices[last_gate + 3], row + r); + } else { + for (std::size_t i = tmp; i <= last_gate; i = i + 2) { + assignment.enable_selector(selector_indices[i], row + r); + } + assignment.enable_selector(selector_indices[witness_amount + last_gate + 3], row + r); + } + } else { + assignment.enable_selector(selector_indices[witness_amount + last_gate + 3], row); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_gate_component::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_COMPONENT_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_array_swap.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_array_swap.hpp new file mode 100644 index 000000000..adeaa84a6 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_array_swap.hpp @@ -0,0 +1,311 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification array swapping component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_ARRAY_SWAP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_ARRAY_SWAP_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Input: t, array + // Output: if t == 0, if t == 1 + // Does NOT check that t is really a bit. + // Configuration is suboptimal: we do rows of the form + // t, a1, b1, o11, o12, a2, b2, o21, o22, ... + // We could reuse t among multiple different rows for a better configuration, but that would be + // more complex than what we can quickly implement now. + template + class fri_array_swap; + + template + class fri_array_swap, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t half_array_size; + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t witness_amount; + std::size_t half_array_size; + + public: + gate_manifest_type(std::size_t witness_amount_, std::size_t half_array_size_) : + witness_amount((witness_amount_ - 1) / 4), half_array_size(half_array_size_) {}; + + bool operator<(gate_manifest_type const& other) const { + return witness_amount < other.witness_amount || + (witness_amount == other.witness_amount && half_array_size < other.half_array_size); + } + + std::uint32_t gates_amount() const override { + return fri_array_swap::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t half_array_size) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, half_array_size)); + return manifest; + } + + static manifest_type get_manifest(std::size_t half_array_size) { + static manifest_type manifest = manifest_type( + // TODO: make the manifest depend on half_array_size + // this requires the manifest rework + std::shared_ptr(new manifest_range_param(5, 5 + 4 * (half_array_size - 1) + 1, 4)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t half_array_size) { + return (2 * half_array_size + (witness_amount - 1) / 4 - 1) / ((witness_amount - 1) / 4); + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), half_array_size); + const std::string component_name = "fri array swap component"; + + struct input_type { + var t; // swap control bit + std::vector arr; // the array with elements to swap + + std::vector> all_vars() { + std::vector> result; + result.reserve(1 + arr.size()); + result.push_back(t); + result.insert(result.end(), arr.begin(), arr.end()); + return result; + } + }; + + struct result_type { + std::vector output; // the array with possibly swapped elements + + result_type(const fri_array_swap &component, std::size_t start_row_index) { + const std::size_t array_size = 2 * component.half_array_size; + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + output.reserve(array_size); + for (std::size_t row = 0, pair_index = 0; row < rows_amount; row++) { + for (std::size_t offset = 1; offset < witness_amount - 1 && pair_index < array_size; + offset += 4, pair_index += 2) { + output.emplace_back( + var(component.W(offset + 2), start_row_index + row, false)); + output.emplace_back( + var(component.W(offset + 3), start_row_index + row, false)); + } + } + } + + std::vector> all_vars() { + std::vector> result; + result.reserve(output.size()); + result.insert(result.end(), output.begin(), output.end()); + return result; + } + }; + + template + explicit fri_array_swap(ContainerType witness, std::size_t half_array_size_) : + component_type(witness, {}, {}, get_manifest(half_array_size_)), + half_array_size(half_array_size_) {}; + + template + fri_array_swap(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t half_array_size_) : + component_type(witness, constant, public_input, get_manifest(half_array_size_)), + half_array_size(half_array_size_) {}; + + fri_array_swap( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t half_array_size_) : + component_type(witnesses, constants, public_inputs, get_manifest(half_array_size_)), + half_array_size(half_array_size_) {}; + }; + + template + using plonk_fri_array_swap = + fri_array_swap, + BlueprintFieldType>; + + template + typename plonk_fri_array_swap::result_type generate_assignments( + const plonk_fri_array_swap &component, + assignment> + &assignment, + const typename plonk_fri_array_swap::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_fri_array_swap; + using value_type = typename BlueprintFieldType::value_type; + + BOOST_ASSERT(2 * component.half_array_size == instance_input.arr.size()); + const std::size_t array_size = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + value_type t = var_value(assignment, instance_input.t); + + for (std::size_t row = 0, pair_index = 0; row < rows_amount; row++) { + assignment.witness(component.W(0), start_row_index + row) = + var_value(assignment, instance_input.t); + for (std::size_t offset = 1; offset < witness_amount - 1; offset += 4, pair_index += 2) { + if (pair_index < array_size) { + value_type a_val = var_value(assignment, instance_input.arr[pair_index]); + value_type b_val = var_value(assignment, instance_input.arr[pair_index + 1]); + assignment.witness(component.W(offset), start_row_index + row) = a_val; + assignment.witness(component.W(offset + 1), start_row_index + row) = b_val; + if (t == 0) { + assignment.witness(component.W(offset + 2), start_row_index + row) = a_val; + assignment.witness(component.W(offset + 3), start_row_index + row) = b_val; + } else { + assignment.witness(component.W(offset + 2), start_row_index + row) = b_val; + assignment.witness(component.W(offset + 3), start_row_index + row) = a_val; + } + } else { + assignment.witness(component.W(offset), start_row_index + row) = 0; + assignment.witness(component.W(offset + 1), start_row_index + row) = 0; + assignment.witness(component.W(offset + 2), start_row_index + row) = 0; + assignment.witness(component.W(offset + 3), start_row_index + row) = 0; + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fri_array_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_array_swap::input_type + &instance_input) { + + using component_type = plonk_fri_array_swap; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + BOOST_ASSERT(2 * component.half_array_size == instance_input.arr.size()); + + std::vector constraints; + constraints.reserve(component.half_array_size); + var t = var(component.W(0), 0, true); + const std::size_t witness_amount = component.witness_amount(); + for (std::size_t offset = 1; offset < witness_amount - 1; offset += 4) { + var input_a_var = var(component.W(offset), 0, true), + output_a_var = var(component.W(offset + 2), 0, true), + input_b_var = var(component.W(offset + 1), 0, true), + output_b_var = var(component.W(offset + 3), 0, true); + + constraints.emplace_back(output_a_var - input_a_var * (1 - t) - input_b_var * t); + constraints.emplace_back(output_b_var - input_b_var * (1 - t) - input_a_var * t); + } + + return bp.add_gate(constraints); + } + + template + void generate_copy_constraints( + const plonk_fri_array_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_array_swap::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_fri_array_swap; + using var = typename component_type::var; + + BOOST_ASSERT(2 * component.half_array_size == instance_input.arr.size()); + const std::size_t array_size = instance_input.arr.size(); + const std::size_t witness_amount = component.witness_amount(); + const std::size_t rows_amount = component.rows_amount; + + for (std::size_t row = 0, pair_index = 0; row < rows_amount; row++) { + bp.add_copy_constraint( + {instance_input.t, var(component.W(0), start_row_index + row, false)}); + for (std::size_t offset = 1; offset < witness_amount - 1 && pair_index < array_size; + offset += 4, pair_index += 2) { + bp.add_copy_constraint( + {instance_input.arr[pair_index], + var(component.W(offset), start_row_index + row, false)}); + bp.add_copy_constraint( + {instance_input.arr[pair_index + 1], + var(component.W(offset + 1), start_row_index + row, false)}); + } + } + } + + template + typename plonk_fri_array_swap::result_type generate_circuit( + const plonk_fri_array_swap &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_array_swap::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_fri_array_swap; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector( + selector_index, start_row_index, start_row_index + component.rows_amount - 1); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_ARRAY_SWAP_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_cosets.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_cosets.hpp new file mode 100644 index 000000000..85f13a549 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_cosets.hpp @@ -0,0 +1,522 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification coset generating component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_COSETS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_COSETS_HPP + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + // compute the number of lines if we need to place o object with pl object per line + template + T1 lfit(T1 o, T2 pl) { + return o/pl + (o % pl > 0); + } + } // namespace detail + + // Uses parameters n, omega + // Input: x (challenge) + // Output: vector of length n with triplets < (s,-s,b) >, where s_0 = omega^{x % 2^n}, s_{i+1} = s_i^2, + // b = 0 or 1, showing whether the pair (s,-s) needs reordering + // For details see https://www.notion.so/nilfoundation/FRI-cosets-generator-910475aa46e54bdc986407d178428a8a + // + + using detail::lfit; + + template + class fri_cosets; + + template + class fri_cosets, + BlueprintFieldType>: + public plonk_component { + + static std::size_t gates_amount_internal(std::size_t witness_amount, std::size_t n, std::size_t total_bits) { + const std::size_t l = witness_amount / 9; // number of 9-blocks per row + const std::size_t last_l = n % l; // 9-blocks in transition row. If 0, no transition row exists + const std::size_t nineb_rows = lfit(n,l); // number of rows with 9-blocks + const std::size_t bits = total_bits-n; + const std::size_t remaining_bits = bits - ((last_l > 0)? 3*(l - last_l)-1 : 0); + + return (nineb_rows > 1) + (nineb_rows > 2) + 1 + (remaining_bits > 0); + } + + + static std::size_t rows_amount_internal(std::size_t witness_amount, + std::size_t n, + std::size_t total_bits) { + + std::size_t trans_9_bl_space = 9*n % witness_amount; // space occupied by 9-blocks in transition line + // space for 3-bit_blocks in transition line + std::size_t trans_line_bits = (trans_9_bl_space > 0) ? (witness_amount - trans_9_bl_space)/3 - 1 : 0; + + return lfit(9*n, witness_amount) + + lfit(total_bits-n - trans_line_bits, witness_amount/3 - 1 ) // 3-bit_blocks in all cols but the 1st three + + 1; // the row for storing 0 + } + + public: + using component_type = plonk_component; + + class gate_manifest_type : public component_gate_manifest { + + std::array gates_footprint(std::size_t WA, std::size_t N, std::size_t TB) const { + std::size_t l = WA / 9; + std::size_t last_l = N % l; + std::size_t nineb_rows = lfit(N,l); + std::size_t bits = TB-N; + + std::size_t remaining_bits = bits - ((last_l > 0)? 3*(l - last_l)-1 : 0); + + return { WA, last_l, (nineb_rows > 1), (nineb_rows > 2), (remaining_bits > 0) }; + } + + public: + std::size_t witness_amount; + std::size_t n; + const std::size_t total_bits = BlueprintFieldType::modulus_bits; + + gate_manifest_type(std::size_t witness_amount_, std::size_t n_) + : witness_amount(witness_amount_), n(n_) {} + + std::uint32_t gates_amount() const override { + return fri_cosets::gates_amount_internal(witness_amount,n,total_bits); + } + + bool operator<(const component_gate_manifest *other) const override { + std::size_t o_witness_amount = dynamic_cast(other)->witness_amount; + std::size_t o_n = dynamic_cast(other)->n; + + std::array gates = gates_footprint(witness_amount,n,total_bits); + std::array o_gates = gates_footprint(o_witness_amount,o_n,total_bits); + return (gates < o_gates); + } + }; + + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using manifest_type = plonk_component_manifest; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t n, value_type omega) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount,n)); + return manifest; + } + + static manifest_type get_manifest(std::size_t n, value_type omega) { + static manifest_type manifest = manifest_type( + std::shared_ptr( + new manifest_range_param(9, n * 9 + 1, 9) + ), + true // constant column required + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t n, value_type omega) { + return rows_amount_internal(witness_amount,n,BlueprintFieldType::modulus_bits); + } + // Initialized by constructor + std::size_t n; + value_type omega; + // aliases and derivatives + const std::size_t total_bits = BlueprintFieldType::modulus_bits; // the total amount of bits for storing a field element + + const std::size_t WA = this->witness_amount(); + const std::size_t nine_bl_per_line = WA / 9; // 9-blocks per line + const std::size_t bits_blocks_count = total_bits-n; // number of bit blocks + + const std::size_t gates_amount = gates_amount_internal(this->witness_amount(), n, total_bits); + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), n, total_bits); + const std::string component_name = "fri cosets component"; + struct input_type { + var x = var(0, 0, false); + + std::vector> all_vars() { + return {x}; + } + }; + + struct result_type { + std::vector> output = {}; + + result_type(const fri_cosets &component, std::size_t start_row_index) { + const std::size_t n = component.n; + const std::size_t l = component.nine_bl_per_line; + + output.clear(); + for(std::size_t b = n; b > 0; b--) { + std::size_t i = (b-1) / l; // blocks are numbered 0..(n-1). i = row of block b + std::size_t j = (b-1) % l; // j = number of block b in i-th row + output.push_back({ var(component.W(9*j + 5), start_row_index + i, false, var::column_type::witness), + var(component.W(9*j + 6), start_row_index + i, false, var::column_type::witness), + var(component.W(9*j + 7), start_row_index + i, false, var::column_type::witness) }); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { + res.push_back(e[0]); res.push_back(e[1]); res.push_back(e[2]); + } + return res; + } + }; + + template + fri_cosets(WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t n_, + value_type omega_): + component_type(witness, constant, public_input, get_manifest(n_, omega_)), + n(n_), + omega(omega_) { + }; + + fri_cosets(std::initializer_list< + typename component_type::witness_container_type::value_type> witnesses, + std::initializer_list< + typename component_type::constant_container_type::value_type> constants, + std::initializer_list< + typename component_type::public_input_container_type::value_type> public_inputs, + std::size_t n_, + value_type omega_): + component_type(witnesses, constants, public_inputs, get_manifest(n_, omega_)), + n(n_), + omega(omega_) { + }; + }; + + template + using plonk_fri_cosets = + fri_cosets, + BlueprintFieldType>; + + template + typename plonk_fri_cosets::result_type + generate_assignments( + const plonk_fri_cosets &component, + assignment> &assignment, + const typename plonk_fri_cosets::input_type instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t WA = component.WA; + const std::size_t n = component.n; + const std::size_t l = component.nine_bl_per_line; + + typename BlueprintFieldType::integral_type + x_decomp = typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.x).data), + pm1_decomp = typename BlueprintFieldType::integral_type(BlueprintFieldType::value_type::modulus - 1); + + value_type w_power = component.omega; + value_type coset_element = 1; + + // fill the 9-blocks + // top-down part + for(std::size_t b = 0; b < n; b++) { + std::size_t i = start_row_index + b / l; + std::size_t j = b % l; + assignment.witness(component.W(9*j),i) = value_type(x_decomp); + assignment.witness(component.W(9*j+1),i) = value_type(pm1_decomp); + // W(9j + 2) = sgn(pm1_decomp - x_decomp) + assignment.witness(component.W(9*j+2),i) = value_type((x_decomp < pm1_decomp) - (pm1_decomp < x_decomp)); + assignment.witness(component.W(9*j+3),i) = w_power; + coset_element *= (x_decomp % 2 == 1 ? w_power : 1); + assignment.witness(component.W(9*j+4),i) = coset_element; + assignment.witness(component.W(9*j+7),i) = value_type(x_decomp % 2); + assignment.witness(component.W(9*j+8),i) = value_type(pm1_decomp % 2); + x_decomp /= 2; + pm1_decomp /= 2; + w_power *= w_power; + } + // down-top part + for(std::size_t b = n; b > 0; b--) { + std::size_t i = start_row_index + (b-1) / l; + std::size_t j = (b-1) % l; + assignment.witness(component.W(9*j+5),i) = coset_element; + assignment.witness(component.W(9*j+6),i) = (-1)*coset_element; + coset_element = coset_element * coset_element; + } + + std::size_t i = (9*n) / WA; + std::size_t j = (9*n) % WA; + assignment.witness(component.W(j),start_row_index + i) = value_type(x_decomp); + assignment.witness(component.W(j+1),start_row_index + i) = value_type(pm1_decomp); + // W(j + 2) = sgn(pm1_decomp - x_decomp) + assignment.witness(component.W(j+2),start_row_index + i) = + value_type((x_decomp < pm1_decomp) - (pm1_decomp < x_decomp)); + j += 3; + while(i < component.rows_amount-1) { + assignment.witness(component.W(j),start_row_index + i) = value_type(x_decomp % 2); + assignment.witness(component.W(j+1),start_row_index + i) = value_type(pm1_decomp % 2); + assignment.witness(component.W(j+2),start_row_index + i) = + value_type((x_decomp < pm1_decomp) - (pm1_decomp < x_decomp)); + x_decomp = x_decomp / 2; + pm1_decomp = pm1_decomp / 2; + // W(j + 2) = sgn(pm1_decomp - x_decomp) + j += 3; + if (j == WA) { + i++; + assignment.witness(component.W(0),start_row_index + i) = value_type(x_decomp); + assignment.witness(component.W(1),start_row_index + i) = value_type(pm1_decomp); + // W(2) = sgn(pm1_decomp - x_decomp) + assignment.witness(component.W(j+2),start_row_index + i) = + value_type((x_decomp < pm1_decomp) - (pm1_decomp < x_decomp)); + j = 3; + } + } + + return typename plonk_fri_cosets::result_type(component, start_row_index); + } + + template + void generate_copy_constraints( + const plonk_fri_cosets &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_fri_cosets::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fri_cosets::var; + + // input + bp.add_copy_constraint({instance_input.x, var(component.W(0), start_row_index, false)}); + + // omega + bp.add_copy_constraint({var(0, start_row_index, false, var::column_type::constant), + var(component.W(3), start_row_index, false)}); + + // everything that's over total_bits should be zero + std::size_t WA = component.WA; // witness_amount + std::size_t l = component.nine_bl_per_line; // number of 9-blocks per row + std::size_t last_l = component.n % l; // 9-blocks in transition row. If 0, no transition row exists + std::size_t remaining_bits = component.bits_blocks_count - ((last_l > 0)? 3*(l - last_l)-1 : 0); + + if (remaining_bits % (WA/3 - 1) > 0) { // Are there extra bits in the last row? + for(std::size_t j = 3*(remaining_bits % (WA/3 - 1) + 1); j < WA; j++) { + bp.add_copy_constraint({var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(j), start_row_index + component.rows_amount - 2, false)}); + } + } + + // final row first 3-block is all zeros + bp.add_copy_constraint({var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(0), start_row_index + component.rows_amount - 1, false)}); + bp.add_copy_constraint({var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(1), start_row_index + component.rows_amount - 1, false)}); + bp.add_copy_constraint({var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(2), start_row_index + component.rows_amount - 1, false)}); + } + + template + typename plonk_fri_cosets::result_type + generate_circuit( + const plonk_fri_cosets &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_fri_cosets::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fri_cosets::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + const std::size_t WA = component.WA; + + const std::size_t l = component.nine_bl_per_line; + const std::size_t nineb_rows = lfit(component.n,l); // number of rows with 9-blocks + const std::size_t last_l = component.n % l; // 9-blocks in transition row. If 0, no transition row exists + const std::size_t bits = component.bits_blocks_count; + + std::size_t selector_index; + + std::vector nine_block; + std::vector bit_line; + constraint_type first_add_W1 = var(component.W(1),0) + 1; + constraint_type first_add_W2 = var(component.W(2),0) * (1-var(component.W(2),0)); + constraint_type first_W4 = var(component.W(3),0)*var(component.W(7),0) + 1-var(component.W(7),0) - var(component.W(4),0); + + // Store typical constraints for every column + nine_block.resize(WA); + for(std::size_t j = 0; j < l; j++) { + var W0 = var(component.W(9*j),0), // x/2^j + W1 = var(component.W(9*j + 1),0), // (p-1)/2^j + W2 = var(component.W(9*j + 2),0), // sgn( (p-1-x)/2^j ) + W3 = var(component.W(9*j + 3),0), // omega^{2^j} + W4 = var(component.W(9*j + 4),0), // omega^{b_j...b_0} + W5 = var(component.W(9*j + 5),0), // (omega^{2^{n-1-j}})^x + W6 = var(component.W(9*j + 6),0), // -(omega^{2^{n-1-j}})^x + W7 = var(component.W(9*j + 7),0), // b_j = the j-th bit of x binary decomposition + W8 = var(component.W(9*j + 8),0), // c_j = the j-th bit of (p-1) binary decomposition + W0next = var(component.W(9*((j+1) % l)),(j+1)/l), + W1next = var(component.W(9*((j+1) % l) + 1),(j+1)/l), + W2next = var(component.W(9*((j+1) % l) + 2),(j+1)/l), + W3prev = var(component.W(9*((l+j-1) % l) + 3), -(j == 0)), + W4prev = var(component.W(9*((l+j-1) % l) + 4), -(j == 0)), + W5next = var(component.W(9*((j+1) % l) + 5),(j+1)/l); + + nine_block[9*j] = W0 - 2*W0next - W7; + nine_block[9*j+1] = W1 - 2*W1next - W8; + nine_block[9*j+2] = (W8 - W7)*(1 - W2next)*(1 + W2next) + W2next - W2; + nine_block[9*j+3] = W3 - W3prev * W3prev; + nine_block[9*j+4] = W4 - W4prev * (W3*W7 + 1-W7); + nine_block[9*j+5] = W5 - W5next * W5next; + nine_block[9*j+6] = W5 + W6; + nine_block[9*j+7] = (1 - W7) * W7; + nine_block[9*j+8] = (1 - W8) * W8; + } + + bit_line.resize(WA); + bit_line[0] = var(component.W(0),1); + bit_line[1] = var(component.W(1),1); + bit_line[2] = var(component.W(2),0) - var(component.W(5),0); // the first sign in the bit_line is just a copy of the 2nd + for(std::size_t j = WA-3; j > 0; j -= 3) { + var Wj = var(component.W(j),0), + Wj1 = var(component.W(j+1),0), + Wj2 = var(component.W(j+2),0), + Wj2next = var(component.W((j+5) % WA), (j+3 == WA)); // the sign in the next 3-block may be in the next line + bit_line[0] *= 2; + bit_line[0] += Wj; + bit_line[1] *= 2; + bit_line[1] += Wj1; + bit_line[j] = Wj * (Wj - 1); + bit_line[j+1] = Wj1 * (Wj1 - 1); + bit_line[j+2] = (Wj1 - Wj)*(1 - Wj2next)*(1 + Wj2next) + Wj2next - Wj2; + } + bit_line[0] -= var(component.W(0),0); + bit_line[1] -= var(component.W(1),0); + + std::vector cs1; + if (nineb_rows > 1) { // there is a starting row which is not final (gate type 1) + cs1 = {nine_block[0],nine_block[1],nine_block[2]}; + cs1.push_back(first_add_W1); + cs1.push_back(first_add_W2); + cs1.push_back(first_W4); + cs1.insert(cs1.end(),std::next(nine_block.begin(),5),nine_block.end()); + selector_index = bp.add_gate(cs1); // type 1 gate + // Applying gate type 1 to line 0 + assignment.enable_selector(selector_index, start_row_index); + } + + if (nineb_rows > 2) { // there is a middle row (gate type 2) + selector_index = bp.add_gate(nine_block); // type 2 gate + // Applying gate type 2 to lines 1--(nineb_rows - 2) + for(std::size_t i = 1; i < nineb_rows - 1; i++) { + assignment.enable_selector(selector_index, start_row_index + i); + } + } + + // The gate for the line where the 9-blocks end + std::vector cs3; + std::size_t last = (last_l > 0)? last_l : l; // The number of the last 9-block in the row + cs3 = {nine_block[0],nine_block[1],nine_block[2]}; + if (nineb_rows > 1) { // if the first 9-block is a regular middle 9-block, otherwise there's no "previous" + cs3.push_back(nine_block[3]); + cs3.push_back(nine_block[4]); + } else { + cs3.push_back(first_add_W1); + cs3.push_back(first_add_W2); + cs3.push_back(first_W4); + } + cs3.insert(cs3.end(),std::next(nine_block.begin(),5),std::next(nine_block.begin(),9*(last-1)+5)); + cs3.push_back(var(component.W(9*(last - 1) + 5),0) - var(component.W(9*(last - 1) + 4),0)); + cs3.push_back(nine_block[9*(last-1)+6]); + cs3.push_back(nine_block[9*(last-1)+7]); + cs3.push_back(nine_block[9*(last-1)+8]); + + if (last_l > 0) { // there are bits on the transition line + constraint_type mid = var(component.W(0),1), + mid1 = var(component.W(1),1); + + for(std::size_t j = WA-3; j > 9*last_l; j -= 3) { + mid *= 2; + mid += var(component.W(j),0); + mid1 *= 2; + mid1 += var(component.W(j+1),0); + } + mid -= var(component.W(9*last_l),0); + mid1 -= var(component.W(9*last_l + 1),0); + cs3.push_back(mid); + cs3.push_back(mid1); + + // the sign bit should be just a copy from the next block + var Wl2 = var(component.W(9*last_l + 2),0), + Wl2next = var(component.W(9*last_l + 5),0); + cs3.push_back( Wl2 - Wl2next ); + + cs3.insert(cs3.end(), + std::next(bit_line.begin(),9*last_l + 3), + bit_line.end()); + } + selector_index = bp.add_gate(cs3); // type 3 gate + // Applying gate type 3 to line (nineb_rows - 1) + assignment.enable_selector(selector_index, start_row_index + nineb_rows - 1); + + // the number of bits not fitting on the "transition" line + std::size_t remaining_bits = bits - (last_l > 0 ? 3*(l - last_l)-1 : 0); + if (remaining_bits > 0) { + selector_index = bp.add_gate(bit_line); // type 4 gate + // Applying gate type 4 to lines nineb_rows -- (nineb_rows + lfit(remaining_bits,WA/3-1) - 2) + for(std::size_t i = 0; i < lfit(remaining_bits, WA/3 - 1); i++) { + assignment.enable_selector(selector_index, start_row_index + nineb_rows + i); + } + } + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + return typename plonk_fri_cosets::result_type(component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_fri_cosets &component, + assignment> &assignment, + const typename plonk_fri_cosets::input_type &instance_input, + const std::size_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = component.omega; + assignment.constant(component.C(0), start_row_index + 1) = 0; // a zero to make a copy-constraint with + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_COSETS_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_lin_inter.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_lin_inter.hpp new file mode 100644 index 000000000..205971968 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/fri_lin_inter.hpp @@ -0,0 +1,215 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for FRI verification linear interpolation component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_LIN_INTER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_LIN_INTER_HPP + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // linear interpolation with points (s,y0), (-s,y1) at point alpha + // Input: s, y0, y1, alpha + // Output: y = y0 + (y1 - y0)*(s - alpha)/(2s) + // DOES NOT CHECK THAT s != 0 + template + class fri_lin_inter; + + template + class fri_lin_inter, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return fri_lin_inter::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(5)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + const std::string component_name = "fri linear interpolation component"; + + struct input_type { + var s, y0, y1, alpha; + + std::vector> all_vars() { + return {s, y0, y1, alpha}; + } + }; + + struct result_type { + var output; + + result_type(const fri_lin_inter &component, std::uint32_t start_row_index) { + output = var(component.W(4), start_row_index, false, var::column_type::witness); + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + explicit fri_lin_inter(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fri_lin_inter(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fri_lin_inter( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_fri_lin_inter = + fri_lin_inter< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; + + template + typename plonk_fri_lin_inter::result_type generate_assignments( + const plonk_fri_lin_inter &component, + assignment> + &assignment, + const typename plonk_fri_lin_inter::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + value_type s = var_value(assignment, instance_input.s), + y0 = var_value(assignment, instance_input.y0), + y1 = var_value(assignment, instance_input.y1), + alpha = var_value(assignment, instance_input.alpha); + + assignment.witness(component.W(0), start_row_index) = y0; + assignment.witness(component.W(1), start_row_index) = y1; + assignment.witness(component.W(2), start_row_index) = s; + assignment.witness(component.W(3), start_row_index) = alpha; + assignment.witness(component.W(4), start_row_index) = + y0 + (y1 - y0) * (s - alpha) * (value_type(2) * s).inversed(); + + return typename plonk_fri_lin_inter::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fri_lin_inter &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_lin_inter::input_type + &instance_input) { + + using var = typename plonk_fri_lin_inter::var; + + auto interpolation_constraint = + 2 * var(component.W(2), 0, true) * (var(component.W(4), 0, true) - var(component.W(0), 0, true)) - + (var(component.W(1), 0, true) - var(component.W(0), 0, true)) * + (var(component.W(2), 0, true) - var(component.W(3), 0, true)); + + return bp.add_gate({interpolation_constraint}); + } + + template + void generate_copy_constraints( + const plonk_fri_lin_inter &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_lin_inter::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fri_lin_inter::var; + + bp.add_copy_constraint({var(component.W(0), start_row_index, false), instance_input.y0}); + bp.add_copy_constraint({var(component.W(1), start_row_index, false), instance_input.y1}); + bp.add_copy_constraint({var(component.W(2), start_row_index, false), instance_input.s}); + bp.add_copy_constraint({var(component.W(3), start_row_index, false), instance_input.alpha}); + } + + template + typename plonk_fri_lin_inter::result_type generate_circuit( + const plonk_fri_lin_inter &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fri_lin_inter::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, start_row_index); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_fri_lin_inter::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FRI_LIN_INTER_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/gate_argument_verifier.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/gate_argument_verifier.hpp new file mode 100644 index 000000000..77d2f1618 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/gate_argument_verifier.hpp @@ -0,0 +1,435 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the GATE_ARGUMENT_VERIFIER component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_ARGUMENT_VERIFIER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_ARGUMENT_VERIFIER_HPP + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class basic_constraints_verifier; + + template + class basic_constraints_verifier< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 1; + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount, + const std::vector &gate_sizes) { + + std::size_t r = 0; + + for (std::size_t i = 0; i < gate_sizes.size(); i++) { + if (gate_sizes[i] == 1) { + r += mul::get_rows_amount(witness_amount); + } else { + r += gate_component::get_rows_amount(witness_amount, gate_sizes[i] - 1); + } + } + + if (gate_sizes.size() > 1) { + std::size_t total_deg = std::accumulate(gate_sizes.begin(), gate_sizes.end() - 1, 0); + r += gate_component::get_rows_amount(witness_amount, total_deg); + } + + return r; + } + + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + using gate_component = detail::gate_component; + using mul = multiplication>; + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::vector &gate_sizes) { + return rows_amount_internal(witness_amount, gate_sizes); + } + + const std::vector gate_sizes; + const std::size_t rows_amount = rows_amount_internal(this->witness_amount(), gate_sizes); + constexpr static const std::size_t gates_amount = 0; + const std::string component_name = "gate argument verifier component"; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 0; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::vector &gate_sizes) { + + std::vector::iterator min_degree = + std::min_element(gate_sizes.begin(), gate_sizes.end()); + std::vector::iterator max_degree = + std::max_element(gate_sizes.begin(), gate_sizes.end()); + + gate_manifest manifest = gate_manifest(gate_manifest_type()); + if (*min_degree == 1 && *max_degree > *min_degree) { + manifest = manifest.merge_with(mul::get_gate_manifest(witness_amount)) + .merge_with(gate_component::get_gate_manifest( + witness_amount, *max_degree - 1)); + + } else if (*min_degree == 1 && *min_degree == *max_degree) { + manifest = manifest.merge_with(mul::get_gate_manifest(witness_amount)); + + } else { + manifest = manifest.merge_with( + gate_component::get_gate_manifest(witness_amount, *min_degree - 1)); + } + + if (gate_sizes.size() > 1 && *max_degree == 1) { + std::size_t total_deg = std::accumulate(gate_sizes.begin(), gate_sizes.end() - 1, 0); + manifest = manifest.merge_with( + gate_component::get_gate_manifest(witness_amount, total_deg)); + } + + return manifest; + } + + static manifest_type get_manifest(std::vector &gate_sizes_) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_range_param(3, 15)), false) + .merge_with(gate_component::get_manifest(0)); // independent of degree + return manifest; + } + + struct input_type { + var theta; + std::vector constraints; + std::vector selectors; + + std::vector> all_vars() { + std::vector> vars; + vars.push_back(theta); + vars.insert(vars.end(), constraints.begin(), constraints.end()); + vars.insert(vars.end(), selectors.begin(), selectors.end()); + return vars; + } + }; + + struct result_type { + var output; + + result_type(const basic_constraints_verifier &component, std::uint32_t start_row_index) { + if (component.gate_sizes.size() == 1 && component.gate_sizes[0] == 1) { + output = var(component.W(2), start_row_index + component.rows_amount - 1, false); + } else { + output = var(component.W(component.witness_amount() - 1), + start_row_index + component.rows_amount - 1, false); + } + } + + std::vector> all_vars() { + return {output}; + } + }; + + template + basic_constraints_verifier(ContainerType witness, std::vector &gate_sizes_) : + component_type(witness, {}, {}, get_manifest(gate_sizes_)), gate_sizes(gate_sizes_) {}; + + template + basic_constraints_verifier(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::vector &gate_sizes_) : + component_type(witness, constant, public_input, get_manifest(gate_sizes_)), + gate_sizes(gate_sizes_) {}; + + basic_constraints_verifier( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::vector &gate_sizes_) : + component_type(witnesses, constants, public_inputs, get_manifest(gate_sizes_)), + gate_sizes(gate_sizes_) {}; + }; + + template + using plonk_basic_constraints_verifier = basic_constraints_verifier< + crypto3::zk::snark::plonk_constraint_system>; + + template + void generate_assignments_constant( + const plonk_basic_constraints_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_basic_constraints_verifier::input_type + &instance_input, + const std::size_t start_row_index) { + // std::vector::iterator max_size = + // std::max_element(component.gate_sizes.begin(), component.gate_sizes.end()); + // if (*max_size > 1) { + assignment.constant(component.C(0), start_row_index) = BlueprintFieldType::value_type::zero(); + assignment.constant(component.C(0), start_row_index + 1) = BlueprintFieldType::value_type::one(); + // }else{ + // assignment.constant(component.C(0), start_row_index + 1) = BlueprintFieldType::value_type::one(); + // } + } + + template + typename plonk_basic_constraints_verifier::result_type + generate_assignments( + const plonk_basic_constraints_verifier &component, + assignment> + &assignment, + const typename plonk_basic_constraints_verifier::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t n_sl = component.gate_sizes.size(); + std::size_t witness_amount = component.witness_amount(); + using var = typename plonk_basic_constraints_verifier::var; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + using gate_component = detail::gate_component; + using mul = multiplication>; + + std::size_t row = start_row_index; + std::vector G; + std::vector witnesses; + for (std::uint32_t i = 0; i < witness_amount; i++) { + witnesses.push_back(component.W(i)); + } + std::size_t start = 0; + for (std::size_t i = 0; i < n_sl; i++) { + + std::size_t c_size = component.gate_sizes[i]; + if (c_size == 1) { + mul mul_instance = + mul(witnesses, std::array(), std::array()); + + typename mul::input_type mul_input = {instance_input.constraints[start], instance_input.selectors[i]}; + typename mul::result_type mul_result = + generate_assignments(mul_instance, assignment, mul_input, row); + G.push_back(mul_result.output); + row += mul_instance.rows_amount; + } else { + gate_component gate_instance = gate_component(witnesses, std::array(), + std::array(), c_size - 1); + + std::vector constraints; + constraints.insert(constraints.begin(), instance_input.constraints.begin() + start, + instance_input.constraints.begin() + start + component.gate_sizes[i]); + typename gate_component::input_type gate_input = {instance_input.theta, constraints, + instance_input.selectors[i]}; + + typename gate_component::result_type gate_i_result = + generate_assignments(gate_instance, assignment, gate_input, row); + + G.push_back(gate_i_result.output); + row += gate_instance.rows_amount; + } + start += component.gate_sizes[i]; + } + + if (n_sl > 1) { + std::size_t total_deg = + std::accumulate(component.gate_sizes.begin(), component.gate_sizes.end() - 1, 0); + + gate_component final_gate = gate_component(witnesses, std::array(), + std::array(), total_deg); + + std::vector constraints; + std::size_t j = 0, sum = 0; + for (std::size_t i = 0; i <= total_deg; i++) { + if (i == sum) { + constraints.push_back(G[j]); + sum += component.gate_sizes[j]; + j++; + } else { + constraints.push_back( + var(component.C(0), start_row_index, false, var::column_type::constant)); + } + } + var q = var(component.C(0), start_row_index + 1, false, var::column_type::constant); + + typename gate_component::input_type gate_input = {instance_input.theta, constraints, q}; + + // gate_i_result + generate_assignments(final_gate, assignment, gate_input, row); + + row += final_gate.rows_amount; + } + + return + typename plonk_basic_constraints_verifier::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_basic_constraints_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_basic_constraints_verifier::input_type + instance_input) { + + return {}; + } + + template + void generate_copy_constraints( + const plonk_basic_constraints_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_basic_constraints_verifier::input_type + instance_input, + const std::size_t start_row_index) { + } + + template + typename plonk_basic_constraints_verifier::result_type + generate_circuit( + const plonk_basic_constraints_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_basic_constraints_verifier::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_basic_constraints_verifier::var; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + using gate_component = detail::gate_component; + using mul = multiplication>; + + std::size_t row = start_row_index; + std::size_t n_sl = component.gate_sizes.size(); + std::size_t witness_amount = component.witness_amount(); + + std::vector witnesses; + for (std::uint32_t i = 0; i < witness_amount; i++) { + witnesses.push_back(component.W(i)); + } + + std::size_t start = 0; + std::vector G; + for (std::size_t i = 0; i < n_sl; i++) { + if (component.gate_sizes[i] == 1) { + mul mul_instance = + mul(witnesses, std::array(), std::array()); + typename mul::input_type mul_input = {instance_input.constraints[start], instance_input.selectors[i]}; + typename mul::result_type mul_result = + generate_circuit(mul_instance, bp, assignment, mul_input, row); + + G.push_back(mul_result.output); + row += mul_instance.rows_amount; + } else { + gate_component gate_instance = + gate_component(witnesses, std::array(), std::array(), + component.gate_sizes[i] - 1); + std::vector constraints; + constraints.insert(constraints.begin(), instance_input.constraints.begin() + start, + instance_input.constraints.begin() + start + component.gate_sizes[i]); + typename gate_component::input_type gate_input = {instance_input.theta, constraints, + instance_input.selectors[i]}; + + typename gate_component::result_type gate_i_result = + generate_circuit(gate_instance, bp, assignment, gate_input, row); + G.push_back(gate_i_result.output); + row += gate_instance.rows_amount; + } + start += component.gate_sizes[i]; + } + + if (n_sl > 1) { + + generate_assignments_constant(component, bp, assignment, instance_input, start_row_index); + + std::size_t total_deg = + std::accumulate(component.gate_sizes.begin(), component.gate_sizes.end() - 1, 0); + + gate_component final_gate = gate_component(witnesses, std::array(), + std::array(), total_deg); + + std::vector constraints; + std::size_t j = 0, sum = 0; + for (std::size_t i = 0; i <= total_deg; i++) { + if (i == sum) { + constraints.push_back(G[j]); + sum += component.gate_sizes[j]; + j++; + } else { + constraints.push_back( + var(component.C(0), start_row_index, false, var::column_type::constant)); + } + } + var q = var(component.C(0), start_row_index + 1, false, var::column_type::constant); + + typename gate_component::input_type gate_input = {instance_input.theta, constraints, q}; + + // gate_i_result + generate_circuit(final_gate, bp, assignment, gate_input, row); + + row += final_gate.rows_amount; + } + + std::vector selectors = generate_gates(component, bp, assignment, instance_input); + + assert(selectors.empty()); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return + typename plonk_basic_constraints_verifier::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_GATE_ARGUMENT_VERIFIER_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/lookup_argument_verifier.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/lookup_argument_verifier.hpp new file mode 100644 index 000000000..39c06d8e7 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/lookup_argument_verifier.hpp @@ -0,0 +1,942 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the LOOKUP_ARGUMENT_VERIFIER component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LOOKUP_ARGUMENT_VERIFIER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LOOKUP_ARGUMENT_VERIFIER_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class lookup_verifier; + + template + class lookup_verifier< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 1; + + constexpr static const std::size_t + rows_amount_internal(std::size_t witness_amount, const std::size_t lookup_gates_size, + const std::vector &lookup_gate_constraints_sizes, + const std::vector &lookup_gate_constraints_lookup_input_sizes, + const std::size_t lookup_tables_size, + const std::vector &lookup_table_lookup_options_sizes, + const std::vector &lookup_table_columns_numbers) { + + std::size_t row = 2; + + std::size_t lu_value_size = 0; + for (std::size_t i = 0; i < lookup_tables_size; i++) { + for (std::size_t j = 0; j < lookup_table_lookup_options_sizes[i]; j++) { + row += + 2 * gate_component::get_rows_amount(witness_amount, lookup_table_columns_numbers[i]); + row += 2 * mul::get_rows_amount(witness_amount); + lu_value_size++; + } + } + + std::size_t lu_input_size = 0; + for (std::size_t g_id = 0; g_id < lookup_gates_size; g_id++) { + for (std::size_t c_id = 0; c_id < lookup_gate_constraints_sizes[g_id]; c_id++) { + std::size_t lookup_input_size = lookup_gate_constraints_lookup_input_sizes[lu_input_size++]; + row += gate_component::get_rows_amount(witness_amount, lookup_input_size); + } + } + + row += f1_loop::get_rows_amount(witness_amount, lu_input_size); + row += f1_loop::get_rows_amount(witness_amount, lu_value_size); + row += f1_loop::get_rows_amount(witness_amount, lu_input_size + lu_value_size); + row += 2 * mul::get_rows_amount(witness_amount); + row += f3_loop::get_rows_amount(witness_amount, lu_input_size + lu_value_size - 1); + row += 4 / witness_amount + 1; + row += 5 / witness_amount + 1; + return row; + } + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + using gate_component = detail::gate_component; + using f1_loop = detail::f1_loop; + using f3_loop = detail::f3_loop; + using mul = multiplication>; + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount, std::size_t lookup_gates_size, + std::vector &lookup_gate_constraints_sizes, + std::vector &lookup_gate_constraints_lookup_input_sizes, + std::size_t lookup_tables_size, std::vector &lookup_table_lookup_options_sizes, + std::vector &lookup_table_columns_numbers) { + + return rows_amount_internal(witness_amount, lookup_gates_size, lookup_gate_constraints_sizes, + lookup_gate_constraints_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_numbers); + } + + const std::size_t lookup_gates_size; + const std::size_t lookup_tables_size; + const std::vector lookup_table_lookup_options_sizes; + const std::vector lookup_table_columns_numbers; + const std::vector lookup_gate_constraints_sizes; + const std::vector lookup_gate_constraints_lookup_input_sizes; + const std::size_t rows_amount = + rows_amount_internal(this->witness_amount(), lookup_gates_size, lookup_gate_constraints_sizes, + lookup_gate_constraints_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_numbers); + + constexpr static std::size_t gates_amount = 3; + const std::string component_name = "lookup argument verifier component"; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return 3; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, std::size_t lookup_gates_size, + std::vector &lookup_gate_constraints_sizes, + std::vector &lookup_gate_constraints_lookup_input_sizes, + std::size_t lookup_tables_size, std::vector &lookup_table_lookup_options_sizes, + std::vector &lookup_table_columns_numbers) { + + gate_manifest manifest = + gate_manifest(gate_manifest_type()) + .merge_with(mul::get_gate_manifest(witness_amount)) + // depends only on witness amount, so we skip proper calculation + .merge_with(f1_loop::get_gate_manifest(witness_amount, 1)) + // depends only on witness amount, so we skip proper calculation + .merge_with(f3_loop::get_gate_manifest(witness_amount, 1)) + // incorrect, but the manifest doesn't depend on the degree + // because all gates are instantiated anyways + .merge_with(gate_component::get_gate_manifest(witness_amount, 1)); + + return manifest; + } + + static manifest_type get_manifest( + std::size_t lookup_gates_size, + std::vector &lookup_gate_constraints_sizes, + std::vector &lookup_gate_constraints_lookup_input_sizes, + std::size_t lookup_tables_size, + std::vector &lookup_table_lookup_options_sizes, + std::vector &lookup_table_columns_numbers + ) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_range_param(4, 15)), false) + .merge_with(mul::get_manifest()) + // does not depend on actual value, so we cheat here + .merge_with(f1_loop::get_manifest(1)) + // does not depend on actual value, so we cheat here + .merge_with(f3_loop::get_manifest(1)) + // does not depend on actual value, so we cheat here + .merge_with(gate_component::get_manifest(1)); + return manifest; + } + + struct input_type { + var theta; + var beta; + var gamma; + std::vector alphas; + std::array V_L_values; + std::array q_last; + std::array q_blind; + var L0; + + std::vector lookup_gate_selectors; + std::vector lookup_gate_constraints_table_ids; + std::vector lookup_gate_constraints_lookup_inputs; + + std::vector lookup_table_selectors; + std::vector lookup_table_lookup_options; + + std::vector shifted_lookup_table_selectors; + std::vector shifted_lookup_table_lookup_options; + + std::vector sorted; + + std::vector> all_vars() { + + std::vector> vars; + vars.push_back(theta); + vars.push_back(beta); + vars.push_back(gamma); + vars.insert(vars.end(), alphas.begin(), alphas.end()); + vars.insert(vars.end(), V_L_values.begin(), V_L_values.end()); + vars.insert(vars.end(), q_last.begin(), q_last.end()); + vars.insert(vars.end(), q_blind.begin(), q_blind.end()); + vars.push_back(L0); + vars.insert(vars.end(), lookup_gate_selectors.begin(), lookup_gate_selectors.end()); + vars.insert(vars.end(), lookup_gate_constraints_table_ids.begin(), + lookup_gate_constraints_table_ids.end()); + vars.insert(vars.end(), lookup_gate_constraints_lookup_inputs.begin(), + lookup_gate_constraints_lookup_inputs.end()); + vars.insert(vars.end(), lookup_table_selectors.begin(), lookup_table_selectors.end()); + vars.insert(vars.end(), lookup_table_lookup_options.begin(), lookup_table_lookup_options.end()); + vars.insert(vars.end(), shifted_lookup_table_selectors.begin(), + shifted_lookup_table_selectors.end()); + vars.insert(vars.end(), shifted_lookup_table_lookup_options.begin(), + shifted_lookup_table_lookup_options.end()); + vars.insert(vars.end(), sorted.begin(), sorted.end()); + + return vars; + } + }; + + struct result_type { + std::array output; + + result_type(const lookup_verifier &component, std::uint32_t start_row_index) { + std::size_t w = component.witness_amount(); + std::size_t offset = 4 / w + 5 / w + 2; + output = { + var(component.W(2 % w), start_row_index + component.rows_amount - offset + 2 / w, false), + var(component.W(4 % w), start_row_index + component.rows_amount - offset + 4 / w, false), + var(component.W(5 % w), start_row_index + component.rows_amount - 1, false), + var(component.W(2), start_row_index + component.rows_amount - offset - 1, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2], output[3]}; + } + }; + + template + lookup_verifier(ContainerType witness, std::size_t lookup_gates_size_, + std::vector &lookup_gate_constraints_sizes_, + std::vector &lookup_gate_constraints_lookup_input_sizes_, + std::size_t lookup_tables_size_, + std::vector &lookup_table_lookup_options_sizes_, + std::vector &lookup_table_columns_numbers_) : + component_type(witness, {}, {}, + get_manifest(lookup_gates_size_, lookup_gate_constraints_sizes_, + lookup_gate_constraints_lookup_input_sizes_, lookup_tables_size_, + lookup_table_lookup_options_sizes_, lookup_table_columns_numbers_)), + lookup_gates_size(lookup_gates_size_), + lookup_tables_size(lookup_tables_size_), + lookup_table_lookup_options_sizes(lookup_table_lookup_options_sizes_), + lookup_table_columns_numbers(lookup_table_columns_numbers_), + lookup_gate_constraints_sizes(lookup_gate_constraints_sizes_), + lookup_gate_constraints_lookup_input_sizes(lookup_gate_constraints_lookup_input_sizes_) {} + + template + lookup_verifier(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t lookup_gates_size_, + std::vector &lookup_gate_constraints_sizes_, + std::vector &lookup_gate_constraints_lookup_input_sizes_, + std::size_t lookup_tables_size_, + std::vector &lookup_table_lookup_options_sizes_, + std::vector &lookup_table_columns_numbers_) : + component_type(witness, constant, public_input, + get_manifest(lookup_gates_size_, lookup_gate_constraints_sizes_, + lookup_gate_constraints_lookup_input_sizes_, lookup_tables_size_, + lookup_table_lookup_options_sizes_, lookup_table_columns_numbers_)), + lookup_gates_size(lookup_gates_size_), + lookup_tables_size(lookup_tables_size_), + lookup_table_lookup_options_sizes(lookup_table_lookup_options_sizes_), + lookup_table_columns_numbers(lookup_table_columns_numbers_), + lookup_gate_constraints_sizes(lookup_gate_constraints_sizes_), + lookup_gate_constraints_lookup_input_sizes(lookup_gate_constraints_lookup_input_sizes_) {} + + lookup_verifier( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list + public_inputs, + std::size_t lookup_gates_size_, std::vector &lookup_gate_constraints_sizes_, + std::vector &lookup_gate_constraints_lookup_input_sizes_, + std::size_t lookup_tables_size_, std::vector &lookup_table_lookup_options_sizes_, + std::vector &lookup_table_columns_numbers_) : + component_type(witnesses, constants, public_inputs, + get_manifest(lookup_gates_size_, lookup_gate_constraints_sizes_, + lookup_gate_constraints_lookup_input_sizes_, lookup_tables_size_, + lookup_table_lookup_options_sizes_, lookup_table_columns_numbers_)), + lookup_gates_size(lookup_gates_size_), + lookup_tables_size(lookup_tables_size_), + lookup_table_lookup_options_sizes(lookup_table_lookup_options_sizes_), + lookup_table_columns_numbers(lookup_table_columns_numbers_), + lookup_gate_constraints_sizes(lookup_gate_constraints_sizes_), + lookup_gate_constraints_lookup_input_sizes(lookup_gate_constraints_lookup_input_sizes_) {} + }; + + template + using plonk_lookup_verifier = + lookup_verifier>; + + template + void generate_assignments_constant( + const plonk_lookup_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_lookup_verifier::input_type + &instance_input, + const std::size_t start_row_index) { + + typename BlueprintFieldType::value_type one = BlueprintFieldType::value_type::one(); + for (std::size_t i = 0; i < instance_input.lookup_table_selectors.size(); i++) { + assignment.constant(component.C(0), start_row_index + i) = one + i; + } + } + + template + typename plonk_lookup_verifier::result_type generate_assignments( + const plonk_lookup_verifier &component, + assignment> + &assignment, + const typename plonk_lookup_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + using var = typename plonk_lookup_verifier::var; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using f1_loop = detail::f1_loop; + using f3_loop = detail::f3_loop; + using gate_component = detail::gate_component; + using mul = multiplication>; + + std::vector witnesses; + for (std::uint32_t i = 0; i < witness_amount; i++) { + witnesses.push_back(component.W(i)); + } + + typename BlueprintFieldType::value_type one = BlueprintFieldType::value_type::one(); + typename BlueprintFieldType::value_type q_last = var_value(assignment, instance_input.q_last[0]); + typename BlueprintFieldType::value_type q_last_shifted = + var_value(assignment, instance_input.q_last[1]); + typename BlueprintFieldType::value_type q_blind = var_value(assignment, instance_input.q_blind[0]); + typename BlueprintFieldType::value_type q_blind_shifted = + var_value(assignment, instance_input.q_blind[1]); + typename BlueprintFieldType::value_type L0 = var_value(assignment, instance_input.L0); + typename BlueprintFieldType::value_type V_L = var_value(assignment, instance_input.V_L_values[0]); + typename BlueprintFieldType::value_type V_L_shifted = + var_value(assignment, instance_input.V_L_values[1]); + + typename BlueprintFieldType::value_type F0 = (one - V_L) * L0; + typename BlueprintFieldType::value_type F1 = q_last * (V_L * V_L - V_L); + + std::vector assignments; + + typename BlueprintFieldType::value_type mask_value = (one - (q_last + q_blind)); + typename BlueprintFieldType::value_type shifted_mask_value = (one - (q_last_shifted + q_blind_shifted)); + + assignment.witness(component.W(0), row) = q_last; + assignment.witness(component.W(1), row) = q_blind; + assignment.witness(component.W(2), row) = mask_value; + assignment.witness(component.W(0), row + 1) = q_last_shifted; + assignment.witness(component.W(1), row + 1) = q_blind_shifted; + assignment.witness(component.W(2), row + 1) = shifted_mask_value; + + var var_mask = var(component.W(2), row, false); + var var_shifted_mask = var(component.W(2), row + 1, false); + + row += 2; + + std::vector lookup_values; + std::vector shifted_lookup_values; + + std::size_t start_pos = 0, offset = 0; + std::size_t num_tables = instance_input.lookup_table_selectors.size(); + assert(num_tables == component.lookup_tables_size); + for (std::size_t i = 0; i < num_tables; i++) { + var selector = instance_input.lookup_table_selectors[i]; + var shifted_selector = instance_input.shifted_lookup_table_selectors[i]; + var t_id_inc = var(component.C(0), start_row_index + i, false, var::column_type::constant); + + for (std::size_t j = 0; j < component.lookup_table_lookup_options_sizes[i]; j++) { + offset = component.lookup_table_columns_numbers[i]; + std::vector gate_constraints; + gate_constraints.push_back(t_id_inc); + gate_constraints.insert(gate_constraints.end(), + instance_input.lookup_table_lookup_options.begin() + start_pos, + instance_input.lookup_table_lookup_options.begin() + start_pos + + offset); + + gate_component gate_instance = gate_component(witnesses, std::array(), + std::array(), offset); + + typename gate_component::input_type gate_input = {instance_input.theta, gate_constraints, + selector}; + + typename gate_component::result_type gate_i_result = + generate_assignments(gate_instance, assignment, gate_input, row); + + row += gate_instance.rows_amount; + + mul mul_instance = + mul(witnesses, std::array(), std::array()); + + typename mul::input_type mul_input = {gate_i_result.output, var_mask}; + typename mul::result_type mul_result = + generate_assignments(mul_instance, assignment, mul_input, row); + row += mul_instance.rows_amount; + lookup_values.push_back(mul_result.output); + + gate_constraints.clear(); + gate_constraints.push_back(t_id_inc); + gate_constraints.insert(gate_constraints.end(), + instance_input.shifted_lookup_table_lookup_options.begin() + start_pos, + instance_input.shifted_lookup_table_lookup_options.begin() + start_pos + + offset); + + // gate_instance = gate_component(witnesses, std::array(), + // std::array(), offset); + gate_input = {instance_input.theta, gate_constraints, shifted_selector}; + gate_i_result = generate_assignments(gate_instance, assignment, gate_input, row); + row += gate_instance.rows_amount; + + mul_input = {gate_i_result.output, var_shifted_mask}; + mul_result = generate_assignments(mul_instance, assignment, mul_input, row); + row += mul_instance.rows_amount; + shifted_lookup_values.push_back(mul_result.output); + + start_pos += offset; + } + } + assert(lookup_values.size() == shifted_lookup_values.size()); + + std::vector lookup_input; + std::size_t start = 0, lookup_input_size = 0, ctr = 0; + std::size_t num_gates = instance_input.lookup_gate_selectors.size(); + assert(num_gates == component.lookup_gates_size); + for (std::size_t g_id = 0; g_id < num_gates; g_id++) { + + var selector = instance_input.lookup_gate_selectors[g_id]; + for (std::size_t c_id = 0; c_id < component.lookup_gate_constraints_sizes[g_id]; c_id++) { + + lookup_input_size = component.lookup_gate_constraints_lookup_input_sizes[ctr]; + std::vector gate_constraints; + gate_constraints.push_back(instance_input.lookup_gate_constraints_table_ids[ctr++]); + gate_constraints.insert(gate_constraints.begin() + 1, + instance_input.lookup_gate_constraints_lookup_inputs.begin() + start, + instance_input.lookup_gate_constraints_lookup_inputs.begin() + start + + lookup_input_size); + + gate_component gate_instance = + gate_component(witnesses, std::array(), std::array(), + lookup_input_size); + typename gate_component::input_type gate_input = {instance_input.theta, gate_constraints, + selector}; + + typename gate_component::result_type gate_i_result = + generate_assignments(gate_instance, assignment, gate_input, row); + + lookup_input.push_back(gate_i_result.output); + row += gate_instance.rows_amount; + start += lookup_input_size; + } + } + + std::vector s0, s1, s2; + std::size_t k = (instance_input.sorted.size() + 1) / 3; + for (std::size_t i = 0; i < k; i++) { + s0.push_back(instance_input.sorted[i]); + s1.push_back(instance_input.sorted[k + i]); + if (i >= 1) { + s2.push_back(instance_input.sorted[2 * k + i - 1]); + } + } + + assert(s0.size() == s1.size()); + + f1_loop h_loop = + f1_loop(witnesses, std::array(), std::array(), s0.size()); + typename f1_loop::input_type h_loop_input = {instance_input.beta, instance_input.gamma, s0, s1}; + + typename f1_loop::result_type h_loop_result = + generate_assignments(h_loop, assignment, h_loop_input, row); + + typename BlueprintFieldType::value_type h = var_value(assignment, h_loop_result.output); + row += h_loop.rows_amount; + + f1_loop g_loop_1 = f1_loop(witnesses, std::array(), std::array(), + lookup_input.size()); + + typename f1_loop::input_type g_loop_input = {instance_input.beta, instance_input.gamma, lookup_input, + lookup_input}; + + typename f1_loop::result_type g_loop_result = + generate_assignments(g_loop_1, assignment, g_loop_input, row); + + typename BlueprintFieldType::value_type g1 = var_value(assignment, g_loop_result.output); + row += g_loop_1.rows_amount; + + f1_loop g_loop_2 = f1_loop(witnesses, std::array(), std::array(), + lookup_values.size()); + + typename f1_loop::input_type g_loop_input_2 = {instance_input.beta, instance_input.gamma, lookup_values, + shifted_lookup_values}; + + typename f1_loop::result_type g_loop_result_2 = + generate_assignments(g_loop_2, assignment, g_loop_input_2, row); + + typename BlueprintFieldType::value_type g2 = var_value(assignment, g_loop_result_2.output); + row += g_loop_2.rows_amount; + + typename BlueprintFieldType::value_type g = g1 * g2; + + mul mul_instance = mul(witnesses, std::array(), std::array()); + typename mul::input_type mul_input = {g_loop_result.output, g_loop_result_2.output}; + typename mul::result_type mul_result = generate_assignments(mul_instance, assignment, mul_input, row); + row += mul_instance.rows_amount; + + typename BlueprintFieldType::value_type F2 = mask_value * (V_L_shifted * h - V_L * g); + + s0.erase(s0.begin()); + assert(s0.size() == s2.size()); + + f3_loop F3_loop = + f3_loop(witnesses, std::array(), std::array(), s0.size()); + typename f3_loop::input_type F3_loop_input = {instance_input.alphas, s0, s2}; + + typename f3_loop::result_type F3_loop_result = + generate_assignments(F3_loop, assignment, F3_loop_input, row); + + typename BlueprintFieldType::value_type F3 = var_value(assignment, F3_loop_result.output); + row += F3_loop.rows_amount; + + mul_input = {F3_loop_result.output, instance_input.L0}; + mul_result = generate_assignments(mul_instance, assignment, mul_input, row); + row += mul_instance.rows_amount; + + F3 = F3 * L0; + assert(F3 == var_value(assignment, mul_result.output)); + + assignments.clear(); + assignments.push_back(V_L); + assignments.push_back(L0); + assignments.push_back(F0); + assignments.push_back(q_last); + assignments.push_back(F1); + + std::size_t r = 0, i = 0, j = 0; + for (i = 0; i < assignments.size(); i++) { + r = i / witness_amount; + j = i % witness_amount; + assignment.witness(component.W(j), row + r) = assignments[i]; + } + row += r + 1; + + assignments.clear(); + assignments.push_back(V_L); + assignments.push_back(mask_value); + assignments.push_back(h); + assignments.push_back(V_L_shifted); + assignments.push_back(g); + assignments.push_back(F2); + + for (i = 0; i < assignments.size(); i++) { + r = i / witness_amount; + j = i % witness_amount; + assignment.witness(component.W(j), row + r) = assignments[i]; + } + row += r; + + return typename plonk_lookup_verifier::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_lookup_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_lookup_verifier::input_type + instance_input) { + + using var = typename plonk_lookup_verifier::var; + std::size_t witness_amount = component.witness_amount(); + + std::vector selectors; + + auto constraint = var(component.W(2), 0) - (1 - (var(component.W(1), 0) + var(component.W(0), 0))); + selectors.push_back(bp.add_gate(constraint)); + + std::vector> locs; + + std::size_t r = 0, j = 0; + for (std::size_t i = 0; i < 5; i++) { + r = i / witness_amount; + j = i % witness_amount; + locs.push_back(std::make_pair(j, r)); + } + + auto _vl = var(component.W(locs[0].first), locs[0].second); + auto _l0 = var(component.W(locs[1].first), locs[1].second); + auto _f0 = var(component.W(locs[2].first), locs[2].second); + auto _q_last = var(component.W(locs[3].first), locs[3].second); + auto _f1 = var(component.W(locs[4].first), locs[4].second); + + auto constraint_3 = _f0 - (1 - _vl) * _l0; + auto constraint_4 = _f1 - _q_last * (_vl * _vl - _vl); + + selectors.push_back(bp.add_gate({constraint_3, constraint_4})); + + locs.clear(); + for (std::size_t i = 0; i < 6; i++) { + r = i / witness_amount; + j = i % witness_amount; + locs.push_back(std::make_pair(j, r)); + } + + _vl = var(component.W(locs[0].first), locs[0].second); + auto _m = var(component.W(locs[1].first), locs[1].second); + auto _h = var(component.W(locs[2].first), locs[2].second); + auto _vl2 = var(component.W(locs[3].first), locs[3].second); + auto _g = var(component.W(locs[4].first), locs[4].second); + auto _f2 = var(component.W(locs[5].first), locs[5].second); + + auto constraint_5 = _f2 - _m * (_vl2 * _h - _vl * _g); + selectors.push_back(bp.add_gate({constraint_5})); + + return selectors; + } + + template + void generate_copy_constraints( + const plonk_lookup_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_lookup_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + using var = typename plonk_lookup_verifier::var; + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using f1_loop = detail::f1_loop; + using f3_loop = detail::f3_loop; + using gate_component = detail::gate_component; + using mul = multiplication>; + + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.q_last[0]}); + bp.add_copy_constraint({var(component.W(0), row + 1, false), instance_input.q_last[1]}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.q_blind[0]}); + bp.add_copy_constraint({var(component.W(1), row + 1, false), instance_input.q_blind[1]}); + + row += 2; + + std::size_t lu_value_size = 0; + for (std::size_t i = 0; i < component.lookup_tables_size; i++) { + for (std::size_t j = 0; j < component.lookup_table_lookup_options_sizes[i]; j++) { + row += 2 * gate_component::get_rows_amount(witness_amount, + component.lookup_table_columns_numbers[i]); + row += 2 * mul::get_rows_amount(witness_amount); + lu_value_size++; + } + } + + std::size_t lu_input_size = 0; + for (std::size_t g_id = 0; g_id < component.lookup_gates_size; g_id++) { + for (std::size_t c_id = 0; c_id < component.lookup_gate_constraints_sizes[g_id]; c_id++) { + std::size_t lookup_input_size = + component.lookup_gate_constraints_lookup_input_sizes[lu_input_size]; + row += gate_component::get_rows_amount(witness_amount, lookup_input_size); + lu_input_size++; + } + } + + std::size_t m = lu_value_size + lu_input_size; + row += f1_loop::get_rows_amount(witness_amount, m); + std::size_t h_output_col = (3 * m) % (witness_amount - 1); + if (h_output_col == 0) { + h_output_col = witness_amount - 1; + } + std::size_t h_row_offset = 1; + if(3*m + 1 <= witness_amount){ + h_row_offset = 2; + } + var h_var = var(component.W(h_output_col), row - h_row_offset, false); + row += f1_loop::get_rows_amount(witness_amount, lu_value_size); + row += f1_loop::get_rows_amount(witness_amount, lu_input_size); + row += mul::get_rows_amount(witness_amount); + var g_var = var(component.W(2), row - 1, false); + row += f3_loop::get_rows_amount(witness_amount, m - 1); + row += mul::get_rows_amount(witness_amount); + + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.V_L_values[0]}); + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.L0}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.q_last[0]}); + + row += 4 / witness_amount + 1; + bp.add_copy_constraint({var(component.W(0), row, false), instance_input.V_L_values[0]}); + bp.add_copy_constraint({var(component.W(1), row, false), var(component.W(2), start_row_index, false)}); + bp.add_copy_constraint({var(component.W(2), row, false), h_var}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.V_L_values[1]}); + bp.add_copy_constraint({var(component.W(4 % witness_amount), row + 4 / witness_amount, false), g_var}); + } + + template + typename plonk_lookup_verifier::result_type generate_circuit( + const plonk_lookup_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_lookup_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + generate_assignments_constant(component, bp, assignment, instance_input, start_row_index); + + std::size_t row = start_row_index; + std::size_t witness_amount = component.witness_amount(); + + using var = typename plonk_lookup_verifier::var; + + typedef crypto3::zk::snark::plonk_constraint_system + ArithmetizationType; + + using f1_loop = detail::f1_loop; + using f3_loop = detail::f3_loop; + using gate_component = detail::gate_component; + using mul = multiplication>; + + std::vector witnesses; + for (std::uint32_t i = 0; i < witness_amount; i++) { + witnesses.push_back(component.W(i)); + } + + var var_mask = var(component.W(2), row, false); + var var_shifted_mask = var(component.W(2), row + 1, false); + + row += 2; + + std::vector lookup_values; + std::vector shifted_lookup_values; + + std::size_t start_pos = 0, offset = 0; + std::size_t num_tables = instance_input.lookup_table_selectors.size(); + assert(num_tables == component.lookup_tables_size); + for (std::size_t i = 0; i < num_tables; i++) { + var selector = instance_input.lookup_table_selectors[i]; + var shifted_selector = instance_input.shifted_lookup_table_selectors[i]; + var t_id_inc = var(component.C(0), start_row_index + i, false, var::column_type::constant); + + for (std::size_t j = 0; j < component.lookup_table_lookup_options_sizes[i]; j++) { + offset = component.lookup_table_columns_numbers[i]; + std::vector gate_constraints; + gate_constraints.push_back(t_id_inc); + gate_constraints.insert(gate_constraints.end(), + instance_input.lookup_table_lookup_options.begin() + start_pos, + instance_input.lookup_table_lookup_options.begin() + start_pos + + offset); + + gate_component gate_instance = gate_component(witnesses, std::array(), + std::array(), offset); + + typename gate_component::input_type gate_input = {instance_input.theta, gate_constraints, + selector}; + + typename gate_component::result_type gate_i_result = + generate_circuit(gate_instance, bp, assignment, gate_input, row); + + row += gate_instance.rows_amount; + + mul mul_instance = + mul(witnesses, std::array(), std::array()); + + typename mul::input_type mul_input = {gate_i_result.output, var_mask}; + typename mul::result_type mul_result = + generate_circuit(mul_instance, bp, assignment, mul_input, row); + row += mul_instance.rows_amount; + lookup_values.push_back(mul_result.output); + + gate_constraints.clear(); + gate_constraints.push_back(t_id_inc); + gate_constraints.insert(gate_constraints.end(), + instance_input.shifted_lookup_table_lookup_options.begin() + start_pos, + instance_input.shifted_lookup_table_lookup_options.begin() + start_pos + + offset); + + // gate_instance = gate_component(witnesses, std::array(), + // std::array(), offset); + gate_input = {instance_input.theta, gate_constraints, shifted_selector}; + gate_i_result = generate_circuit(gate_instance, bp, assignment, gate_input, row); + row += gate_instance.rows_amount; + + mul_input = {gate_i_result.output, var_shifted_mask}; + mul_result = generate_circuit(mul_instance, bp, assignment, mul_input, row); + row += mul_instance.rows_amount; + shifted_lookup_values.push_back(mul_result.output); + + start_pos += offset; + } + } + assert(lookup_values.size() == shifted_lookup_values.size()); + + std::vector lookup_input; + std::size_t start = 0, lookup_input_size = 0, ctr = 0; + std::size_t num_gates = instance_input.lookup_gate_selectors.size(); + assert(num_gates == component.lookup_gates_size); + for (std::size_t g_id = 0; g_id < num_gates; g_id++) { + + var selector = instance_input.lookup_gate_selectors[g_id]; + for (std::size_t c_id = 0; c_id < component.lookup_gate_constraints_sizes[g_id]; c_id++) { + + lookup_input_size = component.lookup_gate_constraints_lookup_input_sizes[ctr]; + std::vector gate_constraints; + gate_constraints.push_back(instance_input.lookup_gate_constraints_table_ids[ctr++]); + gate_constraints.insert(gate_constraints.begin() + 1, + instance_input.lookup_gate_constraints_lookup_inputs.begin() + start, + instance_input.lookup_gate_constraints_lookup_inputs.begin() + start + + lookup_input_size); + + gate_component gate_instance = + gate_component(witnesses, std::array(), std::array(), + lookup_input_size); + typename gate_component::input_type gate_input = {instance_input.theta, gate_constraints, + selector}; + + typename gate_component::result_type gate_i_result = + generate_circuit(gate_instance, bp, assignment, gate_input, row); + + lookup_input.push_back(gate_i_result.output); + row += gate_instance.rows_amount; + start += lookup_input_size; + } + } + + std::vector s0, s1, s2; + std::size_t k = (instance_input.sorted.size() + 1) / 3; + for (std::size_t i = 0; i < k; i++) { + s0.push_back(instance_input.sorted[i]); + s1.push_back(instance_input.sorted[k + i]); + if (i >= 1) { + s2.push_back(instance_input.sorted[2 * k + i - 1]); + } + } + + assert(s0.size() == s1.size()); + + f1_loop h_loop = + f1_loop(witnesses, std::array(), std::array(), s0.size()); + typename f1_loop::input_type h_loop_input = {instance_input.beta, instance_input.gamma, s0, s1}; + + // h_loop_result + generate_circuit(h_loop, bp, assignment, h_loop_input, row); + + row += h_loop.rows_amount; + + f1_loop g_loop_1 = f1_loop(witnesses, std::array(), std::array(), + lookup_input.size()); + + typename f1_loop::input_type g_loop_input = {instance_input.beta, instance_input.gamma, lookup_input, + lookup_input}; + + typename f1_loop::result_type g_loop_result = + generate_circuit(g_loop_1, bp, assignment, g_loop_input, row); + + row += g_loop_1.rows_amount; + + f1_loop g_loop_2 = f1_loop(witnesses, std::array(), std::array(), + lookup_values.size()); + + typename f1_loop::input_type g_loop_input_2 = {instance_input.beta, instance_input.gamma, lookup_values, + shifted_lookup_values}; + + typename f1_loop::result_type g_loop_result_2 = + generate_circuit(g_loop_2, bp, assignment, g_loop_input_2, row); + + row += g_loop_2.rows_amount; + + mul mul_instance = mul(witnesses, std::array(), std::array()); + typename mul::input_type mul_input = {g_loop_result.output, g_loop_result_2.output}; + typename mul::result_type mul_result = generate_circuit(mul_instance, bp, assignment, mul_input, row); + row += mul_instance.rows_amount; + + s0.erase(s0.begin()); + assert(s0.size() == s2.size()); + + f3_loop F3_loop = + f3_loop(witnesses, std::array(), std::array(), s0.size()); + typename f3_loop::input_type F3_loop_input = {instance_input.alphas, s0, s2}; + + typename f3_loop::result_type F3_loop_result = + generate_circuit(F3_loop, bp, assignment, F3_loop_input, row); + + row += F3_loop.rows_amount; + + mul_input = {F3_loop_result.output, instance_input.L0}; + mul_result = generate_circuit(mul_instance, bp, assignment, mul_input, row); + row += mul_instance.rows_amount; + + std::vector selectors = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selectors[0], start_row_index); + assignment.enable_selector(selectors[0], start_row_index + 1); + assignment.enable_selector(selectors[1], row); + + row += 4 / witness_amount + 1; + assignment.enable_selector(selectors[2], row); + + row += 5 / witness_amount; + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_lookup_verifier::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_LOOKUP_ARGUMENT_VERIFIER_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/permutation_argument_verifier.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/permutation_argument_verifier.hpp new file mode 100644 index 000000000..fb0191f14 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/placeholder/permutation_argument_verifier.hpp @@ -0,0 +1,357 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the PERMUTATION_ARGUMENT_VERIFIER component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PERMUTATION_ARGUMENT_VERIFIER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PERMUTATION_ARGUMENT_VERIFIER_HPP + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + template + class permutation_verifier; + + template + class permutation_verifier< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + + constexpr static const std::uint32_t ConstantsAmount = 0; + + constexpr static const std::size_t rows_amount_internal(std::size_t witness_amount, std::size_t m) { + return m + 2; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = nil::blueprint::plonk_component_manifest; + + const std::size_t m; + + std::size_t rows_amount = rows_amount_internal(this->witness_amount(), m); + constexpr static const std::size_t gates_amount = 4; + const std::string component_name = "permutation argument verifier component"; + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t m) { + return rows_amount_internal(witness_amount, m); + } + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return permutation_verifier::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t m) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest(std::size_t m) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_single_value_param(6)), false); + return manifest; + } + + struct input_type { + std::vector f; + std::vector Se; + std::vector Ssigma; + var L0; + var V; + var V_zeta; + var q_last; + var q_pad; + std::array thetas; + + std::vector> all_vars() { + std::vector> vars; + vars.insert(vars.end(), f.begin(), f.end()); + vars.insert(vars.end(), Se.begin(), Se.end()); + vars.insert(vars.end(), Ssigma.begin(), Ssigma.end()); + vars.push_back(L0); + vars.push_back(V); + vars.push_back(V_zeta); + vars.push_back(q_last); + vars.push_back(q_pad); + vars.push_back(thetas[0]); + vars.push_back(thetas[1]); + return vars; + } + }; + + struct result_type { + std::array output; + + result_type(const permutation_verifier &component, std::uint32_t start_row_index) { + output = {var(component.W(0), start_row_index + component.rows_amount - 2, false), + var(component.W(4), start_row_index + component.rows_amount - 2, false), + var(component.W(0), start_row_index + component.rows_amount - 1, false)}; + } + + std::vector> all_vars() { + return {output[0], output[1], output[2]}; + } + }; + + template + permutation_verifier(ContainerType witness, std::size_t m_) : + component_type(witness, {}, {}, get_manifest(m_)), m(m_) {}; + + template + permutation_verifier(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t m_) : + component_type(witness, constant, public_input, get_manifest(m_)), + m(m_) {}; + + permutation_verifier( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t m_) : + component_type(witnesses, constants, public_inputs, get_manifest(m_)), + m(m_) {}; + }; + + template + using plonk_permutation_verifier = permutation_verifier< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_permutation_verifier::result_type + generate_assignments( + const plonk_permutation_verifier &component, + assignment> + &assignment, + const typename plonk_permutation_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + assert(instance_input.f.size() == component.m); + + std::size_t m = component.m; + + std::vector f, Se, Ssigma; + for (std::size_t i = 0; i < m; i++) { + f.push_back(var_value(assignment, instance_input.f[i])); + Se.push_back(var_value(assignment, instance_input.Se[i])); + Ssigma.push_back(var_value(assignment, instance_input.Ssigma[i])); + } + typename BlueprintFieldType::value_type one = BlueprintFieldType::value_type::one(); + typename BlueprintFieldType::value_type fe = one; + typename BlueprintFieldType::value_type fsigma = one; + + typename BlueprintFieldType::value_type theta_1 = var_value(assignment, instance_input.thetas[0]); + typename BlueprintFieldType::value_type theta_2 = var_value(assignment, instance_input.thetas[1]); + + typename BlueprintFieldType::value_type L0_y = var_value(assignment, instance_input.L0); + typename BlueprintFieldType::value_type Vsigma_y = var_value(assignment, instance_input.V); + typename BlueprintFieldType::value_type Vsigma_zetay = var_value(assignment, instance_input.V_zeta); + typename BlueprintFieldType::value_type q_last_y = var_value(assignment, instance_input.q_last); + typename BlueprintFieldType::value_type q_pad_y = var_value(assignment, instance_input.q_pad); + + for (std::size_t i = 0; i < m; i++) { + fe = fe * (f[i] + theta_1 * Se[i] + theta_2); + fsigma = fsigma * (f[i] + theta_1 * Ssigma[i] + theta_2); + assignment.witness(component.W(0), row + i) = fe; + assignment.witness(component.W(1), row + i) = f[i]; + assignment.witness(component.W(2), row + i) = Se[i]; + assignment.witness(component.W(4), row + i) = Ssigma[i]; + assignment.witness(component.W(5), row + i) = fsigma; + + if (i & 1) { + assignment.witness(component.W(3), row + i) = theta_2; + } else { + assignment.witness(component.W(3), row + i) = theta_1; + } + } + row += component.m; + + assignment.witness(component.W(0), row) = L0_y * (one - Vsigma_y); + assignment.witness(component.W(1), row) = q_last_y; + assignment.witness(component.W(2), row) = q_pad_y; + assignment.witness(component.W(3), row) = L0_y; + assignment.witness(component.W(4), row) = + (1 - (q_last_y + q_pad_y)) * (Vsigma_zetay * fsigma - Vsigma_y * fe); + + row++; + + assignment.witness(component.W(0), row) = q_last_y * (Vsigma_y * Vsigma_y - Vsigma_y); + assignment.witness(component.W(1), row) = Vsigma_y; + assignment.witness(component.W(2), row) = Vsigma_y * Vsigma_y; + assignment.witness(component.W(3), row) = Vsigma_zetay; + + return typename plonk_permutation_verifier::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_permutation_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_permutation_verifier::input_type + instance_input) { + + using var = typename plonk_permutation_verifier::var; + + auto constraint_1 = var(component.W(0), 0) - var(component.W(1), 0) - + var(component.W(2), 0) * var(component.W(3), 0) - var(component.W(3), +1); + auto constraint_2 = var(component.W(5), 0) - var(component.W(1), 0) - + var(component.W(4), 0) * var(component.W(3), 0) - var(component.W(3), +1); + + auto constraint_3 = var(component.W(0), +1) - + var(component.W(0), 0) * + (var(component.W(1), +1) + var(component.W(2), +1) * var(component.W(3), 0) + + var(component.W(3), +1)); + auto constraint_4 = var(component.W(5), +1) - + var(component.W(5), 0) * + (var(component.W(1), +1) + var(component.W(4), +1) * var(component.W(3), 0) + + var(component.W(3), +1)); + + std::size_t first_selector_index = + bp.add_gate({constraint_1, constraint_2, constraint_3, constraint_4}); + + auto constraint_5 = var(component.W(0), 0) - + var(component.W(0), -1) * + (var(component.W(1), 0) + var(component.W(2), 0) * var(component.W(3), 0) + + var(component.W(3), +1)); + auto constraint_6 = var(component.W(5), 0) - + var(component.W(5), -1) * + (var(component.W(1), 0) + var(component.W(4), 0) * var(component.W(3), 0) + + var(component.W(3), +1)); + + std::size_t second_selector_index = + bp.add_gate({constraint_3, constraint_4, constraint_5, constraint_6}); + + auto constraint_7 = var(component.W(0), 0) - + var(component.W(0), -1) * + (var(component.W(1), 0) + var(component.W(2), 0) * var(component.W(3), 0) + + var(component.W(3), -1)); + auto constraint_8 = var(component.W(5), 0) - + var(component.W(5), -1) * + (var(component.W(1), 0) + var(component.W(4), 0) * var(component.W(3), 0) + + var(component.W(3), -1)); + std::size_t third_selector_index = bp.add_gate({constraint_7, constraint_8}); + + auto constraint_9 = var(component.W(0), 0) - var(component.W(3), 0) * (1 - var(component.W(1), +1)); + auto constraint_10 = var(component.W(4), 0) - (1 - var(component.W(1), 0) - var(component.W(2), 0)) * + (var(component.W(3), +1) * var(component.W(5), -1) - + var(component.W(1), +1) * var(component.W(0), -1)); + + auto constraint_11 = var(component.W(2), +1) - var(component.W(1), +1) * var(component.W(1), +1); + auto constraint_12 = var(component.W(0), +1) - + var(component.W(1), 0) * (var(component.W(2), +1) - var(component.W(1), +1)); + + std::size_t fourth_selector_index = + bp.add_gate({constraint_9, constraint_10, constraint_11, constraint_12}); + + return {first_selector_index, second_selector_index, third_selector_index, fourth_selector_index}; + } + + template + void generate_copy_constraints( + const plonk_permutation_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_permutation_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + std::size_t m = component.m; + + using var = typename plonk_permutation_verifier::var; + + for (std::size_t i = 0; i < m; i++) { + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.f[i]}); + bp.add_copy_constraint({var(component.W(2), row, false), instance_input.Se[i]}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.thetas[(i & 1)]}); + bp.add_copy_constraint({var(component.W(4), row, false), instance_input.Ssigma[i]}); + row++; + } + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.q_last}); + bp.add_copy_constraint({var(component.W(2), row, false), instance_input.q_pad}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.L0}); + row++; + bp.add_copy_constraint({var(component.W(1), row, false), instance_input.V}); + bp.add_copy_constraint({var(component.W(3), row, false), instance_input.V_zeta}); + } + + template + typename plonk_permutation_verifier::result_type + generate_circuit( + const plonk_permutation_verifier &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_permutation_verifier::input_type + instance_input, + const std::uint32_t start_row_index) { + + std::size_t row = start_row_index; + + std::vector selectors = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selectors[0], row); + + for (row = start_row_index + 2; row < start_row_index + component.m - (component.m & 1); row += 2) { + assignment.enable_selector(selectors[1], row); + } + + row = start_row_index + component.m; + if (component.m & 1) { + assignment.enable_selector(selectors[2], row - 1); + } + assignment.enable_selector(selectors[3], row); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_permutation_verifier::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PERMUTATION_ARGUMENT_VERIFIER_HPP \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/final_polynomial_check.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/final_polynomial_check.hpp new file mode 100644 index 000000000..443c86dc1 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/final_polynomial_check.hpp @@ -0,0 +1,279 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include "nil/blueprint/components/algebra/fields/plonk/addition.hpp" +#include "nil/blueprint/components/algebra/fields/plonk/multiplication.hpp" +#include "nil/blueprint/components/systems/snark/plonk/placeholder/detail/expression_evaluation_component.hpp" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + + using detail::expression_evaluation_component; + + template + class final_polynomial_check; + + // checks that the polynomial defined by power + 1 coefficients has values equal to 2*lambda passed values + // at 2*lambda points of the form (s, -s) + // (where one of the points is passed, and the other one is inferred) + // coefficients passed highest to lowest power + template + class final_polynomial_check< + crypto3::zk::snark::plonk_constraint_system> + : public plonk_component { + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using manifest_type = nil::blueprint::plonk_component_manifest; + using expression_evaluator_type = plonk_expression_evaluation_component; + + std::size_t power; + std::size_t lambda; + + static const std::size_t rows_amount = 0; + static const std::size_t gates_amount = 0; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return final_polynomial_check::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t power, + std::size_t labmda) { + static gate_manifest manifest = gate_manifest_type(); + return manifest; + } + + static manifest_type get_manifest(std::size_t power, std::size_t labmda) { + static manifest_type manifest = + manifest_type(std::shared_ptr(new manifest_single_value_param(3)), true); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t power, + std::size_t labmda) { + return final_polynomial_check::rows_amount; + } + + struct input_type { + std::vector coefficients; + std::vector points; + std::vector values; + + std::vector> all_vars() { + std::vector> result; + for (auto &coefficient : coefficients) { + result.push_back(coefficient); + } + for (auto &point : points) { + result.push_back(point); + } + for (auto &value : values) { + result.push_back(value); + } + return result; + } + }; + + struct result_type { + // fail if the check is not satisfied + result_type(const final_polynomial_check &component, std::uint32_t start_row_index) {} + + std::vector> all_vars() { + return {}; + } + }; + + template + final_polynomial_check(ContainerType witness, std::size_t power_, std::size_t lambda_) : + component_type(witness, {}, {}, get_manifest(power_, lambda_)), + power(power_), lambda(lambda_) + {}; + + template + final_polynomial_check(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t power_, std::size_t lambda_) : + component_type(witness, constant, public_input, get_manifest(power_, lambda_)), + power(power_), lambda(lambda_) + {}; + + final_polynomial_check( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t power_, std::size_t lambda_) : + component_type(witnesses, constants, public_inputs, get_manifest(power_, lambda_)), + power(power_), lambda(lambda_) + {}; + + inline std::tuple> build_mapping_and_constraints( + const input_type &instance_input) const { + + std::unordered_map coefficient_mapping; + // map coefficients to themselves; we can directly put them into an expression + for (auto coefficient : instance_input.coefficients) { + coefficient_mapping[coefficient] = coefficient; + } + // the only relative vars present, thus cannot possibly conflict with the mapping + var s_var = var(0, 0, true, var::column_type::witness), + y_var = var(0, 1, true, var::column_type::witness); + constraint_type constraint_s = instance_input.coefficients[0]; + for (std::size_t i = 1; i < instance_input.coefficients.size(); i++) { + constraint_s = instance_input.coefficients[i] + s_var * constraint_s; + } + constraint_s = constraint_s - y_var; + constraint_type constraint_m_s = instance_input.coefficients[0]; + for (std::size_t i = 1; i < instance_input.coefficients.size(); i++) { + constraint_m_s = instance_input.coefficients[i] - s_var * constraint_m_s; + } + constraint_m_s = constraint_m_s - y_var; + return std::make_tuple(constraint_s, constraint_m_s, coefficient_mapping); + } + }; + + template + using plonk_final_polynomial_check = final_polynomial_check< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_final_polynomial_check::result_type generate_assignments( + const plonk_final_polynomial_check + &component, + assignment> + &assignment, + const typename plonk_final_polynomial_check::input_type + instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_final_polynomial_check; + using expression_evaluator_type = typename component_type::expression_evaluator_type; + using expression_evaluator_input_type = typename expression_evaluator_type::input_type; + using var = typename component_type::var; + + BOOST_ASSERT(instance_input.coefficients.size() == component.power + 1); + BOOST_ASSERT(instance_input.points.size() == component.lambda); + BOOST_ASSERT(instance_input.values.size() == 2 * component.lambda); + + auto mapping_and_constraints = component.build_mapping_and_constraints(instance_input); + for (std::size_t i = 0; i < instance_input.points.size(); i++) { + var point = instance_input.points[i]; + var value = instance_input.values[2 * i], + value_m = instance_input.values[2 * i + 1]; + std::unordered_map mapping = std::get<2>(mapping_and_constraints); + mapping.insert({var(0, 0, true, var::column_type::witness), point}); + mapping.insert({var(0, 1, true, var::column_type::witness), value}); + expression_evaluator_type evaluator( + component._W, component._C, component._PI, std::get<0>(mapping_and_constraints)); + expression_evaluator_input_type input = {mapping}; + generate_assignments(evaluator, assignment, input, start_row_index); + expression_evaluator_type evaluator_m( + component._W, component._C, component._PI, std::get<1>(mapping_and_constraints)); + mapping.erase(var(0, 1, true, var::column_type::witness)); + mapping.insert({var(0, 1, true, var::column_type::witness), value_m}); + input = {mapping}; + generate_assignments(evaluator_m, assignment, input, start_row_index); + } + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_final_polynomial_check::result_type generate_circuit( + const plonk_final_polynomial_check + &component, + circuit> + &bp, + assignment> + &assignment, + const typename plonk_final_polynomial_check::input_type + instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_final_polynomial_check; + using expression_evaluator_type = typename component_type::expression_evaluator_type; + using expression_evaluator_input_type = typename expression_evaluator_type::input_type; + using var = typename component_type::var; + + BOOST_ASSERT(instance_input.coefficients.size() == component.power + 1); + BOOST_ASSERT(instance_input.points.size() == component.lambda); + BOOST_ASSERT(instance_input.values.size() == 2 * component.lambda); + + var zero = assignment.add_batch_constant_variable(0); + + auto mapping_and_constraints = component.build_mapping_and_constraints(instance_input); + for (std::size_t i = 0; i < instance_input.points.size(); i++) { + var point = instance_input.points[i]; + var value = instance_input.values[2 * i], + value_m = instance_input.values[2 * i + 1]; + std::unordered_map mapping = std::get<2>(mapping_and_constraints); + mapping.insert({var(0, 0, true, var::column_type::witness), point}); + mapping.insert({var(0, 1, true, var::column_type::witness), value}); + expression_evaluator_type evaluator( + component._W, component._C, component._PI, std::get<0>(mapping_and_constraints)); + expression_evaluator_input_type input = {mapping}; + auto result = generate_circuit(evaluator, bp, assignment, input, start_row_index); + bp.add_copy_constraint({result.output, zero}); + expression_evaluator_type evaluator_m( + component._W, component._C, component._PI, std::get<1>(mapping_and_constraints)); + mapping.erase(var(0, 1, true, var::column_type::witness)); + mapping.insert({var(0, 1, true, var::column_type::witness), value_m}); + input = {mapping}; + auto result_m = generate_circuit(evaluator_m, bp, assignment, input, start_row_index); + bp.add_copy_constraint({result_m.output, zero}); + } + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_input_type.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_input_type.hpp new file mode 100644 index 000000000..6a3888141 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_input_type.hpp @@ -0,0 +1,266 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Object, that helps to transform placeholder proof to public input column for recursive circuit +//---------------------------------------------------------------------------// +#ifndef BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_PROOF_INPUT_TYPE_HPP +#define BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_PROOF_INPUT_TYPE_HPP + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail{ + template + class placeholder_proof_input_type{ + public: + using field_type = typename PlaceholderParams::field_type; + using value_type = typename field_type::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using proof_type = nil::crypto3::zk::snark::placeholder_proof; + using common_data_type = typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type; + using constraint_system_type = typename PlaceholderParams::constraint_system_type; + using placeholder_info_type = nil::crypto3::zk::snark::placeholder_info; + + placeholder_proof_input_type( + const common_data_type& common_data, + const constraint_system_type& constraint_system, + const typename PlaceholderParams::commitment_scheme_params_type &fri_params, + std::size_t start_row_index = 0 + ) : common_data(common_data), constraint_system(constraint_system), fri_params(fri_params) + { + placeholder_info = nil::crypto3::zk::snark::prepare_placeholder_info( + constraint_system, + common_data); + + fill_vector(); + } + public: + std::vector vector(){ + return var_vector; + } + std::vector commitments() const{ + return _commitments; + } + std::vector fri_roots() const{ + return _fri_roots; + } + var challenge() const{ + return _challenge; + } + const std::vector> &merkle_tree_positions() const{ + return _merkle_tree_positions; + } + const std::vector> &initial_proof_values() const{ + return _initial_proof_values; + } + const std::vector> &initial_proof_hashes() const{ + return _initial_proof_hashes; + } + const std::vector> &round_proof_values() const{ + return _round_proof_values; + } + const std::vector> &round_proof_hashes() const{ + return _round_proof_hashes; + } + protected: + std::vector _commitments; + var _challenge; + std::vector _fri_roots; + std::vector> _merkle_tree_positions; + std::vector> _initial_proof_values; + std::vector> _initial_proof_hashes; + std::vector> _round_proof_values; + std::vector> _round_proof_hashes; + + void fill_vector() { + auto &desc = common_data.desc; + + std::size_t cur = 0; + _commitments.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(_commitments[0]); + _commitments.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(_commitments[1]); + _commitments.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(_commitments[2]); + + if( placeholder_info.use_lookups ){ //nil::crypto3::zk::snark::LOOKUP_BATCH + _commitments.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(_commitments[3]); + } + + // Challenge + _challenge = var(0, cur++, false, var::column_type::public_input); + var_vector.push_back(_challenge); + + // TODO: Commitment scheme may be different + // Z-s + // Fixed values batch. + // Permutation polynomials + std::cout << "placeholder_info.permutation_size = " << constraint_system.permuted_columns().size() << std::endl; + for(auto &column: constraint_system.permuted_columns()){ + std::cout << "Permuted column " << column << std::endl; + } + std::size_t permutation_size = constraint_system.permuted_columns().size(); + std::size_t points_num = 0; + for(std::size_t i = 0; i < permutation_size * 2; i++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num++; + } + // Special selectors + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num += 4; + //Constant columns + for( std::size_t i = 0; i < desc.constant_columns; i++){ + for( std::size_t j = 0; j < common_data.columns_rotations[desc.witness_columns + desc.public_input_columns + i].size(); j++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num++; + } + } + //Selector columns + for( std::size_t i = 0; i < desc.selector_columns; i++){ + for( std::size_t j = 0; j < common_data.columns_rotations[desc.witness_columns + desc.public_input_columns + desc.public_input_columns + i].size(); j++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num++; + } + } + //Variable values + //Witness columns + for( std::size_t i = 0; i < desc.witness_columns; i++){ + for( std::size_t j = 0; j < common_data.columns_rotations[i].size(); j++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num++; + } + } + //Public input columns + for( std::size_t i = 0; i < desc.public_input_columns; i++){ + for( std::size_t j = 0; j < common_data.columns_rotations[i + desc.witness_columns].size(); j++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + points_num++; + } + } + std::cout << "Proof input points num = " << points_num << std::endl; + //Permutation Polynomials + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + if( placeholder_info.use_lookups ){ //lookup permutation polynomial + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + } + //Quotient batch + // TODO: place it to one single place to prevent code duplication + for(std::size_t i = 0; i < placeholder_info.quotient_size; i++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + } + // Lookup columns + if( placeholder_info.use_lookups ){ //lookup sorted columns + for(std::size_t i = 0; i < constraint_system.sorted_lookup_columns_number(); i++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + } + } + // FRI roots + for(std::size_t i = 0; i < fri_params.r; i++){ + _fri_roots.push_back(var(0, cur++, false, var::column_type::public_input)); + var_vector.push_back(_fri_roots[i]); + } + + // Query proofs + _merkle_tree_positions.resize(fri_params.lambda); + _initial_proof_values.resize(fri_params.lambda); + _initial_proof_hashes.resize(fri_params.lambda); + _round_proof_values.resize(fri_params.lambda); + _round_proof_hashes.resize(fri_params.lambda); + std::cout << "Poly input num = " << placeholder_info.poly_num << std::endl; + for( std::size_t i = 0; i < fri_params.lambda; i++){ + // Initial proof values + _initial_proof_values[i] = {}; + for( std::size_t j = 0; j < placeholder_info.poly_num; j++ ){ + auto val0 = var(0, cur++, false, var::column_type::public_input); + auto val1 = var(0, cur++, false, var::column_type::public_input); + _initial_proof_values[i].push_back(val0); + _initial_proof_values[i].push_back(val1); + var_vector.push_back(val0); + var_vector.push_back(val1); + } + // Initial proof positions + _merkle_tree_positions[i].resize(log2(fri_params.D[0]->m) - 1); + for( std::size_t j = 0; j < log2(fri_params.D[0]->m) - 1; j++ ){ + var pos_var = var(0, cur++, false, var::column_type::public_input); + var_vector.push_back(pos_var); + _merkle_tree_positions[i][j] = pos_var; + } + // Initial proof hashes + for( std::size_t j = 0; j < placeholder_info.batches_num * (log2(fri_params.D[0]->m) - 1); j++ ){ + var hash_var = var(0, cur++, false, var::column_type::public_input); + var_vector.push_back(hash_var); + _initial_proof_hashes[i].push_back(hash_var); + } + // Round proof values + for( std::size_t j = 0; j < fri_params.r; j++){ + var y0_var = var(0, cur++, false, var::column_type::public_input); + var y1_var = var(0, cur++, false, var::column_type::public_input); + var_vector.push_back(y0_var); + var_vector.push_back(y1_var); + _round_proof_values[i].push_back(y0_var); + _round_proof_values[i].push_back(y1_var); + } + // Round proof hashes + for( std::size_t j = 0; j < placeholder_info.round_proof_layers_num; j++ ){ + var hash_var = var(0, cur++, false, var::column_type::public_input); + var_vector.push_back(hash_var); + _round_proof_hashes[i].push_back(hash_var); + } + } + // Final polynomials + std::size_t final_polynomial_size = std::pow(2, std::log2(fri_params.max_degree + 1) - fri_params.r + 1) - 2; + for( std::size_t i = 0; i < final_polynomial_size; i++){ + var_vector.push_back(var(0, cur++, false, var::column_type::public_input)); + } + } + private: + const common_data_type &common_data; + const constraint_system_type &constraint_system; + std::vector var_vector; + const typename PlaceholderParams::commitment_scheme_params_type &fri_params; + placeholder_info_type placeholder_info; + }; + } + } + } +} + +#endif diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_wrapper.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_wrapper.hpp new file mode 100644 index 000000000..4f1ac707a --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/proof_wrapper.hpp @@ -0,0 +1,156 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Object, that helps to transform placeholder proof to public input column for recursive circuit +//---------------------------------------------------------------------------// +#ifndef BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_PROOF_WRAPPER_HPP +#define BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_PROOF_WRAPPER_HPP + +#include + +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail{ + template + class placeholder_proof_wrapper{ + public: + using field_type = typename PlaceholderParams::field_type; + using proof_type = nil::crypto3::zk::snark::placeholder_proof; + using common_data_type = typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type; + placeholder_proof_wrapper(const common_data_type& common_data, const proof_type& proof) + : common_data(common_data), proof(proof) { + fill_vector(); + } + public: + std::vector vector(){ + return _proof_field_vector; + } + std::vector merkle_tree_positions(){ + return _merkle_tree_positions; + } + std::vector initial_proof_hashes(){ + return _initial_proof_hashes; + } + protected: + void fill_vector() { + _proof_field_vector.push_back(proof.commitments.at(1)); + _proof_field_vector.push_back(proof.commitments.at(2)); + _proof_field_vector.push_back(proof.commitments.at(3)); + + if( proof.commitments.find(4) != proof.commitments.end() ){ /*nil::crypto3::zk::snark::LOOKUP_BATCH*/ + _proof_field_vector.push_back(proof.commitments.at(4)); + } + + _proof_field_vector.push_back(proof.eval_proof.challenge); + + // TODO: Commitment scheme may be different + auto eval_proof = proof.eval_proof.eval_proof; + auto batch_info = eval_proof.z.get_batch_info(); + for(const auto& [k, v]: batch_info){ + for(std::size_t i = 0; i < v; i++){ + BOOST_ASSERT(eval_proof.z.get_poly_points_number(k, i) != 0); + for(std::size_t j = 0; j < eval_proof.z.get_poly_points_number(k, i); j++){ + _proof_field_vector.push_back(eval_proof.z.get(k, i, j)); + } + } + } + + for( std::size_t i = 0; i < eval_proof.fri_proof.fri_roots.size(); i++){ + _proof_field_vector.push_back(eval_proof.fri_proof.fri_roots[i]); + } + + _merkle_tree_positions.resize(eval_proof.fri_proof.query_proofs.size()); + _initial_proof_values.resize(eval_proof.fri_proof.query_proofs.size()); + _initial_proof_hashes.resize(eval_proof.fri_proof.query_proofs.size()); + for( std::size_t i = 0; i < eval_proof.fri_proof.query_proofs.size(); i++){ + for( const auto &[j, initial_proof]: eval_proof.fri_proof.query_proofs[i].initial_proof){ + for( std::size_t k = 0; k < initial_proof.values.size(); k++){ + _proof_field_vector.push_back(initial_proof.values[k][0][0]); + _proof_field_vector.push_back(initial_proof.values[k][0][1]); + _initial_proof_values[i].push_back(initial_proof.values[k][0][0]); + _initial_proof_values[i].push_back(initial_proof.values[k][0][1]); + } + } + + std::size_t x_index = 0; + for( const auto &[j, initial_proof]: eval_proof.fri_proof.query_proofs[i].initial_proof){ + _merkle_tree_positions[i].resize(initial_proof.p.path().size()); + std::cout << "Initial proof position "; + for( std::size_t k = 0; k < initial_proof.p.path().size(); k++){ + _proof_field_vector.push_back(initial_proof.p.path()[k][0].position()); + _merkle_tree_positions[i][k] = initial_proof.p.path()[k][0].position(); + std::cout << initial_proof.p.path()[k][0].position() << " "; + } + std::cout << " => " << x_index << std::endl; + break; + } + + for( const auto &[j, initial_proof]: eval_proof.fri_proof.query_proofs[i].initial_proof){ + //std::cout << "Initial proof hashes: " << std::endl; + for( std::size_t k = 0; k < initial_proof.p.path().size(); k++){ + _proof_field_vector.push_back(initial_proof.p.path()[k][0].hash()); + _initial_proof_hashes[i].push_back(initial_proof.p.path()[k][0].hash()); + //std::cout << "\t" << _proof_field_vector.size() << " "; + //std::cout << "\t" << initial_proof.p.path()[k][0].hash() << std::endl; + } + } + for( std::size_t j = 0; j < eval_proof.fri_proof.query_proofs[i].round_proofs.size(); j++){ + const auto &round_proof = eval_proof.fri_proof.query_proofs[i].round_proofs[j]; + _proof_field_vector.push_back(round_proof.y[0][0]); + _proof_field_vector.push_back(round_proof.y[0][1]); + } + for( std::size_t j = 0; j < eval_proof.fri_proof.query_proofs[i].round_proofs.size(); j++){ + const auto& p = eval_proof.fri_proof.query_proofs[i].round_proofs[j].p; + for( std::size_t k = 0; k < p.path().size(); k++){ + _proof_field_vector.push_back(p.path()[k][0].hash()); + } + } + } + + for( std::size_t i = 0; i < eval_proof.fri_proof.final_polynomial.size(); i++){ + _proof_field_vector.push_back(eval_proof.fri_proof.final_polynomial[i]); + } + } + private: + const common_data_type common_data; + const proof_type proof; + std::vector _proof_field_vector; + std::vector> _merkle_tree_positions; + // lambda * batches_num * 2 values. Convenient for merkle leaves calculation. + std::vector> _initial_proof_values; + std::vector> _initial_proof_hashes; + }; + } + } + } +} + +#endif diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/verifier.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/verifier.hpp new file mode 100644 index 000000000..0a9b9a29c --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/plonk/verifier/verifier.hpp @@ -0,0 +1,799 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Placeholder verifier circuit component +//---------------------------------------------------------------------------// + +#ifndef BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_VERIFIER_HPP +#define BLUEPRINT_COMPONENTS_FLEXIBLE_VERIFIER_PLACEHOLDER_VERIFIER_HPP + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + class plonk_flexible_verifier: public plonk_component{ + public: + using placeholder_info_type = nil::crypto3::zk::snark::placeholder_info; + using component_type = plonk_component; + using value_type = typename BlueprintFieldType::value_type; + using var = typename component_type::var; + + using poseidon_component_type = plonk_flexible_poseidon; + using swap_component_type = plonk_flexible_swap; + using colinear_checks_component_type = plonk_flexible_colinear_checks; + using constant_pow_component_type = plonk_flexible_constant_pow; + using x_index_component_type = plonk_flexible_x_index; + + std::size_t rows_amount; + std::size_t fri_params_r; + std::size_t fri_params_lambda; + value_type fri_omega; + std::size_t fri_domain_size; + std::size_t fri_initial_merkle_proof_size; + placeholder_info_type placeholder_info; + + struct challenges{ + var eta; + var perm_beta; + var perm_gamma; + var lookup_theta; + var lookup_gamma; + var lookup_beta; + std::vector lookup_alphas; + var gate_theta; + std::array alphas; + std::vector fri_alphas; + std::vector fri_xs; + var lpc_theta; + var xi; + }; + + struct input_type { + std::vector proof; + std::vector commitments; + std::vector fri_roots; + std::vector> merkle_tree_positions; + std::vector> initial_proof_values; + std::vector> initial_proof_hashes; + std::vector> round_proof_values; + std::vector> round_proof_hashes; + var challenge; + + std::vector> all_vars() { + std::vector> result; + result.reserve(proof.size()); + result.insert(result.end(), proof.begin(), proof.end()); + + return result; + } + + input_type(detail::placeholder_proof_input_type proof_input){ + proof = proof_input.vector(); + commitments = proof_input.commitments(); + fri_roots = proof_input.fri_roots(); + challenge = proof_input.challenge(); + merkle_tree_positions = proof_input.merkle_tree_positions(); + initial_proof_values = proof_input.initial_proof_values(); + initial_proof_hashes = proof_input.initial_proof_hashes(); + round_proof_values = proof_input.round_proof_values(); + round_proof_hashes = proof_input.round_proof_hashes(); + } + }; + struct result_type { + static constexpr std::size_t output_size = 1; + std::array output = {var(0, 0, false)}; + + result_type(std::uint32_t start_row_index) { + output[0] = var(0, start_row_index, false); + } + + std::vector> all_vars() { + std::vector> result; + result.insert(result.end(), output.begin(), output.end()); + return result; + } + }; + + using manifest_type = plonk_component_manifest; + + static const std::size_t gates_amount = 0; + + class gate_manifest_type : public component_gate_manifest { + std::size_t num_gates; + public: + gate_manifest_type(std::size_t witness_amount, std::size_t domain_size){ + std::cout << "Verifier gate_manifet_type constructor with witness = " << witness_amount << std::endl; + num_gates = poseidon_component_type::get_gate_manifest(witness_amount).get_gates_amount(); + std::size_t constant_pow_gates = constant_pow_component_type::get_gate_manifest( + witness_amount, (BlueprintFieldType::modulus - 1)/domain_size + ).get_gates_amount(); + std::cout << "Constant component gates " << constant_pow_gates << std::endl; + num_gates += constant_pow_gates; + std::cout << "X-index gates " << 1 << std::endl; + num_gates += 1; + std::cout << "Colinear checks component gate " << 1 << std::endl; + num_gates += 1; + } + std::uint32_t gates_amount() const override { + std::cout << "Verifier gates_amount " << num_gates << std::endl; + return num_gates; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + SrcParams src_params, + const typename SrcParams::constraint_system_type &constraint_system, + const typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type &common_data, + const typename SrcParams::commitment_scheme_params_type &fri_params + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, fri_params.D[0]->size())); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(5)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount, + SrcParams src_params, + const typename SrcParams::constraint_system_type &constraint_system, + const typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type &common_data, + const typename SrcParams::commitment_scheme_params_type &fri_params + ) { + auto &desc = common_data.desc; + auto placeholder_info = nil::crypto3::zk::snark::prepare_placeholder_info( + constraint_system, + common_data + ); + + auto vk0 = common_data.vk.constraint_system_with_params_hash; + auto vk1 = common_data.vk.fixed_values_commitment; + auto fri_params_r = fri_params.r; + auto fri_params_lambda = fri_params.lambda; + auto fri_omega = fri_params.D[0]->get_domain_element(1); + auto fri_domain_size = fri_params.D[0]->size(); + auto fri_initial_merkle_proof_size = log2(fri_params.D[0]->m) - 1; + + using poseidon_component_type = typename plonk_flexible_verifier::poseidon_component_type; + std::size_t poseidon_rows = poseidon_component_type::get_rows_amount(witness_amount); + + using constant_pow_component_type = typename plonk_flexible_verifier::constant_pow_component_type; + std::size_t constant_pow_rows = constant_pow_component_type::get_rows_amount(witness_amount, (BlueprintFieldType::modulus - 1)/fri_domain_size); + + using x_index_component_type = typename plonk_flexible_verifier::x_index_component_type; + std::size_t x_index_rows = x_index_component_type::get_rows_amount(witness_amount, fri_initial_merkle_proof_size); + + using colinear_checks_component_type = typename plonk_flexible_verifier::colinear_checks_component_type; + std::size_t colinear_checks_rows = colinear_checks_component_type::get_rows_amount(witness_amount, fri_params_r); + + auto rows_amount = poseidon_rows * 15 + poseidon_rows * fri_params_r + poseidon_rows * fri_params_lambda; //challenges + for( std::size_t i = 0; i < placeholder_info.batches_num; i++){ + rows_amount += poseidon_rows * placeholder_info.batches_sizes[i] * fri_params_lambda; + rows_amount += poseidon_rows * fri_initial_merkle_proof_size * fri_params_lambda; + } + for( std::size_t i = 0; i < fri_params.r-1; i++){ + rows_amount += poseidon_rows * fri_params_lambda * (fri_initial_merkle_proof_size - i); + } + rows_amount += constant_pow_rows * fri_params_lambda; + rows_amount += x_index_rows * fri_params_lambda; + rows_amount += colinear_checks_rows * fri_params_lambda; + return rows_amount; + } + + template < + typename WitnessContainerType, + typename ConstantContainerType, + typename PublicInputContainerType + > + plonk_flexible_verifier( + WitnessContainerType witnesses, + ConstantContainerType constants, + PublicInputContainerType public_inputs, + SrcParams src_params, + const typename SrcParams::constraint_system_type &constraint_system, + const typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type &common_data, + const typename SrcParams::commitment_scheme_params_type &fri_params + ): component_type(witnesses, constants, public_inputs, get_manifest()) + { + auto &desc = common_data.desc; + placeholder_info = nil::crypto3::zk::snark::prepare_placeholder_info( + constraint_system, + common_data + ); + + vk0 = common_data.vk.constraint_system_with_params_hash; + vk1 = common_data.vk.fixed_values_commitment; + fri_params_r = fri_params.r; + fri_params_lambda = fri_params.lambda; + fri_omega = fri_params.D[0]->get_domain_element(1); + fri_domain_size = fri_params.D[0]->size(); + fri_initial_merkle_proof_size = log2(fri_params.D[0]->m) - 1; + + using poseidon_component_type = typename plonk_flexible_verifier::poseidon_component_type; + using constant_pow_component_type = typename plonk_flexible_verifier::constant_pow_component_type; + using x_index_component_type = typename plonk_flexible_verifier::x_index_component_type; + using colinear_checks_component_type = typename plonk_flexible_verifier::colinear_checks_component_type; + + poseidon_component_type poseidon_instance( + witnesses, constants, public_inputs + ); + std::size_t poseidon_rows = poseidon_instance.rows_amount; + + x_index_component_type x_index_instance( + witnesses, constants, public_inputs, + fri_initial_merkle_proof_size, fri_omega + ); + std::size_t x_index_rows = x_index_instance.rows_amount; + + constant_pow_component_type constant_pow_instance( + witnesses, constants, public_inputs, + (BlueprintFieldType::modulus - 1)/fri_domain_size + ); + std::size_t constant_pow_rows = constant_pow_instance.rows_amount; + + colinear_checks_component_type colinear_checks_instance( + witnesses, constants, public_inputs, + fri_params_r + ); + std::size_t colinear_checks_rows = colinear_checks_instance.rows_amount; + + rows_amount = poseidon_rows * 15 + poseidon_rows * fri_params_r + poseidon_rows * fri_params_lambda; //challenges + for( std::size_t i = 0; i < placeholder_info.batches_num; i++){ + rows_amount += poseidon_rows * placeholder_info.batches_sizes[i] * fri_params_lambda; + rows_amount += poseidon_rows * fri_initial_merkle_proof_size * fri_params_lambda; + } + for( std::size_t i = 0; i < fri_params.r-1; i++){ + rows_amount += poseidon_rows * fri_params_lambda * (fri_initial_merkle_proof_size - i); + } + rows_amount += constant_pow_rows * fri_params_lambda; + rows_amount += x_index_rows * fri_params_lambda; + rows_amount += colinear_checks_rows * fri_params_lambda; + // Change after implementing minimized permutation_argument + } + + std::vector all_witnesses() const{ + return this->_W; + } + + typename BlueprintFieldType::value_type vk0; + typename BlueprintFieldType::value_type vk1; + }; + + template + typename plonk_flexible_verifier::result_type + generate_assignments( + const plonk_flexible_verifier &component, + assignment> &assignment, + const typename plonk_flexible_verifier::input_type instance_input, + const std::uint32_t start_row_index + ) { + using component_type = plonk_flexible_verifier; + + using swap_component_type = typename component_type::swap_component_type; + using swap_input_type = typename swap_component_type::input_type; + + using poseidon_component_type = typename component_type::poseidon_component_type; + using constant_pow_component_type = typename component_type::constant_pow_component_type; + using x_index_component_type = typename component_type::x_index_component_type; + using colinear_checks_component_type = typename component_type::colinear_checks_component_type; + using var = typename component_type::var; + + std::size_t poseidon_rows = 0; + std::size_t constant_pow_rows = 0; + std::size_t swap_rows = 0; + + typename component_type::challenges challenges; + constant_pow_component_type constant_pow_instance( + component.all_witnesses(), std::array({component.C(0)}), std::array(), + (BlueprintFieldType::modulus - 1)/component.fri_domain_size + ); + + std::size_t row = start_row_index; + std::cout << "Generate assignments" << std::endl; + + const typename component_type::result_type result(start_row_index); + // Set constants + assignment.constant(component.C(0),start_row_index) = typename BlueprintFieldType::value_type(0); + assignment.constant(component.C(0),start_row_index+1) = typename BlueprintFieldType::value_type(1); + assignment.constant(component.C(0),start_row_index+2) = component.vk0; + assignment.constant(component.C(0),start_row_index+3) = component.vk1; + + var zero_var = var(component.C(0), start_row_index, false, var::column_type::constant); + var vk0_var = var(component.C(0), start_row_index+2, false, var::column_type::constant); + var vk1_var = var(component.C(0), start_row_index+3, false, var::column_type::constant); + + typename poseidon_component_type::input_type poseidon_input = {zero_var, vk0_var, vk1_var}; + poseidon_component_type poseidon_instance(component.all_witnesses(), std::array({component.C(0)}), std::array()); + std::cout << "Poseidon prepared" << std::endl; + auto poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + + std::vector> swapped_vars; + + challenges.eta = poseidon_output.output_state[2]; + auto variable_value_var = instance_input.commitments[0]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + poseidon_input = {challenges.eta, variable_value_var, zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.perm_beta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + poseidon_input = {challenges.perm_beta, zero_var, zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.perm_gamma = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + // TODO: if use_lookups + poseidon_input = {challenges.perm_gamma, instance_input.commitments[1], zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.gate_theta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + for(std::size_t i = 0; i < 8; i++){ + poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.alphas[i] = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + } + + poseidon_input = {poseidon_output.output_state[2], instance_input.commitments[2], zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + poseidon_rows += poseidon_instance.rows_amount; + challenges.xi = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + BOOST_ASSERT(var_value(assignment, challenges.xi) == var_value(assignment, instance_input.challenge)); + + poseidon_input = {poseidon_output.output_state[2], vk1_var, instance_input.commitments[0]}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + poseidon_input = {poseidon_output.output_state[2], instance_input.commitments[1], instance_input.commitments[2]}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.lpc_theta = poseidon_output.output_state[2]; + std::cout << "lpc_theta = " << var_value(assignment, challenges.lpc_theta) << std::endl; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + + // TODO: if use_lookups state[1] should be equal to sorted polynomial commitment + // poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + // poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + // row += poseidon_instance.rows_amount; + + for( std::size_t i = 0; i < component.fri_params_r; i+=1){ + poseidon_input = {poseidon_output.output_state[2], instance_input.fri_roots[i], zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.fri_alphas.push_back(poseidon_output.output_state[2]); + std::cout << "alpha_challenge = " << var_value(assignment, challenges.fri_alphas[i]) << std::endl; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + } + + for( std::size_t i = 0; i < component.fri_params_lambda; i+=1){ + poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + challenges.fri_xs.push_back(poseidon_output.output_state[2]); + std::cout << "x_challenge = " << var_value(assignment, challenges.fri_xs[i]) << std::endl; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + } + + std::size_t challenge_poseidon_rows = poseidon_rows; + + std::vector xs; + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename constant_pow_component_type::input_type constant_pow_input = {challenges.fri_xs[i]}; + typename constant_pow_component_type::result_type constant_pow_output = generate_assignments( + constant_pow_instance, assignment, constant_pow_input, row + ); + xs.push_back(constant_pow_output.y); + row+= constant_pow_instance.rows_amount; + constant_pow_rows += constant_pow_instance.rows_amount; + } + + x_index_component_type x_index_instance( + component.all_witnesses(), std::array({component.C(0)}), std::array(), + component.fri_initial_merkle_proof_size, component.fri_omega + ); + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename x_index_component_type::input_type x_index_input; + x_index_input.x = xs[i]; + for( std::size_t j = 0; j < component.fri_initial_merkle_proof_size; j++){ + x_index_input.b.push_back(instance_input.merkle_tree_positions[i][j]); + } + typename x_index_component_type::result_type x_index_output = generate_assignments( + x_index_instance, assignment, x_index_input, row + ); + row += x_index_instance.rows_amount; + } + + std::size_t colinear_checks_rows = 0; + colinear_checks_component_type colinear_checks_instance( + component.all_witnesses(), std::array({component.C(0)}), + std::array(), component.fri_params_r + ); + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename colinear_checks_component_type::input_type colinear_checks_input(component.fri_params_r); + colinear_checks_input.x = xs[i]; + colinear_checks_input.ys.push_back(zero_var); // Fix after 1st round will be ready + colinear_checks_input.ys.push_back(zero_var); // Fix after 1st round will be ready + colinear_checks_input.bs.push_back(zero_var); // Set it to x_index component output + for( std::size_t j = 0; j < component.fri_params_r; j++){ + colinear_checks_input.ys.push_back(instance_input.round_proof_values[i][2*j]); + colinear_checks_input.ys.push_back(instance_input.round_proof_values[i][2*j + 1]); + colinear_checks_input.alphas.push_back(challenges.fri_alphas[j]); + colinear_checks_input.bs.push_back(instance_input.merkle_tree_positions[i][instance_input.merkle_tree_positions[i].size() - j - 1]); + } + typename colinear_checks_component_type::result_type colinear_checks_output = generate_assignments( + colinear_checks_instance, assignment, colinear_checks_input, row + ); + row += colinear_checks_instance.rows_amount; + colinear_checks_rows += colinear_checks_instance.rows_amount; + } + + + // Query proof check + // Construct Merkle leaves + std::size_t merkle_leaf_rows = 0; + std::size_t merkle_proof_rows = 0; + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + // Initial proof merkle leaf + std::size_t cur = 0; + std::size_t cur_hash = 0; + std::cout << "Query " << i << std::endl; + for( std::size_t j = 0; j < component.placeholder_info.batches_num; j++){ + poseidon_input.input_state[0] = zero_var; + for( std::size_t k = 0; k < component.placeholder_info.batches_sizes[j]; k++, cur+=2){ + poseidon_input.input_state[1] = instance_input.initial_proof_values[i][cur]; + poseidon_input.input_state[2] = instance_input.initial_proof_values[i][cur+1]; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + poseidon_input.input_state[0] = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + merkle_leaf_rows += poseidon_instance.rows_amount; + } + var hash_var = poseidon_output.output_state[2]; +// std::cout << "First hash i = " << i << "; cur_hash = " << cur_hash << " = " << instance_input.initial_proof_hashes[i][cur_hash] << " = " << var_value(assignment, instance_input.initial_proof_hashes[i][cur_hash]) << std::endl; + for( std::size_t k = 0; k < component.fri_initial_merkle_proof_size; k++){ + swap_input_type swap_input; + swap_input.inp = {instance_input.merkle_tree_positions[i][k], instance_input.initial_proof_hashes[i][cur_hash], hash_var}; + auto swap_result = assignment.template add_input_to_batch( + swap_input, 0); + poseidon_input = {zero_var, swap_result.output[0], swap_result.output[1]}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); +// std::cout << "\t(" +// << var_value(assignment, poseidon_input.input_state[1]) << ", " +// << var_value(assignment, poseidon_input.input_state[2]) << ", " +// << ") => " << var_value(assignment, poseidon_output.output_state[2]) << std::endl; + hash_var = poseidon_output.output_state[2]; + cur_hash++; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + merkle_proof_rows += poseidon_instance.rows_amount; + } + } + // Round proofs + cur = 0; + cur_hash = 0; + var hash_var; + var y0; + var y1; + for( std::size_t j = 0; j < component.fri_params_r; j++){ + if(j != 0){ + poseidon_input = {zero_var, y0, y1}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + hash_var = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + merkle_proof_rows += poseidon_instance.rows_amount; + for( std::size_t k = 0; k < component.fri_initial_merkle_proof_size - j; k++){ + swap_input_type swap_input; + swap_input.inp = {instance_input.merkle_tree_positions[i][k], instance_input.round_proof_hashes[i][cur_hash], hash_var}; + auto swap_result = assignment.template add_input_to_batch( + swap_input, 0); + poseidon_input = {zero_var, swap_result.output[0], swap_result.output[1]}; + poseidon_output = generate_assignments(poseidon_instance, assignment, poseidon_input, row); + row += poseidon_instance.rows_amount; + poseidon_rows += poseidon_instance.rows_amount; + merkle_proof_rows += poseidon_instance.rows_amount; + hash_var = poseidon_output.output_state[2]; + cur_hash++; + } + } + else { + // TODO remove it when 1st round will be ready + cur_hash += component.fri_initial_merkle_proof_size; + } + y0 = instance_input.round_proof_values[i][cur*2]; + y1 = instance_input.round_proof_values[i][cur*2 + 1]; + cur++; + } + } + + std::cout << "Generated assignments real rows for " << component.all_witnesses().size() << " witness = " << row - start_row_index << std::endl << std::endl << std::endl; + std::cout << "Poseidon rows = " << poseidon_rows << std::endl; + std::cout << "Challenge rows = " << challenge_poseidon_rows << std::endl; + std::cout << "Merkle leaf rows = " << merkle_leaf_rows << std::endl; + std::cout << "Merkle proof rows = " << merkle_proof_rows << std::endl; + std::cout << "Constant pow rows = " << constant_pow_rows << std::endl; + std::cout << "Swap rows = " << swap_rows << std::endl; + std::cout << "Colinear checks rows = " << colinear_checks_rows << std::endl; + return result; + } + + + template + const typename plonk_flexible_verifier::result_type + generate_circuit( + const plonk_flexible_verifier &component, + circuit> &bp, + assignment> &assignment, + const typename plonk_flexible_verifier::input_type &instance_input, + const std::size_t start_row_index + ) { + std::cout << "Generate circuit" << std::endl; + using component_type = plonk_flexible_verifier; + using var = typename component_type::var; + using poseidon_component_type = typename component_type::poseidon_component_type; + using swap_component_type = typename component_type::swap_component_type; + using swap_input_type = typename swap_component_type::input_type; + using constant_pow_component_type = typename component_type::constant_pow_component_type; + using x_index_component_type = typename component_type::x_index_component_type; + using colinear_checks_component_type = typename component_type::colinear_checks_component_type; + typename component_type::challenges challenges; + + std::size_t row = start_row_index; + + const typename plonk_flexible_verifier::result_type result(start_row_index); + var zero_var = var(component.C(0), start_row_index, false, var::column_type::constant); + var vk0_var = var(component.C(0), start_row_index+2, false, var::column_type::constant); + var vk1_var = var(component.C(0), start_row_index+3, false, var::column_type::constant); + + typename poseidon_component_type::input_type poseidon_input = {zero_var, vk0_var, vk1_var}; + poseidon_component_type poseidon_instance(component.all_witnesses(), std::array({component.C(0)}), std::array()); + auto poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + + constant_pow_component_type constant_pow_instance( + component.all_witnesses(), std::array({component.C(0)}), std::array(), + (BlueprintFieldType::modulus - 1)/component.fri_domain_size + ); + + challenges.eta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + poseidon_input = {challenges.eta, instance_input.commitments[0], zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.perm_beta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + poseidon_input = {challenges.perm_beta, zero_var, zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.perm_gamma = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + //TODO if use_lookups + + poseidon_input = {challenges.perm_gamma, instance_input.commitments[1], zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.gate_theta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + for(std::size_t i = 0; i < 8; i++){ + poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.alphas[i] = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + } + poseidon_input = {poseidon_output.output_state[2], instance_input.commitments[2], zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.xi = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + bp.add_copy_constraint({challenges.xi, instance_input.challenge}); + + poseidon_input = {poseidon_output.output_state[2], vk1_var, instance_input.commitments[0]}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + row += poseidon_instance.rows_amount; + + poseidon_input = {poseidon_output.output_state[2], instance_input.commitments[1], instance_input.commitments[2]}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.lpc_theta = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + + // TODO: if use_lookups state[1] should be equal to sorted polynomial commitment + // poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + // poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + // row += poseidon_instance.rows_amount; + + for( std::size_t i = 0; i < component.fri_params_r; i++){ + poseidon_input = {poseidon_output.output_state[2], instance_input.fri_roots[i], zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.fri_alphas.push_back(poseidon_output.output_state[2]); + row += poseidon_instance.rows_amount; + } + + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + poseidon_input = {poseidon_output.output_state[2], zero_var, zero_var}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + challenges.fri_xs.push_back(poseidon_output.output_state[2]); + row += poseidon_instance.rows_amount; + } + + std::vector xs; + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename constant_pow_component_type::input_type constant_pow_input = {challenges.fri_xs[i]}; + typename constant_pow_component_type::result_type constant_pow_output = generate_circuit( + constant_pow_instance, bp, assignment, constant_pow_input, row + ); + xs.push_back(constant_pow_output.y); + row+= constant_pow_instance.rows_amount; + } + + x_index_component_type x_index_instance( + component.all_witnesses(), std::array({component.C(0)}), std::array(), + component.fri_initial_merkle_proof_size, component.fri_omega + ); + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename x_index_component_type::input_type x_index_input; + x_index_input.x = xs[i]; + for( std::size_t j = 0; j < component.fri_initial_merkle_proof_size; j++ ){ + x_index_input.b.push_back(instance_input.merkle_tree_positions[i][j]); + } + typename x_index_component_type::result_type x_index_output = generate_circuit( + x_index_instance, bp, assignment, x_index_input, row + ); + row += x_index_instance.rows_amount; + } + + colinear_checks_component_type colinear_checks_instance( + component.all_witnesses(), std::array({component.C(0)}), + std::array(), component.fri_params_r + ); + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + typename colinear_checks_component_type::input_type colinear_checks_input(component.fri_params_r); + colinear_checks_input.x = xs[i]; + colinear_checks_input.ys.push_back(zero_var); // Fix after 1st round will be ready + colinear_checks_input.ys.push_back(zero_var); // Fix after 1st round will be ready + colinear_checks_input.bs.push_back(zero_var); // Set it to x_index component output + for( std::size_t j = 0; j < component.fri_params_r; j++){ + colinear_checks_input.ys.push_back(instance_input.round_proof_values[i][2*j]); + colinear_checks_input.ys.push_back(instance_input.round_proof_values[i][2*j + 1]); + colinear_checks_input.alphas.push_back(challenges.fri_alphas[j]); + colinear_checks_input.bs.push_back(instance_input.merkle_tree_positions[i][instance_input.merkle_tree_positions[i].size() - j - 1]); + } + typename colinear_checks_component_type::result_type colinear_checks_output = generate_circuit( + colinear_checks_instance, bp, assignment, colinear_checks_input, row + ); + row += colinear_checks_instance.rows_amount; + } + + // Query proof check + for( std::size_t i = 0; i < component.fri_params_lambda; i++){ + std::cout << "Query proof " << i << std::endl; + // Initial proof merkle leaf + std::size_t cur = 0; + std::size_t cur_hash = 0; + for( std::size_t j = 0; j < component.placeholder_info.batches_num; j++){ + poseidon_input.input_state[0] = zero_var; + for( std::size_t k = 0; k < component.placeholder_info.batches_sizes[j]; k++, cur+=2){ + poseidon_input.input_state[1] = instance_input.initial_proof_values[i][cur]; + poseidon_input.input_state[2] = instance_input.initial_proof_values[i][cur+1]; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + poseidon_input.input_state[0] = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + } + var hash_var = poseidon_output.output_state[2]; + for( std::size_t k = 0; k < component.fri_initial_merkle_proof_size; k++){ + swap_input_type swap_input; + swap_input.inp = {instance_input.merkle_tree_positions[i][k], instance_input.initial_proof_hashes[i][cur_hash], hash_var}; + auto swap_result = assignment.template add_input_to_batch( + swap_input, 1); + poseidon_input = {zero_var, swap_result.output[0], swap_result.output[1]}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + hash_var = poseidon_output.output_state[2]; + cur_hash++; + row += poseidon_instance.rows_amount; + } + if( j == 0 ) + bp.add_copy_constraint({poseidon_output.output_state[2], vk1_var}); + else + bp.add_copy_constraint({poseidon_output.output_state[2], instance_input.commitments[j-1]}); + } + // Compute y-s for first round + std::size_t round_merkle_proof_size = component.fri_initial_merkle_proof_size; + // Round proofs + cur = 0; + cur_hash = 0; + var hash_var; + var y0; + var y1; + + for( std::size_t j = 0; j < component.fri_params_r; j++){ + if(j != 0){ + poseidon_input = {zero_var, y0, y1}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + hash_var = poseidon_output.output_state[2]; + row += poseidon_instance.rows_amount; + for( std::size_t k = 0; k < component.fri_initial_merkle_proof_size - j; k++){ + swap_input_type swap_input; + swap_input.inp = {instance_input.merkle_tree_positions[i][k], instance_input.round_proof_hashes[i][cur_hash], hash_var}; + auto swap_result = assignment.template add_input_to_batch( + swap_input, 1); + poseidon_input = {zero_var, swap_result.output[0], swap_result.output[1]}; + poseidon_output = generate_circuit(poseidon_instance, bp, assignment, poseidon_input, row); + row += poseidon_instance.rows_amount; + hash_var = poseidon_output.output_state[2]; + cur_hash++; + } + bp.add_copy_constraint({poseidon_output.output_state[2], instance_input.fri_roots[j]}); + } else { + cur_hash += component.fri_initial_merkle_proof_size; + } + y0 = instance_input.round_proof_values[i][cur*2]; + y1 = instance_input.round_proof_values[i][cur*2 + 1]; + cur++; + } + } + + std::cout << "Circuit generated real rows = " << row - start_row_index << std::endl; + return result; + } + } + } +} + +#endif diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/r1cs_pp_zksnark/verifier.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/r1cs_pp_zksnark/verifier.hpp new file mode 100644 index 000000000..4d66023d6 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/r1cs_pp_zksnark/verifier.hpp @@ -0,0 +1,717 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for the the R1CS ppzkSNARK verifier component. +// +// The component r1cs_ppzksnark_verifier_component verifiers correct computation of +// r1cs_ppzksnark::verifier_strong_input_consistency. The component is built from two main sub-components: +// - r1cs_ppzksnark_verifier_process_vk_component, which verifies correct computation of +// r1cs_ppzksnark_verifier_process_vk, and +// - r1cs_ppzksnark_online_verifier_component, which verifies correct computation of +// r1cs_ppzksnark_online_verifier_strong_input_consistency. See r1cs_ppzksnark.hpp for description of the aforementioned +// functions. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_PPZKSNARK_VERIFIER_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_PPZKSNARK_VERIFIER_COMPONENT_HPP + +#include + +#include +#include +#include +#include +#include +//#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + namespace components { + + using namespace nil::crypto3::algebra::pairing; + + template + class r1cs_ppzksnark_proof_variable : public component { + public: + typedef typename CurveType::scalar_field_type FieldType; + + std::shared_ptr> g_A_g; + std::shared_ptr> g_A_h; + std::shared_ptr> g_B_g; + std::shared_ptr> g_B_h; + std::shared_ptr> g_C_g; + std::shared_ptr> g_C_h; + std::shared_ptr> g_H; + std::shared_ptr> g_K; + + std::vector>> all_G1_vars; + std::vector>> all_G2_vars; + + std::vector>> all_G1_checkers; + std::shared_ptr> G2_checker; + + blueprint_variable_vector proof_contents; + + r1cs_ppzksnark_proof_variable(blueprint &bp) : component(bp) { + const std::size_t num_G1 = 7; + const std::size_t num_G2 = 1; + + g_A_g.reset(new element_g1(bp)); + g_A_h.reset(new element_g1(bp)); + g_B_g.reset(new element_g2(bp)); + g_B_h.reset(new element_g1(bp)); + g_C_g.reset(new element_g1(bp)); + g_C_h.reset(new element_g1(bp)); + g_H.reset(new element_g1(bp)); + g_K.reset(new element_g1(bp)); + + all_G1_vars = {g_A_g, g_A_h, g_B_h, g_C_g, g_C_h, g_H, g_K}; + all_G2_vars = {g_B_g}; + + all_G1_checkers.resize(all_G1_vars.size()); + + for (std::size_t i = 0; i < all_G1_vars.size(); ++i) { + all_G1_checkers[i].reset(new element_g1_is_well_formed(bp, *all_G1_vars[i])); + } + G2_checker.reset(new element_g2_is_well_formed(bp, *g_B_g)); + + assert(all_G1_vars.size() == num_G1); + assert(all_G2_vars.size() == num_G2); + } + void generate_gates() { + for (auto &G1_checker : all_G1_checkers) { + G1_checker->generate_gates(); + } + + G2_checker->generate_gates(); + } + void generate_assignments( + const typename r1cs_ppzksnark::proof_type + &proof) { + std::vector G1_elems; + std::vector G2_elems; + + G1_elems = {proof.g_A.g, proof.g_A.h, proof.g_B.h, proof.g_C.g, + proof.g_C.h, proof.g_H, proof.g_K}; + G2_elems = {proof.g_B.g}; + + assert(G1_elems.size() == all_G1_vars.size()); + assert(G2_elems.size() == all_G2_vars.size()); + + for (std::size_t i = 0; i < G1_elems.size(); ++i) { + all_G1_vars[i]->generate_assignments(G1_elems[i]); + } + + for (std::size_t i = 0; i < G2_elems.size(); ++i) { + all_G2_vars[i]->generate_assignments(G2_elems[i]); + } + + for (auto &G1_checker : all_G1_checkers) { + G1_checker->generate_assignments(); + } + + G2_checker->generate_assignments(); + } + static std::size_t size() { + const std::size_t num_G1 = 7; + const std::size_t num_G2 = 1; + return (num_G1 * element_g1::num_field_elems + + num_G2 * element_g2::num_field_elems); + } + }; + + template + class r1cs_ppzksnark_verification_key_variable + : public component { + public: + typedef typename CurveType::scalar_field_type FieldType; + + std::shared_ptr> alphaA_g2; + std::shared_ptr> alphaB_g1; + std::shared_ptr> alphaC_g2; + std::shared_ptr> gamma_g2; + std::shared_ptr> gamma_beta_g1; + std::shared_ptr> gamma_beta_g2; + std::shared_ptr> rC_Z_g2; + std::shared_ptr> encoded_IC_base; + std::vector>> encoded_IC_query; + + blueprint_variable_vector all_bits; + blueprint_linear_combination_vector all_vars; + std::size_t input_size; + + std::vector>> all_G1_vars; + std::vector>> all_G2_vars; + + std::shared_ptr> packer; + + // Unfortunately, g++ 4.9 and g++ 5.0 have a bug related to + // incorrect inlining of small functions: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65307, which + // produces wrong assembly even at -O1. The test case at the bug + // report is directly derived from this code here. As a temporary + // work-around we mark the key functions noinline to hint compiler + // that inlining should not be performed. + + // TODO: remove later, when g++ developers fix the bug. + + __attribute__((noinline)) + r1cs_ppzksnark_verification_key_variable(blueprint &bp, + const blueprint_variable_vector &all_bits, + const std::size_t input_size) : + component(bp), + all_bits(all_bits), input_size(input_size) { + const std::size_t num_G1 = 2 + (input_size + 1); + const std::size_t num_G2 = 5; + + assert(all_bits.size() == (element_g1::size_in_bits() * num_G1 + + element_g2::size_in_bits() * num_G2)); + + this->alphaA_g2.reset(new element_g2(bp)); + this->alphaB_g1.reset(new element_g1(bp)); + this->alphaC_g2.reset(new element_g2(bp)); + this->gamma_g2.reset(new element_g2(bp)); + this->gamma_beta_g1.reset(new element_g1(bp)); + this->gamma_beta_g2.reset(new element_g2(bp)); + this->rC_Z_g2.reset(new element_g2(bp)); + + all_G1_vars = {this->alphaB_g1, this->gamma_beta_g1}; + all_G2_vars = {this->alphaA_g2, this->alphaC_g2, this->gamma_g2, this->gamma_beta_g2, + this->rC_Z_g2}; + + this->encoded_IC_query.resize(input_size); + this->encoded_IC_base.reset(new element_g1(bp)); + this->all_G1_vars.emplace_back(this->encoded_IC_base); + + for (std::size_t i = 0; i < input_size; ++i) { + this->encoded_IC_query[i].reset(new element_g1(bp)); + all_G1_vars.emplace_back(this->encoded_IC_query[i]); + } + + for (auto &G1_var : all_G1_vars) { + all_vars.insert(all_vars.end(), G1_var->all_vars.begin(), G1_var->all_vars.end()); + } + + for (auto &G2_var : all_G2_vars) { + all_vars.insert(all_vars.end(), G2_var->all_vars.begin(), G2_var->all_vars.end()); + } + + assert(all_G1_vars.size() == num_G1); + assert(all_G2_vars.size() == num_G2); + assert(all_vars.size() == (num_G1 * element_g1::num_variables() + + num_G2 * element_g2::num_variables())); + + packer.reset(new multipacking_component( + bp, all_bits, all_vars, FieldType::size_in_bits())); + } + void generate_gates(const bool enforce_bitness) { + packer->generate_gates(enforce_bitness); + } + void generate_assignments( + const typename r1cs_ppzksnark< + typename CurveType::pairing::pair_curve_type>::verification_key_type &vk) { + std::vector G1_elems; + std::vector G2_elems; + + G1_elems = {vk.alphaB_g1, vk.gamma_beta_g1}; + G2_elems = {vk.alphaA_g2, vk.alphaC_g2, vk.gamma_g2, vk.gamma_beta_g2, vk.rC_Z_g2}; + + assert(vk.encoded_IC_query.rest.indices.size() == input_size); + G1_elems.emplace_back(vk.encoded_IC_query.first); + for (std::size_t i = 0; i < input_size; ++i) { + assert(vk.encoded_IC_query.rest.indices[i] == i); + G1_elems.emplace_back(vk.encoded_IC_query.rest.values[i]); + } + + assert(G1_elems.size() == all_G1_vars.size()); + assert(G2_elems.size() == all_G2_vars.size()); + + for (std::size_t i = 0; i < G1_elems.size(); ++i) { + all_G1_vars[i]->generate_assignments(G1_elems[i]); + } + + for (std::size_t i = 0; i < G2_elems.size(); ++i) { + all_G2_vars[i]->generate_assignments(G2_elems[i]); + } + + packer->generate_assignments_from_packed(); + } + void generate_assignments(const std::vector &vk_bits) { + all_bits.fill_with_bits(this->bp, vk_bits); + packer->generate_assignments_from_bits(); + } + + std::vector get_bits() const { + return all_bits.get_bits(this->bp); + } + + static std::size_t __attribute__((noinline)) size_in_bits(const std::size_t input_size) { + const std::size_t num_G1 = 2 + (input_size + 1); + const std::size_t num_G2 = 5; + const std::size_t result = element_g1::size_in_bits() * num_G1 + + element_g2::size_in_bits() * num_G2; + return result; + } + + static std::vector get_verification_key_bits( + const typename r1cs_ppzksnark< + typename CurveType::pairing::pair_curve_type>::verification_key_type &r1cs_vk) { + + typedef typename CurveType::scalar_field_type FieldType; + + const std::size_t input_size_in_elts = + r1cs_vk.encoded_IC_query.rest.indices + .size(); // this might be approximate for bound verification keys, however they + // are not + // supported by r1cs_ppzksnark_verification_key_variable + const std::size_t vk_size_in_bits = + r1cs_ppzksnark_verification_key_variable::size_in_bits(input_size_in_elts); + + blueprint bp; + blueprint_variable_vector vk_bits; + vk_bits.allocate(bp, vk_size_in_bits); + r1cs_ppzksnark_verification_key_variable vk(bp, vk_bits, input_size_in_elts); + vk.generate_assignments(r1cs_vk); + + return vk.get_bits(); + } + }; + + template + class r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable { + public: + typedef typename CurveType::scalar_field_type FieldType; + + std::shared_ptr> encoded_IC_base; + std::vector>> encoded_IC_query; + + std::shared_ptr> vk_alphaB_g1_precomp; + std::shared_ptr> vk_gamma_beta_g1_precomp; + + std::shared_ptr> pp_G2_one_precomp; + std::shared_ptr> vk_alphaA_g2_precomp; + std::shared_ptr> vk_alphaC_g2_precomp; + std::shared_ptr> vk_gamma_beta_g2_precomp; + std::shared_ptr> vk_gamma_g2_precomp; + std::shared_ptr> vk_rC_Z_g2_precomp; + + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable() { + // will be allocated outside + } + + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable( + blueprint &bp, + const typename r1cs_ppzksnark< + typename CurveType::pairing::pair_curve_type>::verification_key &r1cs_vk) { + + encoded_IC_base.reset(new element_g1(bp, r1cs_vk.encoded_IC_query.first)); + encoded_IC_query.resize(r1cs_vk.encoded_IC_query.rest.indices.size()); + for (std::size_t i = 0; i < r1cs_vk.encoded_IC_query.rest.indices.size(); ++i) { + assert(r1cs_vk.encoded_IC_query.rest.indices[i] == i); + encoded_IC_query[i].reset( + new element_g1(bp, r1cs_vk.encoded_IC_query.rest.values[i])); + } + + vk_alphaB_g1_precomp.reset(new g1_precomputation(bp, r1cs_vk.alphaB_g1)); + vk_gamma_beta_g1_precomp.reset(new g1_precomputation(bp, r1cs_vk.gamma_beta_g1)); + + pp_G2_one_precomp.reset(new g2_precomputation( + bp, CurveType::pairing::pair_curve_type::template g2_type<>::value_type::one())); + vk_alphaA_g2_precomp.reset(new g2_precomputation(bp, r1cs_vk.alphaA_g2)); + vk_alphaC_g2_precomp.reset(new g2_precomputation(bp, r1cs_vk.alphaC_g2)); + vk_gamma_beta_g2_precomp.reset(new g2_precomputation(bp, r1cs_vk.gamma_beta_g2)); + vk_gamma_g2_precomp.reset(new g2_precomputation(bp, r1cs_vk.gamma_g2)); + vk_rC_Z_g2_precomp.reset(new g2_precomputation(bp, r1cs_vk.rC_Z_g2)); + } + }; + + template + class r1cs_ppzksnark_verifier_process_vk_component + : public component { + public: + typedef typename CurveType::scalar_field_type FieldType; + + std::shared_ptr> compute_vk_alphaB_g1_precomp; + std::shared_ptr> compute_vk_gamma_beta_g1_precomp; + + std::shared_ptr> compute_vk_alphaA_g2_precomp; + std::shared_ptr> compute_vk_alphaC_g2_precomp; + std::shared_ptr> compute_vk_gamma_beta_g2_precomp; + std::shared_ptr> compute_vk_gamma_g2_precomp; + std::shared_ptr> compute_vk_rC_Z_g2_precomp; + + r1cs_ppzksnark_verification_key_variable vk; + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable + &pvk; // important to have a reference here + + r1cs_ppzksnark_verifier_process_vk_component( + blueprint &bp, + const r1cs_ppzksnark_verification_key_variable &vk, + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable &pvk) : + component(bp), + vk(vk), pvk(pvk) { + pvk.encoded_IC_base = vk.encoded_IC_base; + pvk.encoded_IC_query = vk.encoded_IC_query; + + pvk.vk_alphaB_g1_precomp.reset(new g1_precomputation()); + pvk.vk_gamma_beta_g1_precomp.reset(new g1_precomputation()); + + pvk.pp_G2_one_precomp.reset(new g2_precomputation()); + pvk.vk_alphaA_g2_precomp.reset(new g2_precomputation()); + pvk.vk_alphaC_g2_precomp.reset(new g2_precomputation()); + pvk.vk_gamma_beta_g2_precomp.reset(new g2_precomputation()); + pvk.vk_gamma_g2_precomp.reset(new g2_precomputation()); + pvk.vk_rC_Z_g2_precomp.reset(new g2_precomputation()); + + compute_vk_alphaB_g1_precomp.reset( + new precompute_G1_component(bp, *vk.alphaB_g1, *pvk.vk_alphaB_g1_precomp)); + compute_vk_gamma_beta_g1_precomp.reset(new precompute_G1_component( + bp, *vk.gamma_beta_g1, *pvk.vk_gamma_beta_g1_precomp)); + + pvk.pp_G2_one_precomp.reset(new g2_precomputation( + bp, CurveType::pairing::pair_curve_type::template g2_type<>::value_type::one())); + compute_vk_alphaA_g2_precomp.reset( + new precompute_G2_component(bp, *vk.alphaA_g2, *pvk.vk_alphaA_g2_precomp)); + compute_vk_alphaC_g2_precomp.reset( + new precompute_G2_component(bp, *vk.alphaC_g2, *pvk.vk_alphaC_g2_precomp)); + compute_vk_gamma_beta_g2_precomp.reset(new precompute_G2_component( + bp, *vk.gamma_beta_g2, *pvk.vk_gamma_beta_g2_precomp)); + compute_vk_gamma_g2_precomp.reset( + new precompute_G2_component(bp, *vk.gamma_g2, *pvk.vk_gamma_g2_precomp)); + compute_vk_rC_Z_g2_precomp.reset( + new precompute_G2_component(bp, *vk.rC_Z_g2, *pvk.vk_rC_Z_g2_precomp)); + } + + void generate_gates() { + compute_vk_alphaB_g1_precomp->generate_gates(); + compute_vk_gamma_beta_g1_precomp->generate_gates(); + + compute_vk_alphaA_g2_precomp->generate_gates(); + compute_vk_alphaC_g2_precomp->generate_gates(); + compute_vk_gamma_beta_g2_precomp->generate_gates(); + compute_vk_gamma_g2_precomp->generate_gates(); + compute_vk_rC_Z_g2_precomp->generate_gates(); + } + + void generate_assignments() { + compute_vk_alphaB_g1_precomp->generate_assignments(); + compute_vk_gamma_beta_g1_precomp->generate_assignments(); + + compute_vk_alphaA_g2_precomp->generate_assignments(); + compute_vk_alphaC_g2_precomp->generate_assignments(); + compute_vk_gamma_beta_g2_precomp->generate_assignments(); + compute_vk_gamma_g2_precomp->generate_assignments(); + compute_vk_rC_Z_g2_precomp->generate_assignments(); + } + }; + + template + class r1cs_ppzksnark_online_verifier_component + : public component { + public: + typedef typename CurveType::scalar_field_type FieldType; + + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable pvk; + + blueprint_variable_vector input; + std::size_t elt_size; + r1cs_ppzksnark_proof_variable proof; + blueprint_variable result; + const std::size_t input_len; + + std::shared_ptr> acc; + std::shared_ptr> accumulate_input; + + std::shared_ptr> proof_g_A_g_acc; + std::shared_ptr> compute_proof_g_A_g_acc; + std::shared_ptr> proof_g_A_g_acc_C; + std::shared_ptr> compute_proof_g_A_g_acc_C; + + std::shared_ptr> proof_g_A_h_precomp; + std::shared_ptr> proof_g_A_g_acc_C_precomp; + std::shared_ptr> proof_g_A_g_acc_precomp; + std::shared_ptr> proof_g_A_g_precomp; + std::shared_ptr> proof_g_B_h_precomp; + std::shared_ptr> proof_g_C_h_precomp; + std::shared_ptr> proof_g_C_g_precomp; + std::shared_ptr> proof_g_K_precomp; + std::shared_ptr> proof_g_H_precomp; + + std::shared_ptr> proof_g_B_g_precomp; + + std::shared_ptr> compute_proof_g_A_h_precomp; + std::shared_ptr> compute_proof_g_A_g_acc_C_precomp; + std::shared_ptr> compute_proof_g_A_g_acc_precomp; + std::shared_ptr> compute_proof_g_A_g_precomp; + std::shared_ptr> compute_proof_g_B_h_precomp; + std::shared_ptr> compute_proof_g_C_h_precomp; + std::shared_ptr> compute_proof_g_C_g_precomp; + std::shared_ptr> compute_proof_g_K_precomp; + std::shared_ptr> compute_proof_g_H_precomp; + + std::shared_ptr> compute_proof_g_B_g_precomp; + + std::shared_ptr> check_kc_A_valid; + std::shared_ptr> check_kc_B_valid; + std::shared_ptr> check_kc_C_valid; + std::shared_ptr> check_QAP_valid; + std::shared_ptr> check_CC_valid; + + blueprint_variable kc_A_valid; + blueprint_variable kc_B_valid; + blueprint_variable kc_C_valid; + blueprint_variable QAP_valid; + blueprint_variable CC_valid; + + blueprint_variable_vector all_test_results; + std::shared_ptr> all_tests_pass; + + r1cs_ppzksnark_online_verifier_component( + blueprint &bp, + const r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable &pvk, + const blueprint_variable_vector &input, + const std::size_t elt_size, + const r1cs_ppzksnark_proof_variable &proof, + const blueprint_variable &result) : + component(bp), + pvk(pvk), input(input), elt_size(elt_size), proof(proof), result(result), + input_len(input.size()) { + // accumulate input and store base in acc + acc.reset(new element_g1(bp)); + std::vector> IC_terms; + for (std::size_t i = 0; i < pvk.encoded_IC_query.size(); ++i) { + IC_terms.emplace_back(*(pvk.encoded_IC_query[i])); + } + accumulate_input.reset(new G1_multiscalar_mul_component( + bp, *(pvk.encoded_IC_base), input, elt_size, IC_terms, *acc)); + + // allocate results for precomputation + proof_g_A_h_precomp.reset(new g1_precomputation()); + proof_g_A_g_acc_C_precomp.reset(new g1_precomputation()); + proof_g_A_g_acc_precomp.reset(new g1_precomputation()); + proof_g_A_g_precomp.reset(new g1_precomputation()); + proof_g_B_h_precomp.reset(new g1_precomputation()); + proof_g_C_h_precomp.reset(new g1_precomputation()); + proof_g_C_g_precomp.reset(new g1_precomputation()); + proof_g_K_precomp.reset(new g1_precomputation()); + proof_g_H_precomp.reset(new g1_precomputation()); + + proof_g_B_g_precomp.reset(new g2_precomputation()); + + // do the necessary precomputations + // compute things not available in plain from proof/vk + proof_g_A_g_acc.reset(new element_g1(bp)); + compute_proof_g_A_g_acc.reset( + new element_g1_add(bp, *(proof.g_A_g), *acc, *proof_g_A_g_acc)); + proof_g_A_g_acc_C.reset(new element_g1(bp)); + compute_proof_g_A_g_acc_C.reset(new element_g1_add( + bp, *proof_g_A_g_acc, *(proof.g_C_g), *proof_g_A_g_acc_C)); + + compute_proof_g_A_g_acc_precomp.reset( + new precompute_G1_component(bp, *proof_g_A_g_acc, *proof_g_A_g_acc_precomp)); + compute_proof_g_A_g_acc_C_precomp.reset(new precompute_G1_component( + bp, *proof_g_A_g_acc_C, *proof_g_A_g_acc_C_precomp)); + + // do other precomputations + compute_proof_g_A_h_precomp.reset( + new precompute_G1_component(bp, *(proof.g_A_h), *proof_g_A_h_precomp)); + compute_proof_g_A_g_precomp.reset( + new precompute_G1_component(bp, *(proof.g_A_g), *proof_g_A_g_precomp)); + compute_proof_g_B_h_precomp.reset( + new precompute_G1_component(bp, *(proof.g_B_h), *proof_g_B_h_precomp)); + compute_proof_g_C_h_precomp.reset( + new precompute_G1_component(bp, *(proof.g_C_h), *proof_g_C_h_precomp)); + compute_proof_g_C_g_precomp.reset( + new precompute_G1_component(bp, *(proof.g_C_g), *proof_g_C_g_precomp)); + compute_proof_g_H_precomp.reset( + new precompute_G1_component(bp, *(proof.g_H), *proof_g_H_precomp)); + compute_proof_g_K_precomp.reset( + new precompute_G1_component(bp, *(proof.g_K), *proof_g_K_precomp)); + compute_proof_g_B_g_precomp.reset( + new precompute_G2_component(bp, *(proof.g_B_g), *proof_g_B_g_precomp)); + + // check validity of A knowledge commitment + kc_A_valid.allocate(bp); + check_kc_A_valid.reset( + new check_e_equals_e_component(bp, + *proof_g_A_g_precomp, + *(pvk.vk_alphaA_g2_precomp), + *proof_g_A_h_precomp, + *(pvk.pp_G2_one_precomp), + kc_A_valid)); + + // check validity of B knowledge commitment + kc_B_valid.allocate(bp); + check_kc_B_valid.reset( + new check_e_equals_e_component(bp, + *(pvk.vk_alphaB_g1_precomp), + *proof_g_B_g_precomp, + *proof_g_B_h_precomp, + *(pvk.pp_G2_one_precomp), + kc_B_valid)); + + // check validity of C knowledge commitment + kc_C_valid.allocate(bp); + check_kc_C_valid.reset( + new check_e_equals_e_component(bp, + *proof_g_C_g_precomp, + *(pvk.vk_alphaC_g2_precomp), + *proof_g_C_h_precomp, + *(pvk.pp_G2_one_precomp), + kc_C_valid)); + + // check QAP divisibility + QAP_valid.allocate(bp); + check_QAP_valid.reset(new check_e_equals_ee_component(bp, + *proof_g_A_g_acc_precomp, + *proof_g_B_g_precomp, + *proof_g_H_precomp, + *(pvk.vk_rC_Z_g2_precomp), + *proof_g_C_g_precomp, + *(pvk.pp_G2_one_precomp), + QAP_valid)); + + // check coefficients + CC_valid.allocate(bp); + check_CC_valid.reset( + new check_e_equals_ee_component(bp, + *proof_g_K_precomp, + *(pvk.vk_gamma_g2_precomp), + *proof_g_A_g_acc_C_precomp, + *(pvk.vk_gamma_beta_g2_precomp), + *(pvk.vk_gamma_beta_g1_precomp), + *proof_g_B_g_precomp, + CC_valid)); + + // final constraint + all_test_results.emplace_back(kc_A_valid); + all_test_results.emplace_back(kc_B_valid); + all_test_results.emplace_back(kc_C_valid); + all_test_results.emplace_back(QAP_valid); + all_test_results.emplace_back(CC_valid); + + all_tests_pass.reset(new conjunction(bp, all_test_results, result)); + } + + void generate_gates() { + accumulate_input->generate_gates(); + + compute_proof_g_A_g_acc->generate_gates(); + compute_proof_g_A_g_acc_C->generate_gates(); + + compute_proof_g_A_g_acc_precomp->generate_gates(); + compute_proof_g_A_g_acc_C_precomp->generate_gates(); + + compute_proof_g_A_h_precomp->generate_gates(); + compute_proof_g_A_g_precomp->generate_gates(); + compute_proof_g_B_h_precomp->generate_gates(); + compute_proof_g_C_h_precomp->generate_gates(); + compute_proof_g_C_g_precomp->generate_gates(); + compute_proof_g_H_precomp->generate_gates(); + compute_proof_g_K_precomp->generate_gates(); + compute_proof_g_B_g_precomp->generate_gates(); + + check_kc_A_valid->generate_gates(); + check_kc_B_valid->generate_gates(); + check_kc_C_valid->generate_gates(); + check_QAP_valid->generate_gates(); + check_CC_valid->generate_gates(); + + all_tests_pass->generate_gates(); + } + + void generate_assignments() { + accumulate_input->generate_assignments(); + + compute_proof_g_A_g_acc->generate_assignments(); + compute_proof_g_A_g_acc_C->generate_assignments(); + + compute_proof_g_A_g_acc_precomp->generate_assignments(); + compute_proof_g_A_g_acc_C_precomp->generate_assignments(); + + compute_proof_g_A_h_precomp->generate_assignments(); + compute_proof_g_A_g_precomp->generate_assignments(); + compute_proof_g_B_h_precomp->generate_assignments(); + compute_proof_g_C_h_precomp->generate_assignments(); + compute_proof_g_C_g_precomp->generate_assignments(); + compute_proof_g_H_precomp->generate_assignments(); + compute_proof_g_K_precomp->generate_assignments(); + compute_proof_g_B_g_precomp->generate_assignments(); + + check_kc_A_valid->generate_assignments(); + check_kc_B_valid->generate_assignments(); + check_kc_C_valid->generate_assignments(); + check_QAP_valid->generate_assignments(); + check_CC_valid->generate_assignments(); + + all_tests_pass->generate_assignments(); + } + }; + + template + class r1cs_ppzksnark_verifier_component : public component { + public: + typedef typename CurveType::scalar_field_type FieldType; + + std::shared_ptr> + pvk; + std::shared_ptr> compute_pvk; + std::shared_ptr> online_verifier; + + r1cs_ppzksnark_verifier_component(blueprint &bp, + const r1cs_ppzksnark_verification_key_variable &vk, + const blueprint_variable_vector &input, + const std::size_t elt_size, + const r1cs_ppzksnark_proof_variable &proof, + const blueprint_variable &result) : + component(bp) { + pvk.reset( + new r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable()); + compute_pvk.reset( + new r1cs_ppzksnark_verifier_process_vk_component(bp, vk, *pvk)); + online_verifier.reset(new r1cs_ppzksnark_online_verifier_component( + bp, *pvk, input, elt_size, proof, result)); + } + + void generate_gates() { + compute_pvk->generate_gates(); + + online_verifier->generate_gates(); + } + + void generate_assignments() { + compute_pvk->generate_assignments(); + online_verifier->generate_assignments(); + } + }; + } // namespace components + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_PPZKSNARK_VERIFIER_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/systems/snark/set_commitment.hpp b/libs/blueprint/include/nil/blueprint/components/systems/snark/set_commitment.hpp new file mode 100644 index 000000000..d9c3b32eb --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/systems/snark/set_commitment.hpp @@ -0,0 +1,113 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SNARK_SET_COMMITMENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SNARK_SET_COMMITMENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace snark { + + typedef std::vector set_commitment; + + struct set_membership_proof { + std::size_t address; + merkle_authentication_path merkle_path; + + bool operator==(const set_membership_proof &other) const { + return (this->address == other.address && this->merkle_path == other.merkle_path); + } + + std::size_t size_in_bits() const { + if (merkle_path.empty()) { + return (8 * sizeof(address)); + } else { + return (8 * sizeof(address) + merkle_path[0].size() * merkle_path.size()); + } + } + }; + + template + class set_commitment_accumulator { + private: + std::shared_ptr> tree; + std::map, std::size_t> hash_to_pos; + + public: + std::size_t depth; + std::size_t digest_size; + std::size_t value_size; + + set_commitment_accumulator(const std::size_t max_entries, const std::size_t value_size = 0) : + value_size(value_size) { + depth = static_cast(std::ceil(std::log2(max_entries))); + digest_size = Hash::digest_bits; + + tree.reset(new merkle_tree(depth, digest_size)); + } + + void add(const std::vector &value) { + assert(value_size == 0 || value.size() == value_size); + const std::vector hash = Hash::get_hash(value); + if (hash_to_pos.find(hash) == hash_to_pos.end()) { + const std::size_t pos = hash_to_pos.size(); + tree->set_value(pos, hash); + hash_to_pos[hash] = pos; + } + } + + bool is_in_set(const std::vector &value) const { + assert(value_size == 0 || value.size() == value_size); + const std::vector hash = Hash::get_hash(value); + return (hash_to_pos.find(hash) != hash_to_pos.end()); + } + + set_commitment get_commitment() const { + return tree->get_root(); + } + + set_membership_proof get_membership_proof(const std::vector &value) const { + const std::vector hash = Hash::get_hash(value); + auto it = hash_to_pos.find(hash); + assert(it != hash_to_pos.end()); + + set_membership_proof proof; + proof.address = it->second; + proof.merkle_path = tree->get_path(it->second); + + return proof; + } + }; + + } // namespace snark + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SNARK_SET_COMMITMENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/components/voting/r1cs/encrypted_input_voting.hpp b/libs/blueprint/include/nil/blueprint/components/voting/r1cs/encrypted_input_voting.hpp new file mode 100644 index 000000000..08b700569 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/components/voting/r1cs/encrypted_input_voting.hpp @@ -0,0 +1,148 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_VOTING_ENCRYPTED_INPUT_VOTING_COMPONENT_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_VOTING_ENCRYPTED_INPUT_VOTING_COMPONENT_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + template, + typename MerkleTreeHashComponent = HashComponent, + typename Field = typename HashComponent::field_type> + struct encrypted_input_voting : public component { + using field_type = Field; + using hash_component = HashComponent; + using merkle_proof_validating_component = + merkle_proof_validate; + using merkle_proof_component = typename merkle_proof_validating_component::merkle_proof_component; + + digest_variable sn_computed; + digest_variable pk; + digest_variable pk_leaf; + hash_component pk_hasher; + MerkleTreeHashComponent pk_leaf_hasher; + merkle_proof_validating_component root_validator; + hash_component sn_hasher; + bit_vector_copy_component check_sn; + + block_variable m; + block_variable eid; + digest_variable sn; + block_variable sk; + + /** + * @warning If you just want to compute intermediate fields (\p rt and \p sn) it is sufficient to + * instantiate encrypted_input_voting component and call \p generate_assignments, but if you want + * to check satisfiability of the CS you have to call \p generate_assignments for \p rt and \p sn + * with expected values before call \p is_satisfied for \p bp. This is due to using of the + * bit_vector_copy_component which is responsible for both logics: copying of the computed fields + * (\p rt and \p sn) and comparison of the computed and passed values. So, if you don't call \p + * generate_assignments for \p rt and \p sn satisfiability check will always be positive, i.e. + * false positive error happens. Another solution - instead of manual calling to the \p + * generate_assignments for \p rt and \p sn just use encrypted_input_voting's \p + * generate_assignments accepting additional parameters \p root and \p sn. + */ + encrypted_input_voting(blueprint &bp, + const block_variable &m, + const block_variable &eid, + const digest_variable &sn, + const digest_variable &rt, + const detail::blueprint_linear_combination_vector &address_bits, + const merkle_proof_component &path, + const block_variable &sk, + const detail::blueprint_linear_combination &read_successful) : + component(bp), + // private fields + sn_computed(bp, hash_component::digest_bits), pk(bp, hash_component::digest_bits), + pk_leaf(bp, MerkleTreeHashComponent::digest_bits), pk_hasher(bp, sk, pk), + pk_leaf_hasher(bp, pk, pk_leaf), + root_validator(bp, path.tree_depth, address_bits, pk_leaf, rt, path, read_successful), + sn_hasher(bp, + std::vector { + eid, + sk, + }, + sn_computed), + check_sn(bp, sn_computed.bits, sn.bits, read_successful, field_type::number_bits), + // public fields + m(m), eid(eid), sn(sn), sk(sk) { + } + + // TODO: review all necessary constrains, for example, eid + void generate_gates() { + pk_hasher.generate_gates(); + pk_leaf_hasher.generate_gates(); + root_validator.generate_gates(); + sn_hasher.generate_gates(); + check_sn.generate_gates(false, false); + + math::linear_combination sum_m_i; + for (const auto &m_i : m.bits) { + // m_i == 0 or m_i == 1 + generate_boolean_r1cs_constraint( + this->bp, static_cast>(m_i)); + sum_m_i = sum_m_i + m_i; + } + // sum_m_i == 1 + this->bp.add_r1cs_constraint( + snark::r1cs_constraint(Field::value_type::one(), sum_m_i, Field::value_type::one())); + } + + private: + void generate_assignments() { + pk_hasher.generate_assignments(); + pk_leaf_hasher.generate_assignments(); + root_validator.generate_assignments(); + sn_hasher.generate_assignments(); + check_sn.generate_assignments(); + } + + public: + /** + * @brief Witness generation should be called every time we update + */ + void generate_assignments(const std::vector &root, const std::vector &sn) { + generate_assignments(); + root_validator.root.generate_assignments(root); + this->sn.generate_assignments(sn); + } + + inline std::size_t get_input_size() const { + return m.block_size + eid.block_size + sn.digest_size + root_validator.root.digest_size; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VOTING_ENCRYPTED_INPUT_VOTING_COMPONENT_HPP diff --git a/libs/blueprint/include/nil/blueprint/detail/huang_lu.hpp b/libs/blueprint/include/nil/blueprint/detail/huang_lu.hpp new file mode 100644 index 000000000..bf1735099 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/detail/huang_lu.hpp @@ -0,0 +1,93 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + // Implementing [https://arxiv.org/pdf/1907.04505.pdf] + // 11/9 approximation ratio + std::unordered_map huang_lu( + std::list> &sizes, + std::size_t agent_amount) { + sizes.sort( + [](const std::pair &a, + const std::pair &b) { + return a.second > b.second; + } + ); + std::size_t max_item = 0, + sum = 0; + for (auto [key, item] : sizes) { + max_item = std::max(max_item, item); + sum += item; + } + std::size_t left = std::max((sum + agent_amount - 1) / agent_amount, + max_item); + std::size_t right = 2 * left; + std::unordered_map best_assignment; + std::unordered_map assignment; + std::list> tasks_remaning; + while (left < right) { + std::size_t threshold = left + (right - left) / 2; + tasks_remaning = sizes; + + for (std::size_t i = 0; i < agent_amount; i++) { + std::size_t curr_bundle_size = 0; + for (auto it = tasks_remaning.begin(); + it != tasks_remaning.end();) { + + if (curr_bundle_size + it->second <= threshold) { + assignment[it->first] = i; + curr_bundle_size += it->second; + it = tasks_remaning.erase(it); + } else { + it++; + } + } + if (curr_bundle_size == 0) { + break; + } + } + if (tasks_remaning.size() == 0) { + right = threshold; + best_assignment = assignment; + } else { + left = threshold + 1; + } + + if (threshold == left) { + break; + } + } + return best_assignment; + } + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/detail/lookup_table_loaders.hpp b/libs/blueprint/include/nil/blueprint/detail/lookup_table_loaders.hpp new file mode 100644 index 000000000..8e80698f9 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/detail/lookup_table_loaders.hpp @@ -0,0 +1,146 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + struct value_vector_parser : boost::spirit::qi::grammar(), + boost::spirit::qi::ascii::space_type> { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using return_type = std::vector; + + value_vector_parser(std::size_t size) : value_vector_parser::base_type(start) { + using boost::spirit::qi::uint_parser; + using boost::spirit::qi::_val; + using boost::spirit::qi::_1; + using boost::spirit::qi::_2; + using boost::spirit::qi::repeat; + using boost::phoenix::construct; + using boost::phoenix::val; + auto number = uint_parser(); + start = repeat(size)[number]; + + boost::spirit::qi::on_error( + start, + std::cerr << val("Error! Expecting ") << boost::spirit::qi::_4 << val(" here: \"") + << construct(boost::spirit::_3, boost::spirit::_2) << val("\"\n") + ); + } + + boost::spirit::qi::rule start; + }; + + template + bool parse_lookup_table( + std::istream &ist, + const std::size_t line_size, + std::vector> &result) { + using value_type = typename BlueprintFieldType::value_type; + std::string line; + // Get the table size + std::getline(ist, line); + std::size_t table_size = std::stoull(line); + result.resize(line_size); + for (auto &column : result) { + column.resize(table_size); + } + for (std::size_t i = 0; i < table_size; i++) { + std::getline(ist, line); + std::vector row; + value_vector_parser parser(line_size); + boost::spirit::qi::ascii::space_type space; + bool parsing_result = + boost::spirit::qi::phrase_parse(line.begin(), line.end(), parser, space, row); + if (!parsing_result) { + return false; + } + for (std::size_t j = 0; j < line_size; j++) { + result[j][i] = row[j]; + } + } + return true; + } + + // Loads the table from file, trying multiple different filen paths if one fails + template + bool load_lookup_table( + const std::set &candidate_file_paths, + const std::size_t line_size, + std::vector> &result) { + + for (const auto &path : candidate_file_paths) { + // try opening the file + std::ifstream file(path); + if (!file.is_open()) { + continue; + } + auto status = parse_lookup_table(file, line_size, result); + if (status) { + return true; + } + } + return false; + } + + // This forcefully includes the table in the binary + // It's not in binary form here because for current tables the ASCII form is actually smaller + // due to the advanced compression stratgy of "we don't have to write leading zeroes" + template + bool load_lookup_table_from_bin( + std::string table_name, + std::vector> &result) { + + if (table_name == "8_split_4") { + const std::string table_data = _8_SPLIT_4; + std::stringstream ss(table_data); + return parse_lookup_table(ss, 2, result); + } else if (table_name == "8_split_7") { + const std::string table_data = _8_SPLIT_7; + std::stringstream ss(table_data); + return parse_lookup_table(ss, 2, result); + } else { + return false; + } + } + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/detail/lookup_table_precomputes.hpp b/libs/blueprint/include/nil/blueprint/detail/lookup_table_precomputes.hpp new file mode 100644 index 000000000..0c4282b85 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/detail/lookup_table_precomputes.hpp @@ -0,0 +1,27 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define _8_SPLIT_4 "65536\nfb ffcf\nf7 ff3f\nf3 ff8f\nf3 ff2f\nfb ff4f\nf7 ff1f\nf3 ff0f\nef fcff\nfb f7cf\ndf f3df\nd7 fb3f\ndf f37f\ndb fb4f\nd7 fb1f\nf3 f78f\nd7 f39f\nf3 f72f\ndf f35f\ncf f8ff\nef f4ff\ndf f1ff\ncf f0ff\nb7 ef3f\nbf cf7f\nb7 cf3f\nb7 ef1f\nb7 cf1f\neb fecf\neb fcef\nfb fdcf\nef fcdf\neb fccf\ncf f8df\neb f6cf\neb f4ef\ndb f1ef\ncb f0ef\nfb f5cf\nef f4df\neb f4cf\ndf f1df\ndb f1cf\ncf f0df\ncb f0cf\naf ecff\nef dcff\naf ccff\nab eccf\neb dccf\nab cccf\nbf c7ff\ndf d3df\n9f c3df\n97 eb3f\n9f e37f\nd7 db3f\n9f cb7f\n97 cb3f\nb7 c7bf\ndf d37f\nbf c77f\n9f c37f\n97 eb1f\nb3 e72f\n9f e35f\nd7 db1f\n97 cb1f\nd7 d39f\n97 c39f\nf3 d72f\ndb d36f\nb3 c72f\n9b c36f\ndf d35f\n9f c35f\nfd fff3\nf9 ffcb\nf9 ffe3\nfd ffd3\nfb ffc7\nf9 ffc3\nf5 ff3b\n8f e8ff\naf e4ff\n9f e1ff\n8f e0ff\nf7 ff37\nf5 ff33\ncf d8ff\n8f c8ff\nef d4ff\ndf d1ff\ncf d0ff\nbf c5ff\naf c4ff\n9f c1ff\n8f c0ff\nf1 ff2b\nf1 ff8b\nf9 ff4b\nf1 ff0b\nf3 ff27\nf1 ff23\nf5 ff93\nf3 ff87\nf1 ff83\nf7 ff17\nfd ff53\nf5 ff13\nfb ff47\nf3 ff07\nf9 ff43\nf1 ff03\ned fcfb\ned fef3\nef fcf7\nfd fdf3\ned fcf3\ne9 fceb\ne9 fecb\nf9 fdcb\ne9 fccb\ne9 fee3\neb fce7\nf9 fde3\ne9 fce3\ned fed3\ne9 fec3\nfd fdd3\ned fcd3\nfb fdc7\nf9 fdc3\ne9 fcc3\nf9 f7cb\nfb f7c7\nf9 f7c3\nd9 fb4b\nf1 f78b\nd7 fb17\ndb fb47\nd3 fb07\nd9 fb43\nf3 f787\nf1 f783\ncd f8fb\ned f4fb\ncd f0fb\ncf f8f7\ncd f8f3\nef f4f7\ncf f0f7\ned f4f3\ncd f0f3\ne9 f4eb\nc9 f0eb\ne9 f6cb\nf9 f5cb\ne9 f4cb\nd9 f1cb\nc9 f0cb\ncb f8e7\nc9 f8e3\neb f4e7\ncb f0e7\ne9 f4e3\nc9 f0e3\ncd f8d3\ndb f9c7\nd9 f9c3\nc9 f8c3\ne9 f6c3\ned f4d3\ncd f0d3\nfb f5c7\ndb f1c7\nf9 f5c3\ne9 f4c3\nd9 f1c3\nc9 f0c3\nbf cff7\nfd dff3\nf9 dfe3\nfd dfd3\nbd cf7b\n8b e8cf\nab e4ef\n9b e1ef\n8b e0ef\nab e4cf\n9f e1df\n9b e1cf\n8b e0cf\nb7 ef37\nb7 cfb7\nbf cf77\nb7 cf37\nbd cf73\n8b c8cf\neb d4ef\ndb d1ef\ncb d0ef\nab c4ef\n9b c1ef\n8b c0ef\nef d4df\neb d4cf\ndf d1df\ndb d1cf\ncf d0df\ncb d0cf\naf c4df\nab c4cf\n9f c1df\n9b c1cf\n8b c0cf\nb3 ef27\nb3 cf27\nf1 df23\nb7 ef17\nbd ef53\nb3 ef07\nb9 ef43\nb7 cf17\nfd df53\nbd cf53\nb3 cf07\nb9 cf43\naf ecf7\nbd edf3\nad ecf3\nef dcf7\naf ccf7\nfd ddf3\ned dcf3\nbd cdf3\nad ccf3\nb9 ede3\na9 ece3\ne9 dee3\nf9 dde3\ne9 dce3\nb9 cde3\na9 cce3\nbd edd3\nb9 edc3\na9 ecc3\ned dcd3\nad ccd3\nf9 ddc3\ne9 dcc3\nb9 cdc3\na9 ccc3\nbd c7fb\n9f cbf7\nbf c7f7\n9f c3f7\nbd c7f3\n9d cb7b\nb5 c7bb\nbd c77b\nb5 c73b\n9d c37b\n97 cbb7\n9f cb77\n97 cb37\n9d cb73\nb7 c7b7\n97 c3b7\nb5 c7b3\nd7 d337\nbf c777\nb7 c737\n9f c377\n97 c337\nbd c773\nb5 c733\n9d c373\nb1 c72b\n93 cb27\nd3 d327\nb3 c727\n93 c327\nb1 c723\n97 eb17\n93 eb07\nd7 db17\n97 cb17\nd3 db07\n93 cb07\nf7 d717\nd7 d317\nb7 c717\n97 c317\nf3 d707\nd3 d307\nb3 c707\n93 c307\nad e4fb\n8d e0fb\nad c6fb\ned d4fb\ncd d0fb\nbd c5fb\nad c4fb\n9d c1fb\n8d c0fb\n8f e8f7\n8d e8f3\naf e4f7\nad e4f3\n8d e0f3\ncf d8f7\n8f c8f7\ncd d8f3\n9d c9f3\n8d c8f3\naf c6f7\n8f c2f7\nad c6f3\nef d4f7\ncf d0f7\nbf c5f7\naf c4f7\n9f c1f7\ned d4f3\ncd d0f3\nbd c5f3\nad c4f3\n9d c1f3\n8d c0f3\na9 e4eb\n89 e0eb\ne9 d4eb\nc9 d0eb\na9 c4eb\n89 c0eb\na9 e4cb\n89 e0cb\n89 e8e3\nab e4e7\na9 e4e3\n89 e0e3\nc9 d8e3\n89 c8e3\neb d4e7\ncb d0e7\nab c4e7\ne9 d4e3\nc9 d0e3\na9 c4e3\n89 c0e3\n89 e8c3\na9 e4c3\n89 e0c3\ncd d8d3\n8d c8d3\nc9 d8c3\n89 c8c3\ned d4d3\ncd d0d3\nad c4d3\n8d c0d3\ne9 d4c3\nc9 d0c3\na9 c4c3\n89 c0c3\nfa ffce\nfd 7ff3\n7f 3ff7\nf6 ff3e\nfb 7fcf\nf6 ffbc\nf7 ff3d\nf6 ff3c\nf2 ff8e\nfa ff4e\nf6 ff1e\nf2 ff0e\nf9 7fe3\n7b 3fe7\nf9 7fcb\nfd 7fd3\nfb 7fc7\nf9 7fc3\nf6 ff9c\nf7 ff1d\nf6 ff1c\nea fcce\nf5 7fb3\nf5 7f33\n7d 3f73\n7b bf4f\nf3 7f8f\nfb 7f4f\nf3 7f0f\n7b 3f4f\n79 bf4b\n7d bf53\n7b bf47\n79 bf43\n73 bf07\nf1 7fa3\nf1 7f8b\nf5 7f93\nf3 7f87\nf1 7f83\nf1 7f23\n79 3f63\nf9 7f4b\nf1 7f0b\n79 3f4b\nfd 7f53\nfb 7f47\nf9 7f43\nf5 7f13\nf3 7f07\nf1 7f03\n7d 3f53\n7b 3f47\n79 3f43\n73 3f07\ne7 fe3f\ne7 fcbf\nf7 fd3f\nef fc7f\ne7 fc3f\nc7 fa3f\ne7 f4bf\nd7 f1bf\nc7 f0bf\nd7 f93f\ncf f87f\nc7 f83f\nf7 f53f\nef f47f\ne7 f43f\ncf f07f\nc7 f03f\na7 ee3f\naf ce7f\na7 ce3f\na7 ecbf\ne7 dcbf\na7 ccbf\naf ec7f\na7 ec3f\nbf cd7f\nf7 dd3f\nb7 cd3f\nef dc7f\naf cc7f\ne7 dc3f\na7 cc3f\n87 ea3f\nc7 da3f\n87 ca3f\n87 e8bf\n87 c8bf\na7 e4bf\n97 e1bf\n87 e0bf\ne7 d4bf\na7 c4bf\nd7 d1bf\n97 c1bf\nc7 d0bf\n87 c0bf\n97 e93f\n8f e87f\n87 e83f\nd7 d93f\n97 c93f\ncf d87f\n8f c87f\nc7 d83f\n87 c83f\naf e47f\na7 e43f\n8f e07f\n87 e03f\nf7 d53f\nb7 c53f\nef d47f\naf c47f\ne7 d43f\na7 c43f\ndf d17f\n9f c17f\nd7 d13f\n97 c13f\ncf d07f\n8f c07f\nc7 d03f\n87 c03f\nef 7cff\nfa f7ce\nde f3de\ned 7ef3\nf5 df1b\n6f 3ef7\nd6 fb3e\nf6 f73e\nde f37e\nd6 f33e\n7b bdcf\n6b bccf\neb 7ecf\nfb 7dcf\nef 7cdf\neb 7ccf\n7b 3dcf\n6b 3ccf\nd7 fb3d\nd6 fb3c\nd7 f3bd\nf6 f7bc\nd6 f3bc\nf6 f73c\nd6 f33c\nda fb4e\nd6 fb1e\nf2 f78e\nd6 f39e\nfa f74e\nf6 f71e\nf2 f70e\nde f35e\nda f34e\nd6 f31e\n79 bdcb\n6d bcdb\n69 bccb\n7d bdd3\n7b bdc7\n79 bdc3\n6d bcd3\n69 bcc3\ne9 7ee3\nf1 df0b\n6b 3ee7\ne9 7ecb\ned 7ed3\ne9 7ec3\nf9 7dcb\ne9 7ccb\n7d 3ddb\n79 3dcb\n6d 3cdb\n69 3ccb\nfd 7dd3\nf9 7dc3\n7f 3dd7\n7d 3dd3\n7b 3dc7\n79 3dc3\n6d 3cd3\n69 3cc3\nd6 fb9c\nd7 fb1d\nd6 fb1c\nf2 f7ac\nf3 f78d\nd7 f39d\nf6 f79c\nf2 f78c\nd6 f39c\nf3 f72d\nf2 f72c\nde f1fe\nce f0fe\n6f bc7f\n67 bc3f\ne7 7cbf\nef 7c7f\ne7 7c3f\n6f 3c7f\n67 3c3f\nca f8ce\nea f6ce\nce f2de\nda f1ee\nca f0ee\nfa f5ce\nea f4ce\nde f1de\nda f1ce\nce f0de\nca f0ce\neb f6cd\nea f6cc\nce f2dc\nd1 f38b\neb f4cd\ne6 fe3e\ne7 fe3d\ne6 fe3c\ne6 fc3e\nc6 fa3e\nc7 fa3d\nc6 fa3c\nc6 f83e\nd7 f93d\nd6 f93c\nc7 f83d\nc6 f83c\ne6 f63e\nce f27e\nc6 f23e\ne6 f63c\nce f27c\nc6 f23c\nd6 f1be\nc6 f0be\nd7 f1bd\nd6 f1bc\nc7 f0bd\nc6 f0bc\nf6 f53e\ne6 f43e\nce f07e\nc6 f03e\nb5 ef3b\ncf f07d\n7f 37ff\n7d 37fb\n7f 37f7\n7d 37f3\nb6 ef3e\nbe cf7e\nb6 cf3e\n7b b7cf\n7b 37ef\nfb 77cf\ndf 73df\n7b 37cf\nb6 efbc\nb7 ef3d\nb6 ef3c\nb7 cfbd\nb6 cfbc\nb7 cf3d\nb6 cf3c\nb6 ef1e\nb6 cf1e\n79 b7cb\n7b b7c7\n79 b7c3\n79 37eb\n7b 37e7\n79 37e3\nf9 77cb\n7d 37db\n79 37cb\nfb 77c7\nf9 77c3\n7f 37d7\n7d 37d3\n7b 37c7\n79 37c3\nb6 ef9c\nb6 ef1c\nb2 cfac\nb6 cf9c\nb2 cf8c\nb3 cf2d\nb2 cf2c\nb7 cf1d\nb3 cf0d\nb6 cf1c\nb2 cf0c\n57 b33f\n5f 337f\n57 333f\naf cefd\nae cefc\n95 cbbb\naf ccfd\na6 ee3e\na6 ec3e\nae ce7e\na6 ce3e\na6 ccbe\ne6 dc3e\nbe cd7e\nae cc7e\na6 cc3e\n5b bb4f\n73 b78f\n57 b39f\n73 b70f\n5f b35f\n5b b34f\n57 b31f\ndb 7b4f\n5b 3b4f\na7 ee3d\na6 ee3c\nf3 778f\nd7 739f\n73 378f\n5b 336f\na7 cebd\na6 cebc\naf ce7d\na7 ce3d\nae ce7c\na6 ce3c\ndf 735f\n73 370f\n5f 335f\n5b 334f\n57 331f\nb7 cdbd\na7 ccbd\na6 ccbc\n9d cbfb\nb7 cd3d\n95 cb3b\naf cc7d\n59 bb4b\n5b bb47\n59 bb43\n53 bb07\n71 b78b\n73 b787\n71 b783\n71 b70b\n59 b34b\nd9 7b4b\n59 3b4b\ndb 7b47\nd9 7b43\nd3 7b07\n5b 3b47\n59 3b43\n53 3b07\nf1 778b\n71 378b\nf3 7787\nf1 7783\n73 3787\n71 3783\n71 370b\n59 334b\nfb 7747\ndb 7347\n7b 3747\n79 3743\n5b 3347\n59 3343\ne7 fe37\nf5 fdb3\ne7 fcb7\nf5 fd3b\ned fc7b\nf7 fd37\nf5 fd33\nef fc77\ne7 fc37\nc7 f8b7\nd5 f93b\ncd f87b\nd7 f937\nd5 f933\ncf f877\ncd f873\nc7 f837\nbf cf7d\na5 ce3b\na7 ee37\na7 ce37\ndf f37d\nc5 f23b\nc7 f237\nbf cdfd\na5 ccbb\na7 ecb7\ne7 dcb7\na7 ccb7\ndf f1fd\nc5 f0bb\ne7 f4b7\nc7 f0b7\nb5 ed3b\nad ec7b\ned dc7b\nad cc7b\nbf cd7d\na5 cc3b\nb5 ed33\naf ec77\nad ec73\na7 ec37\nf7 dd37\nb7 cd37\nf5 dd33\nb5 cd33\nef dc77\naf cc77\ned dc73\nad cc73\ne7 dc37\na7 cc37\nf5 f53b\ned f47b\nd5 f13b\ncd f07b\ndf f17d\nc5 f03b\nf7 f537\nf5 f533\nd5 f133\ncf f077\ncd f073\nc7 f037\n87 e8b7\nc7 d8b7\n87 c8b7\n8d e87b\ncd d87b\n8d c87b\n8f e877\n8d e873\n87 e837\ncf d877\n8f c877\ncd d873\n8d c873\nc7 d837\n87 c837\n87 c2b7\ned 7e73\n6d 3e73\n8d c27b\n87 e237\n8f c277\n8d c273\nc7 d237\n87 c237\n95 c1bb\na7 e4b7\nb5 c5b3\ne7 d4b7\na7 c4b7\n97 c1b7\n95 c1b3\nc7 d0b7\n6d bc7b\n6d 3c7b\n7d bd73\n75 bd33\n6d bc73\n7d 3d73\n75 3d33\nef 7c77\n6f 3c77\n6d 3c73\nad e47b\n95 e13b\n8d e07b\ned d47b\nad c47b\n9d c17b\nd5 d13b\n95 c13b\ncd d07b\n8d c07b\n95 e133\n8d e073\naf c477\ned d473\nad c473\ne7 d437\na7 c437\n9f c177\n9d c173\nd7 d137\n97 c137\nd5 d133\n95 c133\ncf d077\ncd d073\n8d c073\nc7 d037\nbe c7fe\n6f b4ff\n5f b1ff\n4f b0ff\ncf 78ff\n85 c2bb\n9f c3fd\n6f 36ff\nef 74ff\ndf 71ff\n7f 35ff\n6f 34ff\n5f 31ff\n4f 30ff\n9e e3de\nde d3de\n9e c3de\n6d b4fb\n4d b0fb\n6d b4f3\n4d b0f3\n6d 36fb\ned 74fb\ncd 70fb\n7d 35fb\n6d 34fb\n5d 31fb\n4d 30fb\nef 74f7\ned 74f3\ncd 70f3\n7f 35f7\n7d 35f3\n6d 34f3\n5f 31f7\n5d 31f3\n4d 30f3\n96 eb3e\nb6 e73e\n9e e37e\n96 e33e\nd6 db3e\n9e cb7e\n96 cb3e\nb6 c7be\nf6 d73e\nde d37e\nd6 d33e\nbe c77e\nb6 c73e\n9e c37e\n96 c33e\n5b b9cf\n4f b8df\n4b b8cf\n6b b6cf\n4f b2df\n6b b4ef\n5b b1ef\n4b b0ef\n7b b5cf\n6b b4cf\n5b b1cf\n4b b0cf\n96 ebbc\n97 eb3d\n96 eb3c\n97 e3bd\nb6 e7bc\n96 e3bc\n85 e23b\n9f e37d\nb6 e73c\n96 e33c\n97 cbbd\n96 cbbc\nd7 db3d\n97 cb3d\nd6 db3c\n96 cb3c\nd7 d3bd\nb7 c7bd\n97 c3bd\nf6 d7bc\nd6 d3bc\nb6 c7bc\n96 c3bc\nc5 d23b\ndf d37d\n85 c23b\n9f c37d\nf6 d73c\nd6 d33c\nb6 c73c\n96 c33c\ndb 79cf\ncf 78df\ncb 78cf\n5b 39cf\n4b 38cf\n6b 36ef\neb 76cf\n6b 36cf\neb 74ef\ndb 71ef\ncb 70ef\n7b 35ef\n6b 34ef\n5b 31ef\n4b 30ef\nfb 75cf\nef 74df\neb 74cf\ndf 71df\ndb 71cf\ncb 70cf\n7b 35cf\n6b 34cf\n5b 31cf\n4b 30cf\n96 eb1e\nb2 e72e\nb6 e71e\nb2 e70e\n9e e35e\n9a e34e\n96 e31e\nd6 db1e\n96 cb1e\nd6 d39e\n96 c39e\nf2 d72e\nda d36e\nb2 c72e\n9a c36e\nf6 d71e\nf2 d70e\nde d35e\nda d34e\nd6 d31e\n59 b9cb\n4d b8db\n49 b8cb\n5b b9c7\n59 b9c3\n4d b8d3\n49 b8c3\n69 b6cb\n69 b6c3\n69 b4eb\n49 b0eb\n69 b4e3\n49 b0e3\n79 b5cb\n6d b4db\n69 b4cb\n59 b1cb\n4d b0db\n49 b0cb\n7b b5c7\n79 b5c3\n6d b4d3\n69 b4c3\n5b b1c7\n59 b1c3\n4d b0d3\n49 b0c3\n92 ebac\n92 eb8c\n93 eb2d\n92 eb2c\n93 eb0d\n96 eb1c\n92 eb0c\n93 e3ad\nb2 e7ac\n92 e3ac\n93 e38d\nb6 e79c\nb2 e78c\n92 e38c\nb3 e72d\n93 e32d\nb2 e72c\n92 e32c\n92 cbac\nd6 db9c\n96 cb9c\n92 cb8c\nd3 db2d\n93 cb2d\nd2 db2c\n92 cb2c\nd7 db1d\nd3 db0d\n97 cb1d\n93 cb0d\nd6 db1c\nd2 db0c\n96 cb1c\n92 cb0c\nd3 d3ad\n93 c3ad\nd9 79cb\nc9 78cb\n5d 39db\n59 39cb\n4d 38db\n49 38cb\nf2 d7ac\nd2 d3ac\nb2 c7ac\n92 c3ac\nd7 d39d\nd3 d38d\n97 c39d\n93 c38d\nf6 d79c\nf2 d78c\nd6 d39c\nd2 d38c\nb6 c79c\nb2 c78c\n96 c39c\n92 c38c\nb3 c72d\n93 c32d\nf2 d72c\nd2 d32c\nb2 c72c\n92 c32c\n5f 39d7\n5d 39d3\n5b 39c7\n59 39c3\n4d 38d3\n49 38c3\n69 36eb\ne9 76cb\n6d 36db\n69 36cb\ne9 74eb\nc9 70eb\n79 35eb\n69 34eb\n59 31eb\n49 30eb\neb 74e7\ne9 74e3\ncb 70e7\nc9 70e3\n7b 35e7\n79 35e3\n69 34e3\n5b 31e7\n59 31e3\n49 30e3\nf9 75cb\ne9 74cb\nd9 71cb\nc9 70cb\n7d 35db\n79 35cb\n6d 34db\n69 34cb\n5d 31db\n59 31cb\n4d 30db\n49 30cb\nfb 75c7\nf9 75c3\ned 74d3\ne9 74c3\ndb 71c7\nd9 71c3\ncd 70d3\nc9 70c3\n7f 35d7\n7d 35d3\n7b 35c7\n79 35c3\n6d 34d3\n69 34c3\n5f 31d7\n5d 31d3\n5b 31c7\n59 31c3\n4d 30d3\n49 30c3\n9e e1fe\n8e e0fe\nae c6fe\nde d1fe\nce d0fe\nbe c5fe\nae c4fe\n9e c1fe\n8e c0fe\n4f b87f\n47 b83f\n47 b23f\n67 b4bf\n57 b1bf\n47 b0bf\n6f b47f\n67 b43f\n5f b17f\n57 b13f\n4f b07f\n47 b03f\n8e e2fc\n85 e0bb\n9f e1fd\n8f e0fd\n8e e0fc\n8f cafd\n8e cafc\nc7 78bf\n85 c8bb\n9f c9fd\n8f c8fd\nce d2fc\nae c6fc\n8e c2fc\nc5 d0bb\ndf d1fd\nb5 cfbb\ncf d0fd\na5 c4bb\nbf c5fd\n95 c3bb\naf c4fd\n85 c0bb\n9f c1fd\ncf 787f\nc7 783f\n4f 387f\n47 383f\n4f 327f\n47 323f\ne7 74bf\nd7 71bf\nc7 70bf\n67 34bf\n57 31bf\n47 30bf\nef 747f\ne7 743f\ndf 717f\n6f 347f\n67 343f\n5f 317f\n57 313f\n4f 307f\n47 303f\n8e e2de\n9a e1ee\n8a e0ee\n9e e1de\n9a e1ce\n8e e0de\n8a e0ce\nce d2de\n8e c2de\nda d1ee\nca d0ee\n9a c1ee\n8a c0ee\nde d1de\nda d1ce\nce d0de\nca d0ce\n9e c1de\n9a c1ce\n8e c0de\n8a c0ce\n4d b87b\n4f b877\n4d b873\n47 b837\n6d b47b\n4d b07b\n4d b073\n8b e2ed\n8a e2ec\n8b e2cd\n8a e2cc\n8b e0ed\n8a e0ec\n8b e0cd\n8a e0cc\n8b c2ed\nca d2ec\n8a c2ec\n8b c2cd\nce d2dc\nca d2cc\n8e c2dc\n8a c2cc\n71 bf8b\n8b c0cd\n4d 387b\ncf 7877\n4f 3877\n4d 3873\ne7 74b7\nc7 70b7\ned d4db\n67 34b7\ncd d0db\n47 30b7\ned 747b\n6d 347b\n4d 307b\nef 7477\ne7 7437\ncd 7073\n6f 3477\n6d 3473\n4f 3077\n4d 3073\n86 ea3e\n86 e83e\na6 e63e\n8e e27e\n86 e23e\n96 e1be\n86 e0be\na6 e43e\n8e e07e\n86 e03e\nc6 da3e\n86 ca3e\n86 c8be\nc6 d83e\n8e c87e\n86 c83e\ne6 d63e\nce d27e\nc6 d23e\na6 c63e\n8e c27e\n86 c23e\nd6 d1be\nc6 d0be\na6 c4be\n96 c1be\n86 c0be\nf6 d53e\ne6 d43e\nde d17e\nd6 d13e\nce d07e\nc6 d03e\nb6 c53e\nae c47e\na6 c43e\n9e c17e\n96 c13e\n8e c07e\n86 c03e\n87 ea3d\n86 ea3c\n97 e93d\n87 e83d\n96 e93c\n86 e83c\n87 e2bd\n86 e2bc\na6 e63c\n8e e27c\n86 e23c\n97 e1bd\n87 e0bd\n96 e1bc\n86 e0bc\n85 e03b\n9f e17d\n97 e13d\n8f e07d\n87 e03d\n96 e13c\n8e e07c\n86 e03c\n87 cabd\n86 cabc\nc7 da3d\n8f ca7d\n87 ca3d\nc6 da3c\n8e ca7c\n86 ca3c\n97 c9bd\n87 c8bd\n86 c8bc\nd7 d93d\nc7 d83d\n85 c83b\n9f c97d\n97 c93d\n8f c87d\n87 c83d\nc6 d83c\n8e c87c\n86 c83c\nc7 d2bd\na7 c6bd\n87 c2bd\nc6 d2bc\na6 c6bc\n86 c2bc\ne6 d63c\nce d27c\nc6 d23c\nae c67c\na6 c63c\n8e c27c\n86 c23c\nd7 d1bd\nc7 d0bd\nb7 c5bd\na7 c4bd\n97 c1bd\n87 c0bd\nd6 d1bc\nc6 d0bc\nb6 c5bc\na6 c4bc\n96 c1bc\n86 c0bc\nc5 d03b\ndf d17d\nbd cffb\nd7 d13d\nb5 cf3b\ncf d07d\na5 c43b\nbf c57d\n9d c3fb\nb7 c53d\n95 c33b\naf c47d\n8d c2fb\na7 c43d\n85 c03b\n9f c17d\n7f 1fff\nfa 7fce\n7e 3fde\n3f 8ff7\n7d 1ffb\nfd 5ff3\nbf 4ff7\n7d 1ff3\n76 bfbc\n76 bf3c\nf6 7fbc\n76 3fbc\nf6 7f3c\n76 3f3c\n7b 1fef\n7f 1fdf\n7b 1fcf\n3f fdf\n7a bf4e\nf2 7f8e\nfa 7f4e\nf2 7f0e\n7a 3f4e\n7d 9fd3\n73 bf8d\n72 bf8c\n72 bf2c\n73 bf0d\n76 bf1c\n72 bf0c\n72 3fac\n73 3f8d\n76 3f9c\n72 3f8c\n72 3f2c\n73 3f0d\nf6 7f1c\n76 3f1c\n72 3f0c\n79 1feb\nf9 5fe3\n79 1fe3\n3b fe7\n7d 1fdb\n79 1fcb\nfd 5fd3\n7f 1fd7\n7d 1fd3\n79 1fc3\n3f 8f7f\n37 8f3f\n6f 3efd\n6e 3efc\nbf 4f7f\nb7 4f3f\n3f f7f\n37 f3f\n6a bcce\nea 7ece\n6e 3ede\nf9 774b\n7a 3dee\nea 7cce\n6a 3cce\n3d 8f7b\n75 9f33\n3f 8f77\n3d 8f73\n37 8f37\n3d 2f73\n51 bb8b\n6b bccd\n6b 3eed\n6a 3eec\n6b 3ecd\n6e 3edc\n6a 3ecc\nd1 7b8b\neb 7ccd\n51 3b8b\n6b 3ccd\nb7 4fb7\n75 1fb3\n37 fb7\nbd 4f7b\n3d f7b\nfd 5f73\nbf 4f77\nbd 4f73\nb7 4f37\n7d 1f73\n75 1f33\n3f f77\n3d f73\n37 f37\n3d af53\n39 af43\n75 9f93\ncf 7077\n71 9f23\n39 8f63\n7d 9f53\n79 9f43\n75 9f13\n3d 8f53\n39 8f43\nb9 6f63\n39 2f63\n3d 2f53\n39 2f43\nf1 5fa3\n71 1fa3\n75 1f93\n71 1f83\nf9 5f63\nf1 5f23\nb9 4f63\n39 f63\nfd 5f53\nbd 4f53\nb9 4f43\n7d 1f53\n79 1f43\n75 1f13\n71 1f03\n3d f53\n39 f43\neb fe4f\ne7 fe1f\nf3 fd8f\ne7 fc9f\nf3 fd2f\nfb fd4f\nf3 fd0f\neb fc4f\ncb fa4f\nc7 fa1f\nc7 f89f\nd3 f92f\ncb f86f\ndb f94f\nd7 f91f\nd3 f90f\ncf f85f\ncb f84f\nc7 f81f\nc7 f29f\nd3 f1af\na7 ee1f\na7 ce1f\nf3 f58f\ne7 f49f\nd7 f19f\nd3 f18f\nc7 f09f\nf3 f52f\nd3 f12f\ncb f06f\nfb f54f\nf3 f50f\neb f44f\nd3 f10f\ncf f05f\ncb f04f\nc7 f01f\nb3 ed2f\nab ec6f\nf3 dd2f\nb3 cd2f\neb dc6f\nab cc6f\nb3 ed0f\naf ec5f\nab ec4f\na7 ec1f\nf7 dd1f\nb7 cd1f\nf3 dd0f\nb3 cd0f\nef dc5f\naf cc5f\neb dc4f\nab cc4f\ne7 dc1f\na7 cc1f\n87 ea1f\nc7 da1f\n87 ca1f\n93 e92f\nd3 d92f\n93 c92f\n97 e91f\n93 e90f\n8b e84f\n87 e81f\nd7 d91f\n97 c91f\nd3 d90f\n93 c90f\ncb d84f\n8b c84f\nc7 d81f\n87 c81f\nc7 d29f\n87 c29f\ncb d26f\n8b c26f\n93 e1af\nd3 d1af\n93 c1af\n6b be4f\neb 7e4f\n6b 3e4f\n93 e18f\nd7 d19f\nd3 d18f\n93 c18f\nc7 d09f\nb3 e52f\n9b e16f\n93 e12f\n8b e06f\nf3 d52f\nb3 c52f\ndb d16f\n9b c16f\nd3 d12f\n93 c12f\ncb d06f\n8b c06f\n73 bd8f\nf3 7d8f\n73 3d8f\ne7 7c9f\nb3 e50f\nab e44f\na7 e41f\n93 e10f\n8f e05f\n8b e04f\nf7 d51f\nb7 c51f\nf3 d50f\nb3 c50f\neb d44f\ne7 d41f\na7 c41f\ndf d15f\n9f c15f\ndb d14f\n9b c14f\nd7 d11f\nd3 d10f\n93 c10f\ncf d05f\n8f c05f\ncb d04f\n8b c04f\nc7 d01f\n6b bc6f\n6b 3c6f\n7b bd4f\n73 bd0f\n6f bc5f\n6b bc4f\nfb 7d4f\n7b 3d4f\nf3 7d0f\n73 3d0f\n6f 3c5f\neb 7c4f\n6b 3c4f\n67 3c1f\n4b ba4f\ncb 7a4f\n4b 3a4f\n53 b98f\n47 b89f\nd3 798f\n53 398f\nc7 789f\n4b b86f\n4b 386f\n5b b94f\n53 b90f\n4f b85f\n4b b84f\n47 b81f\ndb 794f\n5b 394f\nd3 790f\n53 390f\ncf 785f\n4f 385f\ncb 784f\n4b 384f\nc7 781f\n47 381f\n4b b24f\n47 b21f\n4f 325f\n4b 324f\n47 321f\n53 31af\n73 b58f\n53 b18f\nf3 758f\n73 358f\ne7 749f\nd3 718f\n53 318f\nc7 709f\n6b b46f\n53 b12f\n4b b06f\n6b 346f\n5b 316f\nd3 712f\n53 312f\ncb 706f\n4b 306f\n73 b50f\n5b b14f\n53 b10f\n4f b05f\n4b b04f\nfb 754f\nf3 750f\n73 350f\neb 744f\n67 341f\n5f 315f\n5b 314f\n57 311f\n53 310f\n4f 305f\n4b 304f\n47 301f\n6f 1eff\nef 5cff\nbf 4dff\naf 4cff\n7a b7ce\n5e 3bde\nfa 77ce\nde 73de\n7e 37de\n7a 37ce\n5e 33de\n6d 1efb\naf 4ef7\n6d 1ef3\n2f ef7\nbd 4dfb\nbf 4df7\nbd 4df3\naf 4cf7\nad 4cf3\n56 b33e\nd6 733e\n56 333e\n2b accf\n6b 9ccf\nab 6cef\n3b 2dcf\n2b 2ccf\n56 bbbc\n56 bb3c\n6b 1eef\n57 b3bd\n76 b7bc\n56 b3bc\n57 b33d\n76 b73c\n56 b33c\n6f 1edf\n6b 1ecf\n2f edf\nd6 7bbc\n56 3bbc\nd6 7b3c\n56 3b3c\nab 4cef\nf6 77bc\nd6 73bc\n76 37bc\n56 33bc\n57 333d\nf6 773c\nd6 733c\n76 373c\n56 333c\nef 5cdf\neb 5ccf\naf 4cdf\nab 4ccf\n7f 1ddf\n7b 1dcf\n6f 1cdf\n6b 1ccf\n5a bb4e\n72 b78e\nda 7b4e\n5a 3b4e\nf2 778e\n72 378e\nfa 774e\nf2 770e\nda 734e\nd6 731e\n3d add3\n39 adc3\n2d acd3\n29 acc3\nc7 7037\n69 9ee3\n6d 9ed3\n79 9dc3\n6d 9cd3\n69 9cc3\n39 8dc3\n29 8cc3\na9 6ceb\nb9 6de3\nab 6ce7\na9 6ce3\n3d 2ddb\n39 2dcb\n2d 2cdb\n29 2ccb\n3f 2dd7\n3d 2dd3\n3b 2dc7\n39 2dc3\n2d 2cd3\n29 2cc3\n69 1eeb\ne9 5ee3\n69 1ee3\n2b ee7\n6d 1edb\n69 1ecb\n6d 1ed3\n69 1ec3\n53 bb8d\n52 bb8c\n52 bb2c\n53 bb0d\n56 bb1c\n52 bb0c\n73 b78d\n53 b38d\n72 b78c\n56 b39c\n52 b38c\n53 b32d\n72 b72c\n52 b32c\n73 b70d\n57 b31d\n53 b30d\n76 b71c\n72 b70c\n56 b31c\n52 b30c\nb9 4de3\nab 4ce7\na9 4ce3\n7d 1ddb\n79 1dcb\n39 dcb\nfd 5dd3\nf9 5dc3\nbd 4dd3\nb9 4dc3\nad 4cd3\na9 4cc3\n7f 1dd7\n7d 1dd3\n79 1dc3\n6d 1cd3\n69 1cc3\n3b dc7\n39 dc3\n29 cc3\n3d ddb\nd3 7b8d\n53 3b8d\n52 3b8c\n52 3b2c\n53 3b0d\nd6 7b1c\n56 3b1c\n52 3b0c\nf3 778d\n73 378d\n53 338d\nf2 778c\n72 378c\n52 338c\n53 332d\nf2 772c\n72 372c\n52 332c\n73 370d\n57 331d\n53 330d\n76 371c\n72 370c\n56 331c\n52 330c\n5e b1fe\n4e b0fe\nde 71fe\n7e 35fe\n6e 34fe\n5e 31fe\n4e 30fe\n2f ac7f\n27 ac3f\n2f 8e7f\n27 8e3f\n6f 9c7f\n67 9c3f\n27 8c3f\n2f 2c7f\n27 2c3f\naf 4e7f\na7 4e3f\n2f e7f\n27 e3f\n4f b2fd\n4e b2fc\n45 b0bb\n5f b1fd\ne7 5cbf\nb7 4dbf\na7 4cbf\n4e 3afc\nef 5c7f\ne7 5c3f\nbf 4d7f\nb7 4d3f\naf 4c7f\na7 4c3f\n6f 1c7f\n67 1c3f\n27 c3f\n4f 38fd\n4f 32fd\n6e 36fc\n4e 32fc\nc5 70bb\ndf 71fd\n65 34bb\n7f 35fd\n45 30bb\n5f 31fd\n4a b8ce\n5a b1ee\n4a b0ee\n6a b4ce\n5a b1ce\n4a b0ce\nca 78ce\n4a 38ce\nce 72de\n6a 36ce\n4e 32de\nda 71ee\nca 70ee\n6a 34ee\n5a 31ee\n4a 30ee\nea 74ce\nde 71de\nda 71ce\nca 70ce\n7a 35ce\n6e 34de\n6a 34ce\n5e 31de\n5a 31ce\n4e 30de\n4a 30ce\n2d ac7b\n35 ad33\n2d ac73\n27 8e37\n6d 9c7b\n75 9d33\n6f 9c77\n6d 9c73\n67 9c37\n37 8d37\n2f 8c77\n27 8c37\n2d 2e73\n2d 2c7b\n3d 2d73\n35 2d33\n2f 2c77\n2d 2c73\na7 4eb7\n27 eb7\n4b b8cd\n4a b8cc\nad 4e7b\n2d e7b\n4f b2dd\n4b b2cd\n6a b6cc\n4e b2dc\n4a b2cc\ned 5e73\naf 4e77\nad 4e73\na7 4e37\n6d 1e73\n2f e77\n2d e73\n27 e37\n51 b38b\n6b b4cd\nb5 4dbb\nb7 4db7\nb5 4db3\na7 4cb7\n4b 38ed\ncb 78cd\n4b 38cd\nbd 4d7b\nb5 4d3b\nad 4c7b\n6d 1c7b\nca 78cc\n4e 38dc\n4a 38cc\n4b 32ed\n4a 32ec\nef 5c77\nbf 4d77\nbd 4d73\nb7 4d37\nb5 4d33\naf 4c77\n4b 32cd\nad 4c73\na7 4c37\n7d 1d73\n75 1d33\n6f 1c77\n6d 1c73\n3f d77\n37 d37\n2f c77\n27 c37\nce 72dc\n4e 32dc\n4a 32cc\nd1 738b\neb 74cd\n51 338b\n6b 34cd\n46 b23e\n56 b1be\n46 b0be\n5e b17e\n56 b13e\n4e b07e\n46 b03e\nc6 723e\n46 323e\nd6 71be\nc6 70be\n56 31be\n46 30be\nde 717e\n5e 317e\n56 313e\n4e 307e\n46 303e\n2b ac6f\n2f ac5f\n2b ac4f\n6b 9c6f\n6f 9c5f\n6b 9c4f\n67 9c1f\n27 8c1f\nab 6c6f\n2b 2c6f\n2f 2c5f\n2b 2c4f\n27 2c1f\n47 b2bd\n46 b2bc\n4f b27d\n47 b23d\n4e b27c\n46 b23c\n57 b1bd\n47 b0bd\n56 b1bc\n46 b0bc\n45 b03b\n5f b17d\ne7 5c9f\na7 4c9f\n67 1c9f\nab 4c6f\n6f 1c5f\n6b 1c4f\n67 1c1f\n27 c1f\n46 32bc\n47 323d\nce 727c\nc6 723c\n4e 327c\n46 323c\nd7 71bd\nc7 70bd\n57 31bd\n47 30bd\nd6 71bc\nc6 70bc\n56 31bc\n46 30bc\nc5 703b\ndf 717d\n45 303b\n5f 317d\n3d 2ffb\n57 313d\n2d 2efb\n47 303d\nea fe4e\ne6 fe1e\neb fe4d\nea fe4c\ne7 fe1d\ne6 fe1c\nf2 fd8c\ne2 fc8c\nea fc4e\nd1 fb0b\neb fc4d\nc6 f29e\n2d 8c7b\nc3 fa2d\ne2 f68c\nc7 f29d\nc6 f29c\nc6 fa1e\nca fa4c\nc7 fa1d\nc6 fa1c\ne3 f62d\ne2 f62c\ncb f26d\nca f26c\ne6 f61e\nce f25e\nca f24e\nc6 f21e\n6b bcef\nea f64c\n67 bcbf\ne6 f61c\n4f b8ff\nce f25c\n4b b8ef\nca f24c\n47 b8bf\nc6 f21c\nd2 f1ae\nd3 f1ad\nd2 f1ac\nc3 f0ad\nc2 f0ac\nd6 f19e\nd2 f18e\nc6 f09e\nf2 f58c\nd2 f92c\ne3 f48d\ne2 f48c\nc2 f82c\nd7 f19d\nd6 f19c\nd3 f18d\nd2 f18c\nc6 f09c\nc3 f08d\nc2 f08c\nca f84e\nc6 f81e\nd7 f91d\nd6 f91c\nd2 f90c\ncb f84d\nca f84c\nc7 f81d\nc6 f81c\nc2 f80c\nb1 ef2b\ncb f06d\nf2 f50e\nea f44e\nd2 f10e\nce f05e\nca f04e\nc6 f01e\nd9 f3cb\nf3 f50d\nc9 f2cb\ne3 f40d\nb5 ef1b\ncf f05d\nb1 ef0b\ncb f04d\na3 ce2d\na2 ce2c\na6 ee1e\na6 ce1e\na7 ee1d\na6 ee1c\na7 ce1d\na6 ce1c\na3 ce0d\na2 ce0c\nb2 ed2e\na6 ec1e\ne6 dc1e\na6 cc1e\n83 e2ad\n82 e2ac\nc3 d2ad\n83 c2ad\nc2 d2ac\n82 c2ac\nc6 d29e\n86 c29e\n83 e28d\n82 e28c\nc7 d29d\n87 c29d\nc6 d29c\n86 c29c\nc3 d28d\n83 c28d\nc2 d28c\n82 c28c\n83 ea2d\n82 ea2c\nc3 da2d\n83 ca2d\nc2 da2c\n82 ca2c\nca d26e\n8a c26e\n86 ea1e\nc6 da1e\n86 ca1e\na3 e62d\na2 e62c\n8b e26d\n8a e26c\n83 e22d\n82 e22c\na3 c62d\ne2 d62c\na2 c62c\n8b c26d\nca d26c\n8a c26c\n83 c22d\nc2 d22c\n82 c22c\n87 ea1d\n86 ea1c\n83 ea0d\n82 ea0c\nc7 da1d\n87 ca1d\nc6 da1c\n86 ca1c\nc3 da0d\n83 ca0d\nc2 da0c\n82 ca0c\na6 e61e\n8e e25e\n8a e24e\n86 e21e\ne6 d61e\nce d25e\n8e c25e\nca d24e\n8a c24e\nc6 d21e\n86 c21e\n27 acbf\na6 e61c\n67 9cbf\ne6 d61c\n27 8cbf\na6 c61c\n92 e1ae\nd2 d1ae\n92 c1ae\n93 e1ad\n92 e1ac\n83 e0ad\n82 e0ac\nd3 d1ad\n93 c1ad\nd2 d1ac\n92 c1ac\nc3 d0ad\n83 c0ad\nc2 d0ac\n82 c0ac\n96 e19e\n92 e18e\n86 e09e\nd6 d19e\n96 c19e\nd2 d18e\n92 c18e\nc6 d09e\n86 c09e\n92 e92e\n96 e19c\n93 e18d\n92 e18c\n83 e08d\n82 e08c\nd7 d19d\n97 c19d\nd6 d19c\n96 c19c\nd3 d18d\n93 c18d\nd2 d18c\n92 c18c\nc7 d09d\n87 c09d\nc6 d09c\n86 c09c\nc3 d08d\n83 c08d\nc2 d08c\n82 c08c\n93 e92d\n83 e82d\n82 e82c\n93 c92d\n83 c82d\nc2 d82c\n82 c82c\nb2 e52e\n9a e16e\n92 e12e\n8a e06e\nf2 d52e\nb2 c52e\nda d16e\n9a c16e\nd2 d12e\n92 c12e\nca d06e\n8a c06e\n86 e81e\nc6 d81e\n86 c81e\n93 e12d\n92 e12c\n8b e06d\n8a e06c\n83 e02d\n82 e02c\nb1 cf2b\ncb d06d\n96 e91c\n93 e90d\n87 e81d\n86 e81c\n83 e80d\n82 e80c\nd7 d91d\n97 c91d\n93 c90d\nc7 d81d\n87 c81d\nc6 d81c\n86 c81c\n83 c80d\nc2 d80c\n82 c80c\nb2 e50e\na6 e41e\n92 e10e\n8e e05e\n8a e04e\n86 e01e\nf6 d51e\nb6 c51e\nf2 d50e\nb2 c50e\ne6 d41e\nde d15e\n9e c15e\nda d14e\n9a c14e\nd6 d11e\n96 c11e\nd2 d10e\n92 c10e\nce d05e\n8e c05e\nca d04e\n8a c04e\nc6 d01e\n86 c01e\n96 e11c\n93 e10d\n92 e10c\n8f e05d\n8e e05c\n8b e04d\n8a e04c\n86 e01c\n83 e00d\n82 e00c\n79 bfcb\n93 c10d\nb5 cf1b\n2f 2ef7\ncf d05d\nb1 cf0b\n2b 2ee7\ncb d04d\n71 bf0b\n8b c04d\n69 becb\n83 c00d\n63 3e8d\n62 3e8c\n6a be4e\nea 7e4e\n6a 3e4e\n6b be4d\n6a be4c\n63 be0d\n62 be0c\neb 7e4d\n6b 3e4d\nea 7e4c\n6a 3e4c\n63 3e0d\n62 3e0c\n63 bc8d\n62 bc8c\n73 3d8d\n72 3d8c\ne3 7c8d\n63 3c8d\ne2 7c8c\n62 3c8c\n6a bc4e\nea 7c4e\n6a 3c4e\n59 bbcb\n73 bd0d\n51 bb0b\n6b bc4d\n49 bacb\n63 bc0d\nd9 7bcb\nf3 7d0d\n59 3bcb\n73 3d0d\nd1 7b0b\neb 7c4d\n51 3b0b\n6b 3c4d\nc9 7acb\ne3 7c0d\n49 3acb\n63 3c0d\n46 b29c\n42 b28c\n4a b26c\n43 b22d\n42 b22c\nca 7a4e\n4a 3a4e\n43 ba0d\n42 ba0c\n4b 3a4d\n4a 3a4c\n43 3a0d\n42 3a0c\n46 b21e\n4a 324e\nc6 721e\n46 321e\n63 b60d\n62 b60c\n4e b25c\n4a b24c\n47 b21d\n46 b21c\n43 b20d\n42 b20c\n63 360d\n62 360c\n47 321d\n47 38bf\nc6 721c\n46 321c\n43 320d\n42 320c\n53 b1ad\n52 b1ac\n43 b0ad\n42 b0ac\nd2 71ac\n52 31ac\nc3 70ad\n43 30ad\nc2 70ac\n42 30ac\n43 b88d\n42 b88c\n43 388d\nc2 788c\n42 388c\n72 358e\n52 318e\nc6 709e\n63 b48d\n62 b48c\n56 b19c\n52 b18c\n47 b09d\n46 b09c\n43 b08d\n42 b08c\ne3 748d\n63 348d\ne2 748c\n62 348c\nc7 709d\nc6 709c\nc3 708d\n43 308d\nc2 708c\n42 308c\n52 b12e\n4a b06e\n52 312e\n4a 306e\n39 2feb\n53 312d\n29 2eeb\n43 302d\n4a b84e\nca 784e\n4a 384e\n53 b90d\n52 b90c\n4b b84d\n4a b84c\n43 b80d\n42 b80c\nd3 790d\n53 390d\nd2 790c\n52 390c\ncb 784d\n4b 384d\nca 784c\n4a 384c\nc3 780d\n43 380d\nc2 780c\n42 380c\n72 b50e\n56 b11e\n52 b10e\n4e b05e\n4a b04e\n46 b01e\nf2 750e\n72 350e\nea 744e\n5a 314e\n56 311e\n52 310e\n4e 305e\n4a 304e\n46 301e\n59 b3cb\n73 b50d\n51 b30b\n6b b44d\n49 b2cb\n63 b40d\nd9 73cb\nf3 750d\n59 33cb\n73 350d\n51 330b\n6b 344d\nc9 72cb\ne3 740d\n49 32cb\n63 340d\n3d 2fdb\n57 311d\n39 2fcb\n53 310d\n2d 2edb\n47 301d\n29 2ecb\n43 300d\n72 bfac\n76 bf9c\nf5 ffb3\nf1 ffa3\ned fe73\n7e 1ffe\nea fece\n6a becc\n62 be8c\n72 bd8c\n4a bacc\n3f 87ff\n3f 27ff\nbf 47ff\n7f 17ff\n3f 7ff\na5 4ebb\nbf 4ffd\n25 ebb\n3f ffd\n56 bb9c\n72 b7ac\n76 b79c\nf5 f73b\nf1 f72b\nd7 fb37\nd3 fb27\nc7 fa37\nca fa4e\ncb fa4d\ne3 f68d\nea f64e\nf2 f58e\nf3 f58d\nfa f54e\n42 ba8c\n4a ba4c\n52 b98c\n62 b68c\n72 b58c\nb5 edb3\nbd ed73\n3e 2fde\n7a 1fee\n7e 1fde\n7a 1fce\n3e fde\nab ecef\n8b e8ef\nab ece7\n8b e8e7\n1f 8bf7\n3f 87f7\n3d 87f3\n1f 83f7\n3d 27fb\n3f 27f7\n3d 27f3\n9f 4bf7\n1f bf7\nbd 47fb\n7d 17fb\n3d 7fb\nbf 47f7\nbd 47f3\n9f 43f7\n7f 17f7\n7d 17f3\n3f 7f7\n3d 7f3\n1f 3f7\n95 e93b\nb5 e53b\n97 e937\n95 e933\nb5 e533\n8b e86f\n8f e85f\nab e46f\naf e45f\n3e 8f7e\n36 8f3e\nbe 4f7e\nb6 4f3e\n3e f7e\n36 f3e\n6a b6ce\n6b b6cd\n7a b5ce\nea 76cc\n5f 93df\n1f 83df\n3b 27ef\n3b 27cf\n5f 1bdf\n1f bdf\n36 afbc\n7b 17ef\n3b 7ef\n36 af3c\ndf 53df\n9f 43df\n7f 17df\n7b 17cf\n5f 13df\n3f 7df\n3b 7cf\n1f 3df\n25 8e3b\n3f 8f7d\n37 8f3d\n36 8f3c\nb6 6fbc\n36 2fbc\nb6 6f3c\n36 2f3c\nb7 4fbd\n37 fbd\nb6 4fbc\n76 1fbc\n36 fbc\na5 4e3b\nbf 4f7d\nb7 4f3d\n25 e3b\n3f f7d\n37 f3d\nb6 4f3c\n76 1f3c\n36 f3c\n5f b37f\n5e b37e\n4f b27f\n4e b27e\nd7 73bd\n57 b39d\n4b b26d\nd6 739c\n47 b29f\n4f b25f\n53 b1af\n52 b1ae\n5b b16f\n5a b16e\n5f b15f\nd3 71ad\n47 b29d\n43 b28d\n4e b25e\n4f b25d\n4a b24e\n4b b24d\n57 b19d\n52 b18e\n53 b18d\n5e b15e\n5a b14e\nc6 729c\n4f 38ff\nce 725c\n4b 38ef\nca 724c\nd6 719c\nd2 718c\n39 27eb\n3b 27e7\n39 27e3\n3d 27db\n39 27cb\n3f 27d7\n3d 27d3\n3b 27c7\n39 27c3\n32 af8c\n32 af2c\n36 af1c\n32 af0c\n79 17eb\n39 7eb\n36 8f9c\n32 8f8c\n32 8f2c\n76 9f1c\n36 8f1c\n32 8f0c\n79 17e3\n3b 7e7\n39 7e3\n7d 17db\n79 17cb\n3d 7db\n39 7cb\n7f 17d7\n7d 17d3\n79 17c3\n3f 7d7\n3d 7d3\n3b 7c7\n39 7c3\n32 2fac\n32 2f8c\n32 2f2c\nb6 6f1c\n36 2f1c\n32 2f0c\nb2 4fac\n72 1fac\n32 fac\nb6 4f9c\nb2 4f8c\n76 1f9c\n72 1f8c\n36 f9c\n32 f8c\nb2 4f2c\n32 f2c\nb6 4f1c\nb2 4f0c\n76 1f1c\n72 1f0c\n36 f1c\n32 f0c\n36 af9c\n76 9f9c\nbd ef73\nb9 ef63\nad ee73\nf1 dfa3\nfd df73\n6e 1efe\n17 a33f\n1f 8b7f\n17 8b3f\n37 87bf\n57 933f\n3f 877f\n37 873f\n1f 837f\n17 833f\n1f 237f\n17 233f\n9f 4b7f\n97 4b3f\n1f b7f\n17 b3f\nb7 47bf\n37 7bf\n2f 2efd\n2e 2efc\nbf 477f\n9f 437f\n57 133f\n1f 37f\n17 33f\n6f 1efd\n2f efd\nae 4efc\n6e 1efc\n2e efc\nb5 e73b\nb1 e72b\nf5 d73b\nf1 d72b\n97 eb37\n93 eb27\n87 ea37\nd7 db37\nd3 db27\nf7 d737\nf5 d733\nf3 d727\nf1 d723\nbf cdff\nb7 cdbf\n9f c9ff\nc7 d03d\nad cefb\naf cef7\nbf cdf7\na7 cc3d\n8d cafb\n8f caf7\n9f c9f7\nbf cffd\na5 cebb\nad ce7b\nbd cd7b\ned de73\nf5 ddb3\nfd dd73\na7 ceb7\naf ce77\nad ce73\nb7 cdb7\nb5 cdb3\nbf cd77\nbd cd73\n2e 2ede\n3e 2dde\n2e 2cde\n6a 1eee\n6e 1ede\n6a 1ece\n2e ede\n7e 1dde\n7a 1dce\n6e 1cde\n6a 1cce\neb dcef\nef dcdf\ncf d8df\nab ccef\naf ccdf\n8b c8ef\n8f c8df\neb dce7\ncb d8e7\nab cce7\n8b c8e7\n2f 8cfd\n15 8bbb\n17 8bb7\n1d 8b7b\n1f 8b77\n1d 8b73\n17 8b37\n35 87bb\n37 87b7\n35 87b3\n17 83b7\n3f 8777\n3d 8773\n37 8737\n35 8733\n1f 8377\n1d 8373\n17 8337\n97 4bb7\n17 bb7\n9d 4b7b\n1d b7b\n9f 4b77\n9d 4b73\n97 4b37\n1f b77\n1d b73\n17 b37\nb5 47bb\n35 7bb\nb7 47b7\nb5 47b3\n97 43b7\n37 7b7\n35 7b3\n17 3b7\n2b 2eed\n2a 2eec\n2b 2ecd\n2e 2edc\n2a 2ecc\nbd 477b\nb5 473b\n9d 437b\n35 73b\nbf 4777\nbd 4773\nb7 4737\nb5 4733\n9f 4377\n9d 4373\n97 4337\nd2 dbac\n35 733\n1f 377\n1d 373\n17 337\n6b 1eed\n2b eed\n6a 1eec\n2a eec\n6f 1edd\n6b 1ecd\n6e 1edc\n6a 1ecc\n2e edc\n2a ecc\n8f ca7f\n8e ca7e\n97 c9bf\n9f c97f\n9e c97e\na7 c6bf\na6 c6be\nae c67e\nb7 c5bf\nb6 c5be\nbf c57f\nbe c57e\n9f cbfd\n85 cabb\n87 cab7\n97 c9b7\nbf c7fd\na5 c6bb\na7 c6b7\nb5 c5bb\nb7 c5b7\nf5 d53b\n8d ca7b\n9f cb7d\n85 ca3b\n9d c97b\nad c67b\nbf c77d\na5 c63b\nbd c57b\nb5 c53b\nc7 da37\nd7 d937\nd5 d933\ne7 d637\nf7 d537\nf5 d533\n8f ca77\n8d ca73\n87 ca37\n9f c977\n9d c973\n97 c937\n95 c933\naf c677\nad c673\na7 c637\nbf c577\nbd c573\nb7 c537\nb5 c533\ncb d86f\ncf d85f\ne7 d49f\neb d46f\nef d45f\n8b c86f\n8f c85f\na7 c49f\nab c46f\n2e 8e7e\n26 8e3e\n26 8c3e\nae 4e7e\na6 4e3e\n2e e7e\n26 e3e\na6 4cbe\nbe 4d7e\nae 4c7e\na6 4c3e\n26 c3e\n1f a35f\n1b a34f\n57 939f\n1b 836f\n5f 935f\n5b 934f\n57 931f\n1f 835f\n1b 834f\n1b 236f\n1f 235f\n1b 234f\n17 231f\n2f 8e7d\n27 8e3d\n2e 8e7c\n26 8e3c\n37 8dbd\n27 8cbd\n25 8c3b\n3f 8d7d\n1d 8bfb\n37 8d3d\n15 8b3b\n2f 8c7d\nd7 539f\n97 439f\n57 139f\n9b 436f\n1b 36f\na6 4ebc\naf 4e7d\na7 4e3d\n2f e7d\n27 e3d\nae 4e7c\na6 4e3c\n2e e7c\n26 e3c\na7 4cbd\n27 cbd\nb6 4dbc\nb7 e737\na6 4cbc\na5 4c3b\nbf 4d7d\n95 4b3b\naf 4c7d\n25 c3b\n3f d7d\n15 b3b\n2f c7d\ndf 535f\n9f 435f\n5f 135f\n5b 134f\n57 131f\n1f 35f\n1b 34f\n17 31f\n1f a37f\n5f 937f\n5b 936f\nfb ffcd\ne1 fe8b\ne9 fe63\ne3 fe27\ne9 fe4b\nfb ff4d\ne1 fe0b\ned fe53\neb fe47\ne9 fe43\ne7 fe17\ne3 fe07\nf1 fda3\ne3 fca7\nf1 fd8b\nfb fdcd\ne1 fc8b\nf5 fd93\nf3 fd87\nf1 fd83\nf1 fd2b\ne9 fc6b\nf3 fd27\nf1 fd23\ne3 fc27\nf9 fd4b\nf1 fd0b\ne9 fc4b\nfb fd4d\ne1 fc0b\nfd fd53\nfb fd47\nf9 fd43\nf7 fd17\nf5 fd13\nf3 fd07\nf1 fd03\nef fc57\ned fc53\neb fc47\ne9 fc43\ne7 fc17\ne3 fc07\nc3 fa27\nc9 fa4b\ndb fb4d\nc1 fa0b\nfb f7cd\ne1 f68b\ndb f3cd\nc1 f28b\ncb fa47\nc9 fa43\nc7 fa17\nc3 fa07\ndb f36d\nc1 f22b\nc3 f227\nd3 f987\nd1 f983\ncb f247\nc7 f217\nc3 f207\nd1 f92b\nc9 f86b\ndb f1ed\nc1 f0ab\nd3 f927\nd1 f923\ncb f867\nc9 f863\nc3 f827\ne3 f4a7\nc3 f0a7\nd9 f94b\nd1 f90b\nc9 f84b\ndb f94d\nc1 f80b\nf1 f58b\nfb f5cd\ne1 f48b\nd1 f18b\ndf f1dd\nc5 f09b\ndb f1cd\nc1 f08b\ndb f947\nd9 f943\nd7 f917\nd5 f913\nd3 f907\nd1 f903\ncf f857\ncd f853\ncb f847\nc9 f843\nc7 f817\nc3 f807\nf3 f587\nf1 f583\nd3 f187\nd1 f183\nf1 f52b\ne9 f46b\nd1 f12b\nc9 f06b\ndb f16d\nc1 f02b\nf3 f527\nf1 f523\nd3 f127\nd1 f123\ncb f067\nc9 f063\nc3 f027\nf9 f54b\nf1 f50b\ne9 f44b\nfb f54d\ne1 f40b\nd9 f14b\nd1 f10b\nc9 f04b\ndf f15d\nc5 f01b\ndb f14d\nc1 f00b\nf9 f543\nf7 f517\nf5 f513\nf3 f507\nf1 f503\nd5 f113\nd3 f107\nd1 f103\ncf f057\ncd f053\ncb f047\nc9 f043\nc7 f017\nc3 f007\na9 ee63\na3 ee27\ne9 de63\na9 ce63\na3 ce27\nad ee53\na9 ee43\na7 ee17\na3 ee07\ned de53\nad ce53\na9 ce43\na7 ce17\na3 ce07\nb1 eda3\na3 eca7\nf1 dda3\nb1 cda3\ne3 dca7\na3 cca7\nf1 dd83\nb1 cd83\na9 ec6b\ne9 dc6b\na9 cc6b\nb9 ed63\nb3 ed27\nb1 ed23\nab ec67\na9 ec63\na3 ec27\nf3 dd27\nb3 cd27\nf1 dd23\nb1 cd23\neb dc67\nab cc67\ne3 dc27\na3 cc27\nb5 ed1b\nad ec5b\na9 ec4b\n67 3c37\ned dc5b\n27 2c37\nad cc5b\nb5 ed13\nb3 ed07\nb1 ed03\naf ec57\nad ec53\nab ec47\na9 ec43\na7 ec17\na3 ec07\nfd dd53\nbd cd53\nf9 dd43\nb9 cd43\nf7 dd17\nb7 cd17\nf5 dd13\nb5 cd13\nf3 dd07\nb3 cd07\nf1 dd03\nb1 cd03\nef dc57\naf cc57\ned dc53\nad cc53\neb dc47\nab cc47\ne9 dc43\na9 cc43\ne7 dc17\na7 cc17\ne3 dc07\na3 cc07\n83 ea27\nc3 da27\n83 ca27\n87 ea17\n83 ea07\nc7 da17\n87 ca17\nc3 da07\n83 ca07\nc3 d8a7\n9b e36d\n81 e22b\ndb d36d\nc1 d22b\n9b c36d\n81 c22b\n83 e227\ne3 d627\na3 c627\nc3 d227\n83 c227\n89 e86b\nc9 d86b\n89 c86b\n87 e217\n83 e207\ne7 d617\na7 c617\ne3 d607\na3 c607\nc7 d217\n87 c217\nc3 d207\n83 c207\n93 e927\n91 e923\n8b e867\n89 e863\n83 e827\nd3 d927\n93 c927\nd1 d923\n91 c923\ncb d867\n8b c867\nc9 d863\n89 c863\nc3 d827\n83 c827\n9b e1ed\n81 e0ab\ndb d1ed\nc1 d0ab\n9b c1ed\n81 c0ab\n95 e91b\n8d e85b\n89 e84b\n47 3837\ncd d85b\na3 e4a7\ne3 d4a7\na3 c4a7\nc3 d0a7\n97 e917\n95 e913\n93 e907\n91 e903\n8f e857\n8d e853\n8b e847\n89 e843\n87 e817\n83 e807\nd7 d917\n97 c917\nd5 d913\n95 c913\nd3 d907\n93 c907\nd1 d903\n91 c903\ncf d857\n8f c857\ncd d853\n8d c853\ncb d847\n8b c847\nc9 d843\n89 c843\nc7 d817\n87 c817\nc3 d807\n83 c807\n9b e1cd\n81 e08b\ndf d1dd\nc5 d09b\n9f c1dd\n85 c09b\ndb d1cd\nc1 d08b\n9b c1cd\n81 c08b\nb1 e52b\na9 e46b\n91 e12b\n89 e06b\n9b e16d\n81 e02b\nf1 d52b\nb1 c52b\ne9 d46b\na9 c46b\nd1 d12b\n91 c12b\nc9 d06b\n89 c06b\ndb d16d\nc1 d02b\n9b c16d\n81 c02b\n7b bfcd\n61 be8b\nfb 7fcd\ne1 7e8b\n7b 3fcd\n61 3e8b\nb3 e527\nb1 e523\n93 e127\n91 e123\n89 e063\nf3 d527\nb3 c527\nf1 d523\nb1 c523\neb d467\nab c467\n56 339e\ne9 d463\ne3 d427\na3 c427\nd3 d127\n93 c127\nd1 d123\n91 c123\ncb d067\n89 c063\nc3 d027\nb5 e51b\nb1 e50b\nad e45b\na9 e44b\n95 e11b\n91 e10b\n8d e05b\n89 e04b\n9f e15d\n85 e01b\n9b e14d\n81 e00b\n6f 34f7\nf5 d51b\n6b 34e7\nf1 d50b\n67 3437\ned d45b\n4f 30f7\nd5 d11b\n4b 30e7\nd1 d10b\n47 3037\ncd d05b\n3f 2ff7\ndf d15d\nc5 d01b\n9f c15d\n85 c01b\n3b 2fe7\ndb d14d\nc1 d00b\n9b c14d\n81 c00b\nb5 e513\nb3 e507\nb1 e503\n95 e113\n93 e107\n91 e103\n8f e057\n8d e053\n8b e047\n89 e043\n87 e017\n83 e007\nf7 d517\nb7 c517\nf5 d513\nb5 c513\nf3 d507\nb3 c507\nf1 d503\nb1 c503\naf c457\ned d453\nad c453\neb d447\nab c447\ne9 d443\na9 c443\ne7 d417\na7 c417\ne3 d407\na3 c407\nd7 d117\n97 c117\nd5 d113\n95 c113\nd3 d107\n93 c107\nd1 d103\n91 c103\ncf d057\n8f c057\ncd d053\n8d c053\ncb d047\n8b c047\nc9 d043\n89 c043\nc7 d017\n87 c017\nc3 d007\n83 c007\ne9 7e63\n69 3e63\n69 be4b\n7b bf4d\n61 be0b\ne9 7e4b\n69 3e4b\nfb 7f4d\ne1 7e0b\n7b 3f4d\n61 3e0b\n6b be47\n69 be43\n63 be07\ned 7e53\n6d 3e53\neb 7e47\n6b 3e47\ne9 7e43\n69 3e43\ne3 7e07\n63 3e07\n71 bd8b\nf1 7d8b\n71 3d8b\n7b 3dcd\n61 3c8b\n73 bd87\n71 bd83\n75 3d93\n73 3d87\n71 3d83\n69 bc6b\n69 3c6b\n71 bd23\n69 bc63\n79 3d63\n71 3d23\n6b 3c67\n69 3c63\ne9 dc4b\n63 3c27\n79 bd4b\n71 bd0b\n6d bc5b\n69 bc4b\n7b bd4d\n61 bc0b\nf9 7d4b\n79 3d4b\nf1 7d0b\n71 3d0b\n6d 3c5b\ne9 7c4b\n69 3c4b\nfb 7d4d\ne1 7c0b\n7b 3d4d\n61 3c0b\n7b bd47\n79 bd43\n75 bd13\n73 bd07\n71 bd03\n6f bc57\n6d bc53\n6b bc47\n69 bc43\n67 bc17\n63 bc07\nfd 7d53\n7d 3d53\n7b 3d47\nf9 7d43\n79 3d43\n75 3d13\n73 3d07\n71 3d03\nef 7c57\n6f 3c57\ned 7c53\n6d 3c53\neb 7c47\n6b 3c47\ne9 7c43\n69 3c43\n67 3c17\n63 3c07\n5b bbcd\n41 ba8b\ndb 7bcd\nc1 7a8b\n5b 3bcd\n41 3a8b\n49 ba4b\n5b bb4d\n41 ba0b\nc9 7a4b\n49 3a4b\ndb 7b4d\nc1 7a0b\n5b 3b4d\n41 3a0b\n4b ba47\n49 ba43\n43 ba07\ncb 7a47\n4b 3a47\nc9 7a43\n49 3a43\nc3 7a07\n43 3a07\n7b b7cd\n61 b68b\n5b b3cd\n41 b28b\nfb 77cd\ne1 768b\n7b 37cd\n61 368b\ndb 73cd\nc1 728b\n5b 33cd\n41 328b\n51 b98b\nd1 798b\n51 398b\n53 b987\n51 b983\n53 3987\n51 3983\n7b b74d\n61 b60b\n49 b24b\n5b b34d\n41 b20b\n6a 3cee\ne9 764b\n7b 374d\n61 360b\n4a 38ee\nc9 724b\n49 324b\n5b 334d\n41 320b\n49 b86b\n49 386b\n4b b247\n49 b243\n43 b207\nf2 dd0e\neb 7647\nd2 d90e\ncb 7247\n4b b867\n49 b863\n43 b827\ncb 7867\n4b 3867\n49 3863\nc9 d84b\n43 3827\n5b b1ed\n41 b0ab\ndb 71ed\nc1 70ab\n5b 31ed\n41 30ab\n59 b94b\n51 b90b\n4d b85b\n49 b84b\nd9 794b\n59 394b\nd1 790b\n51 390b\n4d 385b\nc9 784b\n49 384b\n5b 394d\n41 380b\ne3 74a7\ne9 d4cb\n63 34a7\nc3 70a7\nc9 d0cb\n43 30a7\n5b b947\n59 b943\n53 b907\n51 b903\n4f b857\n4d b853\n4b b847\n49 b843\n47 b817\n43 b807\n5b 3947\nd9 7943\n59 3943\n53 3907\n51 3903\ncf 7857\n4f 3857\ncd 7853\n4d 3853\ncb 7847\n4b 3847\nc9 7843\n49 3843\n47 3817\n43 3807\n71 b58b\n7b b5cd\n61 b48b\n51 b18b\n5f b1dd\n45 b09b\n5b b1cd\n41 b08b\nf1 758b\n71 358b\nd1 718b\n51 318b\ndf 71dd\nc5 709b\ndb 71cd\nc1 708b\n5b 31cd\n41 308b\n73 b587\n71 b583\n53 b187\n51 b183\nf3 7587\n73 3587\nf1 7583\n71 3583\nd3 7187\n53 3187\nd1 7183\n51 3183\n69 b46b\n49 b06b\n5b b16d\n41 b02b\ne9 746b\n69 346b\nc9 706b\n49 306b\ndb 716d\nc1 702b\n49 b063\n6b 3467\n69 3463\ne3 7427\ne9 d44b\n63 3427\ncb 7067\n4b 3067\nc9 7063\n49 3063\nc3 7027\nc9 d04b\n43 3027\n71 b50b\n59 b14b\n51 b10b\n4d b05b\n49 b04b\n5f b15d\n45 b01b\n5b b14d\n41 b00b\nf9 754b\nf1 750b\n71 350b\ne9 744b\nd9 714b\n59 314b\nd1 710b\n51 310b\n4d 305b\nc9 704b\n49 304b\n79 b543\n73 b507\n71 b503\n5b b147\n59 b143\n53 b107\n51 b103\n4f b057\n4d b053\n4b b047\n49 b043\n47 b017\n43 b007\nfb 7547\n7b 3547\nf9 7543\n79 3543\nf3 7507\n73 3507\nf1 7503\n71 3503\nef 7457\n6f 3457\ned 7453\n6d 3453\neb 7447\n6b 3447\ne9 7443\n69 3443\ne7 7417\n67 3417\ne3 7407\n63 3407\n7d 9ff3\ndb 7147\n5b 3147\n59 3143\n53 3107\n51 3103\n71 9f03\ncf 7057\n4f 3057\n4d 3053\n6d 9ef3\ncb 7047\n4b 3047\n49 3043\n69 9ec3\nc7 7017\n47 3017\n43 3007\n29 2e63\na9 4e63\n69 1e63\n29 e63\n29 ae43\n2d 8e53\n69 9e43\n29 8e43\n2d 2e53\n29 2e43\ned 5e53\nad 4e53\n6d 1e53\n2d e53\na9 4e43\n69 1e43\n29 e43\nb1 6da3\na3 6ca7\nb1 4da3\na3 4ca7\n3b 2dcd\n21 2c8b\n71 9d83\n31 8d83\n35 2d93\nb1 6d83\n31 2d83\nb5 4d93\n75 1d93\n35 d93\nb1 4d83\n71 1d83\n31 d83\na9 6c6b\n29 2c6b\na9 4c6b\n29 c6b\n31 ad23\n71 9d23\n31 8d23\n63 9c27\n23 8c27\nb9 6d63\n39 2d63\nb1 6d23\n31 2d23\nab 6c67\n2b 2c67\na9 6c63\n29 2c63\na3 6c27\na9 cc4b\n23 2c27\n39 d63\nb1 4d23\n31 d23\neb 5c67\nab 4c67\n2b c67\na9 4c63\n29 c63\na3 4c27\n23 c27\n29 ac4b\n69 9c4b\n29 8c4b\n2d 2c5b\n29 2c4b\ned 5c5b\nad 4c5b\n6d 1c5b\nc3 7a0d\n2d c5b\ne9 5c4b\na9 4c4b\n69 1c4b\n29 c4b\n39 ad43\n35 ad13\n31 ad03\n2b ac47\n29 ac43\n27 ac17\n23 ac07\n79 9d43\n75 9d13\n71 9d03\n6b 9c47\n2b 8c47\n69 9c43\n29 8c43\n67 9c17\n27 8c17\n63 9c07\n23 8c07\n3d 2d53\n39 2d43\n35 2d13\n31 2d03\n2f 2c57\n2d 2c53\n2b 2c47\n29 2c43\n27 2c17\n23 2c07\n7d 1d53\nf9 5d43\nb9 4d43\n79 1d43\n75 1d13\nb1 4d03\n71 1d03\n6f 1c57\n2f c57\n6d 1c53\n2b c47\ne9 5c43\na9 4c43\n69 1c43\n29 c43\n67 1c17\n27 c17\n23 c07\na9 6e63\ne9 5e63\nb9 4d63\nff ffff\nfc fff8\nfe fffe\nfc fff2\nf6 7f9c\nfd fffb\nff fff7\nfc fffa\nfd fff9\nfe fff6\ne5 feb3\nff fff5\nfb ffef\nff ffdf\nf8 ffca\n7d bf73\n79 bf63\n6d be73\nf8 ffe8\nfa ffee\nfe ffde\nee fedc\nfc ffd8\nf9 ffc9\nf8 ffc8\nf8 ffe2\n3e 87fe\nf9 ffeb\nfb ffe7\nff ffd7\nd3 f10d\nb9 efcb\nfc ffd2\nfa ffc6\nf8 ffc2\nbe 47fe\nf9 ffe1\nf8 ffe0\nf8 ffea\nfa ffe6\ne1 fea3\nfb ffe5\nfd ffd9\nfe ffd6\ne5 fe93\nff ffd5\ne3 fe8f\nfd ffd1\ne2 fe8e\nfc ffd0\ne1 fe83\nfb ffc5\nf9 ffc1\nf8 ffc0\nf4 ff3a\n8e e25c\nf a8ff\nf7 ffbf\nff ff7f\n77 bf3f\nf4 ffb8\nf5 ff39\n2f a4ff\nf9 dfc3\n1f a1ff\ne9 dec3\nf a0ff\nf6 ffbe\nf7 ffbd\nfe ff7e\ne5 fe3b\nff ff7d\nee fe7c\n76 bf3e\n77 bf3d\n66 be3c\nf4 ffb2\nf6 ff36\n6b becd\n63 be8d\n61 bc8b\n7b bdcd\n73 bd8d\n4b bacd\n41 b88b\n5b b9cd\nce d25c\n4f 98ff\n8e c25c\nf 88ff\ne1 7c8b\nfb 7dcd\nf3 7d8d\nf2 7d8c\nf5 ffbb\nf7 ffb7\nfd ff7b\nff ff77\n8f c07d\n75 bf3b\nf6 ffb4\nf7 ff35\nf6 ff34\ne3 fe2f\nfd ff71\n2f 86ff\n6f 94ff\nf5 ffb1\n5f 91ff\nf4 ffba\nf5 ffb9\nf6 ffb6\nf7 ffb5\nfc ff7a\nfd ff79\ne5 fe33\nff ff75\nf0 ff2a\nf3 ffaf\nf7 ff9f\nff ff5f\nbb ef4f\n77 bf1f\nf0 ffa8\neb fec7\nf8 ff68\ne3 fe87\nf0 ff28\nf0 ff8a\nf8 ff4a\nf0 ff0a\n2f 26ff\n69 be63\n6d be53\n75 bd93\n79 bd63\n7d bd53\n3f 25ff\n2f 24ff\nf9 5fc3\n1f 21ff\ne9 5ec3\nf 20ff\nf0 ffa2\nf2 ffae\nf6 ff9e\nf7 ff9d\ne1 fe2b\nfb ff6d\nfe ff5e\ne5 fe1b\nff ff5d\ne6 fe9c\nea fe6c\nee fe5c\nba ef4e\na1 ee0b\nbb ef4d\naa ee4c\n73 bf2d\n76 bf1e\n77 bf1d\n62 be2c\n66 be1c\n33 af0d\n22 ae0c\nf4 ff98\nf1 ff89\nf0 ff88\nf5 ff19\nfc ff58\nf4 ff18\nf9 ff49\nf1 ff09\nf8 ff48\nf0 ff08\ncf 58ff\n8f 48ff\nf9 ff61\nf1 ffab\nf3 ffa7\nf5 ff9b\nf7 ff97\nf9 ff6b\nff ff57\ndd fb53\ncb f0cd\nb1 ef8b\nb9 ef4b\nbb ef47\n99 eb43\n8b c06d\n71 bf2b\n8f c05d\n75 bf1b\n77 bf17\n4b b04d\n31 af0b\n33 af07\nf4 ff92\nf2 ff86\nf0 ff82\nf6 ff16\nfc ff52\nf4 ff12\nfa ff46\nf2 ff06\nf8 ff42\nf0 ff02\n6f 16ff\n2f 6ff\nef 54ff\ndf 51ff\ncf 50ff\naf 44ff\n9f 41ff\n8f 40ff\n7f 15ff\n6f 14ff\nf0 ffaa\nf1 ffa9\nf2 ffa6\nf4 ff9a\nf5 ff99\nf6 ff96\nf8 ff6a\ne1 fe23\nfb ff65\nfd ff59\nfe ff56\nf7 ff95\nf6 ff94\ne5 fe13\nff ff55\nf7 ff15\nf6 ff14\ne3 fe0f\nfd ff51\ne2 fe0e\nfc ff50\ne1 fe03\nfb ff45\nf9 ff41\nf8 ff40\nec fef8\nef feff\nff fdff\nec fef2\nfc fdf2\nec fcf2\nee fefe\ne5 fcbb\nff fdfd\nd6 7b9c\nf2 77ac\nf6 779c\ne3 fcaf\nfd fdf1\ned fefb\nef fef7\nfd fdfb\nff fdf7\nee fcf6\ndd f9f3\nec fefa\ned fef9\nee fef6\nfc fdf8\nfd fdf9\ne5 fcb3\nff fdf5\nec fcf8\neb feef\nef fedf\nfb fdef\nff fddf\nea fcee\nbb edcf\naa ecce\ne8 feca\ne8 fcca\ne8 fee2\nf8 fde2\ne8 fce2\nea feee\nee fede\nef fedd\nfa fdee\ne1 fcab\nfb fded\ne5 fc9b\nff fddd\na1 ec8b\nbb edcd\nec fed8\ne9 fec9\ne8 fec8\nf9 fdc9\nf8 fdc8\ne8 fcc8\nf9 fde1\nf8 fde0\ne8 fce0\ne9 feeb\neb fee7\nfc ff78\nef fed7\nc3 f00d\na9 eecb\nf9 fdeb\nfb fde7\nff fdd7\ne8 fcea\nea fce6\nd9 f9e3\ndd f9d3\nbb edc7\n99 e9c3\nec fed2\nea fec6\ne8 fec2\nee fcd6\nfc fdd2\nec fcd2\nea fcc6\nf8 fdc2\ne8 fcc2\nfe ff7c\ne4 fe3a\ne8 feea\nea fee6\ned fed9\nee fed6\nf8 fde8\nfc fdd8\nf8 fdea\nf9 fde9\ne1 fca3\nfb fde5\nfd fdd9\ne8 fce8\nec fcd8\nef fed5\nee fed4\ne5 fc93\nff fdd5\nd5 fb93\nef fcd5\ne3 fc8f\nfd fdd1\ne2 fc8e\nfc fdd0\nd2 fb8e\nec fcd0\ne1 fc83\nfb fdc5\nd1 fb83\neb fcc5\nf9 fdc1\nf8 fdc0\ne8 fcc0\ne4 feb8\ne5 fe39\nec fe78\ne4 fe38\ne7 fc97\nf4 fd38\ne7 febf\nef fe7f\n67 be3f\nf7 fdbf\nff fd7f\ne6 fcbe\nee fc7e\n77 bd3f\n66 bc3e\nfe fff4\ne4 feb2\ne6 fe36\nf4 fdb2\nfe fdf4\ne4 fcb2\ne6 fc36\ne6 febe\nee fe7e\nef fe7d\nf6 fdbc\ne4 fc3a\nfe fd7c\n66 be3e\n67 be3d\nf7 fdbd\nfe fd7e\ne5 fc3b\nff fd7d\ne6 fcbc\nd5 fb3b\nef fc7d\n43 ba8d\n4a ba4e\n4b ba4d\n53 b98d\n41 b80b\n5b b94d\n63 b68d\n6b b64d\n72 b58e\n73 b58d\n61 b40b\n7b b54d\ncb 7a4d\nca 7a4c\nd2 798c\nc1 780b\ndb 794d\n4f 90ff\ne5 feb1\ne7 fe35\nee fe74\ne6 fe34\ned fe71\nec fe70\nf4 fdb0\ne4 fcb0\ndd fbf3\nf7 fd35\ncd faf3\ne7 fc35\ne3 fc2f\nfd fd71\nd3 fb2f\ned fc71\ne2 768c\n6b 3cef\nea 764c\nf2 758c\nff fffd\ne5 febb\ne7 feb7\ned fe7b\nef fe77\ncd fa73\n67 be37\nf5 fdbb\nf7 fdb7\nfd fd7b\nff fd77\nfe fdfc\ne4 fcba\ne6 fcb6\nec fc7a\n75 bd3b\n55 b933\nfe fffc\ne4 feba\ne5 feb9\ne6 feb6\nec fe7a\ned fe79\nee fe76\nef fe75\nf4 fdb8\nf6 fdb4\nef fcd7\nfc fd78\nf7 fdb5\nfc fd7a\ne5 fc33\nff fd75\ne4 fcb8\nb6 c71e\ne7 fcb5\ne6 fcb4\nd5 fb33\nef fc75\nfd fff1\ne3 feaf\ne7 fe9f\neb fe6f\nef fe5f\nab ee4f\n67 be1f\nf3 fdaf\nf7 fd9f\nfc fdf0\ne2 fcae\ne6 fc9e\naa ec4e\n73 bd2f\n66 bc1e\n33 ad0f\nfc fff0\ne2 feae\ne6 fe9e\ne7 fe9d\neb fe6d\nee fe5e\nef fe5d\nf2 fdac\nf6 fd9c\naa ee4e\nab ee4d\n63 be2d\n66 be1e\n67 be1d\n23 ae0d\nf2 fdae\nf7 fd9d\ne1 fc2b\nfb fd6d\ne2 fcac\ne7 fc9d\ne6 fc9c\nd1 fb2b\neb fc6d\na1 ec0b\nbb ed4d\n91 eb0b\nab ec4d\n72 bd2e\n32 ad0e\nfb ffed\ne1 feab\ne3 fea7\nff ffdd\ne5 fe9b\nf4 ff38\ne7 fe97\ne9 fe6b\neb fe67\nef fe57\nc9 fa63\ncd fa53\nbb efcd\na1 ee8b\na9 ee4b\nab ee47\n89 ea43\n63 be27\n67 be17\n23 ae07\nf1 fdab\nf3 fda7\nf5 fd9b\nf7 fd97\nf9 fd6b\nff fd57\nd5 f993\ndd f953\nb9 ed4b\n99 e943\n71 bd2b\n75 bd1b\n77 bd17\n51 b923\n55 b913\n33 ad07\nfc f7f8\ndc f3f8\nff f7ff\nf7 f53d\ndd f3fb\nf6 fd34\ndc fbf2\nfe f7fe\nee f6fc\nf6 f53c\ndc f3fa\ndd f3f9\nc2 f2ae\ndc f3f0\nf7 fd3d\ndd fbfb\ndf fbf7\nfd f7f3\nfd f7fb\nff f7f7\ndf f3f7\nf6 fd3c\ndc fbfa\nde fbf6\nfc f7f2\nfc f7fa\nfd f7f9\nfe f7f6\nde f3f6\nc3 f2af\ndd f3f1\nf8 f7e8\nd8 f3e8\ndf fbdf\nfb f7ef\nff f7df\nf3 f52d\nd9 f3eb\nbb e7cf\nb3 e50d\n99 e3cb\nf2 fd0c\nd8 fbca\nf8 f7ca\nf2 f50c\nd8 f3ca\n3d ad73\nf4 fd30\nda fbee\nde fbde\nce fadc\n8a eacc\nfa f7ee\nfe f7de\nea f6ec\nee f6dc\nf2 f52c\nd8 f3ea\nba e7ce\naa e6cc\ndd f3d9\nfc f7d8\ndc f3d8\nf9 f7c9\nd9 f3c9\nf8 f7c8\nd8 f3c8\n5e 93de\n1e 83de\n1e 2bde\n5e 1bde\n1e bde\nde 53de\n9e 43de\n7e 17de\n7a 17ce\n5e 13de\nf8 f7e0\nd8 f3e0\ndb fbe7\nec fc78\ndf fbd7\nf9 f7e3\nfd f7d3\nb3 ed0d\n99 ebcb\nb9 e7c3\nf9 f7eb\nfb f7e7\nff f7d7\ndb f3e7\nb9 e7cb\nbb e7c7\nda fbc6\nfa f7c6\nda f3c6\nf8 f7c2\nee fc7c\nd4 fb3a\nf4 f73a\nf2 fd2c\nd8 fbea\nda fbe6\nde fbd6\nf8 f7e2\nf9 f7e1\nfc f7d2\nf8 f7ea\nf9 f7e9\nfa f7e6\nfd f7d9\nfe f7d6\nda f3e6\nd9 f3e1\nde f3d6\nc5 fa93\ndf fbd5\nc1 fa83\ndb fbc5\ne3 f68f\nfd f7d1\nc3 f28f\ndd f3d1\ne2 f68e\nfc f7d0\nc2 f28e\ndc f3d0\ne1 f683\nfb f7c5\nc1 f283\ndb f3c5\nf9 f7c1\nd9 f3c1\nf8 f7c0\nd8 f3c0\nd5 fb39\nf4 f7b8\nd4 f3b8\nf5 f739\nd5 f339\ndf fb7f\n57 bb3f\nf7 f7bf\nff f77f\ndd f37b\n77 b73f\n2d acdb\n2d a4fb\n2d a4f3\nee fcf4\nd4 fbb2\nd6 fb36\nde fb7e\nc5 fa3b\ndf fb7d\nce fa7c\n56 bb3e\n57 bb3d\n46 ba3c\nf6 f7be\nf7 f7bd\nfe f77e\ne5 f63b\nff f77d\ne6 f6bc\nee f67c\nd5 f3b9\ndc f37a\ndd f379\n76 b73e\n77 b73d\n66 b63c\nd7 fb35\nd6 fb34\nc3 fa2f\ndd fb71\n3f 8d7f\nd5 fb31\n3e 8d7e\nd4 fb30\nf6 f7b4\nd6 f3b4\nf4 f7b0\nf7 f735\nd7 f335\nf5 f731\nf4 f730\nd7 fbb7\ndd fb7b\ndf fb77\nf5 f7b3\nf5 f7bb\nf7 f7b7\nfd f77b\nd7 f3b7\n75 b73b\n2f 86f7\n2d 86f3\nf 82f7\n6d 94fb\ne3 fead\n4d 90fb\nd3 f3ad\n3d 85fb\nc3 f2ad\n2d 84fb\n6f 94f7\n6d 94f3\n4f 90f7\n3f 85f7\n2f 84f7\n1f 81f7\nea fc6c\nd0 fb2a\nf0 f72a\nee fcfc\nd4 fbba\nd6 fbb6\na6 c61e\nd7 fbb5\ndc fb7a\ndd fb79\nc5 fa33\ndf fb75\nf4 f7b2\nf5 f7b1\ne3 f62f\nfd f771\nf4 f7ba\nf5 f7b9\nf6 f7b6\nf7 f7b5\nfc f77a\nfd f779\nd6 f3b6\nd7 f3b5\n3f 85ff\nd5 f3b1\nc5 f233\ndf f375\nc3 f22f\ndd f371\n29 ac6b\n2d ac5b\ncb fac7\nd8 fb68\nc3 fa87\nd0 fb28\nf0 f7a8\nd0 f3a8\nf1 f729\neb f6c7\nf8 f768\ne3 f687\nf0 f728\ncb f2c7\nd8 f368\nc3 f287\nd0 f328\nd7 fb9f\ndf fb5f\n9b eb4f\n57 bb1f\nf3 f7af\nf7 f79f\nff f75f\nb3 e78f\n73 b72f\n77 b71f\nea fccc\nd0 fb8a\nd8 fb4a\nea fc4c\nd0 fb0a\nf0 f78a\nea f4cc\nd0 f38a\nf8 f74a\nf0 f70a\nd8 f34a\nea f44c\nd0 f30a\n2d 26fb\n29 ac63\n2f ac57\n2d ac53\n3d 25fb\n2d 24fb\n3f 25f7\n3d 25f3\nb5 c51b\n2f 24f7\n2d 24f3\n1f 21f7\n95 c11b\nf 20f7\nea fce4\nd0 fba2\nec fcf0\nd2 fbae\nd6 fb9e\nd7 fb9d\nc1 fa2b\ndb fb6d\nde fb5e\nc5 fa1b\ndf fb5d\nc6 fa9c\nca fa6c\nce fa5c\n93 eb8d\n9a eb4e\n81 ea0b\n9b eb4d\n82 ea8c\n8a ea4c\n53 bb2d\n56 bb1e\n57 bb1d\n42 ba2c\n46 ba1c\nf2 f7ae\nf3 f7ad\nf6 f79e\nf7 f79d\ne1 f62b\nfb f76d\nfe f75e\ne2 f6ac\ne6 f69c\nea f66c\n6f bcff\nee f65c\nd1 f3a9\nb2 e78e\nb3 e78d\nba e74e\na2 e68c\n2b acef\naa e64c\n72 b72e\n73 b72d\n77 b71d\n62 b62c\n66 b61c\nd5 fb19\ndc fb58\nd4 fb18\nd9 fb49\nd1 fb09\nd8 fb48\nd0 fb08\nd5 f399\nf4 f798\nd4 f398\nf1 f789\nd1 f389\nf0 f788\nd0 f388\n27 c3d\nd afb\nf af7\n9d 49f3\n8f 48f7\n8d 48f3\nd9 fb61\nf2 f7a4\nf0 f7a0\nf3 f725\nf1 f721\nf0 f720\nef fcdd\nd5 fb9b\ne4 fc38\nd7 fb97\nd9 fb6b\ndf fb57\nf1 f7a3\nf5 f793\nab eccd\n91 eb8b\n99 eb4b\n9b eb47\nb1 e783\n57 bb17\n13 ab07\nf1 f7ab\nf3 f7a7\nf5 f79b\nf7 f797\nd3 f3a7\nb1 e78b\nb3 e787\n71 b72b\n75 b71b\nee fcd4\nd4 fb92\nea fcc4\nd0 fb82\nd6 fb16\ndc fb52\nda fb46\nd2 fb06\nd8 fb42\nf2 f786\nf0 f782\ne9 5ccb\nfa f746\nf8 f742\nd8 f342\n6d 16fb\n2d 6fb\nad 46f3\n8f 42f7\n2f 6f7\n2d 6f3\nf 2f7\ned 54fb\ncd 50fb\nad 44fb\n9d 41fb\n8d 40fb\n7d 15fb\n6d 14fb\nef 54f7\ned 54f3\ncf 50f7\ncd 50f3\nbd 45f3\naf 44f7\nad 44f3\n9f 41f7\n9d 41f3\n8f 40f7\n8d 40f3\n7f 15f7\n7d 15f3\n6d 14f3\n5f 11f7\n3f 5f7\n2f 4f7\n1f 1f7\nf f7\nea fcec\nd0 fbaa\nd2 fba6\nee fcdc\nd4 fb9a\nd5 fb99\nfd fd79\nd6 fb96\nd8 fb6a\nc1 fa23\ndb fb65\ndd fb59\nde fb56\nf0 f7a2\nf1 f7a1\nf4 f792\nf9 f761\nfc f752\nf0 f7aa\nf1 f7a9\nf2 f7a6\nf3 f7a5\nf4 f79a\nf5 f799\nf6 f796\nf9 f769\ned 5cdb\nfe f756\nd2 f3a6\n3d 85f3\nd3 f3a5\nc1 f223\ndb f365\nd9 f361\ndc f352\nc5 fa13\ndf fb55\nd7 fb15\nd6 fb14\nc3 fa0f\ndd fb51\nc2 fa0e\ndc fb50\nc1 fa03\ndb fb45\nd9 fb41\nd8 fb40\nf7 f795\nd7 f395\nf6 f794\nd6 f394\nf5 f791\nf4 f790\nf3 f785\nf2 f784\nf1 f781\nf0 f780\nf7 f715\nd7 f315\n7d bdf3\ne2 f60e\nfc f750\n75 bdb3\nf4 f710\nf3 f705\n79 bde3\nf8 f740\n71 bda3\nf0 f700\nec f6f8\ncc f2f8\ndf f9ff\ne7 f43d\ncd f2fb\nff f5ff\nee f4fe\ndd f1fb\ncc f0fa\ne6 fc34\ncc faf2\ndc f9f2\ncc f8f2\nce f8fc\nee f6fe\ne6 f43c\ncc f2fa\ncd f2f9\ndc f1f8\nfe f5fe\ne5 f4bb\nff f5fd\nd5 f3bb\nef f4fd\nd4 f3ba\nee f4fc\ndc f1fa\ndd f1f9\ncd f0f9\ncc f0f8\n37 8d3f\ncd faf1\nee f6f4\nec f6f0\ne7 fc3d\ncd fafb\ncf faf7\ned f6f3\ndd f9fb\ndf f9f7\nce f8f6\nfd f5f3\nec f4f2\ned f6fb\nef f6f7\ncf f2f7\nfd f5fb\nff f5f7\nec f4fa\nee f4f6\ndf f1f7\ndd f1f3\nce f0f6\ncc f0f2\ne6 fc3c\ncc fafa\ncd faf9\nce faf6\n39 8d43\ncf faf5\ned f6f1\n9e c35e\ncf f8f5\nce f8f4\nfc f5f2\ne3 f4af\nfd f5f1\nd3 f3af\ned f4f1\nd2 f3ae\nec f4f0\nec f6fa\ned f6f9\nef f6f5\nfc f5f8\nc2 f0ae\ndc f1f0\nfc f5fa\nfd f5f9\nfe f5f6\ne5 f4b3\nff f5f5\ned f4f9\nec f4f8\nd5 f3b3\nef f4f5\nd4 f3b2\nee f4f4\nde f1f6\nc5 f0b3\ndf f1f5\ndc f1f2\nc3 f0af\ndd f1f1\nb5 efb3\ncf f0f5\ne8 f6e8\ncf fadf\ndf f9df\nca f8ee\n9b e9cf\n8a e8ce\neb f6ef\ne3 f42d\nc9 f2eb\nab e6cf\na3 e40d\n89 e2cb\nfb f5ef\nff f5df\nea f4ee\nee f4de\nd9 f1eb\nc8 f0ea\nbb e5cf\naa e4ce\n99 e1cb\ne2 fc0c\nc8 faca\nc8 f8ca\ne8 f6ca\ne2 f40c\nc8 f2ca\nf8 f5ca\ne8 f4ca\nd8 f1ca\nc8 f0ca\nd8 f9e2\nc8 f8e2\ne4 fc30\nca faee\nce fade\ncf fadd\n8b eacd\nda f9ee\nc5 f89b\ndf f9dd\ncf f8dd\nce f8dc\n81 e88b\n9b e9cd\n8b e8cd\nea f6ee\neb f6ed\nee f6de\ne2 f42c\nc8 f2ea\nd8 f1e8\naa e6ce\nab e6cd\nfa f5ee\ne1 f4ab\nfb f5ed\nfe f5de\ne5 f49b\nff f5dd\nd1 f3ab\neb f4ed\nd0 f3aa\nea f4ec\nd5 f39b\nef f4dd\nd4 f39a\nee f4dc\nd8 f1ea\nd9 f1e9\nc9 f0e9\nc8 f0e8\nba e5ce\na1 e48b\nbb e5cd\n91 e38b\nab e4cd\ncd f2d9\nec f6d8\ncc f2d8\ne9 f6c9\nc9 f2c9\ne8 f6c8\nc8 f2c8\ndd f1d9\ncd f0d9\ndc f1d8\ncc f0d8\nf9 f5c9\ne9 f4c9\nd9 f1c9\nc9 f0c9\nf8 f5c8\ne8 f4c8\nd8 f1c8\nc8 f0c8\nea f6e4\ne8 f6e0\ncb fae7\ndc fb78\ncf fad7\ne9 f6e3\ned f6d3\na3 ec0d\n89 eacb\na9 e6c3\ndb f9e7\ndf f9d7\nc8 f8ea\nca f8e6\nf9 f5e3\nfd f5d3\ne8 f4e2\n9b e9c7\nb9 e5c3\ne9 f6eb\neb f6e7\nfc f778\nef f6d7\ncb f2e7\ndc f378\ncf f2d7\na9 e6cb\nf9 f5eb\nfb f5e7\nff f5d7\ne8 f4ea\nea f4e6\ndb f1e7\nd9 f1e3\ndf f1d7\ndd f1d3\nca f0e6\nc8 f0e2\nb9 e5cb\nbb e5c7\n9b e1c7\n99 e1c3\nca fac6\nce f8d6\ndc f9d2\ncc f8d2\nca f8c6\nd8 f9c2\nc8 f8c2\nee f4d6\nce f0d6\nec f4d2\ncc f0d2\nfa f5c6\nea f4c6\nda f1c6\nca f0c6\nf8 f5c2\ne8 f4c2\nd8 f1c2\nc8 f0c2\nde fb7c\nc4 fa3a\nfe f77c\ne4 f63a\nde f37c\nc4 f23a\nf4 f53a\nd4 f13a\nde f17c\nc4 f03a\ne2 fc2c\nc8 faea\nca fae6\n35 8d33\ncb fae5\ncd fad9\nce fad6\ne9 f6e1\nf8 f5e0\nd8 f9ea\n9a c34e\ncb f8e5\nca f8e4\nf8 f5e2\nf9 f5e1\nfc f5d2\ne9 f4e1\ne8 f4e0\ne8 f6ea\ne9 f6e9\neb f6e5\ned f6d9\nf8 f5e8\nfc f5d8\nd8 f1e0\nf8 f5ea\nf9 f5e9\nfa f5e6\ne1 f4a3\nfb f5e5\nfd f5d9\nfe f5d6\ne9 f4e9\ne8 f4e8\nd1 f3a3\neb f4e5\nd0 f3a2\nea f4e4\ned f4d9\nec f4d8\nda f1e6\nc1 f0a3\ndb f1e5\nd8 f1e2\nd9 f1e1\nde f1d6\ndc f1d2\nb1 efa3\ncb f0e5\nc9 f0e1\nc8 f0e0\ncf fad5\nce fad4\n35 8d13\ncb fac5\nc5 f893\ndf f9d5\ncf f8d5\nce f8d4\nc1 f883\ndb f9c5\ncb f8c5\nca f8c4\nef f6d5\ncf f2d5\nee f6d4\nce f2d4\ned f6d1\nec f6d0\neb f6c5\nea f6c4\ne9 f6c1\ne8 f6c0\ne5 f493\nff f5d5\nd5 f393\nef f4d5\nc5 f093\ndf f1d5\nb5 ef93\ncf f0d5\nd4 f392\nee f4d4\ne3 f48f\nfd f5d1\nd3 f38f\ned f4d1\nc3 f08f\ndd f1d1\nb3 ef8f\ncd f0d1\ne2 f48e\nfc f5d0\nd2 f38e\nec f4d0\nc2 f08e\ndc f1d0\nb2 ef8e\ncc f0d0\ne1 f483\nfb f5c5\nd1 f383\neb f4c5\nc1 f083\ndb f1c5\nb1 ef83\ncb f0c5\nd0 f382\nea f4c4\nf9 f5c1\ne9 f4c1\nd9 f1c1\nc9 f0c1\nf8 f5c0\ne8 f4c0\nd8 f1c0\nc8 f0c0\nc5 fa39\ncc fa78\nc4 fa38\nc7 f897\nd4 f938\nc4 f838\ne4 f6b8\nc4 f2b8\ne5 f639\nc5 f239\nec f678\ne4 f638\ncc f278\nc4 f238\nf5 f539\ne5 f439\nd5 f139\nc5 f039\ne7 f497\nf4 f538\nd7 f397\ne4 f438\nc7 f097\nd4 f138\ncf fa7f\n47 ba3f\ndf f97f\nc6 f8be\nce f87e\n57 b93f\n46 b83e\ne7 f6bf\ndf f3fd\nc5 f2bb\ncd f27b\n67 b63f\n5f b37d\n45 b23b\nf7 f5bf\nff f57f\ne6 f4be\nee f47e\nd5 f1bb\ndd f17b\nde f1fc\nc4 f0ba\n77 b53f\n66 b43e\n55 b13b\nde fbf4\nc4 fab2\nc6 fa36\nd4 f9b2\nde f9f4\nc4 f8b2\nc6 f836\nf6 f536\nc6 f036\nce fa7e\ncf fa7d\nc4 f83a\nde f97c\n46 ba3e\n47 ba3d\n56 b93c\nde f97e\nc5 f83b\ndf f97d\ncf f87d\nce f87c\n57 b93d\n47 b83d\n46 b83c\ne6 f6be\ne7 f6bd\nee f67e\nf6 f5bc\ne4 f43a\nfe f57c\nde f3fc\nc4 f2ba\nc5 f2b9\ncc f27a\ncd f279\nd4 f1b8\ncf f0d7\ndc f178\n66 b63e\n67 b63d\nf6 f5be\nf7 f5bd\nfe f57e\ne5 f43b\nff f57d\ne7 f4bd\ne6 f4bc\nd5 f33b\nef f47d\nd4 f33a\nee f47c\nd4 f1ba\nd5 f1b9\nc5 f0b9\nc4 f0b8\n76 b53e\nc7 fa35\nce fa74\nc6 fa34\ncd fa71\n2f 8c7f\nc5 fa31\ncc fa70\n2e 8c7e\nc4 fa30\nd7 f935\nc7 f835\nd6 f934\nc6 f834\nc3 f82f\ndd f971\ncd f871\nd4 f930\ncc f870\nc4 f830\ne6 f6b4\ne4 f6b0\ne7 f635\nc7 f235\nce f274\nc6 f234\ne5 f631\nec f670\ne4 f630\ncc f270\ndd f3f3\nf7 f535\ncd f2f3\ne7 f435\nbd eff3\nd7 f135\nad eef3\nc7 f035\ndc f3f2\nf6 f534\ndb f3ef\nf5 f531\ncb f2ef\ne5 f431\nda f3ee\nf4 f530\nca f2ee\ne4 f430\nc7 fab7\ncd fa7b\ncf fa77\nff f7f5\ne5 f6b3\n47 ba37\nd7 f9b7\ndd f97b\ndf f977\nde f9fc\nc4 f8ba\nc6 f8b6\ncc f87a\nce f876\nf5 f5b3\nfe f5f4\ne4 f4b2\n55 b93b\n75 b533\nff f7fd\ne5 f6bb\ne7 f6b7\ned f67b\nc7 f2b7\ndf f3f5\nc5 f2b3\ncf f277\ncd f273\n47 b237\nf5 f5bb\nf7 f5b7\nfd f57b\nfe f5fc\ne4 f4ba\ne6 f4b6\nec f47a\nd7 f1b7\nd5 f1b3\nc6 f0b6\nde f1f4\nc4 f0b2\nce f076\n75 b53b\n55 b133\nde fbfc\nc4 faba\nc6 fab6\n31 8d03\nc7 fab5\ncc fa7a\ncd fa79\nce fa76\ncf fa75\ncf f8d7\ndc f978\nfe f7f4\ne4 f6b2\ne5 f6b1\ned f671\nf4 f5b0\na6 c41e\nd7 f9b5\ndc f97a\ndd f979\nc5 f833\ndf f975\n96 c31e\nc7 f8b5\naf c45f\nc6 f8b4\ncd f879\ncc f878\ncf f875\nce f874\nf4 f5b2\nf5 f5b1\ne3 f42f\nfd f571\ne5 f4b1\ne4 f4b0\nd3 f32f\ned f471\nfe f7fc\ne4 f6ba\ne5 f6b9\ne6 f6b6\ne7 f6b5\nec f67a\ned f679\nf4 f5b8\nf6 f5b4\nef f4d7\nfc f578\nc6 f2b6\nde f3f4\nc4 f2b2\n2f 84ff\nc5 f2b1\ncf f275\ncd f271\nd6 f1b4\nd4 f1b0\nf4 f5ba\nf5 f5b9\nf6 f5b6\nf7 f5b5\nfc f57a\nd6 f396\nfd f579\ne5 f4b9\ne4 f4b8\ne7 f4b5\ne6 f4b4\ndf f3d7\nec f478\nd6 f1b6\nd7 f1b5\nd4 f1b2\nd5 f1b1\nc5 f033\ndf f175\nc3 f02f\ndd f171\nc7 f0b5\nc6 f0b4\nc5 f0b1\nc4 f0b0\nb5 ef33\ncf f075\nb3 ef2f\ncd f071\nb2 ef2e\ncc f070\nc7 fa9f\ncb fa6f\ncf fa5f\n8b ea4f\n47 ba1f\nd7 f99f\ndf f95f\nc6 f89e\nce f85e\n9b e94f\n8a e84e\n53 b92f\n57 b91f\n46 b81e\nfd f7f1\ne3 f6af\ne7 f69f\ndb f3ed\nc1 f2ab\ndf f3dd\nc5 f29b\n9b e3cd\n81 e28b\n67 b61f\n5b b36d\n41 b22b\n5f b35d\n45 b21b\nf3 f5af\nf7 f59f\nfc f5f0\ne2 f4ae\ne6 f49e\nd1 f1ab\nd5 f19b\nd9 f16b\nb3 e58f\naa e44e\n91 e18b\n99 e14b\n73 b52f\n51 b12b\n55 b11b\nc6 fa9e\nc7 fa9d\ncb fa6d\nce fa5e\ncf fa5d\nd6 f99c\n83 ea8d\n8a ea4e\n8b ea4d\n43 ba2d\n46 ba1e\n47 ba1d\n52 b92c\n56 b91c\nd2 f9ae\nd7 f99d\nc1 f82b\ndb f96d\nde f95e\nc5 f81b\ndf f95d\nc7 f89d\nc6 f89c\ncb f86d\nca f86c\ncf f85d\nce f85c\n81 e80b\n9b e94d\n8b e84d\n8a e84c\n52 b92e\n53 b92d\n57 b91d\n43 b82d\n42 b82c\n47 b81d\n46 b81c\nfc f7f0\ne2 f6ae\ne3 f6ad\ne6 f69e\ne7 f69d\neb f66d\nee f65e\nf2 f5ac\nf6 f59c\na3 e68d\naa e64e\nb2 e58c\n63 b62d\n67 b61d\nf2 f5ae\nf3 f5ad\nf6 f59e\nf7 f59d\ne1 f42b\nfb f56d\ne3 f4ad\ne2 f4ac\ne7 f49d\ne6 f49c\nd1 f32b\neb f46d\nd0 f32a\nea f46c\nb2 e58e\nb3 e58d\na1 e40b\nbb e54d\na3 e48d\na2 e48c\n72 b52e\n76 b51e\ndf fbdd\nc5 fa9b\nd4 fb38\nc7 fa97\nc9 fa6b\ncb fa67\ncf fa57\nfb f7e5\ne1 f6a3\nff f7d5\ne5 f693\n9b ebcd\n81 ea8b\n89 ea4b\n8b ea47\n43 ba27\n47 ba17\nd5 f99b\nd7 f997\nd9 f96b\ndf f957\nf1 f5a3\nf5 f593\nfd f553\n99 e94b\n9b e947\nb1 e583\n51 b92b\n55 b91b\n57 b917\n71 b523\n75 b513\nfb f7ed\ne1 f6ab\ne3 f6a7\nff f7dd\ne5 f69b\nf4 f738\ne7 f697\nc3 f2a7\ndb f3e5\nc1 f2a3\nd4 f338\nc7 f297\ndf f3d5\nc5 f293\ncb f267\nc9 f263\ncf f257\nbb e7cd\na1 e68b\n8b e247\n43 b227\n47 b217\nf1 f5ab\nf3 f5a7\nf5 f59b\nf7 f597\nf9 f56b\nd3 f1a7\nd1 f1a3\nd7 f197\nd5 f193\nb1 e58b\nb3 e587\nb9 e54b\n93 e187\n91 e183\n71 b52b\n75 b51b\n77 b517\n51 b123\n57 b117\n55 b113\nd6 d13c\nbc cffa\n4f 3afd\n7e 37fe\n7a 37ee\n6e 36fe\n6f 36fd\nbd cff9\nbc cff8\nff dfff\nd6 f134\nbc eff2\nbe cff6\nfc dff2\nfe dffe\naa ceec\n52 3bac\n4a 3aec\n6a 36ec\nd7 f13d\nbd effb\nbf eff7\na5 ceb3\nbf cff5\nff dff7\nd3 d12d\nb9 cfeb\nbb cfe7\nd6 f13c\nbc effa\n7b 1fe7\nbd eff9\nbe eff6\na5 eeb3\nbf eff5\nfc dffa\nfe dff6\nd2 d12c\nb8 cfea\nd5 f131\nbb efef\nbf efdf\nfb dfcf\nb8 efe8\nfb dfef\nff dfdf\nb9 cfe9\nb8 cfe8\nbf cfdf\nd2 f10c\nb8 efca\nd4 f130\nba efee\nbe efde\nfa dfce\nfa dfee\nfe dfde\nbe cfde\nae cedc\naa cecc\nba cfe6\nf8 dfe2\n1e a37e\n16 a33e\n1e 8b7e\n16 8b3e\n36 87be\n5e 937e\n56 933e\n3e 877e\n36 873e\n1e 837e\n16 833e\ncd f8f9\n16 233e\n9e 4b7e\n96 4b3e\n1e b7e\n16 b3e\nb6 47be\n36 7be\nd6 533e\nbe 477e\nb6 473e\n9e 437e\n96 433e\n56 133e\nd3 f12d\nb9 efeb\nbb efe7\nd7 f11d\nbd efdb\ncc f078\nbf efd7\nfb dfc7\nb9 efe1\nb8 efe0\nfb dfe7\nff dfd7\na1 cea3\nbb cfe5\nf9 dfe1\nb9 cfe1\nf8 dfe0\nb8 cfe0\nba efc6\nfc dfd2\nd2 f12c\nb8 efea\nba efe6\na1 eea3\nbb efe5\nd6 f11c\nbc efda\n7b 1fc7\nbd efd9\nbe efd6\nfa dfc6\nce f07c\nb4 ef3a\nf8 dfea\nfa dfe6\ne1 dea3\nfb dfe5\nfe dfd6\nbe cfd6\nba cfc6\nce d0fc\nb4 cfba\nf4 df3a\nbc cf7a\nce d07c\nb4 cf3a\na3 ee8f\nbd efd1\na2 ee8e\nbc efd0\na1 ee83\nbb efc5\nb9 efc1\nb8 efc0\na5 ce93\nbf cfd5\na1 ce83\nbb cfc5\nb9 cfc1\nb8 cfc0\n4b 3aed\n4e 3ade\n8a e24c\nb a8ef\nd9 734b\n5a 39ee\n6a 36ee\n6b 36ed\n6e 36de\nf a8df\nb a8cf\n7a 35ee\n61 34ab\n7b 35ed\n7e 35de\nbf ef7f\n37 af3f\nff df7f\nbb cf6f\n77 9f3f\nb5 cfb9\nf4 dfb8\nb4 cfb8\nbd cf79\nb5 cf39\n2b a4ef\nf5 dfb3\n1b a1ef\ne5 deb3\nff dff5\nb a0ef\n2b a4cf\nf5 df93\n1b a1cf\ne5 de93\nff dfd5\nb a0cf\nb7 efbd\nbe ef7e\na5 ee3b\nbf ef7d\nae ee7c\n36 af3e\n37 af3d\n26 ae3c\nce f0f4\nb4 efb2\nfe df7e\ne5 de3b\nff df7d\nb6 ef36\nbc ef72\nce f074\nb4 ef32\nb3 cfad\nba cf6e\na1 ce2b\nbb cf6d\na2 ceac\naa ce6c\n76 9f3e\n33 8f2d\n22 8e2c\nb6 cfb6\nbe cf76\nb6 cf36\nfc df72\nbc cf72\nc1 788b\ndb 79cd\nd3 798d\nea 76ce\neb 76cd\nfa 75ce\ne1 748b\nfb 75cd\n4e 3adc\n4b 3acd\n4a 3acc\n43 3a8d\n42 3a8c\n41 388b\n5b 39cd\n53 398d\n52 398c\n6e 36dc\n6b 36cd\n6a 36cc\n4b 98cf\nb 88cf\n61 348b\n7b 35cd\ncf f0fd\nb5 efbb\nb7 efb7\nbd ef7b\nbf ef77\n9d eb73\n4f b07d\n35 af3b\nf5 dfbb\nff df77\nb6 efb4\ndd db73\n1f 81ff\nb5 efb1\ncb d0ed\nb1 cfab\nb3 cfa7\nb9 cf6b\nbb cf67\nb7 ef35\nb6 ef34\na3 ee2f\nbd ef71\na2 ee2e\nbc ef70\n75 9f3b\n77 9f37\n33 8f27\nb7 cfb5\nb6 cfb4\nb5 cfb1\nb4 cfb0\na5 ce33\nbf cf75\nb7 cf35\nb6 cf34\na3 ce2f\nbd cf71\nb5 cf31\na2 ce2e\nbc cf70\nb4 cf30\n4f 92df\n6b 94ef\nf1 ffa1\n5b 91ef\n6f 94df\n6b 94cf\nf5 ff91\n5f 91df\nf1 ff81\n5b 91cf\nce f0fc\nb4 efba\nb6 efb6\nb7 efb5\nbc ef7a\nbe ef76\na5 ee33\nbf ef75\nf4 dfba\nf5 dfb9\nfc df7a\nfe df76\nca f06c\nb0 ef2a\nca d0ec\nb0 cfaa\nf0 df2a\nb8 cf6a\nca d06c\nb0 cf2a\n1b 29cf\nb 28cf\ncd f0f1\nb3 efaf\nb7 ef9f\nbb ef6f\nbf ef5f\nfb df4f\n37 af1f\n73 9f0f\nfb df6f\nff df5f\nb0 efa8\nb7 cf9f\nbf cf5f\nbb cf4f\nab eec7\nb8 ef68\na3 ee87\nb0 ef28\n73 9f2f\n77 9f1f\n37 8f1f\nb1 cfa9\nf0 dfa8\nb0 cfa8\nb9 cf69\nb1 cf29\nca f0cc\nb0 ef8a\nce f05c\nb4 ef1a\nb8 ef4a\nca f04c\nb0 ef0a\n2b 26ef\n2b 26cf\n9b 61ef\n8b 60ef\n3b 25ef\n2b 24ef\nf5 5fb3\n1b 21ef\n3b 25cf\n2b 24cf\nf5 5f93\n1b 21cf\ncc f0f0\nb2 efae\n1d 81fb\nb3 efad\nb6 ef9e\nba ef6e\na1 ee2b\nbb ef6d\nbe ef5e\na5 ee1b\nbf ef5d\naa ee6c\nae ee5c\nfa df4e\ne1 de0b\nfb df4d\nea de4c\n33 af2d\n36 af1e\n37 af1d\n22 ae2c\n26 ae1c\n72 9f0e\nfa df6e\ne1 de2b\nfb df6d\nfe df5e\ne5 de1b\nff df5d\nee de5c\nb6 cf9e\nb7 cf9d\nb3 cf8d\nbe cf5e\na5 ce1b\nbf cf5d\nba cf4e\na1 ce0b\nbb cf4d\na6 ce9c\na2 ce8c\nae ce5c\naa ce4c\n72 9f2e\n76 9f1e\n77 9f1d\n66 9e1c\nca f0e4\nb0 efa2\n36 8f1e\n37 8f1d\n33 8f0d\n26 8e1c\n22 8e0c\nb2 ef26\nb8 ef62\nb2 cfa6\nf0 dfa2\nba cf66\nb2 cf26\n7f 3fdf\nf8 df62\n3f 2fdf\nb8 cf62\nb1 ef89\nb0 ef88\n6f 1ef7\nb1 ef09\nf4 df98\nb4 cf98\nf0 df88\nb0 cf88\n16 abbc\n16 ab3c\n17 a3bd\n36 a7bc\n16 a3bc\n17 a33d\nef 7cf7\n36 a73c\ncf 78f7\n16 a33c\n4f 1adf\nf adf\n17 8bbd\n16 8bbc\n17 8b3d\n16 8b3c\n37 87bd\n17 83bd\n36 87bc\n16 83bc\n17 833d\nef 5cf7\n36 873c\ncf 58f7\n16 833c\n96 6bbc\n16 2bbc\n96 6b3c\n16 2b3c\n97 63bd\nb6 67bc\n96 63bc\n36 27bc\n16 23bc\n17 233d\nb6 673c\n96 633c\n36 273c\n16 233c\ncb 58cf\n8b 48cf\n5f 19df\n5b 19cf\n4f 18df\n4b 18cf\n1f 9df\n1b 9cf\nf 8df\nb 8cf\n97 4bbd\n17 bbd\ne7 f637\nd6 5bbc\na7 e637\n96 4bbc\n67 b637\n56 1bbc\n97 4b3d\n17 b3d\nd6 5b3c\n96 4b3c\n56 1b3c\n16 b3c\nd7 53bd\nb7 47bd\n97 43bd\n37 7bd\n17 3bd\nf6 57bc\nd6 53bc\nb6 47bc\n96 43bc\n76 17bc\n56 13bc\n36 7bc\n16 3bc\ncb f0ed\nb1 efab\nb3 efa7\ncf f0dd\nb5 ef9b\nc4 f038\nb7 ef97\nb9 ef6b\nbb ef67\nbd ef5b\nbf ef57\n99 eb63\n9d eb53\nf1 df8b\nfb df47\nd9 db43\n4b b06d\n31 af2b\n4f b05d\n35 af1b\n37 af17\ncf 705f\n71 9f0b\n73 9f07\n57 133d\n37 73d\n17 33d\nf6 573c\nd6 533c\nb6 473c\n96 433c\n76 173c\n56 133c\n36 73c\n16 33c\nf1 dfab\nf5 df9b\nfb df67\nff df57\ndd db53\ncf d0dd\nb5 cf9b\ncb d0cd\nb1 cf8b\nbf cf57\nbb cf47\n9d cb53\n99 cb43\n1b 81ef\nb1 efa1\ncf 707f\n71 9f2b\n73 9f27\n75 9f1b\n77 9f17\nb9 ef61\nb8 ef60\n37 8f17\n33 8f07\nb3 cfa5\nb2 cfa4\nf1 dfa1\nb1 cfa1\nf0 dfa0\nb0 cfa0\na1 ce23\nbb cf65\nb3 cf25\nb2 cf24\nf1 df21\nb1 cf21\nce f0d4\nb4 ef92\nca f0c4\nb0 ef82\nb6 ef16\nbc ef52\nba ef46\nb2 ef06\nb8 ef42\nb6 cf16\nfc df52\nbc cf52\nb2 cf06\nb8 cf42\n6b 16ef\n2b 6ef\n6f 16df\n6b 16cf\n4f 12df\n2f 6df\n2b 6cf\nf 2df\ndb 51ef\ncb 50ef\n9b 41ef\n8b 40ef\n7b 15ef\n6b 14ef\neb 54cf\ndf 51df\ndb 51cf\ncf 50df\ncb 50cf\nab 44cf\n9f 41df\n9b 41cf\n8f 40df\n8b 40cf\n7f 15df\n7b 15cf\n6f 14df\n6b 14cf\nca f0ec\nb0 efaa\nb1 efa9\nb2 efa6\n1d 81f3\nb3 efa5\nce f0dc\nb4 ef9a\ndd f179\nb6 ef96\nb8 ef6a\nba ef66\na1 ee23\nbb ef65\nbc ef5a\nbe ef56\nf0 df8a\nf1 df89\nfa df46\nf0 dfaa\nf1 dfa9\nf4 df9a\nf5 df99\nf8 df6a\nfa df66\ne1 de23\nfb df65\nfe df56\nce d0dc\nb4 cf9a\nb5 cf99\nca d0cc\nb0 cf8a\nb1 cf89\nbe cf56\nba cf46\nc6 d03c\nac cefa\nb6 ef94\n1f 81df\nb5 ef91\n1b 81cf\nb1 ef81\na5 ee13\nbf ef55\nb6 ef14\na3 ee0f\nbd ef51\na2 ee0e\nbc ef50\na1 ee03\nbb ef45\nb9 ef41\nb8 ef40\nb7 cf95\nb6 cf94\nb5 cf91\nb4 cf90\nb3 cf85\nb2 cf84\nb1 cf81\nb0 cf80\na5 ce13\nbf cf55\nb7 cf15\nb6 cf14\na3 ce0f\nbd cf51\nb5 cf11\na2 ce0e\nbc cf50\nb4 cf10\na1 ce03\nbb cf45\nb3 cf05\nb2 cf04\nb9 cf41\nb1 cf01\nb8 cf40\nb0 cf00\ndf 737f\nde 737e\nce 727e\n57 33bd\n5e 337e\n5a 336e\n47 32bd\n4e 327e\n4f 327d\nbf edff\nff ddff\nbb cdef\nad cef9\nac cef8\nbd cdf9\nad ccf9\nab ceed\nc6 f034\nac eef2\nbc edf2\nac ecf2\ne5 dcbb\nff ddfd\na1 ccab\nbb cded\nae cef6\nbe cdf6\nae ccf6\nfc ddf2\nec dcf2\nbc cdf2\nac ccf2\n3d 5fb\nd3 73ad\nd6 739e\nd7 739d\nde 735e\n2d 4fb\nc3 72ad\n72 37ac\n76 379c\n53 33ad\n52 33ac\n42 32ac\nc7 f03d\nad eefb\naf eef7\nbf edf7\nae ecf6\n9d e9f3\nc3 d02d\na9 ceeb\nab cee7\na3 ecaf\nbd edf1\nff ddf7\nee dcf6\ndd d9f3\nbb cde7\n99 c9e3\naf cef5\nae cef4\nad cef1\nac cef0\na5 ccb3\nbf cdf5\ne3 dcaf\nfd ddf1\na3 ccaf\nbd cdf1\nc6 f03c\nac eefa\n6b 1ee7\nad eef9\nae eef6\nbe edf6\na5 ecb3\nbf edf5\nfd ddf9\ne5 dcb3\nff ddf5\n36 73e\ned dcf9\nd5 dbb3\nef dcf5\nc2 d02c\na8 ceea\nc5 f031\nab eeef\nbb edef\nbf eddf\naa ecee\nfb ddcf\ne3 7607\nea dcce\naf cedf\nfb ddef\nff dddf\nbf cddf\nbb cdcf\na9 cee9\na8 cee8\nb9 cde9\na9 cce9\nc2 f00c\na8 eeca\nc4 f030\naa eeee\nae eede\nba edee\na1 ecab\nbb eded\nf3 7707\nfa ddce\ne1 dc8b\nfb ddcd\nae cede\naf cedd\nab cecd\ne1 dcab\nfb dded\ne5 dc9b\nff dddd\na5 cc9b\nbf cddd\na1 cc8b\nbb cdcd\nb8 ede2\na8 ece2\naa cee6\ne8 dee2\nba cde6\naa cce6\nf8 dde2\ne8 dce2\nb8 cde2\na8 cce2\nc3 f02d\na9 eeeb\nab eee7\nbb ede7\nbd eddb\nbf edd7\naa ece6\n99 e9e3\n9d e9d3\nfb ddc7\nd9 d9c3\nbc cf78\naf ced7\nb8 cf68\nab cec7\nfb dde7\nea dce6\nd9 d9e3\nbb cdc7\n99 c9c3\nb9 ede1\nb8 ede0\na8 ece0\nab cee5\naa cee4\ne9 dee1\na9 cee1\ne8 dee0\na8 cee0\naa eec6\na1 cca3\nbb cde5\nae ecd6\nf9 dde1\nb9 cde1\nf8 dde0\ne8 dce0\nb8 cde0\na8 cce0\nbc edd2\nac ecd2\nba edc6\naa ecc6\nb8 edc2\na8 ecc2\nee dcd6\nae ccd6\nec dcd2\nac ccd2\nea dcc6\naa ccc6\nf8 ddc2\ne8 dcc2\nb8 cdc2\na8 ccc2\nc2 f02c\na8 eeea\naa eee6\nc6 f01c\nac eeda\nae eed6\nb8 edea\nba ede6\na1 eca3\nbb ede5\n7b 1dc7\nbd edd9\nbe edd6\n7a b74e\n91 eba3\nab ece5\nae ced6\naa cec6\nf9 dde9\ne1 dca3\nfb dde5\nd1 dba3\neb dce5\nba cdc6\nbe ef7c\na4 ee3a\n79 1f63\nae eed4\nbe cffc\na4 ceba\nac ce7a\nbe cf7c\na4 ce3a\n92 eb8e\nac ecd0\n91 eb83\nab ecc5\na9 ecc1\na8 ecc0\nbc cd7a\nac cc7a\nbe cd7c\na4 cc3a\naf ced5\nae ced4\nad ced1\nac ced0\nab cec5\naa cec4\na9 cec1\na8 cec0\nd5 db93\nef dcd5\n95 cb93\naf ccd5\ne1 dc83\nfb ddc5\nd1 db83\neb dcc5\na1 cc83\nbb cdc5\n91 cb83\nab ccc5\nf9 ddc1\nb9 cdc1\na9 ccc1\nf8 ddc0\ne8 dcc0\nb8 cdc0\na8 ccc0\nc7 729f\nd3 71af\nd2 71ae\nd7 719f\ndf 715f\n43 32ad\n4e 325e\n52 31ae\n53 31ad\n56 319e\n5a 316e\n41 302b\n5b 316d\n5e 315e\naf ee7f\n27 ae3f\nb7 edbf\na6 ecbe\nae ec7e\n37 ad3f\n26 ac3e\nd a0fb\ne7 debf\nef de7f\nbd cff1\na3 ceaf\nab ce6f\n67 9e3f\nf7 ddbf\nff dd7f\ne6 dcbe\nee dc7e\nb3 cdaf\nbb cd6f\nbc cdf0\na2 ccae\naa cc6e\n77 9d3f\n66 9c3e\nc9 fae1\n33 8d2f\n63 1c27\na5 ec39\na5 ceb9\na4 ceb8\ne5 de39\nad ce79\na5 ce39\nac ce78\na4 ce38\nb5 cdb9\na5 ccb9\na4 ccb8\n3e 77e\nf5 dd39\nb5 cd39\na5 cc39\naf ccd7\nbc cd78\nae ee7e\naf ee7d\na4 ec3a\nbe ed7c\n26 ae3e\n27 ae3d\nb7 edbd\na5 ec3b\nbf ed7d\na7 ecbd\n95 eb3b\naf ec7d\n36 ad3e\nee de7e\nef de7d\ne4 dc3a\nfe dd7c\nbc cff0\na2 ceae\na3 cead\naa ce6e\nab ce6d\n66 9e3e\n23 8e2d\nf7 ddbd\nfe dd7e\ne5 dc3b\nff dd7d\ne6 dcbc\nd5 db3b\nef dc7d\nb3 cdad\nba cd6e\na1 cc2b\nbb cd6d\na3 ccad\na2 ccac\n91 cb2b\nab cc6d\nbe eff4\na4 eeb2\na6 ee36\nac ee72\nbe ef74\na4 ee32\nb4 edb2\nbe edf4\na4 ecb2\na6 ec36\nbc ed72\nb4 ed32\nac ec72\na6 ceb6\nbe cff4\na4 ceb2\nae ce76\na6 ce36\nec de72\nac ce72\nbe cf74\na4 ce32\nb6 cdb6\na6 ccb6\nf4 ddb2\nfe ddf4\ne4 dcb2\nb4 cdb2\nbe cdf4\na4 ccb2\ne6 dc36\nbe cd76\nb6 cd36\nae cc76\na6 cc36\nfc dd72\nf4 dd32\nec dc72\nbc cd72\nb4 cd32\nac cc72\ne3 768d\nea 764e\nf2 758e\nf3 758d\nfa 754e\ne1 740b\nfb 754d\nc6 729e\nc7 729d\nce 725e\nca 724e\nd6 719e\nd7 719d\nd2 718e\nd3 718d\nde 715e\nc5 701b\ndf 715d\n63 368d\n62 368c\n6b 364d\n73 358d\n72 358c\n61 340b\n7b 354d\n43 328d\n42 328c\n4f 325d\n4e 325c\n4b 324d\n4a 324c\n56 319c\n53 318d\n52 318c\n45 301b\n5f 315d\n41 300b\n5b 314d\nbf effd\na5 eebb\na7 eeb7\nad ee7b\naf ee77\n27 ae37\nb7 edb7\nbd ed7b\na6 ecb6\nae ec76\n9d e973\n35 ad3b\nff dffd\ne5 debb\nd a0f3\ne7 deb7\nef de77\ncd da73\nbb cfed\na1 ceab\na3 cea7\na9 ce6b\nab ce67\n89 ca63\n67 9e37\n23 8e27\nf7 ddb7\nfd dd7b\nff dd77\ne6 dcb6\nec dc7a\nee dc76\nd5 d9b3\ndd d973\nb3 cda7\nb9 cd6b\nbb cd67\n77 9d37\n33 8d27\nf 80ff\na5 eeb1\na7 ee35\nae ee74\na6 ee34\nad ee71\nac ee70\nb5 edb1\nb4 edb0\na4 ecb0\n9d ebf3\nb7 ed35\n8d eaf3\na7 ec35\na3 ec2f\nbd ed71\n93 eb2f\nad ec71\na2 ec2e\nbc ed70\n92 eb2e\nac ec70\na7 ceb5\na6 ceb4\na5 ceb1\na4 ceb0\naf ce75\na7 ce35\nee de74\nae ce74\na6 ce34\ned de71\nad ce71\na5 ce31\nec de70\nac ce70\na4 ce30\nb7 cdb5\nb6 cdb4\na6 ccb4\nb5 cdb1\nf4 ddb0\ne4 dcb0\nb4 cdb0\na4 ccb0\ndd dbf3\nf7 dd35\ncd daf3\ne7 dc35\na5 cc33\nbf cd75\n9d cbf3\nb7 cd35\n95 cb33\naf cc75\n8d caf3\na7 cc35\na4 cc32\nbe cd74\ne3 dc2f\nfd dd71\nd3 db2f\ned dc71\na3 cc2f\nbd cd71\n93 cb2f\nad cc71\ne2 dc2e\nfc dd70\nd2 db2e\nec dc70\na2 cc2e\nbc cd70\n92 cb2e\nac cc70\nbe effc\na4 eeba\na6 eeb6\nac ee7a\nae ee76\naf ee75\nb6 edb4\na4 ec32\nbe ed74\nb6 edb6\nb7 edb5\na5 ec33\nbf ed75\n76 b71e\na7 ecb5\na6 ecb4\n95 eb33\naf ec75\ne5 deb9\nee de76\nef de75\nf6 ddb4\nef dcd7\nfc dd78\ne4 dc32\nfe dd74\n3e 7fe\nf5 ddb9\nf7 ddb5\nfc dd7a\ne5 dc33\nff dd75\ne4 dcb8\ne7 dcb5\ne6 dcb4\nd5 db33\nef dc75\nbd eff1\na3 eeaf\nab ee6f\naf ee5f\neb de4f\n27 ae1f\nb3 edaf\nb7 ed9f\nbb ed6f\nbc edf0\na2 ecae\na6 ec9e\naa ec6e\nae ec5e\nfb dd4f\nea dc4e\n33 ad2f\n26 ac1e\n73 9d0f\neb de6f\nef de5f\na7 ce9f\nbd cfd1\na3 ce8f\naf ce5f\nab ce4f\n67 9e1f\n27 8e1f\nf3 ddaf\nfb dd6f\nff dd5f\nfc ddf0\ne2 dcae\nea dc6e\nee dc5e\nbf cd5f\nbb cd4f\nae cc5e\naa cc4e\n73 9d2f\n77 9d1f\n66 9c1e\ncd fad1\n37 8d1f\nc9 fac1\n33 8d0f\n26 8c1e\nbc eff0\na2 eeae\nd 80fb\na3 eead\na6 ee9e\n72 1f2c\na7 ee9d\naa ee6e\nab ee6d\nae ee5e\naf ee5d\nb6 ed9c\nea de4e\neb de4d\n23 ae2d\n26 ae1e\n27 ae1d\nb2 edae\nb3 edad\nba ed6e\na1 ec2b\nbb ed6d\na5 ec1b\nbf ed5d\na3 ecad\na2 ecac\n91 eb2b\nab ec6d\n95 eb1b\naf ec5d\nfa dd4e\n5b 3be7\ne1 dc0b\nfb dd4d\ne2 dc8c\nd1 db0b\n4b 3ae7\neb dc4d\n32 ad2e\n36 ad1e\n6b 3647\n72 9d0e\nea de6e\neb de6d\nee de5e\nef de5d\na6 ce9e\na7 ce9d\nbc cfd0\na2 ce8e\na3 ce8d\nae ce5e\naf ce5d\naa ce4e\nab ce4d\n66 9e1e\n67 9e1d\n26 8e1e\n27 8e1d\n23 8e0d\nf7 dd9d\nfa dd6e\ne1 dc2b\nfb dd6d\nfe dd5e\n5f 3bf7\ne5 dc1b\nff dd5d\ne2 dcac\ne7 dc9d\ne6 dc9c\nd1 db2b\neb dc6d\nd5 db1b\n4f 3af7\nef dc5d\nb7 cd9d\nb3 cd8d\nbe cd5e\n1f 2bf7\na5 cc1b\nbf cd5d\n1b 2be7\na1 cc0b\nbb cd4d\na7 cc9d\na6 cc9c\na3 cc8d\na2 cc8c\n95 cb1b\nf 2af7\naf cc5d\nbb efed\na1 eeab\na3 eea7\na9 ee6b\nab ee67\nad ee5b\naf ee57\n89 ea63\nfb dfcd\ne1 de8b\neb de47\nc9 da43\n23 ae27\n27 ae17\n63 9e07\nb3 eda7\nb5 ed9b\nb7 ed97\nb9 ed6b\nbb ed67\nbd ed5b\n7a b54e\n91 e9a3\n99 e963\n9d e953\nfb dd47\nd1 d983\nd9 d943\n35 ad1b\n37 ad17\n73 9d07\nfb dfed\ne1 deab\nff dfdd\ne5 de9b\neb de67\nef de57\nc9 da63\ncd da53\nbf cfdd\na5 ce9b\nb4 cf38\na7 ce97\nbb cfcd\na1 ce8b\nb0 cf28\na3 ce87\naf ce57\nab ce47\n8d ca53\n89 ca43\n63 9e27\n67 9e17\n27 8e17\n23 8e07\nf3 dda7\nf9 dd6b\nfb dd67\nff dd57\nd1 d9a3\ndd d953\nbf cd57\nbb cd47\n91 c983\n9d c953\n99 c943\n73 9d27\n77 9d17\n33 8d07\nbc c7fa\nb6 c53c\n9c c3fa\nbf e7ff\nb7 e53d\n9d e3fb\nb5 cd31\n9b cbef\nff d7ff\nf7 d53d\ndd d3fb\nbb c7ef\nb3 c52d\n99 c3eb\n9d cbf9\n3f 77f\ndc dbf8\n9c cbf8\nbd c7f9\n9d c3f9\nfc d7f8\ndc d3f8\nbc c7f8\n9c c3f8\n8e eafc\nbe e7fe\nae e6fc\nb6 e53c\n9c e3fa\n5b 13e7\n9d e3f9\nce dafc\nb4 cd30\n9a cbee\n8a caec\nfe d7fe\nee d6fc\nf6 d53c\ndc d3fa\ndd d3f9\nba c7ee\naa c6ec\nb6 ed34\n9c ebf2\n9e cbf6\nf6 dd34\ndc dbf2\nb6 cd34\n9c cbf2\nbe c7f6\n9e c3f6\nbc c7f2\nb7 ed3d\n9d ebfb\n9f ebf7\nbd e7f3\nbd e7fb\nbf e7f7\n9f e3f7\nf7 dd3d\ndd dbfb\ndf dbf7\nfd d7f3\nb3 cd2d\n99 cbeb\n9b cbe7\nb9 c7e3\nfd d7fb\nff d7f7\ndf d3f7\nb9 c7eb\nbb c7e7\n9b c3e7\n82 e2ae\n9c e3f0\na5 c6b3\nbf c7f5\n85 c2b3\n9f c3f5\n83 c2af\n9d c3f1\nc2 d2ae\ndc d3f0\n82 c2ae\n9c c3f0\nb6 ed3c\n9c ebfa\n5b 1be7\n9d ebf9\n9e ebf6\nbc e7f2\nbc e7fa\n7b 17e7\nbd e7f9\nbe e7f6\n9e e3f6\n83 e2af\n9d e3f1\nde dbf6\nc5 dab3\ndf dbf5\nfc d7f2\nfc d7fa\nfd d7f9\nfe d7f6\nde d3f6\nc3 d2af\ndd d3f1\nb8 c7ea\nb2 c52c\n98 c3ea\nb5 ed31\n9b ebef\nbb e7ef\nbf e7df\nb3 e52d\n99 e3eb\nfb d7cf\ndf dbdf\n9f cbdf\nfb d7ef\nff d7df\nf3 d52d\nd9 d3eb\nbf c7df\nbb c7cf\nb8 e7ca\nb2 e50c\n98 e3ca\nb8 e7e8\n98 e3e8\n99 cbe9\n98 cbe8\nb9 c7e9\n99 c3e9\nf8 d7e8\nd8 d3e8\nb8 c7e8\n98 c3e8\nb4 ed30\n9a ebee\n9e ebde\n8a eaec\nba e7ee\nbe e7de\naa e6ec\nb2 e52c\n98 e3ea\n99 e3e9\nb6 e51c\n9c e3da\nfa d7ce\nea d6cc\nf4 dd30\nda dbee\nde dbde\nce dadc\n9e cbde\n8e cadc\n8a cacc\nfa d7ee\nfe d7de\nea d6ec\nee d6dc\nf2 d52c\nd8 d3ea\nd9 d3e9\nbe c7de\nba c7ce\nae c6dc\naa c6cc\n9a cbe6\nba c7e6\n9a c3e6\nb8 c7e2\n1e a35e\n1a a34e\n56 939e\n5a 936e\n1a 836e\na9 c463\n16 239e\n56 933c\n9a 634e\ncd f8d9\n16 231e\nb3 ed2d\n99 ebeb\n9b ebe7\nb9 e7e3\nbd e7d3\nf9 d7c3\nb9 e7eb\nbb e7e7\nbd e7db\nbf e7d7\n9b e3e7\nfb d7c7\ndb dbe7\nec dc78\ndf dbd7\nf9 d7e3\nfd d7d3\nac cc78\n9f cbd7\n96 439e\n56 139e\n16 39e\nbd c7d3\nb9 c7c3\nf9 d7eb\nfb d7e7\nff d7d7\ndb d3e7\nbf c7d7\nbb c7c7\n9a 436e\n9a ebc6\nda 534e\nd6 531e\nba e7c6\n9a e3c6\nb8 e7c2\n98 ebe0\nb8 e7e0\n98 e3e0\n98 cbe0\nb9 c7e1\n99 c3e1\nf8 d7e0\nd8 d3e0\nb8 c7e0\n98 c3e0\n99 ebe9\n9a ebe6\n6a b64e\n81 eaa3\n9b ebe5\nb6 ed1c\n9c ebda\n9e ebd6\nb8 e7e2\nb9 e7e1\nbc e7d2\nda dbc6\nf8 d7c2\nb8 e7ea\nb9 e7e9\nba e7e6\nbc e7da\n7b 17c7\nbd e7d9\nbe e7d6\n9a e3e6\n99 e3e1\n9e e3d6\nfa d7c6\nda d3c6\nda dbe6\nc1 daa3\ndb dbe5\nde dbd6\nf8 d7e2\nf9 d7e1\nfc d7d2\n9e cbd6\n9a cbc6\nbc c7d2\nb8 c7c2\nf8 d7ea\nf9 d7e9\nfa d7e6\nfe d7d6\nda d3e6\nd9 d3e1\nde d3d6\nbe c7d6\nba c7c6\n9e c3d6\n9a c3c6\n82 ea8e\n9c ebd0\n81 ea83\n9b ebc5\n99 ebc1\n98 ebc0\na3 e68f\nbd e7d1\na2 e68e\nbc e7d0\n82 e28e\n9c e3d0\na1 e683\nbb e7c5\n81 e283\n9b e3c5\nb9 e7c1\n99 e3c1\nb8 e7c0\n98 e3c0\nc5 da93\ndf dbd5\n85 ca93\n9f cbd5\nc1 da83\ndb dbc5\n81 ca83\n9b cbc5\nae ec7c\n94 eb3a\n99 cbc1\n98 cbc0\nc3 d28f\ndd d3d1\n83 c28f\n9d c3d1\nc2 d28e\ndc d3d0\n82 c28e\n9c c3d0\nf9 d7c1\nd9 d3c1\nb9 c7c1\nb4 e73a\n99 c3c1\nf8 d7c0\nd8 d3c0\nb8 c7c0\n98 c3c0\nee dc7c\nd4 db3a\n9c cb7a\nae cc7c\n94 cb3a\nb4 c7ba\nae c4fc\n94 c3ba\nf4 d73a\nbc c77a\nb4 c73a\n9c c37a\nae c47c\n94 c33a\n8b 68ef\nab 64ef\nd a8db\nd a8d3\n9 a8c3\n9f eb7f\n17 ab3f\nb7 e7bf\nbf e77f\n9d e37b\n37 a73f\ndf db7f\n9b cb6f\n57 9b3f\nf7 d7bf\nff d77f\ndd d37b\nb3 c7af\nbb c76f\n99 c36b\n77 973f\n33 872f\n95 cbb9\n37 73f\nd4 dbb8\n94 cbb8\nd5 db39\n9d cb79\n95 cb39\nb5 c7b9\n95 c3b9\nf4 d7b8\nd4 d3b8\nb4 c7b8\n94 c3b8\nbd c779\nb5 c739\n9d c379\n95 c339\n29 a4eb\nfd dff1\ne3 deaf\n9 a0eb\n29 a4e3\ne3 dea7\n9 a0e3\n2d a4db\n29 a4cb\ne7 de9f\nd a0db\nfd dfd1\ne3 de8f\n9 a0cb\n2d a4d3\n29 a4c3\nf4 df38\ne7 de97\nd a0d3\nf0 df28\ne3 de87\n9 a0c3\n97 ebbd\n9e eb7e\n85 ea3b\n9f eb7d\n86 eabc\n8e ea7c\n16 ab3e\n17 ab3d\nb6 e7be\nb7 e7bd\nbe e77e\na5 e63b\nbf e77d\na6 e6bc\nae e67c\n9c e37a\n36 a73e\nde db7e\nc5 da3b\ndf db7d\nce da7c\nac ccf0\n92 cbae\n9a cb6e\n81 ca2b\n9b cb6d\n82 caac\n8a ca6c\n56 9b3e\nf6 d7be\nf7 d7bd\nfe d77e\ne5 d63b\nff d77d\ne6 d6bc\nee d67c\nd5 d3b9\ndc d37a\ndd d379\nb2 c7ae\nb3 c7ad\nba c76e\na1 c62b\nbb c76d\na2 c6ac\naa c66c\n76 973e\n32 872e\nae ecf4\n94 ebb2\n96 eb36\n9c eb72\nae ec74\n94 eb32\n96 cbb6\nee dcf4\nd4 dbb2\nae ccf4\n94 cbb2\nd6 db36\n9e cb76\n96 cb36\ndc db72\nee dc74\nd4 db32\n9c cb72\nae cc74\n94 cb32\nb6 c7b6\n96 c3b6\nb4 c7b2\nbe c776\nb6 c736\n9e c376\n96 c336\nf4 d732\nbc c772\nb4 c732\n9c c372\n4d 98d3\n49 98c3\nd 88d3\n9 88c3\naf ecfd\n95 ebbb\n97 ebb7\n9d eb7b\n9f eb77\nb5 e7b3\nb5 e7bb\nb7 e7b7\nbd e77b\n97 e3b7\nd7 dbb7\ndd db7b\ndf db77\nf5 d7b3\nfd d773\nab cced\n91 cbab\n99 cb6b\n9b cb67\nb1 c7a3\n13 8b27\n31 8723\nf5 d7bb\nf7 d7b7\nfd d77b\nd7 d3b7\ndf d377\ndd d373\nb1 c7ab\nb3 c7a7\nb9 c76b\nbb c767\n93 c3a7\n9b c367\n57 9337\n33 8727\n13 8327\n97 eb35\n96 eb34\n83 ea2f\n9d eb71\n95 eb31\n82 ea2e\n9c eb70\n94 eb30\nb6 e7b4\n96 e3b4\nb4 e7b0\n94 e3b0\nb7 e735\n97 e335\nb5 e731\n95 e331\na2 e62e\nbc e770\nb4 e730\n82 e22e\n9c e370\n94 e330\nd6 dbb4\nd5 dbb1\n37 737\nd4 dbb0\nd7 db35\n85 ca33\n9f cb75\n97 cb35\nd6 db34\n96 cb34\nc3 da2f\ndd db71\nd5 db31\n83 ca2f\n9d cb71\n95 cb31\nc2 da2e\ndc db70\nd4 db30\n82 ca2e\n9c cb70\n94 cb30\nb7 c7b5\n97 c3b5\nf6 d7b4\nd6 d3b4\nb6 c7b4\n96 c3b4\nb5 c7b1\n95 c3b1\nf4 d7b0\nd4 d3b0\nb4 c7b0\n94 c3b0\nf7 d735\nd7 d335\na5 c633\nbf c775\nb7 c735\n85 c233\n9f c375\n97 c335\nf6 d734\nd6 d334\nb6 c734\n96 c334\na3 c62f\nbd c771\nb5 c731\n83 c22f\n9d c371\n95 c331\ne2 d62e\nfc d770\nf4 d730\nc2 d22e\ndc d370\nd4 d330\na2 c62e\nbc c770\nb4 c730\n82 c22e\n9c c370\n94 c330\n69 94eb\n49 90eb\n29 84eb\n9 80eb\n6b 94e7\n69 94e3\n4b 90e7\n49 90e3\n2b 84e7\n29 84e3\n9 80e3\n6d 94db\n69 94cb\ne3 fe8d\n4d 90db\n49 90cb\nc3 f28d\n2d 84db\n29 84cb\na3 ee8d\nd 80db\n9 80cb\n6d 94d3\n69 94c3\n49 90c3\n29 84c3\n9 80c3\n96 ebb6\n66 b61e\n97 ebb5\n9c eb7a\n9e eb76\n85 ea33\n9f eb75\nb4 e7b2\nb5 e7b1\nbc e772\na3 e62f\nbd e771\nb4 e7ba\nb6 e7b6\nb7 e7b5\nbc e77a\nad 4cfb\nbe e776\n96 e3b6\n97 e3b5\n95 e3b1\n8d 48fb\n9e e376\n85 e233\n9f e375\n9c e372\n83 e22f\n9d e371\nd6 dbb6\nd7 dbb5\ndc db7a\ndd db79\nde db76\nc5 da33\ndf db75\nf4 d7b2\nf5 d7b1\nfc d772\ne3 d62f\nfd d771\nf4 d7ba\nf5 d7b9\nf6 d7b6\nf7 d7b5\nfc d77a\nfd d779\ne5 d633\nff d775\nd6 d3b6\nd7 d3b5\nd5 d3b1\nc5 d233\ndf d375\ndc d372\nc3 d22f\ndd d371\naa ec6c\n90 eb2a\nb0 e72a\nea dc6c\nd0 db2a\n98 cb6a\naa cc6c\n90 cb2a\nb0 c7aa\nf0 d72a\nb8 c76a\nb0 c72a\n98 c36a\n91 cb0b\nab cc4d\nb 2ae7\n89 68eb\n8b 68e7\n89 68e3\n1d 29db\n19 29cb\nd 28db\n9 28cb\n1f 29d7\n1d 29d3\n1b 29c7\n19 29c3\nd 28d3\n9 28c3\n9b eb6f\n9f eb5f\ndb db4f\n17 ab1f\nb3 e7af\nb7 e79f\nbf e75f\nf3 d78f\n33 a72f\n73 970f\nd7 db9f\ndb db6f\ndf db5f\n97 cb9f\nad ccd1\n93 cb8f\n9f cb5f\n9b cb4f\n57 9b1f\n17 8b1f\nf3 d7af\nf7 d79f\nfb d76f\nff d75f\nd9 d36b\nb7 c79f\nb3 c78f\nd6 fbb4\nbf c75f\n73 972f\n77 971f\nae ec5c\n94 eb1a\n98 eb4a\naa ec4c\n90 eb0a\nb0 e78a\naa e4cc\n90 e38a\nb4 e71a\nb8 e74a\nb0 e70a\n98 e34a\naa e44c\n90 e30a\n91 eb29\n8b eac7\n98 eb68\n83 ea87\n90 eb28\nb0 e7a8\n90 e3a8\nb1 e729\n91 e329\nab e6c7\nb8 e768\na3 e687\nb0 e728\n8b e2c7\n98 e368\n83 e287\n90 e328\n91 cba9\n90 cba8\nd1 db29\n99 cb69\n91 cb29\nb1 c7a9\n91 c3a9\nf0 d7a8\nd0 d3a8\nb0 c7a8\n90 c3a8\nf1 d729\nd1 d329\nb9 c769\nb1 c729\n99 c369\n91 c329\n29 26eb\n2d 26db\n29 26cb\na9 64eb\n89 60eb\n39 25eb\n29 24eb\nab 64e7\na9 64e3\n8b 60e7\n89 60e3\n3b 25e7\n39 25e3\nb1 c50b\n2b 24e7\n29 24e3\n1b 21e7\n91 c10b\nb 20e7\n3d 25db\n39 25cb\n2d 24db\n29 24cb\n3f 25d7\n3d 25d3\n3b 25c7\n39 25c3\n2d 24d3\n29 24c3\nac ecf0\n92 ebae\n9a eb6e\n81 ea2b\n9b eb6d\n9e eb5e\n85 ea1b\n9f eb5d\n82 eaac\n8a ea6c\n8e ea5c\nec dcd0\nd2 db8e\nda db4e\nc1 da0b\ndb db4d\nca da4c\n16 ab1e\nb2 e7ae\nb3 e7ad\nb6 e79e\na1 e62b\nbb e76d\nbe e75e\na2 e6ac\naa e66c\n2f acff\nae e65c\n91 e3a9\n99 e369\n9c e35a\nf2 d78e\nf3 d78d\nfa d74e\ne2 d68c\n32 a72e\nec dcf0\nd2 dbae\nd6 db9e\nd7 db9d\nda db6e\nc1 da2b\ndb db6d\nde db5e\nc5 da1b\ndf db5d\nc6 da9c\nca da6c\nce da5c\n96 cb9e\n97 cb9d\nac ccd0\n92 cb8e\n93 cb8d\n9e cb5e\n85 ca1b\n9f cb5d\n9a cb4e\n81 ca0b\n9b cb4d\n86 ca9c\n82 ca8c\n8e ca5c\n8a ca4c\n56 9b1e\n16 8b1e\nf2 d7ae\nf3 d7ad\nf6 d79e\nf7 d79d\nfa d76e\ne1 d62b\nfb d76d\nfe d75e\ne2 d6ac\ne6 d69c\nea d66c\n6f 9cff\nee d65c\nd1 d3a9\nd8 d36a\nd9 d369\nb6 c79e\nb7 c79d\nb2 c78e\nb3 c78d\nd5 fbb3\nef fcf5\nbe c75e\n26 8cbe\na5 c61b\nbf c75d\nd1 fba3\neb fce5\nba c74e\na6 c69c\na2 c68c\n2f 8cff\nc5 fab1\nae c65c\n72 972e\na7 ccb5\n76 971e\n91 eb89\n4f 1af7\n91 eb09\nb1 e789\n91 e389\nb0 e788\n90 e388\n94 cb98\n90 cb88\nd5 d399\n95 c399\nf4 d798\nd4 d398\nb4 c798\n94 c398\nd1 d389\n91 c389\nf0 d788\nd0 d388\nb0 c788\n90 c388\naa ece4\n90 eba2\n92 eb26\n98 eb62\n92 cba6\nea dce4\nd0 dba2\naa cce4\n90 cba2\nd2 db26\n9a cb66\n92 cb26\n5f 3bdf\nd8 db62\n1f 2bdf\n98 cb62\nb2 c7a6\n92 c3a6\nb0 c7a2\nf2 d726\nd2 d326\nba c766\nb2 c726\n9a c366\n92 c326\n3f 27df\nb8 c762\n1f 23df\n98 c362\n16 ab9c\n12 ab8c\n13 ab2d\n12 ab2c\n17 ab1d\n13 ab0d\n16 ab1c\n12 ab0c\nef 7c5f\nb ae7\n32 a7ac\n13 a32d\neb 7ce7\n32 a72c\ncb 78e7\n12 a32c\n12 8bac\n56 9b9c\ned fc73\n16 8b9c\ne9 fc63\n12 8b8c\n13 8b2d\n12 8b2c\n57 9b1d\n17 8b1d\n13 8b0d\n56 9b1c\n16 8b1c\n12 8b0c\n13 83ad\n32 87ac\n12 83ac\n57 939d\n97 639f\n53 938d\n76 979c\n56 939c\n13 832d\n96 633e\n52 932c\neb 5ce7\n32 872c\n76 971c\n56 931c\n96 631e\n52 930c\n92 6b8c\n12 2b8c\n92 6b2c\n12 2b2c\n96 6b1c\n92 6b0c\n16 2b1c\n12 2b0c\n89 48e3\nda f346\nc9 58cb\n92 63ac\n93 638d\n13 238d\nb2 678c\n96 639c\n92 638c\n32 278c\n12 238c\n93 632d\n13 232d\nb2 672c\n92 632c\n32 272c\n12 232c\n17 231d\n13 230d\n36 271c\n32 270c\n16 231c\n12 230c\n8d 48d3\n89 48c3\n5f 19d7\n5d 19d3\n59 19c3\n4d 18d3\n49 18c3\n1f 9d7\n1d 9d3\n1b 9c7\n19 9c3\nd 8d3\n9 8c3\na3 e627\n92 4bac\ne3 f607\nd2 5b8c\na7 e617\n96 4b9c\na3 e607\n92 4b8c\n63 b607\n52 1b8c\nd2 5b2c\n92 4b2c\n12 b2c\nd6 5b1c\nd2 5b0c\n96 4b1c\n92 4b0c\n56 1b1c\n52 1b0c\n16 b1c\n12 b0c\nab eced\n91 ebab\n99 eb6b\n9b eb67\n9d eb5b\n9f eb57\nb1 e7a3\nb5 e793\ndb db47\nf1 d783\n17 ab17\n53 9b07\n71 9703\nb1 e7ab\nb3 e7a7\nb5 e79b\nb7 e797\n93 e3a7\nf1 d78b\nf3 d787\nfb d747\ndb d347\n71 970b\n73 9707\n53 9307\nd3 dba7\nef dcdd\nd5 db9b\ne4 dc38\nd7 db97\nd9 db6b\ndb db67\ndf db57\nf1 d7a3\nf5 d793\naf ccdd\n95 cb9b\na4 cc38\n97 cb97\nab cccd\n91 cb8b\n9f cb57\n9b cb47\nb5 c793\nb1 c783\n93 43ad\n13 3ad\n57 9b17\n75 9713\n17 8b17\n13 8b07\n31 8703\nd2 53ac\nb2 47ac\n92 43ac\n32 7ac\n12 3ac\nf1 d7ab\nf3 d7a7\nf5 d79b\nf7 d797\nf9 d76b\nfb d767\nd3 d3a7\ndb d367\n46 329e\nd9 d363\ndf d357\nb5 c79b\nb7 c797\nb1 c78b\nb3 c787\nbf c757\nbb c747\n9f c357\n9b c347\nd3 538d\n97 439d\n93 438d\n53 138d\n75 971b\n77 9717\n53 9327\n57 9317\nfd 5d73\n37 8717\nf9 5d63\n33 8707\nf2 578c\nd6 539c\nd2 538c\nb6 479c\nb2 478c\n96 439c\n92 438c\n72 178c\n52 138c\n36 79c\n32 78c\ned 7473\n16 39c\ne9 7463\n12 38c\n79 1d63\nae ecd4\n94 eb92\naa ecc4\n90 eb82\n96 eb16\n9c eb52\n9a eb46\n92 eb06\n98 eb42\nd3 532d\n93 432d\n53 132d\n13 32d\nf2 572c\nd2 532c\nb2 472c\n92 432c\n87 e29d\n52 132c\n32 72c\n12 32c\nb2 e786\nb0 e782\na9 4ccb\nba e746\n89 48cb\n9a e346\nb8 e742\n98 e342\nee dcd4\nd4 db92\nae ccd4\n94 cb92\nea dcc4\nd0 db82\naa ccc4\n90 cb82\nd6 db16\n96 cb16\ndc db52\n9c cb52\nd2 db06\n92 cb06\nd8 db42\n98 cb42\n57 131d\n53 130d\n17 31d\n13 30d\n37 dbf\nb6 471c\n76 171c\n72 170c\n56 131c\n52 130c\n36 71c\n32 70c\n16 31c\n12 30c\nb6 c716\n96 c316\nf4 d712\nb4 c712\nf2 d706\nd2 d306\nb2 c706\n92 c306\nf0 d702\nb0 c702\n7b b74f\n92 eba4\n79 b74b\n90 eba0\n93 eb25\n92 eb24\n99 eb61\n91 eb21\n98 eb60\n90 eb20\nb2 e7a4\n92 e3a4\nb0 e7a0\n90 e3a0\nb3 e725\n93 e325\nb1 e721\n91 e321\nb8 e760\nb0 e720\n98 e360\n90 e320\nd2 dba4\nd3 db25\n81 ca23\n9b cb65\n93 cb25\nd2 db24\n92 cb24\nd1 db21\n91 cb21\nb3 c7a5\n93 c3a5\nf2 d7a4\nd2 d3a4\nb2 c7a4\n92 c3a4\nb1 c7a1\n91 c3a1\nf0 d7a0\nd0 d3a0\nb0 c7a0\n90 c3a0\nf3 d725\nd3 d325\na1 c623\nbb c765\nfe fdfe\nb3 c725\n81 c223\n9b c365\nde f9fe\n93 c325\nf2 d724\nd2 d324\nb2 c724\n92 c324\nf1 d721\nd1 d321\nfc fdfa\nb1 c721\ndc f9fa\n91 c321\n57 339d\nd0 d320\n69 16eb\n29 6eb\n69 16e3\n2b 6e7\n29 6e3\nef 745f\nb 2e7\n6d 16db\n69 16cb\n2d 6db\n29 6cb\n69 16c3\n2d 6d3\n29 6c3\nc9 50eb\n89 40eb\n79 15eb\n69 14eb\n59 11eb\n49 10eb\n39 5eb\n29 4eb\n19 1eb\n9 eb\ne9 54e3\ncb 50e7\nc9 50e3\na9 44e3\n8b 40e7\n89 40e3\n79 15e3\n69 14e3\n59 11e3\n49 10e3\n3b 5e7\n39 5e3\n2b 4e7\n29 4e3\n1b 1e7\n19 1e3\nb e7\n9 e3\ne9 54cb\ncd 50db\nc9 50cb\na9 44cb\n8d 40db\n89 40cb\n7d 15db\n79 15cb\n6d 14db\n69 14cb\nf3 7f8d\n5d 11db\n59 11cb\ne3 7e8d\n4d 10db\n49 10cb\nd3 738d\n3d 5db\n39 5cb\nc3 728d\n2d 4db\n29 4cb\n19 1cb\n9 cb\ned 54d3\ne9 54c3\ncd 50d3\nc9 50c3\nad 44d3\na9 44c3\n8d 40d3\n89 40c3\n7f 15d7\n7d 15d3\n79 15c3\n6d 14d3\n69 14c3\n59 11c3\n49 10c3\n3b 5c7\n39 5c3\n29 4c3\n1b 1c7\n19 1c3\n9 c3\n91 eba9\n92 eba6\n98 eb6a\n99 eb69\n9a eb66\n81 ea23\n9b eb65\n9c eb5a\n9e eb56\nb0 e7a2\nb1 e7a1\nb4 e792\nb8 e762\nb9 e761\nbc e752\nda db46\nf0 d782\nf8 d742\nb0 e7aa\nb1 e7a9\nb2 e7a6\nb3 e7a5\nb4 e79a\nb6 e796\nb9 e769\na9 4ceb\nba e766\nbc e75a\nad 4cdb\nbe e756\n92 e3a6\n93 e3a5\n91 e3a1\n81 e223\n9b e365\n98 e362\n99 e361\n9c e352\nf0 d78a\nf1 d789\nf2 d786\nd8 d342\nd2 dba6\nd3 dba5\nd5 db99\nfd dd79\nd6 db96\nd8 db6a\nd9 db69\nda db66\nc1 da23\ndb db65\nde db56\nf0 d7a2\nf1 d7a1\nf4 d792\n7f 37df\nf8 d762\nfc d752\n95 cb99\nbd cd79\n96 cb96\n91 cb89\n9e cb56\n9a cb46\nb4 c792\nb0 c782\nd3 fba7\nbc c752\nb8 c742\nf0 d7aa\nf1 d7a9\nf2 d7a6\nf3 d7a5\nf4 d79a\nf5 d799\nf6 d796\nf8 d76a\nf9 d769\nfa d766\ne1 d623\nfb d765\nd2 d3a6\nd3 d3a5\nd1 d3a1\nda d366\nc1 d223\ndb d365\n5f 33df\nd8 d362\n46 329c\nd9 d361\ndc d352\nb4 c79a\nb5 c799\nb6 c796\nb0 c78a\nb1 c789\nb2 c786\nbe c756\nba c746\n9e c356\n9c c352\n9a c346\n98 c342\n93 eb85\n92 eb84\n91 eb81\n90 eb80\n85 ea13\n9f eb55\n96 eb14\n83 ea0f\n9d eb51\n95 eb11\n82 ea0e\n9c eb50\n94 eb10\n81 ea03\n9b eb45\n93 eb05\n92 eb04\n99 eb41\n91 eb01\n98 eb40\n90 eb00\nb6 e794\nb5 e791\nb4 e790\nb3 e785\n93 e385\nb2 e784\n92 e384\nb1 e781\n91 e381\nb0 e780\n90 e380\n3d adf3\na2 e60e\nbc e750\n35 adb3\nb4 e710\nb3 e705\n93 e305\n39 ade3\nb8 e740\n97 cb95\n96 cb94\nd5 db91\n95 cb91\n94 cb90\n93 cb85\n92 cb84\nd1 db81\n91 cb81\n90 cb80\nc5 da13\ndf db55\nd7 db15\n85 ca13\n9f cb55\n97 cb15\nd6 db14\n96 cb14\nc3 da0f\ndd db51\nd5 db11\n83 ca0f\n9d cb51\n95 cb11\nc2 da0e\ndc db50\nd4 db10\n82 ca0e\n9c cb50\n94 cb10\nc1 da03\ndb db45\nd3 db05\n81 ca03\n9b cb45\n93 cb05\nd2 db04\n92 cb04\nd9 db41\nd1 db01\n99 cb41\n91 cb01\nd8 db40\nd0 db00\n98 cb40\n90 cb00\nf7 d795\nd7 d395\nb7 c795\n97 c395\nf6 d794\nd6 d394\nb6 c794\n96 c394\nf5 d791\nd5 d391\nb5 c791\n95 c391\nf4 d790\nd4 d390\nb4 c790\n94 c390\nf3 d785\nd3 d385\nb3 c785\n93 c385\nf2 d784\nd2 d384\nb2 c784\n92 c384\nf1 d781\nd1 d381\nb1 c781\n91 c381\nf0 d780\nd0 d380\nb0 c780\n90 c380\nf7 d715\nd7 d315\nb7 c715\n97 c315\n7d 9df3\ne2 d60e\nfc d750\n3d 8df3\na2 c60e\nd3 fba5\nbc c750\n82 c20e\n1d 89f3\n9c c350\nf3 d705\nd3 d305\nfe fdde\nb3 c705\nde f9de\n93 c305\n79 9de3\nf8 d740\n39 8de3\nb8 c740\na6 cc3c\n8c cafa\nac c6fa\na6 c43c\n8c c2fa\nbc c5fa\nac c4fa\n9c c1fa\n8c c0fa\n9f e9ff\na7 e43d\n8d e2fb\nbf e5ff\nae e4fe\n9d e1fb\n8c e0fa\na5 cc31\n8b caef\ndf d9ff\n9b c9ef\ne7 d43d\ncd d2fb\nab c6ef\na3 c42d\n89 c2eb\nff d5ff\nee d4fe\ndd d1fb\ncc d0fa\nbb c5ef\naa c4ee\n99 c1eb\n8d caf9\n8c caf8\n9d c9f9\n8d c8f9\nad c6f9\n8d c2f9\nec d6f8\ncc d2f8\nac c6f8\n8c c2f8\nbd c5f9\nad c4f9\n9d c1f9\n8d c0f9\nbc c5f8\nac c4f8\n9c c1f8\n8c c0f8\n8f eafd\n8f e8fd\nae e6fe\na6 e43c\n8c e2fa\n4b 12e7\n8d e2f9\nbe e5fe\na5 e4bb\nbf e5fd\n95 e3bb\naf e4fd\n94 e3ba\nae e4fc\n9c e1fa\n5b 11e7\n9d e1f9\n4b 10e7\n8d e0f9\ncf dafd\na4 cc30\n8a caee\n8b caed\n81 c8ab\n9b c9ed\n8b c8ed\nee d6fe\ne6 d43c\ncc d2fa\ndc d1f8\naa c6ee\nab c6ed\nfe d5fe\ne5 d4bb\nff d5fd\nd5 d3bb\nef d4fd\nd4 d3ba\nee d4fc\ndc d1fa\ndd d1f9\ncd d0f9\ncc d0f8\nba c5ee\na1 c4ab\nbb c5ed\n91 c3ab\nab c4ed\n90 c3aa\naa c4ec\na6 ec34\n8c eaf2\n9c e9f2\n8c e8f2\n8e caf6\ne6 dc34\ncc daf2\na6 cc34\n8c caf2\n9e c9f6\n8e c8f6\ndc d9f2\ncc d8f2\n9c c9f2\n8c c8f2\nae c6f6\n8e c2f6\nac c6f2\nbe c5f6\nae c4f6\n9e c1f6\nbc c5f2\nac c4f2\n9c c1f2\n8c c0f2\na7 ec3d\n8d eafb\n8f eaf7\nad e6f3\n9f e9f7\n8e e8f6\nbd e5f3\nac e4f2\nad e6fb\naf e6f7\n8f e2f7\nbd e5fb\nbf e5f7\nac e4fa\nae e4f6\n9f e1f7\n9d e1f3\n8c e0f2\ne7 dc3d\ncd dafb\ncf daf7\ned d6f3\na3 cc2d\n89 caeb\n8b cae7\na9 c6e3\ndf d9f7\nce d8f6\nfd d5f3\nec d4f2\n9b c9e7\nb9 c5e3\ned d6fb\nef d6f7\ncf d2f7\na9 c6eb\nab c6e7\n8b c2e7\nfd d5fb\nff d5f7\nec d4fa\nee d4f6\ndf d1f7\ndd d1f3\nce d0f6\ncc d0f2\nb9 c5eb\nbb c5e7\n9b c1e7\n99 c1e3\n8e eaf4\n8d eaf1\n8c eaf0\n8c e8f0\nae e6f4\n8e e2f4\nac e6f0\n8c e2f0\n8f caf5\nce daf4\n8e caf4\ncd daf1\n8d caf1\n8c caf0\n5e 935e\n8f c8f5\n8e c8f4\n8c c8f0\nfa fdce\naf c6f5\nda f9ce\n8f c2f5\nee d6f4\nce d2f4\nae c6f4\n8e c2f4\nf8 fdca\nad c6f1\nd8 f9ca\n8d c2f1\nec d6f0\ncc d2f0\nac c6f0\n8c c2f0\na5 c4b3\nbf c5f5\n95 c3b3\naf c4f5\n85 c0b3\n9f c1f5\n75 bfb3\n8f c0f5\n94 c3b2\nae c4f4\na3 c4af\nbd c5f1\n93 c3af\nad c4f1\n83 c0af\n9d c1f1\n92 c3ae\nac c4f0\n82 c0ae\n9c c1f0\na6 ec3c\n8c eafa\n4b 1ae7\n8d eaf9\n8e eaf6\n8f eaf5\nad e6f1\n9e e9f6\n5e b35e\n8f e8f5\n8e e8f4\nbc e5f2\na3 e4af\nbd e5f1\n93 e3af\nad e4f1\n92 e3ae\nac e4f0\nac e6fa\n6b 16e7\nad e6f9\naf e6f5\n8f e2f5\n8d e2f1\n82 e0ae\n9c e1f0\nbc e5fa\n7b 15e7\nbd e5f9\nbe e5f6\na5 e4b3\nbf e5f5\n6b 14e7\nad e4f9\n95 e3b3\naf e4f5\n94 e3b2\nae e4f4\n9e e1f6\n85 e0b3\n9f e1f5\n9c e1f2\n83 e0af\n9d e1f1\n8f e0f5\n8e e0f4\n8d e0f1\n8c e0f0\ne6 dc3c\ncc dafa\ncd daf9\nce daf6\ncf daf5\nec d6f2\nc5 d8b3\ndf d9f5\n16 33e\ncd d8f9\ncf d8f5\nce d8f4\nfc d5f2\ne3 d4af\nfd d5f1\nd3 d3af\ned d4f1\nd2 d3ae\nec d4f0\nec d6fa\nee d6f6\nef d6f5\nfc d5f8\nce d2f6\ncf d2f5\nc2 d0ae\ndc d1f0\nfc d5fa\nfd d5f9\nfe d5f6\ne5 d4b3\nff d5f5\ned d4f9\nec d4f8\nd5 d3b3\nef d4f5\nd4 d3b2\nee d4f4\nde d1f6\nc5 d0b3\ndf d1f5\ndc d1f2\nc3 d0af\ndd d1f1\nb5 cfb3\ncf d0f5\nb4 cfb2\nce d0f4\nb3 cfaf\ncd d0f1\nb2 cfae\ncc d0f0\na2 cc2c\n88 caea\na8 c6ea\na2 c42c\n88 c2ea\nb8 c5ea\na8 c4ea\n98 c1ea\n88 c0ea\na5 ec31\n8b eaef\n9b e9ef\n9f e9df\n8a e8ee\nc3 7207\nca d8ce\nab e6ef\na3 e42d\n89 e2eb\neb d6cf\nbb e5ef\nbf e5df\naa e4ee\nae e4de\n99 e1eb\n9d e1db\n88 e0ea\nfb d5cf\nea d4ce\ncf dadf\n8f cadf\ndf d9df\n9f c9df\n9b c9cf\neb d6ef\ne3 d42d\nc9 d2eb\nab c6cf\nfb d5ef\nff d5df\nea d4ee\nee d4de\nd9 d1eb\nc8 d0ea\nbf c5df\nbb c5cf\nae c4de\naa c4ce\na2 ec0c\n88 eaca\na8 e6ca\na2 e40c\n88 e2ca\n8c e0da\nb8 e5ca\na8 e4ca\n98 e1ca\n88 e0ca\n88 eae8\na8 e6e8\n88 e2e8\n89 cae9\n88 cae8\n99 c9e9\n89 c8e9\na9 c6e9\n89 c2e9\ne8 d6e8\nc8 d2e8\na8 c6e8\n88 c2e8\nb9 c5e9\na9 c4e9\n99 c1e9\n89 c0e9\nb8 c5e8\na8 c4e8\n98 c1e8\n88 c0e8\na4 ec30\n8a eaee\n8b eaed\n8e eade\n9a e9ee\n81 e8ab\n9b e9ed\n8b e8ed\nd3 7307\nda d9ce\naa e6ee\nab e6ed\nae e6de\na2 e42c\n88 e2ea\n89 e2e9\na6 e41c\n8c e2da\n98 e1e8\nea d6ce\nba e5ee\na1 e4ab\nbb e5ed\nbe e5de\n91 e3ab\nab e4ed\n90 e3aa\naa e4ec\n98 e1ea\n99 e1e9\n9c e1da\n89 e0e9\n88 e0e8\nfa d5ce\ne1 d48b\nfb d5cd\nd1 d38b\neb d4cd\nd0 d38a\nea d4cc\ne4 dc30\nca daee\nce dade\ncf dadd\n8e cade\n8f cadd\n8b cacd\nc5 d89b\ndf d9dd\ncf d8dd\n85 c89b\n9f c9dd\n81 c88b\n9b c9cd\n8f c8dd\n8b c8cd\nea d6ee\nee d6de\ne2 d42c\nc8 d2ea\nc9 d2e9\nd8 d1e8\nae c6de\naa c6ce\nab c6cd\nfa d5ee\ne1 d4ab\nfb d5ed\nfe d5de\ne5 d49b\nff d5dd\nd1 d3ab\neb d4ed\nd0 d3aa\nea d4ec\nd5 d39b\nef d4dd\nd4 d39a\nee d4dc\nd8 d1ea\nd9 d1e9\nc9 d0e9\nc8 d0e8\nbe c5de\na5 c49b\nbf c5dd\nba c5ce\na1 c48b\nbb c5cd\n95 c39b\naf c4dd\n94 c39a\nae c4dc\n91 c38b\nab c4cd\n90 c38a\naa c4cc\n5b 11c7\n9d e1d9\n98 e9e2\n88 e8e2\n8a cae6\n9a c9e6\n8a c8e6\nd8 d9e2\nc8 d8e2\n98 c9e2\n88 c8e2\naa c6e6\n8a c2e6\na8 c6e2\nba c5e6\naa c4e6\n9a c1e6\nb8 c5e2\na8 c4e2\n98 c1e2\n88 c0e2\na3 ec2d\n89 eaeb\na9 e6e3\nd8 db68\ncb dac7\ne9 d6c3\n9b e9e7\n9d e9db\n9f e9d7\n8a e8e6\nb9 e5e3\nbd e5d3\na8 e4e2\ndb d9c7\nf9 d5c3\na9 e6eb\nab e6e7\nf8 d768\neb d6c7\nd8 d368\ncb d2c7\nb9 e5eb\nbb e5e7\nbd e5db\nbf e5d7\na8 e4ea\naa e4e6\nac e4da\n9b e1e7\n99 e1e3\n9f e1d7\n9d e1d3\n88 e0e2\nfb d5c7\ndb d1c7\nd9 d1c3\ncb dae7\ndc db78\ncf dad7\ne9 d6e3\ned d6d3\n9c cb78\n8f cad7\n98 cb68\n8b cac7\nad c6d3\na9 c6c3\ndb d9e7\ncb 726f\ndf d9d7\nca d8e6\nf9 d5e3\nfd d5d3\ne8 d4e2\n9b c9c7\nbd c5d3\nb9 c5c3\ne9 d6eb\neb d6e7\nfc d778\nef d6d7\ncb d2e7\ndc d378\ncf d2d7\nbc c778\naf c6d7\nb8 c768\nab c6c7\n9c c378\n8f c2d7\n98 c368\n8b c2c7\nf9 d5eb\nfb d5e7\nff d5d7\ne8 d4ea\nea d4e6\ndb d1e7\nd9 d1e3\ndf d1d7\ndd d1d3\nca d0e6\nc8 d0e2\nbf c5d7\nbb c5c7\n9f c1d7\n9d c1d3\n9b c1c7\n99 c1c3\n8a eac6\n8e e8d6\n9c e9d2\n8c e8d2\n9a e9c6\n8a e8c6\n98 e9c2\n88 e8c2\nae e4d6\n8e e0d6\nac e4d2\n8c e0d2\nba e5c6\naa e4c6\n9a e1c6\n8a e0c6\nb8 e5c2\na8 e4c2\n98 e1c2\n88 e0c2\nce d8d6\n8e c8d6\ncc d8d2\n8c c8d2\nca d8c6\n8a c8c6\nd8 d9c2\nc8 d8c2\n98 c9c2\n88 c8c2\nee d4d6\nce d0d6\nae c4d6\n8e c0d6\nec d4d2\ncc d0d2\nac c4d2\n8c c0d2\nea d4c6\nca d0c6\naa c4c6\n8a c0c6\ne8 d4c2\nc8 d0c2\na8 c4c2\n88 c0c2\n8a eae4\n89 eae1\n88 eae0\n98 e9e0\n88 e8e0\naa e6e4\n8a e2e4\na8 e6e0\n88 e2e0\n8b cae5\nca dae4\n8a cae4\nc9 dae1\n89 cae1\n88 cae0\n5a 934e\n8b c8e5\n8a c8e4\n98 c9e0\n88 c8e0\nf6 fdbe\nab c6e5\nd6 f9be\n8b c2e5\nea d6e4\nca d2e4\naa c6e4\n8a c2e4\nf4 fdba\na9 c6e1\nd4 f9ba\n89 c2e1\ne8 d6e0\nc8 d2e0\na8 c6e0\n88 c2e0\na1 c4a3\nbb c5e5\n91 c3a3\nab c4e5\n81 c0a3\n9b c1e5\n71 bfa3\n8b c0e5\n90 c3a2\naa c4e4\nb9 c5e1\na9 c4e1\n99 c1e1\n89 c0e1\nb8 c5e0\na8 c4e0\n98 c1e0\n88 c0e0\na2 ec2c\n88 eaea\n89 eae9\n8b eae5\na6 ec1c\n8c eada\n8e ead6\na9 e6e1\nb8 e5e0\nca dac6\ne8 d6c2\n9a e9e6\n6a b44e\n81 e8a3\n9b e9e5\n5b 19c7\n9d e9d9\n9e e9d6\n5a b34e\n8b e8e5\n8a e8e4\nb8 e5e2\nb9 e5e1\nbc e5d2\na9 e4e1\na8 e4e0\nf8 d5c2\na8 e6ea\na9 e6e9\nab e6e5\nac e6da\nb8 e5e8\n8b e2e5\n89 e2e1\n98 e1e0\nea d6c6\nca d2c6\nb8 e5ea\nb9 e5e9\nba e5e6\na1 e4a3\nbb e5e5\nbc e5da\n7b 15c7\nbd e5d9\nbe e5d6\na9 e4e9\na8 e4e8\n91 e3a3\nab e4e5\n90 e3a2\naa e4e4\n9a e1e6\n81 e0a3\n9b e1e5\n98 e1e2\n99 e1e1\n9e e1d6\n9c e1d2\n8b e0e5\n8a e0e4\n89 e0e1\n88 e0e0\nfa d5c6\nda d1c6\nd8 d1c2\ne2 dc2c\nc8 daea\nca dae6\ncb dae5\nce dad6\ne8 d6e2\ne9 d6e1\nec d6d2\nf8 d5e0\n8e cad6\n8a cac6\nac c6d2\na8 c6c2\nc1 d8a3\ndb d9e5\ncb d8e5\nca d8e4\nf8 d5e2\nf9 d5e1\nfc d5d2\ne9 d4e1\ne8 d4e0\n9a c9c6\nbc c5d2\nb8 c5c2\ne8 d6ea\ne9 d6e9\nea d6e6\neb d6e5\nee d6d6\nf8 d5e8\nca d2e6\ncb d2e5\nc9 d2e1\nce d2d6\nd8 d1e0\nae c6d6\naa c6c6\n8e c2d6\n8a c2c6\nf8 d5ea\nf9 d5e9\nfa d5e6\ne1 d4a3\nfb d5e5\nfe d5d6\ne9 d4e9\ne8 d4e8\nd1 d3a3\neb d4e5\nd0 d3a2\nea d4e4\nda d1e6\nc1 d0a3\ndb d1e5\nd8 d1e2\nd9 d1e1\nde d1d6\ndc d1d2\nb1 cfa3\ncb d0e5\nb0 cfa2\nca d0e4\nc9 d0e1\nc8 d0e0\nbe c5d6\nba c5c6\n9e c1d6\n9c c1d2\n9a c1c6\n98 c1c2\n8c ead0\n8b eac5\n8a eac4\n89 eac1\n88 eac0\n8c e8d0\n8b e8c5\n8a e8c4\n89 e8c1\n88 e8c0\nac e6d0\n8c e2d0\nab e6c5\n8b e2c5\naa e6c4\n8a e2c4\na9 e6c1\n89 e2c1\na8 e6c0\n88 e2c0\na3 e48f\nbd e5d1\n83 e08f\n9d e1d1\na2 e48e\nbc e5d0\n92 e38e\nac e4d0\n82 e08e\n9c e1d0\n8c e0d0\na1 e483\nbb e5c5\n91 e383\nab e4c5\n81 e083\n9b e1c5\n8b e0c5\n90 e382\naa e4c4\n8a e0c4\nb9 e5c1\na9 e4c1\n99 e1c1\n89 e0c1\nb8 e5c0\na8 e4c0\n98 e1c0\n88 e0c0\ncf dad5\n8f cad5\nce dad4\n8e cad4\ncd dad1\n8d cad1\n8c cad0\ncb dac5\n8b cac5\nca dac4\n8a cac4\nc9 dac1\n89 cac1\n88 cac0\nc5 d893\ncb 726d\ndf d9d5\ncf d8d5\n8f c8d5\nce d8d4\n8e c8d4\n8d c8d1\n8c c8d0\nc1 d883\ndb d9c5\ncb d8c5\n81 c883\n9b c9c5\n8b c8c5\nca d8c4\n8a c8c4\n99 c9c1\n89 c8c1\n98 c9c0\n88 c8c0\nef d6d5\ncf d2d5\naf c6d5\n8f c2d5\nee d6d4\nce d2d4\nae c6d4\n8e c2d4\nad c6d1\n8d c2d1\nec d6d0\ncc d2d0\nac c6d0\n8c c2d0\neb d6c5\ncb d2c5\nf6 fd9e\nab c6c5\nd6 f99e\n8b c2c5\nea d6c4\nca d2c4\naa c6c4\n8a c2c4\ne9 d6c1\nc9 d2c1\ne8 d6c0\nc8 d2c0\na8 c6c0\n88 c2c0\ne5 d493\nff d5d5\nd5 d393\nef d4d5\nc5 d093\ndf d1d5\nb5 cf93\ncf d0d5\na5 c493\nbf c5d5\n95 c393\naf c4d5\n85 c093\n9f c1d5\n75 bf93\n8f c0d5\nd4 d392\nee d4d4\nb4 cf92\nce d0d4\n94 c392\nae c4d4\ne3 d48f\nfd d5d1\nd3 d38f\ned d4d1\nc3 d08f\ndd d1d1\nb3 cf8f\ncd d0d1\na3 c48f\nbd c5d1\n93 c38f\nad c4d1\n83 c08f\n9d c1d1\n73 bf8f\n8d c0d1\nd2 d38e\nec d4d0\nc2 d08e\ndc d1d0\nb2 cf8e\ncc d0d0\n92 c38e\nac c4d0\n82 c08e\n9c c1d0\n72 bf8e\n8c c0d0\ne1 d483\nfb d5c5\nd1 d383\neb d4c5\nc1 d083\ndb d1c5\nb1 cf83\ncb d0c5\na1 c483\nbb c5c5\n91 c383\nab c4c5\n81 c083\n9b c1c5\n71 bf83\n8b c0c5\nd0 d382\nea d4c4\nb0 cf82\nca d0c4\n90 c382\naa c4c4\nf9 d5c1\ne9 d4c1\nd9 d1c1\nc9 d0c1\nb9 c5c1\na9 c4c1\n99 c1c1\n89 c0c1\nf8 d5c0\ne8 d4c0\nd8 d1c0\nc8 d0c0\nb8 c5c0\na8 c4c0\n98 c1c0\n88 c0c0\n9e eb7c\n84 ea3a\nbe e77c\na4 e63a\n9e e37c\n84 e23a\nb4 e53a\n94 e13a\n9e e17c\n84 e03a\n9e cbfc\n84 caba\nde db7c\nc4 da3a\n8c ca7a\n9e cb7c\n84 ca3a\n9c c97a\n8c c87a\n9e c97c\n84 c83a\nbe c7fc\na4 c6ba\n9e c3fc\n84 c2ba\nfe d77c\ne4 d63a\nde d37c\nc4 d23a\nac c67a\nbe c77c\na4 c63a\n8c c27a\n9e c37c\n84 c23a\nb4 c5ba\nbe c5fc\na4 c4ba\n94 c1ba\n9e c1fc\n84 c0ba\nf4 d53a\nd4 d13a\nde d17c\nc4 d03a\nbc c57a\nb4 c53a\nac c47a\nbe c57c\na4 c43a\n9c c17a\n94 c13a\n8c c07a\n9e c17c\n84 c03a\n97 e9bf\n9f e97f\n86 e8be\n8e e87e\na7 e6bf\n9f e3fd\n85 e2bb\n8d e27b\nb7 e5bf\na6 e4be\nae e47e\n95 e1bb\n9d e17b\n9e e1fc\n84 e0ba\ncf da7f\n8b ca6f\ndf d97f\nc6 d8be\nce d87e\n9b c96f\n9c c9f0\n82 c8ae\n8a c86e\ne7 d6bf\ndf d3fd\nc5 d2bb\ncd d27b\nbd c7f1\na3 c6af\nab c66f\n9b c3ed\n81 c2ab\n89 c26b\nf7 d5bf\nff d57f\ne6 d4be\nee d47e\nd5 d1bb\ndd d17b\nde d1fc\nc4 d0ba\ncc d07a\nb3 c5af\nbb c56f\nbc c5f0\na2 c4ae\naa c46e\n91 c1ab\n99 c16b\n85 cab9\n84 cab8\nc5 da39\n8d ca79\n85 ca39\ncc da78\nc4 da38\n8c ca78\n84 ca38\n95 c9b9\n85 c8b9\n84 c8b8\n1e 37e\nd5 d939\n9d c979\n95 c939\n8d c879\n85 c839\nc4 d838\n8f c8d7\n9c c978\n8c c878\n84 c838\na5 c6b9\n85 c2b9\ne4 d6b8\nc4 d2b8\na4 c6b8\n84 c2b8\nad c679\na5 c639\n8d c279\n85 c239\nec d678\ne4 d638\ncc d278\nc4 d238\nac c678\na4 c638\n8c c278\n84 c238\nb5 c5b9\na5 c4b9\n95 c1b9\n85 c0b9\nb4 c5b8\na4 c4b8\n94 c1b8\n84 c0b8\nf5 d539\ne5 d439\nd5 d139\nc5 d039\n96 c396\nbd c579\nb5 c539\na5 c439\n95 c139\n85 c039\ne7 d497\nf4 d538\nd7 d397\ne4 d438\nc7 d097\nd4 d138\nb7 cf97\nc4 d038\naf c4d7\nbc c578\na7 c497\nb4 c538\n9f c3d7\nac c478\n97 c397\na4 c438\n8f c0d7\n9c c178\n87 c097\n94 c138\n87 eabd\n8f ea7d\n84 e83a\n9e e97c\n97 e9bd\n9e e97e\n85 e83b\n9f e97d\n87 e8bd\n8f e87d\n8e e87c\na6 e6be\na7 e6bd\nae e67e\nb6 e5bc\na4 e43a\nbe e57c\n9e e3fc\n84 e2ba\n8c e27a\nb6 e5be\nb7 e5bd\na5 e43b\nbf e57d\na7 e4bd\na6 e4bc\n95 e33b\naf e47d\n94 e33a\nae e47c\n94 e1ba\nce da7e\ncf da7d\nc4 d83a\nde d97c\n9c cbf0\n82 caae\n8a ca6e\n8b ca6d\nde d97e\nc5 d83b\ndf d97d\ncf d87d\nce d87c\n9a c96e\n81 c82b\n9b c96d\n82 c8ac\n8b c86d\n8a c86c\ne6 d6be\ne7 d6bd\nee d67e\nf6 d5bc\ne4 d43a\nfe d57c\nde d3fc\nc4 d2ba\nc5 d2b9\ncc d27a\nd4 d1b8\ncf d0d7\ndc d178\nbc c7f0\na2 c6ae\na3 c6ad\naa c66e\nab c66d\nb2 c5ac\nf6 d5be\nf7 d5bd\nfe d57e\ne5 d43b\nff d57d\ne7 d4bd\ne6 d4bc\nd5 d33b\nef d47d\nd4 d33a\nee d47c\nd4 d1ba\nd5 d1b9\ndc d17a\nb6 cf96\ndd d179\nc5 d0b9\nc4 d0b8\nbf cfd7\ncc d078\nb2 c5ae\nb3 c5ad\nba c56e\na1 c42b\nbb c56d\na3 c4ad\na2 c4ac\n91 c32b\nab c46d\n90 c32a\naa c46c\n9e ebf4\n84 eab2\n86 ea36\n9e eb74\n84 ea32\n94 e9b2\n9e e9f4\n84 e8b2\n96 e936\n86 e836\n9c e972\n94 e932\n8c e872\naf 4cfd\n95 4bbb\na6 e636\nb4 e532\n94 e132\n86 cab6\nde dbf4\nc4 dab2\n9e cbf4\n84 cab2\nc6 da36\n8e ca76\n86 ca36\ncc da72\nde db74\nc4 da32\n8c ca72\n9e cb74\n84 ca32\n96 c9b6\n86 c8b6\nd4 d9b2\nde d9f4\nc4 d8b2\n94 c9b2\n9e c9f4\n84 c8b2\nc6 d836\n9e c976\n96 c936\n8e c876\n86 c836\ndc d972\nd4 d932\ncc d872\n9c c972\n94 c932\n8c c872\na6 c6b6\n86 c2b6\nbe c7f4\na4 c6b2\n9e c3f4\n84 c2b2\nc6 d236\nae c676\na6 c636\n8e c276\n86 c236\nfe d774\ne4 d632\nde d374\nc4 d232\nac c672\nbe c774\na4 c632\n8c c272\n9e c374\n84 c232\nb6 c5b6\na6 c4b6\n96 c1b6\nb4 c5b2\nbe c5f4\na4 c4b2\n94 c1b2\n9e c1f4\n84 c0b2\nf6 d536\nd6 d136\nc6 d036\nbe c576\nb6 c536\nae c476\na6 c436\n9e c176\n96 c136\nf4 d532\nd4 d132\nbc c572\nb4 c532\nac c472\n9c c172\n94 c132\n8c c072\n9f ebfd\n85 eabb\n87 eab7\nbf e7f5\na5 e6b3\n97 e9b7\n9d e97b\n9f e977\n86 e8b6\n8c e87a\n8e e876\nb5 e5b3\nbd e573\nbe e5f4\na4 e4b2\nac e472\nbf e7fd\na5 e6bb\na7 e6b7\nad e67b\n87 e2b7\n9f e3f5\n85 e2b3\n8f e277\n8d e273\nb5 e5bb\nb7 e5b7\nbd e57b\nbe e5fc\na4 e4ba\na6 e4b6\n97 e1b7\n95 e1b3\n9d e173\n9e e1f4\n84 e0b2\n8c e072\nc7 dab7\ncd da7b\ncf da77\nff d7f5\ne5 d6b3\ned d673\n9b cbed\n81 caab\n89 ca6b\n8b ca67\nbb c7e5\na1 c6a3\na9 c663\nd7 d9b7\ndd d97b\ndf d977\nc6 d8b6\ncc d87a\nce d876\nf5 d5b3\nfd d573\nfe d5f4\ne4 d4b2\nec d472\n99 c96b\n9b c967\nb1 c5a3\nff d7fd\ne5 d6bb\ne7 d6b7\ned d67b\nc7 d2b7\ndf d3f5\nc5 d2b3\ncf d277\ncd d273\nbb c7ed\na1 c6ab\na3 c6a7\na9 c66b\nab c667\n83 c2a7\n9b c3e5\n81 c2a3\n8b c267\n89 c263\nf5 d5bb\nf7 d5b7\nfd d57b\nfe d5fc\ne4 d4ba\ne6 d4b6\nec d47a\nd7 d1b7\nd5 d1b3\ndf d177\ndd d173\nc6 d0b6\nde d1f4\nc4 d0b2\nce d076\ncc d072\nb1 c5ab\nb3 c5a7\nb9 c56b\nbb c567\n93 c1a7\n91 c1a3\n9b c167\n87 ea35\n8e ea74\n86 ea34\n8d ea71\n85 ea31\n8c ea70\n84 ea30\n6d b45b\n84 e8b0\n97 e935\n87 e835\n96 e934\n86 e834\n83 e82f\n9d e971\n95 e931\n8d e871\n85 e831\n82 e82e\n9c e970\n94 e930\n8c e870\n84 e830\na6 e6b4\n86 e2b4\na4 e6b0\n84 e2b0\na7 e635\n87 e235\n8e e274\n86 e234\na5 e631\n85 e231\nac e670\na4 e630\n8c e270\n84 e230\n9d e3f3\nb7 e535\n8d e2f3\na7 e435\n97 e135\n87 e035\n9c e3f2\nb6 e534\n96 e134\n86 e034\n9b e3ef\nb5 e531\n8b e2ef\na5 e431\n95 e131\n85 e031\n9a e3ee\nb4 e530\n8a e2ee\na4 e430\n94 e130\n84 e030\n87 cab5\nc6 dab4\nc5 dab1\nc7 da35\n8f ca75\n87 ca35\nce da74\nc6 da34\n8e ca74\n86 ca34\ncd da71\nc5 da31\n8d ca71\n85 ca31\ncc da70\nc4 da30\n8c ca70\n84 ca30\n56 931e\n87 c8b5\nd7 d935\nc7 d835\n85 c833\n9f c975\n97 c935\n8f c875\n87 c835\nd6 d934\nc6 d834\n84 c832\n9e c974\n96 c934\n8e c874\n86 c834\nc3 d82f\ndd d971\ncd d871\n83 c82f\n9d c971\n95 c931\n8d c871\n85 c831\nc2 d82e\ndc d970\nd4 d930\ncc d870\nc4 d830\n82 c82e\n9c c970\n94 c930\n8c c870\n84 c830\nf2 fd8e\na7 c6b5\nd2 f98e\n87 c2b5\ne6 d6b4\nc6 d2b4\na6 c6b4\n86 c2b4\ne4 d6b0\nc4 d2b0\na4 c6b0\n84 c2b0\ne7 d635\nc7 d235\nfa fd4e\naf c675\nf2 fd0e\na7 c635\nda f94e\n8f c275\nd2 f90e\n87 c235\nee d674\ne6 d634\nce d274\nc6 d234\nae c674\na6 c634\n8e c274\n86 c234\nec d670\ne4 d630\ncc d270\nc4 d230\nac c670\na4 c630\n8c c270\n84 c230\nb7 c5b5\na7 c4b5\n97 c1b5\n87 c0b5\nb6 c5b4\na6 c4b4\n96 c1b4\n86 c0b4\nb5 c5b1\na5 c4b1\n95 c1b1\n85 c0b1\nb4 c5b0\na4 c4b0\n94 c1b0\n84 c0b0\ndd d3f3\nf7 d535\ncd d2f3\ne7 d435\nbd cff3\nd7 d135\nad cef3\nc7 d035\na5 c433\nbf c575\n9d c3f3\nb7 c535\n95 c333\naf c475\n8d c2f3\na7 c435\n85 c033\n9f c175\n7d bff3\n97 c135\n75 bf33\n8f c075\n6d bef3\n87 c035\ndc d3f2\nf6 d534\ncc d2f2\ne6 d434\nbc cff2\nd6 d134\nac cef2\nc6 d034\na4 c432\nbe c574\n9c c3f2\nb6 c534\n94 c332\nae c474\n8c c2f2\na6 c434\n84 c032\n9e c174\ndb d3ef\nf5 d531\ncb d2ef\ne5 d431\nbb cfef\nd5 d131\nab ceef\nc5 d031\na3 c42f\nbd c571\n9b c3ef\nb5 c531\n93 c32f\nad c471\n8b c2ef\na5 c431\n83 c02f\n9d c171\n73 bf2f\n8d c071\nda d3ee\nf4 d530\nca d2ee\ne4 d430\nba cfee\nd4 d130\naa ceee\nc4 d030\na2 c42e\nbc c570\n9a c3ee\nb4 c530\n92 c32e\nac c470\n8a c2ee\na4 c430\n82 c02e\n9c c170\n72 bf2e\n8c c070\n9e ebfc\n84 eaba\n86 eab6\n87 eab5\n8f ea75\n84 e832\n9e e974\nbe e7f4\na4 e6b2\na5 e6b1\nad e671\nb4 e5b0\na2 e42e\nbc e570\n96 e9b6\n66 b41e\n97 e9b5\n9c e97a\n9e e976\n85 e833\n9f e975\n56 b31e\n87 e8b5\n6f b45f\n86 e8b4\n8f e875\n8e e874\nb4 e5b2\nb5 e5b1\nbc e572\na3 e42f\nbd e571\na5 e4b1\na4 e4b0\n93 e32f\nad e471\n92 e32e\nac e470\nbe e7fc\na4 e6ba\na6 e6b6\na7 e6b5\nac e67a\n9d 4bfb\nb7 4d3d\nae e676\nb6 e5b4\na4 e432\nbe e574\n86 e2b6\n87 e2b5\n9e e3f4\n84 e2b2\n85 e2b1\n8f e275\n8d e271\n96 e1b4\n94 e1b0\n84 e032\n9e e174\n82 e02e\n9c e170\nb4 e5ba\nb6 e5b6\nb7 e5b5\na7 e4b5\na6 e4b4\n96 e1b6\n97 e1b5\n94 e1b2\n95 e1b1\n85 e033\n9f e175\n9c e172\n83 e02f\n9d e171\n87 e0b5\n86 e0b4\n85 e0b1\n84 e0b0\n8f e075\n8e e074\n8d e071\n8c e070\nde dbfc\nc4 daba\nc6 dab6\nc7 dab5\ncc da7a\ncd da79\nce da76\ncf da75\nd6 d9b4\ncf d8d7\ndc d978\nc4 d832\nde d974\nfe d7f4\ne4 d6b2\ne5 d6b1\nec d672\nf4 d5b0\ne2 d42e\nfc d570\nd7 d9b5\ndc d97a\ndd d979\nc5 d833\ndf d975\nc7 d8b5\nc6 d8b4\ncd d879\ncc d878\ncf d875\nce d874\nf4 d5b2\nf5 d5b1\nfc d572\ne3 d42f\nfd d571\ne5 d4b1\ne4 d4b0\nd3 d32f\ned d471\nd2 d32e\nec d470\nfe d7fc\ne4 d6ba\ne5 d6b9\ne6 d6b6\ne7 d6b5\nec d67a\nef d675\nf4 d5b8\nf6 d5b4\nef d4d7\nfc d578\ne4 d432\nfe d574\nc6 d2b6\nc7 d2b5\nde d3f4\nc4 d2b2\nc5 d2b1\nce d276\ncf d275\ncc d272\nd6 d1b4\nd4 d1b0\nc4 d032\nde d174\nc2 d02e\ndc d170\nf4 d5ba\nf5 d5b9\nf6 d5b6\nf7 d5b5\nfc d57a\nd6 d396\nfd d579\nfe d576\ne5 d433\nff d575\ne5 d4b9\ne4 d4b8\ne7 d4b5\ne6 d4b4\ndf d3d7\nec d478\nd5 d333\nef d475\nd4 d332\nee d474\nd6 d1b6\nd7 d1b5\nd4 d1b2\nd5 d1b1\nde d176\nc5 d033\ndf d175\ndc d172\nc3 d02f\ndd d171\nc7 d0b5\nc6 d0b4\nc5 d0b1\nc4 d0b0\nb5 cf33\ncf d075\nb4 cf32\nce d074\nb3 cf2f\ncd d071\nb2 cf2e\ncc d070\n8b ea6f\ncb da4f\n97 e99f\n9b e96f\n9f e95f\n9c e9f0\n82 e8ae\n86 e89e\n8a e86e\n8e e85e\ndb d94f\nca d84e\nbd e7f1\na3 e6af\n9b e3ed\n81 e2ab\nfd d7d1\ne3 d68f\ndb d3cd\nc1 d28b\nb3 e5af\nb7 e59f\nbb e56f\nbc e5f0\na2 e4ae\na6 e49e\naa e46e\nae e45e\n91 e1ab\n95 e19b\n99 e16b\n9d e15b\nf3 d58f\nfb d54f\nfc d5d0\ne2 d48e\nea d44e\nd1 d18b\nc7 da9f\ncb da6f\ncf da5f\n87 ca9f\n9d cbd1\n83 ca8f\n8f ca5f\n8b ca4f\ndb d96f\ndf d95f\nca d86e\nce d85e\n9f c95f\n9b c94f\n8e c85e\n8a c84e\nfd d7f1\ne3 d6af\ne7 d69f\neb d66f\ndb d3ed\nc1 d2ab\ndf d3dd\nc5 d29b\nc9 d26b\na7 c69f\nbd c7d1\na3 c68f\n9f c3dd\n85 c29b\n9b c3cd\n81 c28b\nf3 d5af\nf7 d59f\nfb d56f\nff d55f\nfc d5f0\ne2 d4ae\ne6 d49e\nea d46e\nee d45e\nd1 d1ab\nd5 d19b\nd9 d16b\nb7 c59f\nb3 c58f\nd6 f9b4\nbf c55f\na6 c49e\nbc c5d0\na2 c48e\nc5 f8b3\ndf f9f5\nae c45e\nc1 f8a3\ndb f9e5\naa c44e\n95 c19b\n91 c18b\n9c ebf0\n82 eaae\n6b 1c6f\n86 ea9e\n52 1b2c\n87 ea9d\n8a ea6e\n8b ea6d\n8f ea5d\n96 e99c\nca da4e\ncb da4d\n92 e9ae\n9a e96e\n81 e82b\n9b e96d\n9e e95e\n85 e81b\n9f e95d\n82 e8ac\n8b e86d\n8a e86c\n8f e85d\n8e e85c\nda d94e\nc1 d80b\ndb d94d\ncb d84d\nca d84c\nbc e7f0\na2 e6ae\na3 e6ad\na6 e69e\n72 172c\na7 e69d\nab e66d\nae e65e\nb2 e5ac\nb6 e59c\nfc d7d0\ne2 d68e\ne3 d68d\nea d64e\nf2 d58c\nb2 e5ae\nb3 e5ad\nb6 e59e\nba e56e\na1 e42b\nbb e56d\na5 e41b\nbf e55d\na3 e4ad\na2 e4ac\n91 e32b\nab e46d\n90 e32a\naa e46c\n94 e31a\nae e45c\nf2 d58e\nf3 d58d\nfa d54e\n5b 33e7\ne1 d40b\nfb d54d\ne3 d48d\ne2 d48c\n3f 777\ndc dbf0\nc2 daae\nc6 da9e\nc7 da9d\nca da6e\ncb da6d\nce da5e\ncf da5d\n86 ca9e\n87 ca9d\n9c cbd0\n82 ca8e\n83 ca8d\n8e ca5e\n8f ca5d\n8a ca4e\n8b ca4d\nd7 d99d\nda d96e\nc1 d82b\ndb d96d\nde d95e\nc5 d81b\ndf d95d\nc7 d89d\nc6 d89c\ncb d86d\nca d86c\ncf d85d\nce d85c\n97 c99d\n93 c98d\n9e c95e\n85 c81b\n9f c95d\n81 c80b\n9b c94d\n87 c89d\n86 c89c\n83 c88d\n82 c88c\n8f c85d\n8e c85c\n8b c84d\n8a c84c\nfc d7f0\ne2 d6ae\ne3 d6ad\ne6 d69e\ne7 d69d\nea d66e\nee d65e\nf2 d5ac\nf6 d59c\na6 c69e\na7 c69d\nbc c7d0\na2 c68e\na3 c68d\nc5 fab3\ndf fbf5\nae c65e\nc1 faa3\ndb fbe5\naa c64e\nb6 c59c\nb2 c58c\nf2 d5ae\nf3 d5ad\nf6 d59e\nf7 d59d\nfa d56e\ne1 d42b\nfb d56d\nfe d55e\n5f 33f7\ne5 d41b\nff d55d\ne3 d4ad\ne2 d4ac\ne7 d49d\ne6 d49c\nd1 d32b\neb d46d\nd0 d32a\nea d46c\nb6 c59e\nb7 c59d\nb2 c58e\nb3 c58d\nd5 f9b3\nbe c55e\n1f 23f7\na5 c41b\nbf c55d\nd1 f9a3\nba c54e\n1b 23e7\na1 c40b\nbb c54d\na7 c49d\na6 c49c\na3 c48d\na2 c48c\n9b ebed\n81 eaab\n89 ea6b\n8b ea67\nbb e7e5\na1 e6a3\nd0 db28\nc3 da87\ncb da47\nfb d7c5\ne1 d683\n95 e99b\n97 e997\n99 e96b\n9b e967\n9d e95b\n9f e957\nb1 e5a3\nb5 e593\nb9 e563\ndb d947\nf9 d543\nbb e7ed\na1 e6ab\na3 e6a7\n9b e3e5\n81 e2a3\n8b e267\n89 e263\n8f e257\nfb d7cd\ne1 d68b\nf0 d728\ne3 d687\neb d647\nd0 d328\nc3 d287\ndb d3c5\nc1 d283\ncb d247\nb1 e5ab\nb3 e5a7\nb5 e59b\nb7 e597\nb9 e56b\nbd e55b\n93 e1a7\n91 e1a3\n97 e197\n95 e193\n9b e167\n99 e163\n27 ebd\n9d e153\nf3 d587\nfb d547\nd3 d187\nd1 d183\ndb d147\nd9 d143\nc3 daa7\ndf dbdd\nc5 da9b\nd4 db38\nc7 da97\nc9 da6b\ncb da67\ncf da57\nfb d7e5\ne1 d6a3\nff d7d5\ne5 d693\ne9 d663\n9f cbdd\n85 ca9b\n94 cb38\n87 ca97\n9b cbcd\n81 ca8b\n90 cb28\n83 ca87\n8f ca57\n8b ca47\nbf c7d5\na5 c693\nbb c7c5\na1 c683\nd3 d9a7\nd9 d96b\ndb d967\ndf d957\nf5 d593\nfd d553\n9f c957\n9b c947\nb5 c593\nb1 c583\nbd c553\nb9 c543\nfb d7ed\ne1 d6ab\ne3 d6a7\nff d7dd\ne5 d69b\nf4 d738\ne7 d697\ne9 d66b\neb d667\nc3 d2a7\ndb d3e5\nc1 d2a3\nd4 d338\nc7 d297\ndf d3d5\nc5 d293\ncb d267\nc9 d263\ncf d257\nbf c7dd\na5 c69b\nb4 c738\na7 c697\nbb c7cd\na1 c68b\nb0 c728\na3 c687\naf c657\nab c647\n94 c338\n87 c297\n9f c3d5\n85 c293\n90 c328\n83 c287\n9b c3c5\n81 c283\n8f c257\n8b c247\nf3 d5a7\nf5 d59b\nf7 d597\nf9 d56b\nfb d567\nd3 d1a7\nd1 d1a3\nd7 d197\nd5 d193\ndb d167\n46 309e\nd9 d163\ndf d157\ndd d153\nb5 c59b\nb7 c597\nb1 c58b\nb3 c587\nbf c557\nbb c547\n97 c197\n95 c193\n93 c187\n91 c183\n9f c157\n9d c153\n9b c147\n99 c143\nff 7fff\n7c bff8\n7d 3ff9\nfc 7ff8\n7c 3ff8\n6e befc\nfe 7ffe\n77 3fbd\n66 3ebc\n96 c134\n7c bff2\n7e 3ff6\nfc 7ff2\nb6 6f9c\n97 c13d\n7d bffb\nfd 7ffb\nff 7ff7\nfd dfdb\n77 3fb7\n65 3eb3\n7f 3ff5\n96 c13c\n7c bffa\n7d bff9\n65 beb3\n7f bff5\nfc 7ffa\nfd 7ff9\nfe 7ff6\ne5 7eb3\nff 7ff5\n95 c131\n7b bfef\nfb 7fef\nff 7fdf\n92 c10c\n78 bfca\nf0 df22\n77 3f9f\nf8 7fca\n3d af73\n39 af63\n2d ae73\nd3 7107\n75 9fb3\ncf 70f7\n71 9fa3\n7d 9f73\n79 9f63\n78 bfe8\n79 3fe9\nf8 7fe8\n78 3fe8\n94 c130\n7a bfee\n6a beec\n6e bedc\n2a aecc\nfa 7fee\nfe 7fde\n7c bfd8\nee 7edc\n79 bfc9\n78 bfc8\n73 3fad\n76 3f9e\nf0 df20\n77 3f9d\n62 3eac\nf9 df61\n66 3e9c\n33 2f8d\n22 2e8c\nf a0f7\n7d 3fd9\n8e e0f6\nfc 7fd8\n8b e0e7\nf9 7fc9\nb a0e7\n79 3fc9\n8a e0e6\nf8 7fc8\n7a 3fe6\nf8 7fe2\nf8 dfc2\n1e a1fe\ne8 dec2\ne a0fe\n2e 86fe\nf4 ffb0\n5e 91fe\ne4 feb0\n4e 90fe\nd4 f3b0\n3e 85fe\nc4 f2b0\n2e 84fe\nb4 efb0\n1e 81fe\na4 eeb0\ne 80fe\n93 c12d\n79 bfeb\n97 c11d\n7d bfdb\n8c c078\n7f bfd7\n53 b10d\n39 afcb\nf9 7feb\nfb 7fe7\nff 7fd7\n7a bfc6\nf9 dfcb\n73 3fa7\n4b 30cd\n31 2f8b\n7e 3fd6\nfc 7fd2\nfa 7fc6\n7a 3fc6\nf8 7fc2\n3e 25fe\n2e 24fe\n79 bfe1\n78 bfe0\nde 51fe\nce 50fe\nae 44fe\n9e 41fe\n8e 40fe\n6e 14fe\n61 3ea3\n7b 3fe5\nf9 7fe1\n79 3fe1\n97 e19f\nf8 7fe0\n92 c12c\n78 bfea\n79 bfe9\n61 bea3\n7b bfe5\n96 c11c\n7c bfda\n7d bfd9\n7e bfd6\nf8 7fea\nfa 7fe6\ne1 7ea3\nfb 7fe5\n65 be93\n7f bfd5\n8f e0f7\nfd 7fd9\nfe 7fd6\n63 be8f\n7d bfd1\n62 be8e\n7c bfd0\n61 be83\n7b bfc5\n79 bfc1\n78 bfc0\ne5 7e93\nff 7fd5\n65 3e93\n7f 3fd5\ne3 7e8f\nfd 7fd1\n63 3e8f\n7d 3fd1\ne2 7e8e\nfc 7fd0\n62 3e8e\n7c 3fd0\ne1 7e83\nfb 7fc5\n61 3e83\n7b 3fc5\nf9 7fc1\n79 3fc1\nf8 7fc0\n78 3fc0\n7 aa3f\n8e c07c\n74 bf3a\n86 e21c\n7 a8bf\n17 a93f\nf a87f\n7 a83f\n7f bf7f\nf7 7f3f\nf7 7fbf\nff 7f7f\n7f 3f7f\n77 3f3f\n27 a63f\nf a27f\n7 a23f\n74 bfb8\n75 bf39\n75 3fb9\nf4 7fb8\n74 3fb8\n6f 3ed7\n7c 3f78\n27 a4bf\nf1 df83\n17 a1bf\ne1 de83\nfb dfc5\n7 a0bf\n37 a53f\n2f a47f\n27 a43f\nf9 df43\n1f a17f\nf1 df03\n17 a13f\ne9 de43\nf a07f\ne1 de03\nfb df45\n7 a03f\n77 bfbd\n7e bf7e\n65 be3b\n7f bf7d\n66 bebc\n6e be7c\nf6 7f3e\nf7 7f3d\ne6 7e3c\nf6 7fbe\nf7 7fbd\nfe 7f7e\nee 7e7c\n7e 3f7e\n76 3f3e\n77 3f3d\n6e 3e7c\n66 3e3c\n47 9a3f\n7 8a3f\n8e c0f4\n74 bfb2\n7c bf72\n8e c074\n74 bf32\nfc dfda\n76 3fb6\nf4 7fb2\n7c 3f72\nc6 d21c\n47 98bf\n86 c21c\n7 88bf\n57 993f\n4f 987f\n47 983f\n17 893f\nf 887f\n7 883f\n8f c0fd\n75 bfbb\n7d bf7b\n5d bb73\nf5 7f3b\nf7 7f37\nf5 7fbb\nf7 7fb7\nfd 7f7b\nff 7f77\n7d 3f7b\n7f 3f77\nfd df5b\n77 3f37\n5d 3b73\n67 963f\n4f 927f\n47 923f\n27 863f\nf 827f\n7 823f\n76 bfb4\n75 bfb1\n74 bfb0\n77 bf35\n76 bf34\n63 be2f\n7d bf71\n75 bf31\n62 be2e\n7c bf70\n74 bf30\nfd dfd9\n77 3fb5\nf6 7fb4\nfc dfd8\n76 3fb4\n5f 11ff\nf5 7fb1\n75 3fb1\n5e 11fe\nf4 7fb0\n74 3fb0\nf6 7f34\nfc df58\n76 3f34\n75 3f31\n74 3f30\n67 94bf\n57 91bf\n47 90bf\n27 84bf\n17 81bf\n7 80bf\n77 953f\n6f 947f\n67 943f\nf5 ff31\n5f 917f\ned fef1\n57 913f\ne5 fe31\n4f 907f\n47 903f\ncd f2f1\n37 853f\nc5 f231\n2f 847f\n27 843f\nb5 ef31\n1f 817f\nad eef1\n17 813f\na5 ee31\nf 807f\n7 803f\n8e c0fc\n74 bfba\n75 bfb9\n77 bfb5\n7c bf7a\n7d bf79\n65 be33\n7f bf75\nf4 7f3a\nf5 7f39\nf6 7f36\nf7 7f35\nf4 7fba\nf5 7fb9\nf6 7fb6\nf7 7fb5\nfc 7f7a\nfd 7f79\ne5 7e33\nff 7f75\n7c 3f7a\n7d 3f79\n7e 3f76\n65 3e33\n7f 3f75\n75 3f39\nfc df5a\n76 3f36\nfd df59\n77 3f35\n8a c06c\n70 bf2a\nf 287f\n7 283f\n8d c0f1\n73 bfaf\n77 bf9f\n7b bf6f\n7f bf5f\n3b af4f\nf3 7f2f\nf7 7f1f\nf3 7faf\nf7 7f9f\nff 7f5f\n8a c0cc\n70 bf8a\n8e c05c\n74 bf1a\n77 9f3d\nbb 6f4f\n78 bf4a\n8a c04c\n70 bf0a\n7b 3f6f\n7f 3f5f\n77 3f1f\n3b 2f4f\nf0 7f8a\nf8 7f4a\nf0 7f0a\n78 3f4a\n29 ae63\n2d ae53\nb0 e700\n31 ada3\n39 ad63\n3d ad53\nf 227f\n7 223f\n6d 9e53\n70 bfa8\n71 bf29\n6b bec7\n78 bf68\n63 be87\n70 bf28\n71 3fa9\nf0 7fa8\n70 3fa8\neb 7ec7\nf8 7f68\ne3 7e87\nf0 7f28\n6b 3ec7\n78 3f68\n63 3e87\n70 3f28\n27 24bf\nf1 5f83\n17 21bf\ncc f8f8\n2f 247f\nc4 f8b8\n27 243f\nf9 5f43\n1f 217f\nf1 5f03\n17 213f\ne9 5e43\nf 207f\n8c c0f0\n72 bfae\n73 bfad\n77 bf9d\n7a bf6e\n61 be2b\n7b bf6d\n7e bf5e\n65 be1b\n7f bf5d\n62 beac\n66 be9c\n6a be6c\n6e be5c\n33 af8d\n3a af4e\n21 ae0b\n3b af4d\n22 ae8c\n2a ae4c\nf6 7f1e\nf7 7f1d\ne6 7e1c\nf2 7fae\n5d 11fb\nf3 7fad\nf6 7f9e\nf7 7f9d\n74 bf98\nfe 7f5e\ne6 7e9c\nea 7e6c\nee 7e5c\n71 bf89\n70 bf88\n75 bf19\n1d 1db\nb3 6f8d\n7c bf58\n74 bf18\n76 9f3c\nba 6f4e\naa 6e4c\n79 bf49\n71 bf09\n78 bf48\n70 bf08\n7a 3f6e\n7e 3f5e\n73 3f2d\n76 3f1e\n77 3f1d\n6a 3e6c\n6e 3e5c\n62 3e2c\n66 3e1c\n3a 2f4e\n33 2f0d\n2a 2e4c\n22 2e0c\n86 e0b6\nf4 7f98\n83 e0a7\nf1 7f89\n8e e076\nfc 7f58\n86 e036\nf4 7f18\n8b e067\nf9 7f49\n83 e027\nf1 7f09\n8f 4a7f\n87 4a3f\nf a7f\n7 a3f\n8a c0e4\n70 bfa2\n78 bf62\nf8 dfca\n72 3fa6\nf0 7fa2\n78 3f62\nc7 58bf\n87 48bf\n96 431c\n17 9bf\nf a2fd\nc7 78b7\ne a2fc\ne9 dec1\nf a0fd\ne8 dec0\ne a0fc\nf 8afd\ne 8afc\ncf 587f\nc7 583f\n9f 497f\n97 493f\n8f 487f\n87 483f\n4f 187f\n47 183f\n1f 97f\n17 93f\nf 87f\n7 83f\nf 88fd\n2f 86fd\nf 82fd\ne7 5cb7\n2e 86fc\n8b c0ed\n71 bfab\n8f c0dd\n75 bf9b\n84 c038\n77 bf97\n79 bf6b\n7d bf5b\n7f bf57\n59 bb63\n5d bb53\n4b b0cd\n31 af8b\n39 af4b\n3b af47\n19 ab43\nf1 7f2b\nf3 7f27\nf7 7f17\n15 83bb\n2f 84fd\nf 80fd\ne 80fc\nf1 7fab\nf3 7fa7\nf5 7f9b\nf7 7f97\nf9 7f6b\nff 7f57\n8e c0d4\n74 bf92\ndd 7b53\n8a c0c4\n70 bf82\n76 bf16\ncb 70cd\nb1 6f8b\n7c bf52\n7a bf46\n72 bf06\n78 bf42\n79 3f6b\n7b 3f67\n7d 3f5b\n7f 3f57\nf9 df4b\n73 3f27\n77 3f17\n59 3b63\n5d 3b53\n39 2f4b\n3b 2f47\n4b 304d\n31 2f0b\n33 2f07\n19 2b43\nf4 7f92\nf2 7f86\nf0 7f82\nfc 7f52\nf4 7f12\n7c 3f52\nfa 7f46\nf2 7f06\n7a 3f46\n72 3f06\nf8 7f42\nf0 7f02\n78 3f42\ne 2afc\n27 6bf\nf 28fd\nf 22fd\n8e 62fc\n2e 26fc\ne 22fc\n8e 4afc\n4e 1afc\ne afc\ncc daf8\n2f 67f\nc4 dab8\n27 63f\nf 27f\n7 23f\n86 e236\n8f 48fd\n9f e377\n8e 48fc\n4f 12fd\nf 2fd\nce 52fc\nae 46fc\n8e 42fc\n6e 16fc\n4e 12fc\n2e 6fc\ne 2fc\n72 bfa4\n71 bfa1\n70 bfa0\n73 bf25\n72 bf24\n79 bf61\n71 bf21\n78 bf60\n70 bf20\nb5 4fbb\ncf 50fd\n95 43bb\naf 44fd\n75 3fbb\n8f 40fd\n35 fbb\n4f 10fd\n15 3bb\n2f 4fd\nf fd\n74 3fba\n8e 40fc\ne fc\nf9 dfc9\n73 3fa5\nf8 dfc8\n72 3fa4\n5b 11ef\nf1 7fa1\n71 3fa1\n70 3fa0\nf8 df48\n72 3f24\nf9 7f61\n79 3f61\n71 3f21\nf a0df\n70 3f20\ne7 54bf\nc7 50bf\na7 44bf\n87 40bf\n67 14bf\n57 11bf\n47 10bf\n37 5bf\n27 4bf\n17 1bf\n7 bf\nef 547f\ne7 543f\nd7 513f\ncf 507f\nc7 503f\nb7 453f\naf 447f\na7 443f\n97 413f\n8f 407f\n87 403f\n6f 147f\n67 143f\nf5 7f31\n5f 117f\n47 103f\nc4 d8b8\n27 43f\n7 3f\n8a c0ec\n70 bfaa\n71 bfa9\n73 bfa5\n8e c0dc\n74 bf9a\n75 bf99\n9d c179\n76 bf96\n78 bf6a\n79 bf69\n61 be23\n7b bf65\n7c bf5a\n7d bf59\n7e bf56\nf0 7f2a\n87 e037\nf5 7f19\nf6 7f16\nf0 7faa\nf1 7fa9\nf2 7fa6\n5d 11f3\nf3 7fa5\n77 bf95\nf4 7f9a\n87 e0b7\nf5 7f99\nf6 7f96\nf8 7f6a\ne1 7e23\nfb 7f65\n76 bf94\n8f e077\nfd 7f59\nfe 7f56\n75 bf91\n74 bf90\n73 bf85\n72 bf84\n71 bf81\n70 bf80\n65 be13\n7f bf55\n77 bf15\n76 bf14\n63 be0f\n7d bf51\n75 bf11\n62 be0e\n7c bf50\n74 bf10\n61 be03\n7b bf45\n73 bf05\n72 bf04\n79 bf41\n71 bf01\n78 bf40\n70 bf00\n78 3f6a\n79 3f69\n7a 3f66\n61 3e23\n7b 3f65\n7c 3f5a\n7e 3f56\n71 3f29\nf8 df4a\n72 3f26\nf9 df49\n73 3f25\n76 3f16\nf7 7f95\n77 3f95\nf6 7f94\n76 3f94\n5f 11df\nf5 7f91\n75 3f91\n74 3f90\n5d 11d3\nf3 7f85\n73 3f85\n72 3f84\n5b 11cf\nf1 7f81\n71 3f81\n70 3f80\ne5 7e13\nff 7f55\nf7 7f15\n65 3e13\n7f 3f55\n77 3f15\nf6 7f14\n76 3f14\ne3 7e0f\nfd 7f51\n63 3e0f\n7d 3f51\n75 3f11\ne2 7e0e\nfc 7f50\n62 3e0e\n7c 3f50\n74 3f10\ne1 7e03\nfb 7f45\n61 3e03\n7b 3f45\n73 3f05\n72 3f04\nf9 7f41\n79 3f41\n71 3f01\nf8 7f40\n78 3f40\n70 3f00\nef 7eff\nff 7dff\nf6 771c\n77 3dbf\n6c bef8\n6d 3ef9\nec 7ef8\n6c 3ef8\n7d 3df9\n6d 3cf9\n7c 3df8\n6c 3cf8\n6f befd\nee 7efe\n67 3ebd\ne5 7cbb\nff 7dfd\n77 3dbd\n67 3cbd\n86 c034\n6c bef2\nfb f74f\n7c bdf2\neb f64f\n6c bcf2\nf4 df1a\n6e 3ef6\nec 7ef2\nfb 774f\n7c 3df2\neb 764f\n6c 3cf2\n92 6bac\n96 6b9c\nb2 67ac\nb6 679c\ne3 f627\nd2 5bac\ne7 f617\nd6 5b9c\n87 c03d\n6d befb\n6f bef7\nf2 57ac\nfc f758\n7d bdfb\nc2 f20e\ndc f350\n5d b9f3\nf6 579c\ned 7efb\nef 7ef7\n7f 3ffd\n65 3ebb\ned dedb\n67 3eb7\n6e bef4\n6d bef1\n6c bef0\n63 bcaf\ne2 f60c\n7d bdf1\nf5 df19\n6f 3ef5\nf4 df18\n6e 3ef4\n57 113f\ned 7ef1\n6d 3ef1\n6c 3ef0\n63 3caf\ne2 760c\n7d 3df1\n86 c03c\n6c befa\n6d bef9\n6e bef6\n6f bef5\n7c bdf8\n7d bdf9\n6d bcf9\n6c bcf8\nec 7efa\ned 7ef9\nee 7ef6\n36 a79c\nfc 7df8\nfe 7df6\nee 7cf6\n35 a73b\n75 973b\n71 972b\n78 3dea\n68 3cea\n85 c031\n6b beef\n6f bedf\ne9 f64b\n6a bcee\n3b adcf\n2a acce\neb 7eef\nef 7edf\n82 c00c\n68 beca\n7d 3ff1\n63 3eaf\n68 bcca\nff 7ddf\nf2 770c\n73 3daf\nfb 774d\ne1 760b\n7c 3df0\n62 3cae\nf9 dd63\n66 3c9e\n33 2d8f\ne8 7eca\ne8 7cca\n68 3cca\n57 9b37\n53 9b27\n77 9737\n75 9733\n73 9727\n71 9723\n68 bee8\n69 3ee9\n5f 11d7\ne8 7ee8\n68 3ee8\n79 3de9\n69 3ce9\n78 3de8\n68 3ce8\n84 c030\n6a beee\n6b beed\n6f bedd\n2b aecd\nf9 f74b\n7a bdee\n65 bc9b\n7f bddd\n3a adce\n21 ac8b\n3b adcd\nea 7eee\nee 7ede\nef 7edd\n6c bed8\n69 bec9\n68 bec8\n7c 3ff0\n62 3eae\n63 3ead\nf9 df63\n66 3e9e\n76 3d9c\n79 bdc9\n69 bcc9\n78 bdc8\n68 bcc8\n23 2e8d\n32 2d8c\ne5 7c9b\nff 7ddd\na1 6c8b\nbb 6dcd\nf1 770b\n72 3dae\n73 3dad\n63 3cad\n33 2d8d\n23 2c8d\n22 2c8c\n6d 3ed9\nec 7ed8\n6c 3ed8\ne9 7ec9\n69 3ec9\ne8 7ec8\n68 3ec8\n7d 3dd9\n6d 3cd9\n7c 3dd8\n6c 3cd8\nf9 7dc9\ne9 7cc9\n79 3dc9\n69 3cc9\nf8 7dc8\ne8 7cc8\n78 3dc8\n68 3cc8\nf7 f73f\n78 bde2\ne7 f63f\n68 bce2\nf0 df0a\n6a 3ee6\ne8 7ee2\nf9 7743\n7a 3de6\n83 c02d\n69 beeb\n6b bee7\n87 c01d\n6d bedb\n7c bf78\n6f bed7\n43 b00d\n29 aecb\nf8 f748\n79 bdeb\n7d bddb\n7f bdd7\n68 bcea\nd8 f340\n59 b9e3\n5d b9d3\n3b adc7\n19 a9c3\ne9 7eeb\neb 7ee7\nfc 7f78\nef 7ed7\n6a bec6\n7b 3fed\n61 3eab\ne9 decb\n63 3ea7\n6e bcd6\n7f 3fdd\nf8 df60\n65 3e9b\n74 3f38\n67 3e97\n7c bdd2\n6c bcd2\n7a bdc6\n6a bcc6\nf7 f71f\n78 bdc2\ne7 f61f\n68 bcc2\n3b 2fcd\n21 2e8b\n31 a72b\nea 7ce6\n75 3d9b\n77 3d97\n55 3993\n31 2d8b\n33 2d87\n6e 3ed6\nec 7ed2\nea 7ec6\n6a 3ec6\ne8 7ec2\n35 a71b\nee 7cd6\n7e 3dd6\n6e 3cd6\nfc 7dd2\nec 7cd2\n7c 3dd2\n6c 3cd2\nfa 7dc6\n31 a70b\nea 7cc6\n7a 3dc6\n6a 3cc6\nf8 7dc2\ne8 7cc2\n6a bee4\n69 bee1\n68 bee0\n79 bde1\nf7 f73d\n78 bde0\ne7 f63d\n68 bce0\nf1 df09\n6b 3ee5\nf0 df08\n6a 3ee4\n69 3ee1\n79 3de1\n82 c02c\n68 beea\n69 bee9\n6a bee6\n6b bee5\n86 c01c\n6c beda\n6d bed9\n6e bed6\n78 bde8\n7c bdd8\n78 bdea\n79 bde9\n7d bdd9\n7e bdd6\n69 bce9\n68 bce8\n6d bcd9\n6c bcd8\ne8 7eea\nea 7ee6\n6f bed5\ned 7ed9\nee 7ed6\n6e bed4\n32 a78c\nf8 7de8\n6d bed1\nfc 7dd8\n6c bed0\n6b bec5\n6a bec4\n69 bec1\n68 bec0\n65 bc93\n7f bdd5\n55 bb93\n6f bcd5\n63 bc8f\n7d bdd1\n53 bb8f\n6d bcd1\n62 bc8e\n7c bdd0\n52 bb8e\n6c bcd0\n61 bc83\n7b bdc5\n51 bb83\n6b bcc5\n79 bdc1\n69 bcc1\nf7 f71d\n78 bdc0\ne7 f61d\n68 bcc0\nfa 7de6\nfd 7dd9\nfe 7dd6\ned 7cd9\nec 7cd8\nef 7ed5\n6f 3ed5\nee 7ed4\n6e 3ed4\n6d 3ed1\n6c 3ed0\n6b 3ec5\n6a 3ec4\n69 3ec1\n68 3ec0\nd5 7b93\nef 7cd5\n65 3c93\n7f 3dd5\n55 3b93\n6f 3cd5\ne3 7c8f\nfd 7dd1\nd3 7b8f\ned 7cd1\n63 3c8f\n7d 3dd1\n53 3b8f\n6d 3cd1\ne2 7c8e\nfc 7dd0\nd2 7b8e\nec 7cd0\n62 3c8e\n7c 3dd0\n52 3b8e\n6c 3cd0\nd1 7b83\neb 7cc5\n61 3c83\n7b 3dc5\n51 3b83\n6b 3cc5\nf9 7dc1\ne9 7cc1\n79 3dc1\n69 3cc1\nf8 7dc0\ne8 7cc0\n7e bf7c\n64 be3a\n7e 3ffc\n64 3eba\n6f be7f\ne7 7e3f\nf6 f71c\n77 bdbf\n7f bd7f\ne5 f61b\nff f75d\n66 bcbe\n6e bc7e\nf7 7d3f\ne6 7c3e\ne7 7ebf\nef 7e7f\n6f 3e7f\n67 3e3f\nf7 7dbf\nff 7d7f\ne6 7cbe\nee 7c7e\n7f 3d7f\n77 3d3f\n6e 3c7e\n66 3c3e\n64 beb8\n65 be39\n6c be78\n64 be38\n75 bd39\n65 bc39\n67 bc97\n74 bd38\n65 3eb9\ne4 7eb8\n64 3eb8\nec 7e78\ne4 7e38\n6c 3e78\n64 3e38\n75 3db9\n65 3cb9\n74 3db8\n64 3cb8\n67 bebd\n6e be7e\n6f be7d\nf5 f719\n76 bdbc\n64 bc3a\n7e bd7c\ne6 7e3e\ne7 7e3d\n77 bdbd\n7e bd7e\n65 bc3b\n7f bd7d\n67 bcbd\n55 bb3b\n6f bc7d\ne6 7ebe\nee 7e7e\nef 7e7d\nf6 7dbc\n6e 3e7e\n6f 3e7d\n66 3e3e\n67 3e3d\nf7 7dbd\nfe 7d7e\ne5 7c3b\nff 7d7d\ne7 7cbd\ne6 7cbc\n7e 3d7e\n65 3c3b\n7f 3d7d\n5d 3bfb\n77 3d3d\n4d 3afb\n67 3c3d\n7e bff4\n64 beb2\n66 be36\n6c be72\n7e bf74\n64 be32\nf3 f70f\n74 bdb2\ne3 f60f\nfd f751\n7e bdf4\n64 bcb2\n7c bd72\n74 bd32\n6c bc72\nec deda\n66 3eb6\nfe 7ff4\ne4 7eb2\n7e 3ff4\n64 3eb2\n2f edd\nec 7e72\n6c 3e72\n7e 3f74\n64 3e32\nf4 7db2\nfe 7df4\ne4 7cb2\nf3 770f\n74 3db2\n7c 3d72\n74 3d32\n6c 3c72\n7f bffd\n65 bebb\n67 beb7\n6d be7b\n6f be77\n4d ba73\nff 7f7d\ne5 7e3b\ne7 7e37\nf4 f718\n75 bdbb\n7d bd7b\nfd f759\n7e bdfc\n64 bcba\n6c bc7a\n5d b973\nfe 7d7c\ne4 7c3a\nff 7ffd\ne5 7ebb\ne7 7eb7\ned 7e7b\nef 7e77\ncd 7a73\n6d 3e7b\n6f 3e77\n7f 3f7d\n65 3e3b\ned de5b\n67 3e37\n4d 3a73\nf5 7dbb\n37 a71f\nfd 7d7b\nfe 7dfc\ne4 7cba\n7d 3d7b\n7f 3d77\n75 3d3b\nfd dd5b\n77 3d37\n6c 3c7a\n6e 3c76\n7e 3d7c\n64 3c3a\n5d 3973\n55 3933\n66 beb4\n65 beb1\n64 beb0\n67 be35\n6e be74\n66 be34\n6d be71\n65 be31\n6c be70\n64 be30\n75 bdb1\nf3 f70d\n74 bdb0\ne3 f60d\n64 bcb0\n5d bbf3\n77 bd35\n4d baf3\n67 bc35\n63 bc2f\n7d bd71\n53 bb2f\n6d bc71\n62 bc2e\n7c bd70\n52 bb2e\n6c bc70\ned ded9\n67 3eb5\nec ded8\n66 3eb4\n4f 10ff\ne5 7eb1\n65 3eb1\n4e 10fe\ne4 7eb0\n64 3eb0\nee 7e74\ne6 7e34\n6e 3e74\ned 7e71\n4f 107f\ne5 7e31\n6d 3e71\n65 3e31\nec 7e70\n6c 3e70\n64 3e30\nfd ddd9\n77 3db5\nf5 7db1\n75 3db1\nf4 7db0\ne4 7cb0\nf3 770d\n74 3db0\ne3 760d\n64 3cb0\n63 3c2f\n7d 3d71\n5b 3bef\n75 3d31\n4b 3aef\n65 3c31\n5a 3bee\n74 3d30\n4a 3aee\n64 3c30\n7e bffc\n64 beba\n65 beb9\n66 beb6\n6c be7a\n6d be79\n6e be76\n6f be75\n74 bdb8\nf5 f711\n76 bdb4\n6f bcd7\n7c bd78\n64 bc32\n7e bd74\nfe 7f7c\ne4 7e3a\ne5 7e39\ne6 7e36\ne7 7e35\n75 bdb9\n77 bdb5\n7c bd7a\n65 bc33\n7f bd75\n65 bcb9\n64 bcb8\n55 bb33\n6f bc75\ne5 7c39\ncd 7af3\ne7 7c35\nfe 7ffc\ne4 7eba\ne5 7eb9\ne6 7eb6\nec 7e7a\ned 7e79\nee 7e76\nef 7e75\nf4 7db8\nef 7cd7\n36 a71c\nfc 7d78\n6c 3e7a\n6d 3e79\n6e 3e76\n6f 3e75\n7e 3f7c\n64 3e3a\n65 3e39\n6f 3cd7\n7c 3d78\n64 3c32\n7e 3d74\n67 3c97\n74 3d38\nf5 7db9\n36 a71e\nfc 7d7a\n27 c9f\nfe 7d76\ne5 7cb9\ne4 7cb8\nd5 7b33\nef 7c75\n7c 3d7a\n7e 3d76\n65 3c33\n7f 3d75\n75 3d39\n5f 3bd7\n6c 3c78\n55 3b33\n6f 3c75\n65 3c39\n7d bff1\n63 beaf\n67 be9f\n6b be6f\n6f be5f\n2b ae4f\nfd 7f71\ne3 7e2f\ne7 7e1f\nf2 f70c\n73 bdaf\n7b bd6f\n7f bd5f\nfb f74d\ne1 f60b\n7c bdf0\n62 bcae\n6a bc6e\n6e bc5e\n3b ad4f\n2a ac4e\nfd 7ff1\ne3 7eaf\ne7 7e9f\neb 7e6f\nef 7e5f\n67 9e3d\nab 6e4f\n6b 3e6f\n6f 3e5f\n7d 3f71\n63 3e2f\n67 3e1f\n2b 2e4f\nf3 7daf\nf7 7d9f\n1b be7\nff 7d5f\ne6 7c9e\nb3 6d8f\n7b 3d6f\n7f 3d5f\n73 3d2f\n77 3d1f\n6a 3c6e\n6e 3c5e\n7c 3d70\n62 3c2e\n66 3c1e\n3b 2d4f\n33 2d0f\n2a 2c4e\n7c bff0\n62 beae\n63 bead\n67 be9d\n6a be6e\n6b be6d\n6e be5e\n6f be5d\nf1 f709\n72 bdac\n76 bd9c\n23 ae8d\n2a ae4e\n2b ae4d\ne6 7e1e\ne7 7e1d\nf1 f70b\n72 bdae\n73 bdad\n77 bd9d\n7a bd6e\n61 bc2b\n7b bd6d\n7e bd5e\n65 bc1b\n7f bd5d\n63 bcad\n67 bc9d\n66 bc9c\n51 bb2b\n6b bc6d\n55 bb1b\n6f bc5d\n3a ad4e\n21 ac0b\n3b ad4d\n11 ab0b\n2b ac4d\nfc 7ff0\ne2 7eae\n4d 10fb\ne3 7ead\ne6 7e9e\ne7 7e9d\neb 7e6d\nee 7e5e\nef 7e5d\nf2 7dac\nf6 7d9c\nd db\na3 6e8d\n66 9e3c\naa 6e4e\nab 6e4d\nb2 6d8c\n6a 3e6e\n6b 3e6d\n6e 3e5e\n6f 3e5d\n7c 3f70\n62 3e2e\n63 3e2d\n66 3e1e\n67 3e1d\n2a 2e4e\n2b 2e4d\n23 2e0d\nf2 7dae\nf3 7dad\nf7 7d9d\ne3 7cad\ne2 7cac\ne7 7c9d\ne6 7c9c\nb3 6d8d\na3 6c8d\na2 6c8c\n7a 3d6e\n61 3c2b\n7b 3d6d\n7e 3d5e\n65 3c1b\n7f 3d5d\n72 3d2e\n59 3beb\n73 3d2d\n5d 3bdb\n77 3d1d\n49 3aeb\n63 3c2d\n4d 3adb\n67 3c1d\n3a 2d4e\n21 2c0b\n3b 2d4d\n19 2bcb\n33 2d0d\n9 2acb\n23 2c0d\n7b bfed\n61 beab\n63 bea7\n7f bfdd\n65 be9b\n74 bf38\n67 be97\n69 be6b\n6b be67\n6d be5b\n6f be57\n49 ba63\n4d ba53\n3b afcd\n21 ae8b\n29 ae4b\n2b ae47\nfb 7f6d\ne1 7e2b\ne3 7e27\nff 7f5d\ne5 7e1b\ne7 7e17\nbb 6f4d\na1 6e0b\na3 6e07\nf0 f708\n71 bdab\n75 bd9b\n77 bd97\n79 bd6b\n7d bd5b\n7f bd57\n55 b993\n59 b963\n5d b953\n39 ad4b\n3b ad47\nfb 7fed\ne1 7eab\ne3 7ea7\nff 7fdd\ne5 7e9b\nf4 7f38\ne7 7e97\ne9 7e6b\neb 7e67\nef 7e57\nc9 7a63\ncd 7a53\nbb 6fcd\na1 6e8b\n69 3e6b\n6b 3e67\n6d 3e5b\n6f 3e57\n7b 3f6d\n61 3e2b\ne9 de4b\n63 3e27\n7f 3f5d\n65 3e1b\n67 3e17\n49 3a63\n4d 3a53\n29 2e4b\n2b 2e47\n3b 2f4d\n21 2e0b\n23 2e07\nf1 7dab\nf5 7d9b\n33 a70f\nf9 7d6b\ndd 7953\nb1 6d8b\nb3 6d87\n79 3d6b\n7b 3d67\n7d 3d5b\n7f 3d57\n71 3d2b\nf9 dd4b\n73 3d27\n75 3d1b\n77 3d17\n59 3963\n5d 3953\n51 3923\n55 3913\n39 2d4b\n3b 2d47\n31 2d0b\n33 2d07\nb6 c71c\n37 8dbf\nb5 c71b\n2f 26f7\n36 8dbe\n76 3d3c\n5c 3bfa\n7c 37fa\naf 4efd\na7 4ebd\na5 4cbb\nb6 e736\nbf 4dfd\nae e6f6\nb7 4dbd\n8f 4afd\n7f b7ff\n77 b53d\n5d b3fb\nff 77ff\n77 37bf\n6f 34fd\n55 33bb\n2f 8ef7\n27 8c3d\nd 8afb\nf 8af7\n7c b7f8\n5d 3bf9\ndc 7bf8\n5c 3bf8\n7d 37f9\n5d 33f9\nfc 77f8\ndc 73f8\n7c 37f8\n5c 33f8\n4e bafc\n7e b7fe\n6e b6fc\n76 b53c\n5c b3fa\n57 3bbd\n46 3abc\nfe 77fe\nee 76fc\ndd 73f9\n76 37be\n77 37bd\n66 36bc\n76 bd34\n5c bbf2\n7e 37f6\n7c 37f2\n77 bd3d\n5d bbfb\n7d b7f3\n7d b7fb\nf7 7d3d\ndd 7bfb\nfd 77f3\n6f 3cfd\n55 3bbb\nf7 dd1d\ndd dbdb\n57 3bb7\n75 37b3\nfd 77fb\nff 77f7\ndf 73f7\n75 37bb\nfd d7db\n77 37b7\nf7 d51d\ndd d3db\n57 33b7\n43 32af\n5d 33f1\nc2 72ae\ndc 73f0\n42 32ae\n5c 33f0\n76 bd3c\n5c bbfa\n7c b7f2\n7c b7fa\n7d b7f9\nf6 7d3c\ndc 7bfa\ndd 7bf9\nfc 77f2\nfc 77fa\nfd 77f9\nfe 77f6\nde 73f6\nc3 72af\ndd 73f1\n2d 8e7b\nd3 fb2d\n3d 8d7b\n72 3d2c\n58 3bea\n78 37ea\n75 bd31\n5b bbef\n7b b7ef\n73 b52d\n59 b3eb\n77 b51d\n5d b3db\n3b a7cf\ndf 7bdf\n72 bd0c\n58 bbca\n78 b7ca\n72 b50c\n58 b3ca\nfb 77ef\nff 77df\n73 37af\nf0 d722\n77 379f\n6b 34ed\n51 33ab\n33 278f\n76 3d1c\n5c 3bda\nf2 7d0c\nd8 7bca\n72 3d0c\n58 3bca\n7c 37da\nf8 77ca\nf2 750c\nd8 73ca\n78 37ca\n72 350c\n58 33ca\n6d 9e73\n69 9e63\nf4 d710\n75 9db3\n7d 9d73\n27 8eb7\n2f 8e77\n2d 8e73\n29 8e63\nb6 c714\n37 8db7\nb4 c710\n35 8db3\n3f 8d77\nd3 fb25\n3d 8d73\n78 b7e8\n59 3be9\nd8 7be8\n58 3be8\n79 37e9\n59 33e9\nf8 77e8\nd8 73e8\n78 37e8\n58 33e8\n74 bd30\n5a bbee\n4e badc\n7a b7ee\n6a b6ec\n6e b6dc\n72 b52c\n58 b3ea\n76 b51c\n5c b3da\n3a a7ce\nde 7bde\n5c bbd8\nce 7adc\n59 bbc9\n58 bbc8\neb 764d\n6c 3cf0\n52 3bae\ne9 dc63\n56 3b9e\nd0 db20\n57 3b9d\n42 3aac\nd9 db61\n46 3a9c\n13 2b8d\n5d b3d9\n7c b7d8\n5c b3d8\n79 b7c9\n59 b3c9\n78 b7c8\n58 b3c8\nfa 77ee\nfe 77de\nea 76ec\nee 76dc\n76 97bc\nba 67ce\n72 37ae\n73 37ad\n76 379e\nf0 d720\n77 379d\n62 36ac\nf9 d761\n66 369c\n32 278e\n33 278d\n5d 3bd9\ndc 7bd8\n5c 3bd8\nd9 7bc9\n59 3bc9\nd8 7bc8\n58 3bc8\ndd 73d9\n7d 37d9\n5d 33d9\nfc 77d8\ndc 73d8\n7c 37d8\n5c 33d8\nf9 77c9\nd9 73c9\n79 37c9\n59 33c9\nf8 77c8\nd8 73c8\n78 37c8\n58 33c8\n7a 37e6\n78 37e2\nf4 dfb2\n1a a1ee\nfe dff4\ne4 deb2\na a0ee\nf4 df92\n1a a1ce\n73 bd2d\n59 bbeb\n77 bd1d\n5d bbdb\n6c bc78\n5f bbd7\n79 b7e3\n7d b7d3\n33 ad0d\n19 abcb\n39 a7c3\n79 b7eb\n7d b7db\n7f b7d7\n3b a7c7\nf9 77e3\n5a bbc6\nfd 77d3\n6b 3ced\n51 3bab\n64 3c38\n57 3b97\n71 37a3\n75 3793\n2b 2ccd\n11 2b8b\n31 2783\n7a b7c6\n5a b3c6\n78 b7c2\nf9 77eb\nfb 77e7\nff 77d7\ndb 73e7\n71 37ab\nf9 d7cb\n73 37a7\n75 379b\n77 3797\nf3 d50d\nd9 d3cb\n53 33a7\n31 278b\n33 2787\n5e 3bd6\n5a 3bc6\n7e 37d6\n5e 33d6\n7c 37d2\nfa 77c6\nda 73c6\n7a 37c6\n5a 33c6\nf8 77c2\n78 37c2\n4e 92de\nf0 ffa0\n5a 91ee\nb0 efa0\n1a 81ee\nf4 ff90\n5e 91de\nf0 ff80\n5a 91ce\nb4 ef90\n1e 81de\nb0 ef80\n1a 81ce\ne 28de\n9a 61ee\n8a 60ee\n2a 24ee\n3a 25ce\n2e 24de\n2a 24ce\n1e 21de\ne 20de\ne ade\n5a 19ce\n4a 18ce\n1a 9ce\na 8ce\nce 52de\n8e 42de\n6a 16ce\n4e 12de\nda 51ee\nca 50ee\n9a 41ee\n8a 40ee\nf0 7fa0\n5a 11ee\n78 b7e0\nde 51de\nda 51ce\nce 50de\nca 50ce\n9e 41de\n9a 41ce\n8e 40de\n8a 40ce\n7a 15ce\n6a 14ce\nf4 7f90\n5e 11de\nf0 7f80\n5a 11ce\nd8 7be0\n58 3be0\n79 37e1\n59 33e1\nf8 77e0\nd8 73e0\n78 37e0\n58 33e0\n72 bd2c\n58 bbea\n76 bd1c\n5c bbda\n5d bbd9\n5e bbd6\n78 b7e2\n79 b7e1\n7c b7d2\n78 b7ea\n79 b7e9\n7c b7da\n7d b7d9\n7e b7d6\n5e b3d6\n45 ba93\n5f bbd5\ndd 7bd9\n43 ba8f\n5d bbd1\n42 ba8e\n5c bbd0\nf8 77e2\nf9 77e1\n41 ba83\n5b bbc5\nfc 77d2\n59 bbc1\n58 bbc0\n63 b68f\n7d b7d1\n43 b28f\n5d b3d1\n62 b68e\n7c b7d0\n42 b28e\n5c b3d0\n61 b683\n7b b7c5\n41 b283\n5b b3c5\n79 b7c1\n59 b3c1\n78 b7c0\n58 b3c0\nf8 77ea\nf9 77e9\nfa 77e6\nfd 77d9\nfe 77d6\nda 73e6\nd9 73e1\nde 73d6\nc5 7a93\ndf 7bd5\n45 3a93\n5f 3bd5\nc3 7a8f\ndd 7bd1\n43 3a8f\n5d 3bd1\nc2 7a8e\ndc 7bd0\n42 3a8e\n5c 3bd0\nc1 7a83\ndb 7bc5\n41 3a83\n5b 3bc5\nd9 7bc1\n59 3bc1\nd8 7bc0\n58 3bc0\ne3 768f\nfd 77d1\nc3 728f\ndd 73d1\n63 368f\n7d 37d1\n43 328f\n5d 33d1\ne2 768e\nfc 77d0\nc2 728e\ndc 73d0\n62 368e\n7c 37d0\n42 328e\n5c 33d0\ne1 7683\nfb 77c5\nc1 7283\ndb 73c5\n61 3683\n7b 37c5\n41 3283\n5b 33c5\nf9 77c1\nd9 73c1\n79 37c1\n59 33c1\nf8 77c0\nd8 73c0\n78 37c0\n58 33c0\nea d64c\n6b 9cef\n6f 9cdf\nca d24c\n4b 98ef\n4f 98df\n7 aa37\n8a c24c\nb 88ef\nf 88df\n6e bc7c\n54 bb3a\n74 b73a\n74 37ba\n6e 34fc\n54 33ba\n15 a93b\nd a87b\n15 a933\nf a877\nd a873\n7 a837\n5f bb7f\nd7 7b3f\n77 b7bf\n7f b77f\n5d b37b\n78 3de2\nf7 773f\ndf 7b7f\n5f 3b7f\n57 3b3f\nf7 77bf\nff 777f\ndd 737b\n7f 377f\n77 373f\n5d 337b\n1f a37d\n5 a23b\n16 bbc\n27 a637\n7 a237\nf9 dfc1\n1f a1fd\n5 a0bb\n55 bb39\n74 b7b8\n75 b739\n55 b339\n55 3bb9\nd4 7bb8\n54 3bb8\n4f 3ad7\n5c 3b78\n75 37b9\n55 33b9\nf4 77b8\nd4 73b8\n74 37b8\n54 33b8\n75 3f99\n7 a0b7\n35 a53b\n2d a47b\nef deff\n15 a13b\ne7 de3f\nd a07b\n35 a533\nef def7\n15 a133\n7d 3f59\nf a077\ne7 de37\nd a073\n75 3f19\n7 a037\n57 bbbd\n5e bb7e\n45 ba3b\n5f bb7d\n46 babc\n4e ba7c\nd6 7b3e\nd7 7b3d\nc6 7a3c\n76 b7be\n77 b7bd\n7e b77e\n65 b63b\n7f b77d\n66 b6bc\n6e b67c\n5c b37a\n5d b379\nf6 773e\n78 3de0\nf7 773d\ne6 763c\nd5 7339\nd7 7bbd\nde 7b7e\nce 7a7c\n5e 3b7e\n56 3b3e\n57 3b3d\n4e 3a7c\n46 3a3c\nf6 77be\nf7 77bd\nfe 777e\ne6 76bc\nee 767c\nd5 73b9\ndc 737a\nd7 d99f\ndd 7379\n7e 377e\n76 373e\n77 373d\n6e 367c\n66 363c\n5c 337a\n55 3339\n5c bb72\n6e bc74\n54 bb32\n5c 3b72\n6e 3c74\n54 3b32\nfc d7da\n76 37b6\nf6 d51c\ndc d3da\n56 33b6\n74 37b2\n94 c310\n15 89b3\n4d 987b\nd 887b\n4f 9877\n4d 9873\n47 9837\nf 8877\nd 8873\n7 8837\n6f bcfd\n55 bbbb\n5d bb7b\n75 b7b3\nef 7c7d\nd5 7b3b\nd7 7b37\nfc ddfa\nf5 7733\n75 b7bb\n7d b77b\nf5 773b\nfe ddfe\nf7 7737\nde d9fe\nd7 7337\nef 7cfd\nd5 7bbb\nd7 7bb7\ndd 7b7b\ndf 7b77\nf5 77b3\n5d 3b7b\n5f 3b77\n6f 3c7d\n55 3b3b\ndd db5b\n57 3b37\n7d 3773\nf5 77bb\nf7 77b7\nfd 777b\nff 7777\nd7 73b7\ndf 7377\n7d 377b\n7f 3777\n75 373b\n5f 3377\n5d 3373\n1f 83fd\n5 82bb\n7 82b7\n5f 937d\n45 923b\n1f 837d\n5 823b\n47 9237\nf 8277\nd 8273\n7 8237\n5f 91fd\n45 90bb\n3f 85fd\n25 84bb\n15 81bb\n1f 81fd\n5 80bb\n57 bb35\n56 bb34\n43 ba2f\n5d bb71\n55 bb31\n42 ba2e\n5c bb70\n54 bb30\n76 b7b4\n56 b3b4\n74 b7b0\n77 b735\n57 b335\n75 b731\n55 b331\n62 b62e\n7c b770\n74 b730\n42 b22e\n5c b370\n54 b330\nd6 7b34\ndc db58\n56 3b34\n3f d7f\nd5 7b31\n55 3b31\n3e d7e\nd4 7b30\n54 3b30\nfd d7d9\n77 37b5\ndd d3d9\n57 33b5\nf6 77b4\nd6 73b4\nfc d7d8\n76 37b4\ndc d3d8\n56 33b4\n75 37b1\n55 33b1\nf4 77b0\n74 37b0\n54 33b0\nfd ddfb\nf6 7734\ndd d9fb\nd6 7334\nf4 7730\n74 3730\n54 3330\n35 85b3\n27 84b7\n17 81b7\n15 81b3\n6d 947b\neb feed\n55 913b\ne3 fe2d\n4d 907b\nc3 f22d\n2d 847b\nb3 ef2d\n1d 817b\nab eeed\n15 813b\na3 ee2d\nd 807b\n6d 9473\n67 9437\n57 9137\neb fee5\n55 9133\n4f 9077\n47 9037\n2f 8477\n27 8437\n1f 8177\nb3 ef25\n1d 8173\n17 8137\nab eee5\n15 8133\n5c bb7a\n5d bb79\n45 ba33\n5f bb75\n74 b7b2\n75 b7b1\n7c b772\n63 b62f\n7d b771\nee 7c7c\nd4 7b3a\nd5 7b39\nd6 7b36\nd7 7b35\nfc ddf8\nf5 7731\n74 b7ba\n75 b7b9\n77 b7b5\n7c b77a\n7d b779\n45 b233\n5f b375\n5c b372\n43 b22f\n5d b371\nf4 773a\nf5 7739\nf6 7736\nfe ddfc\ne4 dcba\nf7 7735\nd6 7336\nde d9fc\nc4 d8ba\nd7 7335\ndc d9f8\n3f 57f\nd5 7331\nee 7cfc\nd4 7bba\nd5 7bb9\n37 a73d\nd6 7bb6\ndc 7b7a\ndd 7b79\nc5 7a33\ndf 7b75\nf4 77b2\nf5 77b1\n5c 3b7a\n5d 3b79\n5e 3b76\n45 3a33\n5f 3b75\n6e 3c7c\n54 3b3a\n55 3b39\ndc db5a\n56 3b36\ndd db59\n57 3b35\n7c 3772\n74 3732\nf4 77ba\nf5 77b9\nf6 77b6\nf7 77b5\nfc 777a\nf7 dd9f\nfd 7779\nd6 73b6\nd7 73b5\n3f 5ff\nd5 73b1\nc3 722f\nd7 d997\ndd 7371\n7c 377a\n7e 3776\n74 373a\n75 3739\nfc d75a\n76 3736\n5e 3376\n5c 3372\ndc d35a\n56 3336\n69 9c6b\n6d 9c5b\n29 8c6b\nc3 fa0d\n2d 8c5b\n6a bc6c\n50 bb2a\n70 b72a\n70 37aa\n6a 34ec\n50 33aa\nd 287b\nf 2877\nd 2873\n8d c85b\n7 2837\n57 bb9f\n5b bb6f\n5f bb5f\n1b ab4f\nd7 7b1f\n73 b7af\n77 b79f\n96 ebb4\n7f b75f\n5d b35b\nf9 7deb\n33 a78f\n3b a74f\nf3 772f\n78 3dc2\nf7 771f\nd7 7b9f\ndf 7b5f\n6a bccc\n50 bb8a\n6e bc5c\n54 bb1a\n57 9b3d\n9b 6b4f\n58 bb4a\n6a bc4c\n50 bb0a\n5b 3b6f\n5f 3b5f\n6d 3c71\n53 3b2f\n57 3b1f\n1b 2b4f\n70 b78a\n6a b4cc\n50 b38a\na5 ecb1\n74 b71a\na9 ece1\n78 b74a\n89 e8e1\n58 b34a\nf3 77af\nf7 779f\nff 775f\nd9 736b\nb3 678f\n7b 376f\n73 372f\n77 371f\n59 336b\n5d 335b\nd8 fbc8\n3b 274f\nd0 fb88\n33 270f\n19 234b\n6e 3cdc\n54 3b9a\nea 7ccc\nd0 7b8a\n6a 3ccc\n50 3b8a\nd8 7b4a\nea 7c4c\nd0 7b0a\n58 3b4a\n6a 3c4c\n50 3b0a\n74 379a\n6e 34dc\n54 339a\nf0 778a\nea 74cc\nd0 738a\n70 378a\n6a 34cc\n50 338a\nf8 774a\nf0 770a\nd8 734a\nea 744c\nd0 730a\nf0 d700\n71 9da3\n79 9d63\n7d 9d53\n6b 9c67\n69 9c63\n6f 9c57\n6d 9c53\nb0 c700\n31 8da3\n39 8d63\nd3 fb05\n3d 8d53\n2b 8c67\n29 8c63\n2f 8c57\n3f 25fd\n25 24bb\n51 bb29\n4b bac7\n58 bb68\n43 ba87\n50 bb28\n70 b7a8\n71 b729\n51 b329\n6b b6c7\n78 b768\n63 b687\n70 b728\n4b b2c7\n58 b368\n43 b287\n50 b328\n51 3ba9\nd0 7ba8\n50 3ba8\ncb 7ac7\nd8 7b68\nc3 7a87\nd0 7b28\n4b 3ac7\n58 3b68\n43 3a87\n50 3b28\n71 37a9\n51 33a9\nf0 77a8\nd0 73a8\n70 37a8\n50 33a8\neb 76c7\nf2 dd8e\nf8 7768\ne3 7687\nf0 7728\ncb 72c7\nd2 d98e\nd8 7368\nc3 7287\nd0 7328\n63 3687\n70 3728\n43 3287\n50 3328\nad c4db\n27 24b7\n8d c0db\n7 20b7\ncc f8f0\n2f 2477\nca f8ec\n2d 2473\nad c45b\nc4 f8b0\n27 2437\nf 2077\n8d c05b\n7 2037\neb f64d\n6c bcf0\n52 bbae\n57 bb9d\n5a bb6e\n41 ba2b\n5b bb6d\n5e bb5e\n45 ba1b\n5f bb5d\n46 ba9c\n4a ba6c\n4e ba5c\n13 ab8d\n1a ab4e\n3d d7b\nd3 7b2d\nd6 7b1e\nd7 7b1d\nc6 7a1c\n93 6b0d\n72 b7ae\n73 b7ad\n77 b79d\n61 b62b\n7b b76d\n95 ebb3\naf ecf5\n7e b75e\n65 b61b\n7f b75d\n62 b6ac\n66 b69c\n6a b66c\n85 eab1\n6e b65c\n59 b369\n8d e8f1\n5c b35a\nf8 7dea\n32 a78e\nf9 7de9\n33 a78d\n3a a74e\nf3 772d\nf6 771e\n78 3dc0\nf7 771d\n67 3cbf\ne6 761c\nd6 7b9e\nd7 7b9d\n54 bb98\nde 7b5e\nc6 7a9c\nca 7a6c\nce 7a5c\n51 bb89\n50 bb88\n55 bb19\n93 6b8d\n5c bb58\n54 bb18\n56 9b3c\n9a 6b4e\n59 bb49\n51 bb09\n58 bb48\n50 bb08\n5a 3b6e\n5e 3b5e\n6c 3c70\n52 3b2e\n53 3b2d\n56 3b1e\n57 3b1d\n4a 3a6c\n4e 3a5c\n42 3a2c\n46 3a1c\n1a 2b4e\n13 2b0d\n55 b399\n74 b798\n54 b398\n71 b789\n51 b389\n70 b788\n50 b388\n75 b719\n5d b359\n55 b319\n93 ebad\n7c b758\n74 b718\n5c b358\n54 b318\n79 b749\n71 b709\n59 b349\n51 b309\n78 b748\n70 b708\n58 b348\n50 b308\nf2 77ae\nf3 77ad\nf6 779e\nf7 779d\nd1 dba1\nfe 775e\ne2 76ac\ne6 769c\n6f 3cff\nee 765c\nd1 73a9\nd8 736a\nb2 678e\nb3 678d\n76 973c\nba 674e\n7a 376e\n72 372e\n73 372d\n77 371d\n66 361c\n58 336a\n51 3329\ne9 fcc9\n32 270e\n33 270d\n55 3b99\nd4 7b98\n54 3b98\nd1 7b89\n51 3b89\nd0 7b88\n50 3b88\ndc 7b58\nd4 7b18\n5c 3b58\n54 3b18\nd9 7b49\nd1 7b09\n59 3b49\n51 3b09\nd8 7b48\nd0 7b08\n58 3b48\n50 3b08\nd5 7399\n75 3799\n55 3399\nf4 7798\nd4 7398\n74 3798\n54 3398\nf1 7789\nd1 7389\n71 3789\n51 3389\nf0 7788\nd0 7388\n70 3788\n50 3388\n5d 3359\n55 3319\n7d 3dfb\nfc 7758\n75 3dbb\nf4 7718\n5d 39fb\ndc 7358\n74 3718\n5c 3358\n54 3318\n72 3dac\nf1 7709\n79 3749\n71 3709\n59 3349\n51 3309\n79 3deb\nf8 7748\n71 3dab\nf0 7708\n59 39eb\nd8 7348\n78 3748\n70 3708\n58 3348\n50 3308\n1f bfd\n5 abb\n7 ab7\n9f 4b7d\n85 4a3b\nd a7b\n1f b7d\n5 a3b\n87 4a37\nf a77\nd a73\n7 a37\n96 e336\n9f 49fd\n85 48bb\n58 bb62\n58 3b62\nf8 d7ca\n72 37a6\nf2 d50c\nd8 d3ca\n52 33a6\n70 37a2\ne 82fc\nc7 58b7\n87 48b7\na aacc\n95 493b\n8d 487b\n4d 187b\n1d 97b\n15 93b\nd 87b\ne5 deb1\nb a0ed\ne4 deb0\na a0ec\nf a0dd\ne a0dc\ncf 5877\n97 4937\n95 4933\n8f 4877\n8d 4873\n87 4837\n4f 1877\n4d 1873\n1f 977\n1d 973\n17 937\n15 933\nf 877\nd 873\n7 837\n6b bced\n51 bbab\n6f bcdd\n55 bb9b\n64 bc38\n57 bb97\n59 bb6b\n5d bb5b\n5f bb57\n71 b7a3\n75 b793\n2b accd\n11 ab8b\n19 ab4b\n1b ab47\n31 a783\nd3 7b27\nd7 7b17\nf8 ddea\nf1 7723\nfc ddda\n76 3db6\nf5 7713\n71 b7ab\n75 b79b\n77 b797\n94 ebb0\n7d b75b\n31 a78b\nf9 7de3\n33 a787\nf1 772b\nfa ddee\nf3 7727\nfe ddde\nf7 7717\nda d9ee\nd3 7327\nde d9de\nd7 7317\nef 7cdd\nd5 7b9b\ne4 7c38\nd7 7b97\ndf 7b57\n6e bcd4\n54 bb92\nf1 77a3\nf5 7793\n7e 3df6\nfd 7753\n6a bcc4\n50 bb82\n56 bb16\nab 6ccd\n91 6b8b\n5c bb52\n5a bb46\n52 bb06\nb1 6783\n58 bb42\n59 3b6b\n5b 3b67\n5d 3b5b\n5f 3b57\n6b 3c6d\n51 3b2b\nd9 db4b\n53 3b27\n6f 3c5d\n55 3b1b\n57 3b17\n79 3763\n7d 3753\n19 2b4b\n1b 2b47\n2b 2c4d\n11 2b0b\n13 2b07\nd6 fbbc\n39 2743\n72 b786\n70 b782\n7f 1ddd\n65 1c9b\n76 b716\n69 1ccb\n7a b746\n7b 1dcd\n61 1c8b\n72 b706\n49 18cb\n5a b346\n78 b742\n58 b342\nf1 77ab\nf3 77a7\nf5 779b\nf7 7797\nf9 776b\nff 7757\nd3 73a7\ndf 7357\nb1 678b\nb3 6787\n79 376b\n7b 3767\n7f 3757\n71 372b\n75 371b\n5b 3367\n59 3363\n5f 3357\n5d 3353\n39 274b\nd8 fbc0\n3b 2747\n31 270b\nb0 e5a8\neb dccd\nd1 db8b\n34 712\nbe 4ff6\na0 e4a8\nae 4ef6\n80 e0a8\nb3 65a5\n8e 4af6\ne0 d4a8\nb8 4d48\n9e e9dc\n84 e89a\nb7 4d97\na2 e4a6\nf0 7f88\n82 e0a6\ned d653\n6e 9cf6\n9a e1e4\n80 e0a2\nba 476c\na0 462a\neb d64f\n6c 9cf2\nf2 d5a6\nc1 50a9\nd0 fb02\nea fc44\ne2 d4a6\nb1 4fa9\na2 c4a6\n7a 3d46\nfa d5e4\ne0 d4a2\nb8 4d42\nba c5e4\na0 c4a2\n78 3d42\nc7 7817\ne a25c\nd2 d1a6\nb2 e724\na1 4ca9\nca f844\n92 c1a6\n8a e844\n61 3ca9\nc2 d0a6\n9a 4946\n38 87c2\n5a 3946\nda d1e4\nc0 d0a2\n98 4942\n2e ae5c\n9a c1e4\n80 c0a2\n58 3942\na4 ec38\n97 eb97\n62 1c26\n82 ca24\nf 28d7\n1c 2978\n94 eb38\n87 ea97\n52 1b26\n1e 3de\nd5 d999\ne3 dcad\n2c 6f2\n95 c999\nb3 e5a5\na3 e4a5\n50 b1aa\na2 e4a4\n93 e1a5\nb3 472d\n91 e1a1\nb1 4729\ne2 d60c\n63 9caf\n7d 9df1\n83 e0a5\n30 adaa\na3 462d\n82 e0a4\na2 462c\n81 e0a1\na1 4629\n80 e0a0\na0 4628\nf3 d5a5\ne2 fea6\nb3 c5a5\n58 b948\na2 eea6\nb1 c5a1\nba efe4\na0 eea2\na3 c4a5\n61 3c03\n7b 3d45\n50 91aa\n48 b848\na2 c4a4\ne0 d4a0\n13 b85\n9e 4bfe\nb8 4d40\na0 c4a0\n5e 3bfe\n78 3d40\n93 c1a5\n92 c1a4\nd1 d1a1\ne 2a5e\nda fbe4\nc0 faa2\n91 c1a1\n9a ebe4\n80 eaa2\n83 c0a5\n41 3803\n5b 3945\n29 26e3\n30 8daa\n82 c0a4\nc1 d0a1\n99 4941\nf 5d\n81 c0a1\n59 3941\n96 e996\nb6 4f1e\nba 4f4c\na0 4e0a\nb0 e58a\nba e5cc\na0 e48a\n90 e18a\n75 3713\n7c 9dda\n9a e1cc\n80 e08a\nb3 6587\nb0 c58a\n94 c19a\ne7 f495\n90 c18a\ne3 f485\n9e c1dc\n84 c09a\nb7 4597\n54 3332\n6e 3474\n9a c1cc\n80 c08a\nb3 4587\nb7 4f1d\nb3 4f0d\na7 4e1d\na3 4e0d\n19 2963\na2 4e0c\n4d b871\n93 c985\n1d 37b\nc7 d895\n87 c895\n5d 1973\n86 c894\n83 c885\n82 c884\n19 963\n80 c880\nb0 e588\nbe 4fd6\na0 e488\nae 4ed6\n11 29a1\n97 e397\na4 e438\n62 1426\n82 c224\n8f 62fd\n10 29a0\n90 e188\n9e 4bd6\nf6 5f3c\nf 20d7\n1c 2178\n1 28a1\n94 e338\n87 e297\n52 1326\nb7 6595\n0 28a0\n80 e088\n8e 4ad6\nb3 6585\nf0 d588\nb 8047\n95 c199\n11 9a1\n9e ebdc\n69 1c6b\n84 ea9a\n85 c099\nf d7\n1c 178\n1 8a1\n90 e92a\nd6 f9bc\n39 2543\n9a e96c\n80 e82a\nc6 f8bc\n29 2443\nb3 6d27\nd8 d96a\nc9 7263\nd0 d92a\n79 1543\nc9 fae3\ne3 fc25\n59 b341\nc8 d86a\n9a c96c\n80 c82a\nc6 d8bc\n29 443\nb3 4d27\nb6 e596\n85 6099\nb4 e592\nb2 e586\n81 6089\na6 e496\nbe e5d4\na4 e492\na2 e486\nba e5c4\na0 e482\n92 e186\n82 e086\n6e 9cd6\n9a e1c4\n80 e082\n6c 9cd2\nf6 d596\nb6 c596\nf2 d586\na6 c496\nfe d5d4\ne4 d492\ne2 d486\n43 b0a7\nb1 4f89\nd2 d186\n92 c186\n61 3c89\nc2 d086\n82 c086\n8b e8c7\n98 e968\n1f 23ff\n39 2541\n80 e828\nf 22ff\n29 2441\n68 3e42\n5f 13ff\n79 1541\n1f 3ff\n39 541\n28 2e42\n88 c868\na1 4c23\nbb 4d65\nc0 d828\n4f 12ff\n69 1441\n80 c828\nf 2ff\n29 441\n20 e28\n0 a8a0\nb6 e594\nb3 e585\nb2 e584\nb1 e581\n54 b19a\na4 e490\nd8 f9e8\n3b 256f\na3 e485\n50 b18a\na2 e484\n39 2563\na1 e481\n38 2560\na0 e480\n0 a28\n96 e194\n95 e191\n36 dbc\nb5 4719\n93 e185\n92 e184\n34 ad9a\n26 cbc\n37 a737\na5 4619\n83 e085\n30 ad8a\n82 e084\n81 e081\nb7 c595\n0 88a0\ncd f079\na6 ee96\nb6 c594\n53 b32f\n6d b471\nb4 c590\n51 b32b\n6b b46d\nf3 d585\ne2 fe86\nb3 c585\na7 c495\n54 919a\na4 c490\na3 c485\n50 918a\na2 c484\n39 563\n59 b361\na0 c480\nd7 d195\ned fc79\nc6 fa96\n97 c195\n6b 1c67\nad ec79\n86 ea96\n96 c194\n33 af2f\n4d b071\n95 c191\n69 1c63\n9e ebd4\n84 ea92\nd3 d185\n93 c185\n92 c184\n49 b061\nd1 d181\nda fbc4\nc0 fa82\n91 c181\n9a ebc4\n80 ea82\nc7 d095\n6d 36d3\n74 9d9a\n87 c095\n2d 26d3\n34 8d9a\nc5 d091\n85 c091\nc4 d090\nf1 7f21\n5b 116f\n84 c090\n21 ae2b\n3b af6d\n83 c085\n29 26c3\n30 8d8a\nc1 d081\n8d e0d1\n58 1160\nfe 7756\n81 c081\n18 160\nc0 d080\n80 c080\n8a e866\n82 e826\n5f 39df\nd8 d962\ne4 fe30\n4e 907e\n48 9042\nd0 d1a8\n68 36c2\n92 c926\n8 8042\nca d866\n8a c866\nc0 d0a8\n8f ea77\nf3 55a5\ne2 7ea6\n98 4948\n82 c826\nb0 e52a\nba e56c\na0 e42a\n90 e12a\n7c 9d7a\n9a e16c\n80 e02a\nb3 6527\n6c 9c7a\nf8 d56a\nb8 c56a\nb aa4d\ne8 d46a\n1b b4f\nd8 d16a\nb 84f\n98 c16a\n9a c16c\n80 c02a\nb3 4527\n93 e925\n91 e921\n83 e825\n82 e824\n81 e821\n80 e820\ne9 5ecb\n45 389b\n5f 39dd\nd8 d960\nd3 d925\n8a c864\n82 c824\n93 e387\na0 e428\nae 4e76\n83 e087\n90 e128\n9e 4b76\n6f 9cd7\n7c 9d78\n80 e028\n8e 4a76\ndb d3c7\ne8 d468\n9b c3c7\na8 c468\n3 82ad\nd3 d387\ne0 d428\n13 b0d\n93 c387\na0 c428\na1 c4a1\n5f 3bff\n79 3d41\ncb d0c7\nd8 d168\nb 84d\nbb cfc7\nc8 d068\n7b bfc7\n88 c068\naa 6e66\nb3 cf87\nc0 d028\n73 bf87\n80 c028\na2 6e26\ncf d855\n8c e85a\n9a e94c\n80 e80a\nc9 fac3\ne3 fc05\n5d 1b79\n92 e126\n4d 1879\n7e 9d76\nf0 7f08\n82 e026\n6e 9c76\n6c 9c72\nfa d566\nea d466\nb9 4f69\naa c466\nc2 508c\n6f 34df\ne8 d462\n43 92a7\ne7 debd\nd a0f9\n1b b47\n2f 24df\na8 c462\ne7 f41f\n3 82a7\nda d166\nf2 5d8c\n9a c166\nb2 4d8c\n5f 31df\nd8 d162\nf0 5d88\nb 847\n33 8fa7\nca d066\n99 4b69\nf3 f707\ne2 5c8c\nb3 e707\na2 4c8c\n59 3b69\n4f 30df\nc8 d062\n23 8ea7\nf 20df\n88 c062\n51 3b29\nf3 f5a5\n80 e808\n58 b16a\n93 e125\n65 9c33\n7f 9d75\n92 e124\nfb 57cf\ne3 dc2d\nc9 daeb\n2c 672\n91 e121\n63 9c2f\n7d 9d71\n83 e025\nec 56d0\n30 ad2a\n55 9b33\n6f 9c75\n82 e024\neb 56cf\n80 e020\ne9 56cb\n52 9b2e\n6c 9c70\nc4 d818\ne1 d423\nfb d565\na1 c423\nbb c565\naa ee66\n56 339c\ne9 d461\n6f 34dd\n55 339b\ne8 d460\n13 b05\n81 c023\n9b c165\n8a ea66\n71 bf23\n8b c065\n38 8d6a\n70 bf22\n8a c064\n36 2f9c\nc9 d061\ned fedb\n23 8ea5\n47 309d\nc0 d020\n9e e956\n9c e952\n9a e946\n98 e942\nfa d76c\ne0 d62a\ne a05e\n92 e906\n8c e852\n8a e846\n81 eaa9\n88 e842\n82 e806\nb4 e51a\nb0 e50a\nbe e55c\na4 e41a\n9e c956\n90 e10a\nb5 cf13\ncf d055\n7c 9d5a\nd0 d188\n92 c906\nce d856\ncc d852\n9a e14c\n80 e00a\n6c 9c5a\nca d846\n8a c846\n81 caa9\nc0 d088\ne2 7e86\n8f ea57\nf3 5585\n82 c806\n40 ba20\n18 962\n59 b3e1\nfb 5547\n9a c14c\n80 c00a\n6c b4da\n87 e23d\n8 a8e0\n28 e68\n71 1f8b\nd5 fb1b\nef fc5d\nb 8ae5\n8f 6add\n74 bdba\n29 86e1\n8b 4865\nca 5864\n2a aee6\na aae6\nd5 f11b\nb 80e5\n96 c196\n65 3c99\n19 81e1\n85 e813\n9f e955\n83 e80f\n9d e951\n81 e803\n9b e945\n89 6ae1\n80 e802\n9a e944\n71 3da9\n88 6ae0\n8 2ae0\n6c 34da\n96 e914\n92 e904\n8f e855\n8b e845\n88 e840\n90 c1a2\n87 e815\n31 2d21\n80 e800\n72 3d84\n29 2c61\na3 4e8d\n98 69e0\n97 633d\n18 29e0\n21 2c21\nae 4e56\n11 2921\n9e 4b56\n7c 9d58\nb1 cdab\naa 66e4\nf8 75c2\nc1 d803\ndb d945\nd9 d941\n35 8f1b\n4f 905d\na8 66e0\n8 22e0\nf6 75be\nd8 d940\n97 c915\n8e 4a56\n6c 9c58\n96 c914\n94 eb90\n79 1d61\nd3 d905\n93 eb8f\nad ecd1\n78 1d60\n8e c854\n76 b734\n65 1cb9\n8c c850\n8a c844\nc8 d840\n32 250e\n88 c840\nc7 d815\n87 c815\n86 c814\n85 c811\n84 c810\n83 ea8f\n9d ebd1\n68 1c60\n47 929d\n80 c800\n6 a36\n21 c21\nce d8de\nc7 7217\n95 eb91\n60 1c20\nea 7e46\n41 1821\n85 c019\n1 821\n40 1820\ne6 7e16\n84 c018\n0 820\nc9 5ae1\n67 bc9f\nc8 5ae0\n39 a54b\n27 ac9f\n88 4ae0\nec 54da\nbe ed76\n48 1ae0\n6c 14da\nf6 5dbe\ne2 7e06\n11 101\nc1 d023\ndb d165\n5a 134e\n8b 48e5\n1 1\n98 49e0\n5f 31dd\n45 309b\nd8 d160\nb 845\nfd ffdb\n33 8fa5\n88 48e0\nd7 533d\n58 19e0\n97 433d\n18 9e0\nf4 7dba\na9 46e1\ne8 56e0\n67 b49f\nc8 52e0\na8 46e0\naa 6ee6\n8a 6ae6\n71 3fa3\n8b 40e5\n38 dea\na9 44e1\n19 1e1\neb d4c7\nf8 d568\ne aad6\n94 e192\ne5 7cb1\nb4 471a\n38 a5e0\n4a 9ac6\n14 a918\n3a f6e\n1e a9d6\n66 96be\nc8 5842\n3e f5e\n39 a5e1\n3c a5d2\ne6 543e\n9e e1d4\n84 e092\n3f dff\nbe 475c\nd5 7bb1\na4 461a\n28 a4e0\n5a 99c6\n61 1c83\n7b 1dc5\n78 95c2\nfb 7de7\n28 a6ea\n8a 686e\n94 e19a\n38 a5e8\n96 e196\ne7 7cb5\nb6 471e\nf2 dfa4\n18 a1e0\na1 c689\n2b 6c7\n38 768\ncb 584d\n38 a5ea\n39 a5e9\n2e 8c7c\n14 8b3a\n3c a5da\n5b 13cd\n41 128b\n3d a5d9\n2a 8664\n73 9787\n3e a5d6\n97 69bd\ne8 5442\n29 a4e9\n1e 8b7c\n4 8a3a\n9e e1dc\n84 e09a\n28 a4e8\n36 f36\n2d a4d9\nf3 dfa5\n19 a1e1\n39 769\nf6 df96\n1c a1d2\n6d 3cf1\n53 3baf\n3c 75a\nc6 503e\nb a0e5\n2b 66d\n74 1790\na a0e4\n2a 66c\n73 178f\n9c e95a\n28 668\n71 178b\n78 95ca\nad 64db\n69 94c9\n58 91c2\n4e 9ad6\n73 b585\ncf 7add\n68 96e2\nca 5866\n85 6019\ncb 5865\n6c 96d2\nc5 5ab9\nce 5856\nb2 e506\n6d 1c59\n81 6009\n22 8c0c\n8 8aca\na 8ac6\nd9 53e1\na1 e6a9\n8e 4856\n85 4ab9\n82 e006\n6e 9c56\nc3 700d\na9 6ecb\n65 9eb9\n6c 9c52\nd9 d343\n5a 99e6\ncf 7adf\n4a 326e\n5e 99d6\n7c 95d2\ncc f2f2\ne6 f434\nd5 59b9\nde d1d4\nc4 d092\n19 89c9\n1a 89c6\naa c446\na1 c6a9\n9 88c9\n6e b6fe\na6 c416\n9e c156\n6d 3c59\n75 15bb\n9c c152\nda d146\n9a c146\n69 3c49\n71 15ab\nca d046\n2b ac67\n99 4b49\nc1 d2a9\n8a c046\n81 c2a9\n82 c006\n94 c190\n68 96ea\n6a 96e6\n6c 96da\nce 585e\n32 2d8e\n6d 96d9\ncf 585d\n6e 96d6\nc7 5abd\nd6 d196\n4e 92d6\nfa 7fec\ne0 7eaa\n8d ea7b\nf1 55a9\n7a 3fec\n60 3eaa\n8e 485e\n8f 485d\n87 4abd\n8b 484d\n38 85c8\n66 9e14\n78 95ea\n9b 43e7\n7a 95e6\n49 10e9\n82 c084\n19 163\n39 af61\n7c 95da\n7d 95d9\n7e 95d6\nce f2f6\nd7 59bd\n4d 10d9\nad 64fb\n69 94e9\n8b 42e7\n6d 94d9\n5e 91d6\n31 8fa3\n8f 60f7\n4b 90e5\n8d 60f3\n49 90e1\nfa f56e\n3d 85d9\n46 b01c\n2c aeda\nca 706e\nd d9\n8e e2f6\n97 49bd\n38 85ca\n66 9e16\n39 85c9\n67 9e15\n42 b00c\n28 aeca\nea f46e\n2d 84d9\n29 84c9\n28 84c8\n4a 32c6\nb2 ef84\n1c 81d2\n18 81c2\n8b 6ae5\n9d 41d9\nb1 e5a1\n98 69e8\nb5 e591\n9c 69d8\n89 4a63\na8 66e2\nb8 65e0\nf1 d581\nfa ffc4\ne0 fe82\ne9 f443\nd8 59c8\nff fd5f\n1b 8be7\ne8 56c2\nc9 5841\nf a8d5\ne1 dea1\n2f e5d\nc8 5840\ne a8d4\ne0 dea0\n2e e5c\nc4 5830\nb9 67c3\n75 97b1\n9d 69d9\n8a 4a64\nde d376\n89 68e9\n30 a502\n90 e982\nda 536e\na1 e4a1\n88 68e8\n5a 334e\n8b 68e5\n1 2001\n8d 68d9\nde 535e\n8c 68d8\nb8 65e2\nf8 55c2\naf 4eff\nc9 5041\nf a0d5\n2f 65d\ne1 d6a1\nb9 4f41\nae 4efe\nc8 5040\n8c e07a\n23 e85\nf5 df39\ne a0d4\ne0 d6a0\nb8 4f40\n3d 759\nad 4efb\nc7 503d\n97 e117\n21 e81\n53 3bad\n3c 758\ncb 52ef\ne5 5431\n16 8b16\n11 a383\n2b a4c5\n6 8a16\nd5 5331\nb a0c5\n2b 64d\nb5 4f31\nd4 5330\nf1 df29\na a0c4\nb4 4f30\n39 a5c1\n28 a4c0\nb2 eda4\na8 66ea\n90 eba8\ne7 5635\nf 8ad5\n4e 9ad4\n42 3086\ne 8ad4\n7b b7e7\ne9 56c9\nca 52c6\n94 6118\n4b 9867\nb 88c5\nb8 65ea\nba 65e6\nc3 d80d\nc 252\n96 4b36\n6f 96d5\n30 a78a\n4f 92d5\ne5 d61b\nff d75d\n66 9cbe\n6e 96d4\na9 64e9\n9e 4b7c\n84 4a3a\ncf 5855\nad 64d9\n8f 4855\n6c 96d0\nce 5854\n4c 92d0\ncb 5845\n8b 4845\nac 66d2\n68 96c0\nca 5844\nf9 55c9\nd8 51c2\n4e bad6\n35 8f93\n4f 90d5\n10 a18a\nf 80d5\nd4 d9b8\n37 53f\ncd 72f1\n81 c803\n9b c945\n8b c845\n52 938e\n6c 94d0\n99 c941\nf 805d\nc7 d89f\ncd 7279\n89 c841\n4a bac6\n2a aec6\na aac6\nb 80c5\na 80c4\n9 80c1\n38 85c0\n18 81c0\n8 80c0\nf1 7589\nf1 d5a1\nfa ffe4\ne0 fea2\n2e 2e5e\ne9 f463\n12 838c\nd8 59e8\ne8 56e2\ne9 56e1\nec 56d2\nc9 d841\n25 8e1b\n3f 8f5d\n9a e3ce\nb4 e510\n99 e3c3\nb3 e505\n8e ca56\n7f 1ffd\n65 1ebb\n98 e3c2\nb2 e504\n7e 1ffc\n64 1eba\n88 6ac0\nb1 6589\n5c b15a\n7a 1f4c\n60 1e0a\n92 e30e\nac e450\nb5 c591\nbe efd4\na4 ee92\nad e453\n9c 49d8\n58 b14a\nb1 c581\nba efc4\na0 ee82\na9 e443\n98 49c8\n8e e2fe\na8 e440\nac 46d2\n47 189d\na8 46c2\n43 188d\n8a e2ce\na4 e410\n89 e2c3\na3 e405\n50 b10a\n86 e2be\na0 e400\n60 bea8\n93 43a5\ne7 56bd\n8f 68d5\n67 16bd\nf1 5fa1\n66 16bc\nf0 5fa0\ne5 56b9\ne4 56b8\n64 16b8\ne3 56ad\n8b 68c5\n63 16ad\n62 16ac\n98 69c0\ne0 56a8\ne 20dc\n88 68c0\n34 ad1a\n83 e005\n55 9b13\n6f 9c55\n30 ad0a\n81 e001\n53 9b0f\n6d 9c51\n80 e000\n52 9b0e\n6c 9c50\n37 fbf\n51 1101\nba c5cc\na0 c48a\ne1 d4a1\n9f 4bff\nb9 4d41\ne1 d403\nfb d545\ndb 53ed\nc1 52ab\nea fe46\nb7 6fb5\n5e 39fc\ndd 7359\n44 38ba\ndf d3ff\nf9 d541\ne8 fe42\n9f c3ff\nb9 c541\na8 ee42\nc 22d0\n7f 375f\n96 6bb4\n89 c061\nd2 d184\nb1 cd8b\naa 66c4\ne9 54e1\n1a 8bc6\ne8 54e0\na8 66c0\n11 ab23\n2b ac65\n8 22c0\n7b 374f\n92 6ba4\nf6 759e\n94 c312\nae c454\nd2 d30e\nec d450\n59 1961\na5 c491\n8d e8d1\n58 1960\nf4 ff32\n37 8f9d\na1 c481\n38 560\nf0 ff22\n33 8f8d\n99 e343\n1a a9e6\n88 48c8\nce d2fe\ne8 d440\nb8 45c2\n8c c2d2\na6 c414\nca d2ce\ne4 d410\n8a c2ce\na4 c410\n88 c2c2\na2 c404\nc6 d2be\ne0 d400\n86 c2be\na0 c400\nf 20d5\ne1 56a1\n75 15b9\nf0 57a0\n61 bea9\n6a bc46\ne0 56a0\n81 c003\n9b c145\n8a ea46\n93 638f\nad 64d1\n96 4b16\nb 20c5\nc8 d06a\nfb 5567\nb9 65c1\na9 64c1\nb8 65c0\na8 64c0\nb4 cf12\nce d054\n74 bf12\n8e c054\n65 14b9\nb2 cf0e\ncc d050\n27 8e95\n72 bf0e\n8c c050\n71 bf03\n8b c045\nce fafc\n38 8d4a\naf ceff\nc9 d041\n6f beff\n89 c041\nae cefe\nc8 d040\n23 8e85\n6e befe\n88 c040\n6d bed3\n87 c015\n6c bed2\n86 c014\nab cecf\nc5 d011\n6b becf\n85 c011\naa cece\nc4 d010\n6a bece\n84 c010\na7 cebf\nc1 d001\n67 bebf\n81 c001\na6 cebe\nc0 d000\n66 bebe\n80 c000\ne8 56ea\n16 211e\ne9 56e9\n17 211d\n7f bfff\n99 c141\n88 ea42\ncf 5ad5\naa 6ccc\n90 6b8a\nec 56da\n7f b7f7\ned 56d9\nb1 ed8b\nee 56d6\ne ad4\ncd 5ad1\n8d 4ad1\n4d 1ad1\ncc 5ad0\n8c 4ad0\n4c 1ad0\nc ad0\nc9 52e1\ncb 5ac5\n91 e98b\nce 52d6\nc9 5ac1\n89 4ac1\nbf ed57\n49 1ac1\nc8 5ac0\n88 4ac0\n8 ac0\nf6 5d9e\nac 46da\nae 46d6\na8 46ca\n8e 42d6\n6c 94d8\nb 8867\na2 c486\ncf 58d5\nc5 d013\ndf d155\nce fa56\n8f 48d5\n4e 18d4\ncd 58d1\n61 3ea9\n6a 3c46\n8a ea44\nf9 fd63\n22 8c8c\n60 3ea8\nc1 d003\ndb d145\nca fa46\n8b 48c5\n4a 18c4\nd9 59c1\n99 49c1\nbf cfff\nd9 d141\nc8 fa42\n89 48c1\nee 76f6\n59 19c1\nbe 47f6\nd8 59c0\n98 49c0\ne dc\nbe cffe\nd8 d140\n88 48c0\nd7 531d\n58 19c0\nf8 55ea\nf9 55e9\nfa 55e6\nfc 55da\nfd 55d9\n6f 16d5\n30 278a\n4f 12d5\nfe 55d6\n2f 6d5\nf 2d5\ne9 54e9\ned 56d1\ncd 52d1\nad 46d1\n8d 42d1\n6d 16d1\n4d 12d1\nd7 5bb5\nb7 4fb5\nca 7266\nd 2d1\nd8 51e2\ncb 52c5\n78 1fca\n2a 6c4\na 2c4\nb1 4fa3\ncb 50e5\n78 1dea\nc6 7216\n20 c20\nc9 50e1\ne9 56c1\nc9 52c1\nc4 d810\nd3 5ba5\ne8 56c0\naf 6c77\n51 9b23\n6b 9c65\nc8 52c0\n8f 6877\n4b 9865\na8 46c0\n11 8b23\n2b 8c65\n88 42c0\nb 8865\n28 6c0\nb2 4fa4\n8 2c0\nf6 559e\nad 447b\nbd 45d9\nbe 45d6\nb8 45ca\nb9 45c9\nd0 d1a2\nc8 f840\n30 258a\n3c 8fd8\n35 f93\n4f 10d5\n10 218a\n1c 8bd8\nf d5\nd3 538f\ned 54d1\n93 438f\nad 44d1\n53 138f\n6d 14d1\nd2 538e\nec 54d0\nb1 4f83\ncb 50c5\n78 1dca\n71 3f83\n8b 40c5\n38 dca\n31 f83\n4b 10c5\n18 8bc8\nb c5\na c4\ne9 54c1\nc9 50c1\n89 40c1\n69 14c1\n49 10c1\nf8 55c0\ne8 54c0\nb8 45c0\na8 44c0\n78 15c0\n68 14c0\n38 5c0\ne8 deca\n62 3ea6\n18 948\nfa 7fe4\ne0 7ea2\n8d ea73\nf1 55a1\n7a 3fe4\n60 3ea2\nfe f7dc\ne4 f69a\ne7 549f\neb 546f\n46 12b4\n85 e0b9\n43 10a7\n62 bea4\n84 e0b8\n42 10a6\n61 bea1\n60 bea0\n4d 10f3\ne3 7ea5\ne9 dec9\n63 3ea5\n4b 10ef\ne1 7ea1\n23 8c85\n61 3ea1\ne5 f699\n4a 10ee\ne0 7ea0\n22 8c84\n60 3ea0\ne4 f698\ndb 516f\n36 fb4\n7a bfcc\n60 be8a\n7e 3fdc\n64 3e9a\n75 1599\nfa 7fcc\ne0 7e8a\n8d ea5b\nf1 5589\n38 7ca\nc7 509f\ncb 506f\n26 eb4\nca 506e\n64 3e98\n60 3e88\nb7 6597\n73 9585\n7a bfc4\n60 be82\n8a 42e4\nb0 e502\ne6 7e96\nc4 d098\nf7 5595\n40 18a0\nfe 7fd4\ne4 7e92\nf5 5591\nf6 55be\n7e 3fd4\n64 3e92\n75 1591\nfa 7fc4\ne0 7e82\n8d ea53\nf1 5581\n62 be84\n84 e098\n42 1086\na8 64e0\n61 be81\n60 be80\n4f 10df\ne5 7e91\n65 3e91\n4e 10de\ne4 7e90\n64 3e90\n4b 10cf\ne1 7e81\n61 3e81\n4a 10ce\ne0 7e80\n60 3e80\n9b 416f\n9a 416e\n9f 415f\ne8 7e6a\nfa 7f6c\ne0 7e2a\nf1 5529\n8c 68d0\n7a 3f6c\n60 3e2a\n71 1529\n8a 406e\n8f 405f\n69 be69\n49 106b\n68 be68\n97 e11d\n48 106a\n60 be28\n11 8b0b\n2b 8c4d\n69 3e69\n60 3e28\n76 159e\nf6 7d1c\ndc 7bda\n12 ba4\n4d b279\n63 14ad\n6a 3e66\n2b ecd\ne8 7e62\n21 8ea9\n2a 8c46\n68 3e62\n94 e390\n79 1561\n7a 3f64\n60 3e22\n71 1521\nfe f75c\n7f bdff\ne4 f61a\n6a be64\n8c e078\n4a 1066\n69 be61\n49 1063\n62 be24\n84 e038\n42 1026\nea 7e64\ne9 7e61\n11 8b03\n2b 8c45\n69 3e61\n6e bcfc\n54 bbba\ned f659\nb2 4f84\n60 3e20\n65 bcbb\n7f bdfd\ne4 f618\n8f e0df\nf0 7f20\n5a 116e\n4a 3a6e\nf4 7f10\n5e 115e\n6c be5a\n7d 9559\n9f 4357\n7a bf4c\n60 be0a\n93 4307\n43 10ad\nfd 5559\n6c 3e5a\n8b e2e7\n7d 1559\ne8 7e4a\nf9 5549\n68 3e4a\n79 1549\nfa 7f4c\ne0 7e0a\nf1 5509\n6c 94da\n7a 3f4c\n60 3e0a\n71 1509\nca 52ee\ne4 5430\n32 5ae\n6d be59\nd7 593f\ne3 7e0d\n4d 105b\n75 97bb\n68 be48\n48 104a\n70 97aa\n89 4ae1\ned 54db\n64 be18\n97 4315\nce 58fe\n60 be08\n36 59e\ne0 fc80\ned 7e59\nf5 57bb\nec 7e58\nf4 57ba\n6c 3e58\n74 17ba\n69 3e49\n71 17ab\ne8 7e48\nf0 57aa\n68 3e48\n70 17aa\ne4 7e18\n23 4ad\n6c be52\n6a be46\n5b 13ed\n41 12ab\n3c a5fa\n68 be42\n62 be06\n34 a5ba\n7a bf44\n60 be02\nd5 f91b\nb 88e5\n26 49e\nb0 4d82\ne9 7e49\nf1 57ab\n6e 3e56\n4c 9058\nec 7e52\ne2 fe2e\nfc ff70\n25 8e99\n6c 3e52\n6a 3e46\n3c 25fa\ne8 7e42\nf8 ff60\n21 8e89\ndf 53ff\nf9 5541\n14 a93a\nbd cd5b\n37 2d37\n34 a53a\nee defe\n14 a13a\ndf 7177\n9b 61e7\ne4 7690\n7e 977c\n64 963a\n1c a952\n8d e851\n34 85ba\n45 3013\n5f 3155\n62 9e06\n2c ac58\n3e 85fc\n24 84ba\n35 2f13\n4f 3055\n1c ab58\n14 81ba\n25 2c13\n3f 2d55\nc a858\n42 9a06\ne a87e\n1e 81fc\n4 80ba\n37 5b7\nca f2ec\n34 853a\nc2 f22c\n2c 847a\na aae4\nd4 d9b0\n37 537\n3e a7fc\nf7 7db7\n24 a6ba\n6f be55\n1c 8b5a\n4f 1057\n77 97b7\n6e be54\nbb e54f\n45 12b9\n4e 1056\n76 97b6\n6d be51\n75 97b3\n6c be50\n1d 837b\nd6 5936\n74 97b2\n68 be40\n48 1042\n70 97a2\n66 be14\ne9 7e69\n46 1016\n38 a5c8\n43 1805\n16 19e\n72 bf26\ne0 5e08\n3e 27fc\n24 26ba\n95 c911\n4a 38ec\nc9 7249\n1e 23fc\n4 22ba\n7f 35df\nf8 d562\n4b ba45\nf7 dfbd\n1d a1f9\n53 93a7\n3e 25fc\n24 24ba\n49 12c3\n63 1405\n29 ec3\n43 1005\n10 8b08\n15 a1bb\nf7 df3f\n1d a17b\n22 e2c\ne6 de3e\nc a07a\ndc f9f0\nc2 f8ae\n3f 2577\n6f 3e55\n1c b5a\nb9 e7c9\n77 17b7\n6e 3e54\nb8 e7c8\n76 17b6\ned 7e51\nf5 57b3\n6d 3e51\n75 17b3\nec 7e50\nf4 57b2\n6c 3e50\n74 17b2\n2e 6d4\n6b 3e45\n18 b4a\n3d 25f9\nb5 e7b9\n73 17a7\nea 7e44\nf2 57a6\nbc 65f8\n6a 3e44\nb4 e7b8\n72 17a6\n3c 25f8\na9 cee3\nc3 d025\n39 8741\nac 46d0\n15 8b33\n2f 8c75\ne9 7e41\nf1 57a3\n2c 6d0\nb6 4fb4\n69 3e41\n71 17a3\n67 3e15\n39 25c9\ne6 7e14\nb8 65c8\n66 3e14\n38 25c8\n8c 4a7a\nb1 6529\nc a7a\n31 2529\n1e b7c\n4 a3a\n29 24e9\n9a c966\n4f 9a7f\nab 4c4f\n9 aae9\naf e477\n9e 49fc\n84 48ba\nea feec\n54 913a\nb 8a6f\n6e 9cd4\n54 9b92\n8c 487a\n1c 97a\nc 87a\nbe 47fc\na4 46ba\nef d6df\n70 9d82\n9e 43fc\n84 42ba\ncf d2df\n50 9982\n7e 17fc\n64 16ba\n57 931d\n1c 29d2\n8d 68d1\nde 5356\n3e 7fc\n24 6ba\n1e 3fc\n4 2ba\nac 467a\n78 9d42\nbe 477c\na4 463a\nef d65f\n70 9d02\n8c 427a\n58 9942\nd6 db94\n2c 67a\ne6 deb6\nc a0f2\n1e 37c\n4 23a\na 2a6e\nf2 d584\nd6 d31c\n57 99bf\ndf d35d\nc5 d21b\n46 98be\n4e 987e\nb4 45ba\nac 6c58\nd4 7990\n8b 686d\nbe 45fc\na4 44ba\n9c 6b58\n94 41ba\n8c 6858\nc2 5a06\ne7 74b5\n9e 41fc\n84 40ba\n74 15ba\n6c 3c58\n90 e182\n7e 15fc\n64 14ba\nb7 47b5\n5e 11fc\n44 10ba\n97 43b5\n34 5ba\n2c 2c58\n14 1ba\nc 2858\ned d4d9\n67 34b5\nc7 5a3f\n1e 1fc\n4 ba\n92 c30c\n13 89af\na 886e\n90 c902\n6 801e\nde 737c\nc4 723a\n8c 407a\n90 cb08\na 2ae4\n24 ae38\nc6 583e\n7e 97fc\n64 96ba\n8f 4255\n1c a9d2\n3c f5a\nb2 c5a6\n81 40a9\n90 eb02\naa ec44\n67 96bf\n1f a9d7\nc9 5843\n3f f5f\nba c566\nd2 518c\n6f 967f\n1d b5b\n1f 21df\n98 c162\nb0 4d88\n4d 927b\n60 be20\n1b 83ed\n1 82ab\n3d a7d3\n9b 436d\n81 422b\n55 91bb\n4d b859\n5e 91fc\n44 90ba\n1c 95a\n55 3b13\n6f 3c55\nb9 e5c9\n77 15b7\ne2 fe2c\n4c 907a\n7f 1577\nd1 f321\n3b 856f\n11 81ab\n9 a849\n19 816b\nf7 553f\n52 1384\n1 2201\n8 88c8\n21 c83\n3b dc5\nd5 513b\n30 f80\nff 7df7\n2c a6fa\n1f 297f\n17 293f\n70 bdaa\ne3 562d\nc3 f0a5\n39 a7c1\nf9 f749\n7a bdec\n60 bcaa\n93 41a7\n29 a6c1\n8b 6845\n8 a860\n71 1f0b\n6 9e\n90 4982\n26 ae9c\nc4 fab8\n27 263f\n71 bda9\nb1 cf23\ncb d065\n6b 344f\n82 68a4\n9e 61fc\n40 90a8\n84 60ba\na9 4ec3\nc3 5005\n70 1d0a\n90 e902\n6 a01e\ndc f9f8\n3f 257f\nf0 7daa\nb9 67c1\n70 3daa\nc3 70a5\n39 27c1\nb0 c52a\n3 aa0d\n7a 3dec\nf9 7749\n60 3caa\n80 eaa8\n1d 1f3\nb3 6fa5\n2a ae6c\nb 6d\n54 1190\nd7 59bf\nc6 58be\n8f 42d5\nce 587e\n6c 96fa\n8a 486e\n57 193f\n4e 187e\n46 183e\nf 255\n1b 96f\n84 c890\n13 92f\na 86e\ne7 56bf\ndf 53fd\nc5 52bb\ne5 d413\nff d555\nee fe56\n4a 106e\ne0 7e20\n9c c1da\n16 21b6\n9b 43ed\n81 42ab\na1 c403\nbb c545\naa ee46\n71 3f89\n3 a0a7\nff 557f\ne6 54be\nee 547e\n9d 69f9\nd3 5ba7\nbb 456f\n16 3b4\naa 446e\n7f 157f\n77 153f\n92 e106\n7e 9d56\n4d 1859\nd3 710d\nb9 6fcb\n75 9fb9\n66 143e\n91 c323\nab c465\n58 916a\neb 7eed\n55 113b\n97 4335\nd9 d9e9\n3c 570\n22 42e\n19 16b\n39 af69\n11 12b\n31 af29\nf1 7da9\nf0 7da8\nb 2867\n70 3da8\nf a05f\na3 c487\nb0 c528\n60 3ca8\ne3 54a7\n88 484a\nc1 7a03\ndb 7b45\nf1 f703\ne0 5c88\n72 bda6\n41 38a9\nef f6ff\n70 bda2\ne3 5625\n8f 42d7\n9c 4378\n74 9f10\n68 34c2\nf1 7703\nf8 ddca\n72 3da6\n8 a2c0\nf0 7da2\n66 34be\n88 e84a\nf4 f59a\n6 a2bc\n64 9e10\nb2 c526\n81 4029\n6b b64f\n82 eaa4\nfa 7de4\ne0 7ca2\n3 aa05\n69 b64b\n80 eaa0\nfe f5dc\ne4 f49a\n73 bda5\nf1 f701\n72 bda4\n71 bda1\n60 bca0\n69 34c1\nf9 ddc9\n73 3da5\n40 b8a8\n8e e856\nf1 7da1\n67 34bd\n90 c1aa\ne3 f4a5\nf4 f598\nf a057\nea fc46\ne1 fea9\ne5 fe99\n29 86c1\n74 bd9a\nc3 f085\n7e bddc\n64 bc9a\n97 4197\ne7 fe95\nc7 7095\nb a6f\n2 2aac\nc3 7085\nfa 7dcc\ne0 7c8a\n7a 3dcc\n60 3c8a\n80 ea88\n1d 1d3\nb3 6f85\n75 bd99\n74 bd98\n71 bd89\n70 bd88\n65 bc99\n64 bc98\n97 4195\n61 bc89\n75 3d99\n62 1e24\n5d b173\n74 3d98\n5c b172\n71 3d89\n59 b163\n70 3d88\n58 b162\n31 d21\nbe ef5c\na4 ee1a\nd6 7316\nd9 51e1\na 88c6\n64 3c98\n4c b072\ne0 7c88\n3e a77e\n23 aea7\nd2 7306\n60 3c88\n48 b062\n99 6b49\nca f046\nc1 f2a9\nda 516e\na3 44a7\n48 384a\n2d 84f3\nc3 f2a5\n2a 64c\n41 3aa1\nc5 f299\n76 bd96\n45 3899\n66 96bc\naa 66ce\nef f6df\n70 bd82\ne3 5605\n66 bc96\n1d ab73\nf1 7d89\n76 3d96\n39 a7cb\nf2 7d86\n72 3d86\nef 76df\n70 3d82\nb6 c516\n17 a137\n85 4019\ne1 7c89\n3f a77f\n66 3c96\n1d 2b73\n99 6b63\nb2 c506\n62 3c86\n82 ea84\n19 2b63\nfa 7dc4\ne0 7c82\nd 227b\n77 bd95\nab 66cd\n76 bd94\n13 ab2f\n2d ac71\naa 66cc\n72 bd84\n29 ac61\na6 66bc\n71 bd81\na5 66b9\n66 bc94\n76 3d94\nf5 7d91\n75 3d91\n74 3d90\n73 3d85\n40 b888\nf1 7d81\ne0 d420\n67 349d\n71 3d81\n63 9ca7\ne2 d604\ne7 7c95\nbf e557\n49 12c1\n94 499a\ne5 7c91\ne4 7c90\n9a c3ce\nb4 c510\n64 3c90\ne3 7c85\ne1 7c81\ne0 7c80\naa ec46\na1 eea9\n83 40a7\nd 80f3\na3 eea5\n2f 2ed7\n3c 2f78\n6e bcdc\n54 bb9a\n78 bd6a\nb1 ef23\ncb f065\n7a bd6c\n60 bc2a\n93 4127\n29 a641\n32 a70e\nf8 7d6a\n68 3c6a\n88 ea68\na1 6e23\nbb 6f65\nf9 55c1\n52 bb86\n79 bd69\n9b 6b67\n63 bc87\n70 bd28\n8 2242\n92 6b26\n8b 6a67\n5b bbc7\n68 bc68\n8a 6a66\n53 bb87\n60 bc28\n79 3fe3\n93 4125\n82 6a26\ne3 7c87\n2a a6cc\nf0 7d28\n63 3c87\n70 3d28\nd3 7b87\ne0 7c28\n49 3869\na3 e407\n92 498c\n78 bd62\nb0 c5a8\nd2 73a6\n41 3829\n93 e307\n82 488c\n68 bc62\na0 c4a8\n78 3d48\nc2 72a6\n33 8dad\n7a 3d66\n78 3d62\nc7 7837\ne a27c\n68 3442\n9 a8e3\n88 e240\n66 343e\n68 3c62\n88 ea60\n2a a6e4\n58 3342\n28 a6e0\n8a 6864\n61 bc23\n7b bd65\n60 bc22\n7a bd64\n78 bd60\n37 859f\n58 bbe2\n72 bd24\n51 bb23\n6b bc65\n18 896a\ne8 76c0\n14 93a\n50 bb22\n6a bc64\ne7 76bf\n68 bc60\nfe f576\n27 849f\nff 77fd\ne5 76bb\n60 bc20\nb7 459f\n54 333a\n6e 347c\ndf f35d\nc5 f21b\n46 b8be\n51 3b23\n6b 3c65\n18 96a\n69 3c61\nb2 4d84\ne0 7c20\n82 e804\n7c bd5a\nb5 ef13\ncf f055\nb1 ef03\ncb f045\n7a bd4c\n60 bc0a\n93 4107\neb 56e5\na 2a6c\nfa 7d4c\ne0 7c0a\n87 c23d\n8 88e0\n7a 3d4c\n60 3c0a\n80 ea08\n7d bd59\n3c 85d8\n79 bd49\n75 bd19\n74 bd18\n70 bd08\n65 bc19\n46 309c\nd9 d161\n64 bc18\n7d 3fd3\n97 4115\n7d 3d59\nc7 72b7\n79 3d49\na1 c4a9\nc3 72a7\n70 3d08\nf5 55bb\n8 ae0\nec 7c58\nf4 55ba\n2f ae77\n5e 9b7c\n44 9a3a\n92 c9ae\n8b 62e7\nb9 ef49\n77 1f37\n69 b4e9\ne8 7c48\nf0 55aa\n2b ae67\n65 3c19\n96 c116\ne0 7c08\n7e bd56\n4d 3859\n55 11bb\n7c bd52\n7a bd46\n49 3849\n51 11ab\n78 bd42\nb0 c588\n90 c92a\n89 6263\n72 bd06\n41 3809\n4a 1ae4\nef f65f\n70 bd02\na0 c488\n62 bc06\nbc 45d8\nf9 7d49\n7e 3d56\n7c 3d52\nfa 7d46\nb8 45c8\nf8 7d42\n31 8d89\na 2a64\n39 a74b\nf2 7d06\ne9 7c49\nf1 55ab\n6e 3c56\n8e ea54\n6c 3c52\n8c ea50\n3a a5e6\na8 44c8\n28 a6c0\n92 41a6\n8a 6844\n96 693c\n34 a7b8\n1e a97e\n5 a83b\n1f a97d\n9c 63d8\nf a87d\n8c 62d8\n97 693d\nc3 788d\n3e a556\n35 a7b9\n87 683d\n25 a6b9\n1d 9db\n2e a456\n86 683c\nf7 7db5\n24 a6b8\n86 4a36\n58 31ea\n15 a939\n4b 9ae7\n5 a839\n4a 9ae6\n4 a838\n36 a5bc\n43 122d\n59 bbc3\n73 bd05\n14 a1b8\n42 ba04\n22 c06\ncf f25f\n50 b902\n6b 96e7\n4 a038\n3e a57e\n27 a4bd\nd1 5329\nb1 eda1\n26 a4bc\nb0 eda0\n65 3e11\nb9 ed61\n77 1fb5\n4 8ab8\n37 fb5\n15 a1b9\n43 ba05\nf6 df3e\n1c a17a\n3b f6d\n21 e2b\nf7 df3d\n1d a179\n5 a0b9\n13 b07\n4 a0b8\nbd c5d9\n37 25b5\n12 b06\ne7 de3d\nd a079\n43 9227\n6 803c\ne8 7c4a\n92 c9ac\n8b 62e5\n77 1f35\n52 312c\n38 2fea\n15 89b9\n5 88b9\n6a b6ee\na8 cee2\nc2 d024\n38 8740\n58 11ea\n15 8939\n5 8839\n4 8838\n1d bf3\n37 d35\n4e 9a7e\n4f 9a7d\n60 3ca0\ne4 f498\nda 5166\n3 8aad\n9b cbc7\na8 cc68\n6c 9678\n4c 9278\ncf 58df\na 8a6e\nb 8a6d\n54 9b90\n91 c309\nb 22e5\n12 89ac\n35 85b9\na7 6e17\n63 9e05\n3e affc\n24 aeba\n25 84b9\ne7 d41d\ncd d2db\n47 32b7\n15 81b9\n87 6a17\n43 9a05\n1e abfc\n4 aaba\nd7 d11d\nbd cfdb\n37 2fb7\n5 80b9\nc7 d01d\nad cedb\n27 2eb7\nf dd\ncc 7072\n71 bd29\n93 6b27\n34 85b8\na6 6e16\n62 9e04\n61 bc29\n83 6a27\n24 84b8\ne6 d41c\ncc d2da\n46 32b6\nd6 593c\n4c 1058\n87 4237\n2c ae7a\n3e af7c\n24 ae3a\n76 959c\n1d 8179\n3f 2f77\n86 4236\n2f 84d7\n3c 8578\n21 8ca1\nf 80d7\n1c 8178\n3e 2f76\n4 8038\n1d 3f3\n37 535\n92 492c\n8 48\n30 87a8\n65 bc13\n7f bd55\n5f 3bfd\n45 3abb\ne7 f41d\ncd f2db\n3 82a5\nd5 d31b\n4f 32f7\nef d45d\n56 99be\n84 6ab8\n4 2ab8\n45 983b\n5f 997d\n61 bc03\n7b bd45\n5b 3bed\n41 3aab\n8b 68cf\n47 98bd\n60 bc02\n7a bd44\n5a 3bec\n40 3aaa\n51 11a9\nae 667c\n4f 987d\n5e b3f6\ncc 52d8\n4e 987c\n5f bbff\n79 bd41\na7 cc9f\nad 6679\n5d bbd3\n77 bd15\n5c bbd2\n76 bd14\n2b 2cef\naa 664c\n91 c30b\nb 22e7\nab c44d\n12 89ae\n55 bb13\n6f bc55\n77 95b7\n1c 895a\n13 89ad\n8c 6a78\n84 6a38\n4 2a38\nb 886d\na 886c\n59 3369\n53 998f\nff 77dd\ne5 769b\n4d bad3\n67 bc15\n4c bad2\n66 bc14\n4b bacf\n65 bc11\n4a bace\n64 bc10\nd6 593e\ne2 7e0c\n4c 105a\n6c be58\n74 97ba\nce f276\nd7 593d\n4d 1059\n7e 9556\nb9 67cb\n75 97b9\n14 29b8\n4 28b8\nc7 583d\n3d f59\nc6 583c\n3c f58\n3a 8546\n9 49\n31 87a9\n83 482d\n2a 8446\n21 86a9\n82 482c\nf3 5da5\n20 86a8\nb2 c5a4\n6e 967e\nf4 d592\n6 82b4\n60 bc08\n79 3fc3\n93 4105\n82 6a06\nf6 fdb6\n25 26b9\ncb f8cf\n2e 2456\n84 62b8\ncf f2dd\n50 b980\n24 26b8\n64 943a\n7e 957c\na7 6cb7\n63 9ca5\n4c 927a\n23 86ad\n85 4831\n2d 6d1\nea 7666\na6 cc9e\nac 6678\n5e bbfe\n78 bd40\na4 6638\nef f65d\n56 bbbe\n70 bd00\n72 15ae\n86 c89e\n8c 6278\n58 b940\n84 6238\ncf f25d\n50 b900\n24 2638\n8 aac0\n4 2238\n32 85ac\n35 25b9\n63 3e05\n25 24b9\n1a b4c\n0 a0a\n15 21b9\n43 3a05\nf0 5522\n5 20b9\n34 25b8\n62 3e04\n24 24b8\n33 8f85\n84 6038\n65 3c13\n7f 3d55\n54 91ba\n4c b858\n4 8a38\n37 f35\ne3 7c0f\nfd 7d51\n63 3c0f\n7d 3d51\nef f675\nc4 5ab8\naf e675\n84 4ab8\ne2 7c0e\nfc 7d50\n57 3b95\n62 3c0e\n7c 3d50\n7e 957e\n65 943b\n7f 957d\nc4 f818\n5d 3bd3\n77 3d15\n44 b818\n5c 3bd2\n76 3d14\n5e bbfc\n44 baba\nf2 ff2c\n5c 917a\n4c ba7a\n89 60cb\n45 90b9\n1d 959\n58 3bc2\n72 3d04\nd3 7b0f\ned 7c51\nf5 55b3\ncc 5a78\nd2 51ae\n68 b6c8\nc4 5a38\n8c 4a78\n92 41ae\ne1 7c83\nfb 7dc5\n28 a6c8\n8a 684c\n84 4a38\n52 11ae\n44 1a38\n12 1ae\na 284c\n4 a38\n51 3b03\n6b 3c45\n18 94a\nb5 e5b9\n73 15a7\n92 438e\nac 44d0\nf1 55a3\ncd 7ad3\ne7 7c15\n4d 3ad3\n67 3c15\n14 91a\ncb 7acf\ne5 7c11\n4b 3acf\n65 3c11\na7 449f\nca 7ace\ne4 7c10\n4a 3ace\n64 3c10\n8c e2f2\na6 e434\n95 49b9\nd2 f384\n3c 85d2\nba 47ee\na5 e433\nbf e575\n94 49b8\n95 e333\naf e475\n84 48b8\nb7 e517\n41 1281\nd4 513a\n1d 979\nb2 452e\n5 2a11\n84 4838\n4 838\n97 41bd\n87 6abd\na4 46b8\nf1 d5ab\n8f 6a7d\n61 3e29\n24 6b8\n4d b253\n96 69bc\nf5 7db3\n3c a7f8\na2 ec8c\nec 5678\ne4 5638\n82 e88c\ncc 5278\nc4 5238\nac 4678\nf5 579b\na4 4638\n8c 4278\n22 ac8c\n6c 1678\n64 1638\n44 b0b0\n44 1238\na3 e60d\n24 acb0\n24 638\n4 a0b0\nc 278\n8 8ac0\n4 238\na5 44b9\nb4 ef12\nce f054\n85 40b9\n94 eb12\nae ec54\n91 e181\n55 11b9\n5e 3bfc\n44 3aba\n45 10b9\n76 95b6\n54 bb12\n6e bc54\n35 5b9\n63 1e05\n3e 2ffc\n24 2eba\n15 1b9\n1e 2bfc\n4 2aba\n43 1a05\nb4 45b8\n6 2a3e\n17 13d\na4 44b8\n16 293c\n2c 2e7a\n1d 179\nc 2a7a\n19 89c1\n4d 32f9\n15 139\n1e 2b7c\n4 2a3a\nc7 5097\nd4 5138\n77 3f97\n84 4038\nf6 7db4\n3d a7f9\n87 68bd\nd8 5342\n2e a4d6\n86 68bc\n8e 687c\ne5 7cb3\nff 7df5\n2c a6f8\nd7 799f\n1e 297e\nf 287d\n62 96ac\na6 66be\n72 bd86\n41 3889\na7 66bd\n73 bd85\nb6 65bc\n72 370e\na3 6ca5\n19 23c1\nc3 5205\ncf f2df\n50 b982\n70 1f0a\n85 62b9\n51 b981\n58 b942\n94 61b8\n31 8f83\n8f 60d7\n4b 90c5\n9c 6178\n6a 344c\n50 330a\n81 68a1\nd2 5326\n8a 60ce\n46 90bc\ne5 fc39\n2e 267e\nb8 6f62\ndd fbf9\n26 263e\na9 ccc9\n23 2ca5\n1e 237c\n4 223a\n25 8e11\nd0 7308\n51 39ab\na7 64bd\na6 64bc\n5a 39ec\nd9 7349\n40 38aa\nf7 759f\nc2 d0a4\n38 87c0\nc2 7a06\nb9 4fc3\nd3 5105\n3 8a0d\n6a 344e\n9b 69e5\n81 68a3\nba cf6c\na0 ce2a\nd3 5327\n85 60b9\n84 60b8\n70 1d08\n3e 257e\n25 243b\n3f 257d\n3a 8f6c\n20 8e2a\n95 e339\n53 1327\nc3 582d\n15 29b9\n6a 9446\n39 f49\n9 a8c1\n29 e49\n5 28b9\n5a 9346\n5 2039\n87 e21d\n8 a8c0\n28 e48\n4 2038\ne7 f437\nd6 59bc\n4c 10d8\n7c 97f8\nf0 5d22\n2a 86c6\n8a 4a6e\na3 e427\n92 49ac\n8 c8\n99 c3e3\nb3 c525\na2 ee26\nf1 5da3\n38 87e8\n4e 1a7e\n59 33eb\n73 352d\n46 1a3e\n56 193c\na a6e\nf0 7d88\nb 2847\na1 4e89\n33 afa7\nb a6d\n54 1b90\nf9 5d4b\n3 a2d\nf1 5d0b\n12 92c\nfa f7c4\ne0 f682\nd6 59be\ne2 7e8c\n4c 10da\nc5 583b\ndf 597d\n7d 97f9\nc7 58bd\n6e 94d6\nfa 7f6e\n3d fd9\nd7 f337\nc6 58bc\n3c fd8\ncf 587d\n3a 85c6\n9 c9\nf2 5da4\n39 87e9\n2a 84c6\ne8 76e2\n93 e327\n82 48ac\n89 c2e3\na3 c425\n50 912a\n8a 486c\nd3 598f\n56 193e\nbc 4772\n4f 187d\n46 183c\n1a 96e\n12 92e\n78 3762\nb 86d\n58 b36a\n54 1990\n3 82d\n68 3662\nf6 55bc\nc5 52b9\nee fe54\n82 e88e\n9c e9d0\ncc 527a\n83 e88d\ncd 5279\na3 46ad\nab 466d\nf4 5790\n38 adea\nb2 45ac\n15 3b1\n1d 2bf9\nf8 5f62\n63 1ca5\n8c 42f8\n23 62d\nfe 557e\ne7 54bd\ne6 54bc\ne 885e\ncc 7a7a\nd0 d900\nb2 45ae\n5 2a91\nba 456e\nd 2a51\naa 6e6e\nda 7bec\nc0 7aaa\nd1 51a9\na3 44ad\na2 44ac\n7e 157e\n76 153e\n5d 13fb\n77 153d\n93 e105\n65 9c13\n7f 9d55\n4d 12fb\n67 143d\n76 bd9e\n2b 86c5\nf1 5d21\n92 e104\n4c 3a7a\n17 239f\n90 c322\naa c464\nea 7eec\n54 113a\n8d 42f3\na7 4435\n8d 42f9\n55 1139\n5e 3b7c\n44 3a3a\n2a 2e6e\nc8 72e0\n32 52e\n74 1590\n9 2eb\n23 42d\nfa 77ec\ne0 76aa\n3c 85d0\n22 848e\n5a 33ec\n40 32aa\ne1 76a9\nea 7446\n61 36a9\n6a 3446\nb a8e7\n8a e244\nd6 519e\n8d 407b\nd3 518d\nb4 4538\na7 4497\nc7 f295\nce 7a5e\nd4 7330\n3e 57e\nd2 732c\n3c 57a\nbe c5dc\na4 c49a\nda 73e4\nc0 72a2\n2 8086\n3 8885\n2 8884\nb3 ef0d\n1d 815b\nc6 509e\nc7 509d\n92 e1a4\ndc 51d0\nc2 508e\nc3 508d\n8 aa48\n21 2e03\n3b 2f45\n62 b6a4\n61 b6a1\nc3 7825\n39 2f41\n60 b6a0\n38 2f40\n23 4a7\n19 2b41\n18 2b40\n5e bbdc\n44 ba9a\n55 9199\n2e 2e74\n5a bbcc\n40 ba8a\n2a 2e64\ne3 76a5\n88 6a48\n97 e33d\n18 a9e0\na1 ce89\n2b ec7\n38 f68\ne9 d6c9\n63 36a5\n8 2a48\nfa d5c4\ne0 d482\ne2 76a4\ne1 76a1\n23 8485\n61 36a1\ne0 76a0\n22 8484\n60 36a0\n2d 4f3\nc3 72a5\nda d1c4\nc0 d082\n2b 4ef\nc1 72a1\n3 8085\n41 32a1\n2 8084\n40 32a0\nde 7bdc\nc4 7a9a\nd5 5199\n5e 3bdc\n44 3a9a\n55 1199\nd3 f325\n3d 8573\nda 7bcc\nc0 7a8a\nd1 5189\n7a b7cc\n60 b68a\n49 1861\n5a b3cc\n40 b28a\n31 8d2b\n2a 2664\n6 a09c\n45 ba99\n5e 33dc\n44 329a\nc4 7a98\n44 3a98\n2c ae72\nc0 7a88\na8 ee62\n40 3a88\n39 8561\n28 ae62\n69 bc69\n42 ba86\ncd d851\n5a bbc4\n40 ba82\n41 3289\nd5 5191\n5e 3bd4\n44 3a92\n55 1191\nd1 5181\n96 419e\n93 418d\n19 29c1\n61 16a9\nb 88e7\n8a c244\n62 b686\n7a b7c4\n60 b682\n9e 415e\nb3 cf0f\ncd d051\n5a b3c4\n40 b282\nd2 fb0e\nec fc50\n46 ba94\n42 ba84\n22 c86\n40 ba80\ne6 7696\n40 10a0\ne1 7689\n66 3696\nfe 77d4\ne4 7692\n7e 37d4\n64 3692\ne2 7686\nfa 77c4\ne0 7682\n9a 414e\na4 c498\n20 ca0\nc1 7289\nfb f56f\n24 8498\n66 94be\nf0 dda2\nde 73d4\nc4 7292\n5e 33d4\n44 3292\n2f cdf\nc5 7a91\n45 3a91\nf2 55ae\n2e cde\nc4 7a90\n44 3a90\n43 3a85\nf0 55a2\n42 3a84\n2b ccf\nc1 7a81\n41 3a81\n2a cce\nc0 7a80\n40 3a80\n86 409e\n3d 2f7b\n87 409d\n83 408d\n4a 92e6\n8e 405e\n42 b284\n22 486\n40 b280\n3a 5c4\n20 482\nf0 d5a8\n75 3f1b\n8f 405d\ne6 7694\ne5 7691\n65 3691\nca 52e6\ne3 7685\n13 892f\nc2 582c\nbe 67fc\na4 66ba\n60 96a8\n38 f48\n8c e2f8\n4a 12e6\n63 3685\nc1 d021\ne2 7684\ne1 7681\nc0 5828\n61 3681\ne0 7680\n49 bae3\n63 bc25\n47 3295\nd2 730e\nec 7450\nf2 dda4\nc6 7294\nab 64cf\n67 94bd\n25 c1b\n3f d5d\n2f 4df\nc5 7291\n45 3291\nf0 dda0\n47 b835\n43 3285\nce 72fe\ne8 7440\n41 3281\n40 3280\n71 3f0b\n8b 404d\nda 7b6c\nc0 7a2a\nd1 5129\n5a 3b6c\n40 3a2a\n51 1129\n76 159c\n48 ba68\n28 c6a\nd7 59b5\n4 82b8\n37 7b5\n41 ba29\n40 ba28\nfc ddd2\ne8 766a\n4c 18d0\n14 8932\nb 884d\n49 3a69\na 884c\n84 42b8\n71 1d09\n68 b642\n3 880d\n4 2b8\n41 3a29\n2 880c\n40 3a28\nb2 e5a6\n81 60a9\n67 149d\nf1 5d81\n66 149c\nf0 5d80\n1d 379\n63 148d\n62 148c\n19 369\n68 b668\n61 b629\n60 b628\n49 b269\n72 158e\nd3 d985\n29 46b\nda d9c4\nc0 d882\n36 8f9e\nb5 ef11\n1f 815f\n41 b229\n3b 56d\n21 42b\n40 b228\n3a 56c\n20 42a\ne3 dc8f\nfd ddd1\ne9 7669\n63 9c8f\n7d 9dd1\n69 3669\nfc ddd0\ne2 dc8e\ne8 7668\nb 804d\n15 8931\n3 800d\n2 800c\n54 9990\n40 3228\n8b c24d\nc 88f0\nfe 7fdc\ne4 7e9a\nf5 5599\n4a 3a66\n48 3a62\n59 1161\n51 1121\n52 118c\n9 69\n3a 8566\n4e 3a5e\n6a 3666\n5 831\n22 8406\n13 238f\n2d 24d1\nb7 6db5\n42 ba24\n22 c26\nfe 77dc\ne4 769a\n4a 3266\n48 3262\n2 8006\nb4 e512\ncb 7a65\nc8 d842\n3e 8f5e\nca 7a64\n3a 7ce\nf1 dd89\nc9 7a61\nb 8845\n49 3a61\n92 4b84\n2 8804\n40 3a20\n92 e1a6\nb2 472e\nfd d753\n7e 9df6\n47 109d\nd1 5981\n43 108d\n2b 8467\n42 108c\n2a 8466\n68 b660\n48 3a68\nac ecfa\n61 b621\n60 b620\n4a b264\n2a 466\n8e e8fe\n43 b225\n42 b224\nd9 d9e1\n22 426\n8c e8fa\n41 b221\n40 b220\n3a 564\n20 422\nf2 dd2c\nd8 dbea\neb 7665\ne8 d442\ne7 de9d\nd a0d9\nf1 dd2b\nea 7664\n48 ba4a\n11 8303\n2b 8445\nb2 4784\n5e bb5c\n44 ba1a\n55 9119\n5a bb4c\n40 ba0a\ne8 dce8\ne1 7621\nd9 d34b\n53 3327\n5a 99ee\ne0 7620\n60 3620\n4f 98d5\nf 82d5\nd5 5931\nd2 d92c\ncb 7265\ne5 7699\nb4 65b2\n70 95a0\nc8 d042\n55 bbb3\n6f bcf5\n3e 875e\nd1 d92b\nca 7264\nf1 d589\nfa ffcc\ne0 fe8a\nc3 d887\nd0 d928\nc9 7261\nb 8045\n92 4384\n3 8005\n48 98e8\n8c 68fa\n41 3221\n2a 46e\nc0 7220\n2 8004\n40 3220\ndd 5159\n4c 3a5a\n5d 1159\na7 46b7\nc8 7a4a\nd9 5149\n48 3a4a\na3 46a7\n59 1149\n68 3e68\nde 7b5c\nc4 7a1a\nd5 5119\n5e 3b5c\n44 3a1a\n55 1119\nd6 f336\nc5 58bb\ndf 59fd\nda 7b4c\nc0 7a0a\nd1 5109\n5a 3b4c\n40 3a0a\n51 1109\n36 59c\n32 58c\n9d ebf1\n83 eaaf\n6c b65a\n4f 38df\nc8 d862\n99 ebe1\n68 b64a\n7e b75c\n95 ebb1\n64 b61a\n4c b25a\n5e b35c\n44 b21a\n5a b34c\n40 b20a\n48 ba48\n28 c4a\na3 c6a5\n90 e900\n6 a01c\nc2 5226\nd5 719b\n45 ba19\n44 ba18\n3e d5c\n24 c1a\n40 ba08\nc4 f298\nf7 7795\ne8 764a\nb4 cd12\n7f 3dff\nfe 775c\ne4 761a\n7b 3def\nfa 774c\ne0 760a\n4c 325a\nc8 724a\n94 c912\n48 324a\n14 8912\nb1 478b\n5f 39ff\nde 735c\nc4 721a\n5e 335c\n44 321a\ne8 7c40\nce 7afe\n5b 39ef\nda 734c\nc0 720a\n5e bbd4\n44 ba92\n55 9191\n5a 334c\n40 320a\nc 88d2\ne4 7c30\nca 7aee\nc1 50ab\ndb 51ed\ncd 7a59\ncc 7a58\nc8 7a48\nc5 7a19\nc4 7a18\n44 3a18\nc0 7a08\n27 a6bf\nb1 4d81\n4f 38dd\nc8 d860\n32 252e\n68 b648\nd2 512e\n96 e3be\nb0 e500\ne6 7e94\n65 b619\n64 b618\n61 b609\n47 389d\nc0 d820\n60 b608\n4c b258\n48 b248\nb2 4d2e\n44 b218\n40 b208\naa 4cee\n4c ba52\n4a ba46\nf6 dfbe\n1c a1fa\n3b fed\n21 eab\n48 ba42\n42 ba06\n14 a1ba\n69 3ceb\ne8 7648\n9a cbce\nb4 cd10\n66 3cbc\ne5 7619\n65 3cbb\n7f 3dfd\ne4 7618\n7b 3ded\n61 3cab\ne0 7608\n4d 38fb\ncc 7258\n4c 3258\n49 38eb\nc8 7248\n94 c910\n48 3248\n14 8910\n45 38bb\n5f 39fd\nc4 7218\n45 ba91\n41 3209\n35 d3b\ncb 7aed\n41 38ab\n5b 39ed\nc0 7208\n44 ba90\n40 3208\nc 88d0\nce 7a56\nde fb74\nc4 fa32\n7 8a9d\nc9 7a49\n4e 3a56\n2c 8c58\ncc 7a52\nc2 fa2e\ndc fb70\n5 8a99\n4c 3a52\nca 7a46\n4a 3a46\n28 8c48\nc8 7a42\nd8 fb60\n1 8a89\nbf 4fff\nd9 5141\n48 3a42\n3f fff\n59 1141\n3b fcf\n55 1111\n16 19c\n60 3680\n35 a5b3\nc3 d825\n39 8f41\n65 b439\n89 e869\ne a876\n81 e829\n6 a836\n1c a972\n14 a932\n35 a5bb\n3d a57b\n42 122c\nee fc5c\nd4 fb1a\na 8ae4\n6e 94de\n2c a47a\na1 e4a9\n81 e0a9\n1f 355\n5 213\n8f 4af7\nb1 6d0b\n6d 9cf9\n74 3f98\n6 a0b6\ndd dbf9\n26 63e\nb1 e529\nd0 7980\na1 e429\n91 e129\n81 e029\n8f 4a77\n74 3f18\n6 a036\n34 a532\nee def6\n14 a132\nc7 7a3d\n7 881d\nc5 7a39\n5 8811\n2d c7b\nc3 7a2d\n5 283b\n1f 297d\n6d 9673\nb8 e5c8\n76 15b6\n46 9a36\n18 81ea\n51 b3a3\n6b b4e5\ne 8a76\n9 8a6b\nb 8a67\n52 3b0e\n6c 3c50\n74 15b2\n69 b4e1\nc 8a72\n31 a521\n29 8663\n72 9786\n3c a5d8\nb7 e51f\n41 1289\n85 c213\n9f c355\n6 88b6\n3b 7ed\n21 6ab\n1a 36e\nd1 d929\n56 9936\n8a 426e\n46 9836\n5e 13fc\n44 12ba\n99 c969\n1e 8976\n91 c929\n16 8936\n89 c869\ne 8876\n81 c829\n77 15b5\n1c 958\n6 8836\n54 9932\n88 426a\n4c 9872\ne0 7e88\nc 8872\nb4 6598\nd0 d1a0\n3 885\nd6 d314\n57 99b7\n5f 9977\nc5 d213\ndf d355\n46 98b6\nc9 d869\n4e 9876\n75 95b3\n53 bb0f\n6d bc51\ne aad4\n7d 9573\n9e 4bdc\naf e657\n84 4a9a\n90 c308\na 22e4\n11 89ab\n65 b43b\n7f b57d\n92 c304\n13 89a7\n1b 8967\n2e 8676\n46 129c\nd0 5b80\n26 8636\ne 8276\nfd 7f73\n26 e9c\n31 85a3\na6 669c\nab ecc7\nb8 ed68\nb5 4d13\n13 abad\n2c 8672\n44 1298\nc 8272\nfb 7f6f\n24 e98\nf 82df\nd5 593b\n26 84b6\n81 c0a9\n59 3949\na3 6ea7\n49 ba69\n72 1d8e\nde 597c\nc4 583a\n1a a9ce\n42 ba0e\n5c bb50\ncc f858\n14 81b2\nc a850\nd5 7b13\nef 7c55\nf7 55b7\n54 11ba\na7 44b5\n4c 3858\n91 492b\nc1 d029\n46 9036\n15 b39\n92 c386\nb9 c569\na8 ee6a\nd 79\n56 119c\nb1 c529\nba ef6c\na0 ee2a\n36 8536\n5 39\nd2 d92e\ncb 7267\n2e 8476\nd0 5980\n46 109c\na1 c429\nca d8ee\nc3 7227\n26 8436\n72 bf86\n99 c169\n88 ea6a\nbb 6f67\n1e 8176\n36 d9c\n62 be86\n89 c069\nab 6e67\nfd 7d73\n37 a717\n26 c9c\n81 c029\na3 6e27\n74 9532\nb0 c5a2\n8e eafe\na8 ec40\n1d a9d3\n3d f5b\n6a 3cec\n50 3baa\ne9 7649\n9b cbcf\nb5 cd11\n6e 3656\n3f 25df\nb8 c562\nd0 5188\nb aa45\nf7 f51f\n13 83a7\n6d 967b\n7 81d\nea 7646\n85 4811\n6a 3646\n5 811\ne8 7642\nf8 f760\n21 8689\n83 480d\n68 3642\nc3 d087\nd0 d128\n3 80d\n4f ba55\neb decd\n11 a109\n1f b57\n4e ba54\n2e c56\n56 93b6\n9b e14f\n25 eb9\n1d b53\ne6 7616\n40 1020\n62 3cac\ne1 7609\n66 3616\n4d ba51\n4c ba50\nc2 7a04\n2c c52\n4f 9277\n67 1e9d\n4d 9273\n65 1e99\n4c ba5a\n5d 9159\ne2 7606\n62 3606\n4a ba44\n97 e13f\n21 ea9\n52 93a6\n2a c46\nf6 dfbc\n1c a1f8\n48 ba40\n28 c42\n23 86a7\n46 ba14\nc9 7a69\n26 c16\nf2 df8c\n18 a1c8\n2b 8667\n43 128d\n40 ba00\n11 2383\n2b 24c5\n6 a16\n29 24c1\nb3 6da5\naf 46d5\ne6 5cbe\nb 8267\n9 8263\n97 e11f\nf8 7f60\n21 e89\nf6 df9c\n1c a1d8\nc 2a72\n15 131\na 2a4e\n4e 3256\n11 8b81\n6e 947e\n7f 3ddf\nf8 dd62\n53 9ba7\ncc 7252\nc2 f22e\ndc f370\n5 8299\n4c 3252\n6c 9472\nca 7246\n4a 3246\n28 8448\n48 3242\n62 b684\n6 8a1c\n46 3216\n95 69b9\nea d446\ne1 d6a9\n4b b067\nb9 4f49\nb1 4783\n13 a9ad\nc2 7206\nc 2872\n83 4227\na5 643b\nbf 657d\n61 9429\n96 619c\n9b 4365\n81 4223\n94 6198\n75 95bb\n6d bc59\n69 bee3\n83 c025\n7d 957b\n75 3f13\n8f 4055\nd2 7b0c\n3c d5a\ne1 d4a9\nb9 4d49\n66 94b6\n3e d56\n35 fb9\n44 ba12\n5e bb54\n6c 947a\n3d f79\n92 c924\n8 8040\n55 91b3\n4d b851\n5f 9177\nf3 ff25\n5d 9173\nc1 d0a9\n99 4949\n46 90b6\n1e 956\n4e 9076\n1d b79\n77 b717\n66 1c9c\n31 85ab\n29 ac49\n39 856b\n3b 8567\n53 118d\n11 81a3\n9 a841\n86 629c\n1b 8167\n19 8163\nf 28ff\n8e 625c\nf5 553b\n50 1380\nf7 5537\n69 b669\n3a a5ce\na 88c4\nd7 5137\n6d b651\n87 c21d\n8 88c0\n4 38\nd5 5133\n8a 42c6\n10 8b00\ndf f17f\n69 1ee9\ncf 7a55\nd7 53b7\n4f 3a55\nfc 5572\n99 e3c9\n57 13b7\nce 7a54\nd6 53b6\n4e 3a54\n98 e3c8\n56 13b6\ncd 7a51\n4d 3a51\nfa 556e\ncc 7a50\n35 a7b3\n4c 3a50\n7b 354f\n92 69a4\n8e 42d4\ncb 7a45\nba cfec\na0 ceaa\n9d 61f9\nd3 53a7\ne 2d4\n4b 3a45\nf8 5562\n95 e3b9\n53 13a7\n50 11a0\nf6 7796\n4a 3a44\n94 e3b8\n52 13a6\nf6 5fbc\n1c 21f8\n8c 42d0\nf 8875\nc9 7a41\neb 54e5\nd1 53a3\nc 2d0\n49 3a41\n6b 14e5\n51 13a3\nc8 7a40\nea 54e4\nd0 53a2\n47 3a15\nf4 5532\n46 3a14\nf2 5f8c\n18 21c8\n2f c5f\nc5 7a11\n45 3a11\nf2 552e\n40 3a00\ncc 5a72\n22 ac06\nf1 7521\n8c 4a72\nb1 6521\n4c 1a72\n71 3521\n69 34e1\nc a72\n31 2521\n29 24e1\n91 4981\n96 49b6\n2c aed0\nb aae5\n86 48b6\n76 3796\n35 8539\nd4 59b2\n7e 37dc\n64 369a\nea fee4\n54 9132\n4b ba65\nd 827b\nc6 5836\n3c f52\n9e 4976\n3c 87f2\n72 97ae\n6a 96ee\n49 ba61\n1a a9c6\n3a f4e\n54 1932\n4c 1872\n5e 1974\n44 1832\n1c 972\n14 932\nc 872\n1e 974\n4 832\nc3 d8a5\n3d a7f3\nc3 5805\nb8 6de8\nac 4672\n47 183d\n8c 4272\nc3 d82d\nc 272\n9d c95b\n17 2937\nc 287a\nb a2e5\n86 40b6\n29 a6e1\n8b 6865\n8 862\n42 3a0e\n5c 3b50\n6c bc5a\n9f 4157\n54 11b2\n4c 3850\nc9 f8eb\n2c 2472\n18 836a\ne6 5436\n96 e93e\n4b b265\nc8 d8e0\n2b 467\nc6 5036\nf3 dd0d\nd9 dbcb\n53 3ba7\n3c 752\nbe 4576\nf0 d580\nae 4476\nb5 6db3\n71 9da1\n9e 4176\nd0 d180\n8e 4076\nb4 ed3a\n69 b661\n3a a5c6\n94 e93a\n49 b261\n29 463\n1a a1c6\nbc 4572\nac 4472\n7 2b7\n9c 4172\n8c 4072\nc1 d8ab\ndb d9ed\n3e 574\n24 432\n44 b230\n22 ae2e\n3c af70\n34 af30\n2c ae70\n1e 174\n4 32\n24 ae30\n28 ac60\n52 11a4\nb4 c598\naf 46fd\n30 da0\nf2 77a6\n36 8d3e\ncc faf0\n2f 2677\n2e 8cfe\nc4 fab0\nad c65b\n27 2637\n14 893a\nd 2273\n34 8598\n80 c008\na2 6e06\n8d c25b\ne 88fe\n7 2237\nd1 d323\neb d465\na2 6ca4\n18 23c0\nd4 f9b0\nbd c55b\n37 2537\ne7 d495\n1f 2177\ne5 d491\n27 cbf\na6 461c\n5d 1bd9\n6e b654\nf6 7db6\n3d a7fb\n3c a7fa\nd6 5136\n67 b615\n35 a7bb\n4c 3a58\nf2 7d8e\na7 46b5\n6e 14fc\n54 13ba\n65 b611\n34 a7ba\n64 b610\nce 50f6\nae ecde\n63 b605\ncf d27d\n50 9920\n94 6932\n32 a7ae\n8b 4a67\na9 4663\nbc 65d8\nf2 5786\n4e b254\ncb d8cf\n2e 456\nf6 ddb6\n25 6b9\n4c b250\nb6 4d36\n4a b244\nc7 d8bf\n2a 446\n48 b240\nc5 d8bb\ndf d9fd\n28 442\nb2 4d26\n46 b214\nc3 d88f\ndd d9d1\nc9 7269\n26 416\n1a 896e\nf1 dd8b\nea 76c4\n92 6b86\nb9 6d69\n44 b210\nae 4cf6\n40 b200\naa 4ce6\nf0 7d82\n88 cac8\n2 2aa4\nf9 d563\n66 349e\nb a67\nf0 5fa8\n29 663\nb4 e798\n72 1786\n3c 25d8\n34 2598\ne6 f436\nd5 59bb\n17 831f\ndd 597b\nce 5876\n6c 96f2\nc2 7a0e\ndc 7b50\na2 e426\n91 49ab\ne5 743b\nff 757d\n29 aec1\n98 c3e2\nb2 c524\n7d 357b\n99 496b\n9b 4967\n39 87e3\nb1 45a3\n5f 1977\n55 193b\n4c 187a\n85 4a33\n9f 4b75\nd 251\n97 4b35\ne 2ad4\n7d 1573\ne6 d494\n6c 1472\n7e 1574\n64 1432\n13 29ad\n1b 967\n86 c096\n11 92b\n59 99c1\n9d 69d3\n75 9d1b\n6e 3654\n74 9d18\n6d 3651\nbe 4d7c\na4 4c3a\nf2 dd0c\nd8 dbca\n52 3ba6\neb 7645\n6b 3ce7\nf1 dd0b\nea 7644\n1f a97f\n71 9d0b\n6a 3644\n4 a83a\n1e a97c\n37 8d17\nc0 fa28\n78 3d6a\nb1 6f23\ncb 7065\nc7 52b7\n91 6109\n8b eae7\n7d 1d59\n82 e80e\n9c e950\nee dcdc\nd4 db9a\ne7 7615\n67 3cb7\ned dcdb\ne6 7614\n6d 9cdb\n66 3614\nec dcd8\n66 3cb4\ne5 7611\n57 3317\n5e 99de\n6c 9cd8\n65 3611\nb6 4d3c\n9c 4bfa\n65 3cb3\n7f 3df5\ne4 7610\n64 3610\n98 e940\ne0 d628\ne a05c\n4d ba59\nea dccc\nd0 db8a\ne3 7605\n48 326a\n5c 99d2\n4c ba58\nc2 7a0c\n2c c5a\ne9 dccb\n63 3ca7\ne2 7604\n69 9ccb\n62 3604\ne8 dcc8\n62 3ca4\ne1 7601\n53 3307\n5a 99ce\n61 3ca3\n7b 3de5\ne0 7600\n60 3600\n72 b70e\na3 eca5\nc3 522d\n19 a3c1\nab 4667\n8b 4267\n89 4263\nf9 5569\nd2 5386\n9c 61d8\nf 88d5\n37 d3f\ncd 7af1\nb 4d\n95 4931\n33 87ad\n86 483e\n1c 8972\na1 6c2b\nbb 6d6d\n55 933b\n6f 947d\n66 3c9c\nf9 dd61\n7f 3ddd\n65 3c9b\nf8 dd60\n4d 38f3\ncc 7250\n4c 3250\n9 a2c1\n9a 496c\n80 482a\n3d d73\nd3 7b25\n4d 98db\n46 3214\n99 6969\n39 d43\ncf 7af5\n44 3210\n35 d33\ncb 7ae5\n40 3200\nc8 dae0\n2b 667\n36 259c\ne0 7e08\n16 219e\n9 263\nf6 5f9c\n1c 21d8\n3 227\n16 219c\n14 2198\nc6 50b6\nf3 dd8d\n3c 7d2\n68 16c0\nb3 45a7\n9a c1ec\n80 c0aa\n83 4a2d\n2a 8646\n91 41a3\n27 a6bd\n89 6841\n75 153b\n90 e102\n7c 9d52\nd7 5195\na4 cc98\n7e 157c\n64 143a\nb7 4735\n5f 1177\nc2 fa2c\n2c 8c7a\nf3 7f25\n5d 1173\nc6 d094\n3e 8d7c\n24 8c3a\neb 7ee5\n55 1133\ne2 7e24\n4c 1072\n5e 1174\n44 1032\n39 56b\n31 52b\nd0 d9a0\n33 527\n9e e9fe\n53 b325\nf0 7d08\n13 127\n33 af25\n11 123\n31 af21\n99 63c1\n90 c12a\nc9 f2e3\ne3 f425\n70 b5aa\n38 2d4a\n5a b1ec\n40 b0aa\n18 294a\nc3 7007\n65 9eb3\n7f 9ff5\nf9 d5cb\n73 35a7\nf0 75aa\n70 35aa\ne6 56be\n5a 99c4\n40 9882\n9e 69d6\n7a 35ec\n60 34aa\nb3 67a5\n80 e2a8\n50 b9a8\n50 31aa\na3 64a5\n5a 31ec\n40 30aa\n93 63a5\n65 1eb3\n7f 1ff5\nd1 79a9\n78 b5c2\n51 39a9\n50 39a8\n68 b4c2\n91 c129\n9a eb6c\n80 ea2a\nb3 6f27\n16 8136\nc0 78a8\n83 c087\n90 c128\ndf fbfd\nc5 fabb\n28 2642\nb2 6f26\n40 38a8\nf7 759d\n71 b5a9\n66 9c3c\naa 6c4e\n4c 9afa\n93 63a7\n61 b4a9\n39 2d49\n83 62a7\n51 b1a9\n41 b0a9\n19 2949\n68 3e40\n70 17a2\nf1 75a9\n71 35a9\n80 e002\n9a e144\na7 ee9f\n72 1f2e\ne1 74a9\n61 34a9\n8a e044\nc1 70a9\nf2 f5a6\n41 30a9\ne0 5488\n72 b5a6\n54 9b10\n48 30c2\n44 9a10\n92 c126\n61 3c29\n70 b5a2\n3a 2d46\n31 2fa9\n38 2d42\nc0 5088\n52 b1a6\na7 e69f\n72 172e\n50 b1a2\nbf e7dd\na5 e69b\n70 172a\n1a 2946\n18 2942\nf2 75a6\n74 9710\nf8 d5ca\n72 35a6\nf0 75a2\ne2 74a6\n64 9610\ne8 d4ca\n62 34a6\n82 e2a4\n73 1f2d\n71 1f29\n5 8ab9\ne 8856\nd2 71a6\n54 9310\nd8 d1ca\n52 31a6\nda d94c\nc0 d80a\nd0 71a2\nc2 70a6\n44 9210\n38 27c2\nc8 d0ca\n42 30a6\n92 6b06\n4e 9af4\n2a 844c\n10 830a\n41 b8a1\n61 1e29\n4f b0d7\n5c b178\n60 1e28\nf7 f595\n36 27be\n49 30c1\nc2 d224\nd5 f199\n39 2fc1\n79 bfe3\n93 c125\n82 ea26\n78 bfe2\n92 c124\n5d 317b\n72 b5a4\n71 b5a1\n8 a848\n10 81aa\n21 2c03\n3b 2d45\n62 b4a4\n8b c0c7\n98 c168\nba 6f66\n61 b4a1\n1f 2bff\n39 2d41\n60 b4a0\n52 b1a4\n51 b1a1\n71 1729\n5 82b9\ne 8056\n50 b1a0\n63 162d\n42 b0a4\n62 162c\n6b bcc7\n78 bd68\n9a 6b66\n41 b0a1\n61 1629\n19 2941\n40 b0a0\n18 2940\n60 1628\nf3 75a5\nc0 f0a8\nfa 7f64\n23 e8d\ne0 7e22\n22 8c06\nf9 d5c9\n73 35a5\n18 2948\n40 b0a8\n90 eb08\n4e 1af6\nf8 d5c8\n72 35a4\na5 46b9\nb5 6d93\n71 9d81\n4f b8f7\nce f254\nf0 75a0\n9 82c1\n54 b99a\ne9 d4c9\n63 34a5\n8 2848\n10 1aa\n20 2c22\n3a 2d64\ne0 74a0\nd3 71a5\n56 b19e\nd1 5121\n2 8806\nd9 d1c9\n53 31a5\nd8 d1c8\n52 31a4\nc0 d808\n85 42b9\n8e 4056\nae ee54\nd0 71a0\nc9 d0c9\n43 30a5\nc2 70a4\n38 27c0\nc8 d0c8\n42 30a4\neb 7cc7\n32 a70c\nf8 7d68\nc1 70a1\n37 27bd\nc0 70a0\n89 42c1\nd4 799a\n1 2281\na7 6c95\nab c4c7\nb8 c568\nda 79cc\nc0 788a\n74 b59a\n70 b58a\n94 e312\nae e454\n9d 49d9\n7a b5cc\n60 b48a\n5e b1dc\n44 b09a\n77 3597\n5a b1cc\n40 b08a\n65 9e93\n7f 9fd5\n74 359a\n10 2ba0\n70 358a\n7c 9fd8\n7e 35dc\n64 349a\nb7 6795\n0 2aa0\n55 b999\n54 b998\n51 b989\ne 287e\n50 b988\n54 319a\na7 6495\n50 318a\na3 6485\n5c 9bd8\nd8 d9e0\n3b 567\n8 806a\n41 b223\n5b b365\n45 b899\n41 b889\n5e 31dc\n44 309a\n5a 31cc\n40 308a\n93 6385\n4c 9ad8\n65 1e93\n7f 1fd5\nd5 7999\nc2 5a24\n55 3999\n42 1a24\nd4 7998\n54 3998\n3c ad72\n51 3989\n72 97ac\nb6 67be\nd0 7988\n2e a47e\nb8 ed62\n50 3988\n38 ad62\n11 921\n9e eb5c\n84 ea1a\nc4 7898\n7 aab7\n44 3898\n2c ac72\n39 8563\n5a 3bcc\n40 3a8a\n51 1189\n2f 2cff\nae 665c\nc0 7888\n3 aaa7\na8 ec62\na 20e4\n90 c108\n6e 9ef4\nb2 6f06\n40 3888\n28 ac62\n75 b599\n71 b589\n66 9c1c\n4c 9ada\n65 b499\n61 b489\n55 b199\n51 b189\n45 b099\n41 b089\n75 3599\n62 1624\n71 3589\n56 b996\n76 1f1e\n65 3499\n51 3189\n39 a563\n45 3099\n76 b596\n41 3089\n72 b586\n2b a6ef\nd1 7989\n56 3996\ne2 7ca6\n29 a6eb\n8b 686f\ne a87c\n57 b99f\n52 3986\n9 2863\n92 c106\n61 3c09\n42 3886\n8b 4ae5\nef 54df\n74 b592\n2b a46f\n66 b496\n35 2f99\n62 b486\n31 2f89\n56 b196\n25 2c99\na7 4cb5\n76 171e\n54 b192\ne5 de33\nff df75\nb a06f\nfe 5ffe\n44 ba10\n52 b186\ne3 de27\n9 a063\nfc 5ff2\n46 b096\n15 2b99\n97 4bb5\n66 161e\n42 b086\n76 3596\n64 3e10\nb8 ed60\n72 3586\n29 2463\ne1 7489\n66 3496\n1d 2373\ne2 7486\n99 6363\n62 3486\n82 e284\n19 2363\n6e 9ed4\nd6 7196\nd1 7189\n56 3196\n54 3192\nd2 7186\n89 6063\n44 3a10\n52 3186\n8f 687d\ne6 7cb4\n2d a6f9\n56 b994\nd a871\n8a 62cc\nc1 7089\nf2 f586\n46 3096\n46 b894\n66 1e1c\n41 b881\n5c b158\n61 1e09\nc2 7086\n40 b880\n60 1e08\nea 56e4\n36 279e\n57 3995\n56 3994\nd 2871\nd5 7991\n55 3991\n53 3985\n52 3984\n9 2861\nd1 7981\nc7 7895\nc5 7891\nc4 7890\nc3 7885\n79 bfc3\n93 c105\n82 ea06\n43 3885\n78 bfc2\n92 c104\n42 3884\n5d 315b\nc1 7881\ndc 7158\nb0 cf20\n37 2f9d\nc0 7880\n77 b595\n76 b594\n13 a32f\n2d a471\n75 b591\n71 b581\n14 819a\n66 b494\n18 960\n81 c881\n65 b491\n64 b490\n10 818a\n62 b484\n19 a361\ne2 f6a6\n61 b481\n68 be60\ned 5cfb\nfe f776\n27 869f\n57 b195\n77 171d\n93 e905\n56 b194\ne7 de35\nd a071\n55 b191\n75 1719\n91 e901\n7 a01d\nc3 5227\n54 b190\ne5 de31\nb a06d\n74 1718\n53 b185\n73 170d\n52 b184\n51 b181\n71 1709\n67 161d\n83 e805\n46 b094\n66 161c\n7c bd58\n45 b091\n65 1619\n15 2139\nef 5efd\n81 e801\n44 b090\n64 1618\n63 160d\nd2 f38c\n3c 85da\n42 b084\n62 160c\n78 bd48\nc2 f2a6\n41 b081\n61 1609\nf7 7595\nc4 f098\nf5 7591\n75 3591\nf3 7585\nc0 f088\nfa 7f44\ne0 7e02\n73 3585\n40 b088\n4e 1ad6\n72 3584\n29 2461\nf1 7581\n70 bd2a\n39 a741\na9 eee3\nc3 f025\ne7 7495\n94 419a\n67 3495\n14 19a\n34 af98\ne5 7491\ne4 7490\ne3 7485\n90 418a\n63 3485\n10 18a\n30 af88\ne1 7481\ne0 7480\n87 e01f\ne8 7e60\na7 469f\n1e 21f6\nd7 7195\nfe 7776\n27 69f\n6d bcfb\nec f658\n57 3195\n24 ac98\nd5 7191\n55 3191\nd4 7190\n8b 606d\nd3 7185\nb7 4fbf\nd1 5101\n53 3185\n52 3184\nd1 7181\n58 b96a\n91 eb23\nab ec65\n47 3095\n14 ab98\nc6 7094\nc5 7091\nea 56e6\n7c 3d58\nc6 72b6\n45 3091\n50 b92a\n19 a341\n89 eae3\na3 ec25\nc4 7090\n43 3085\nbc 45da\nc2 7084\n42 3084\nf8 7d48\nc1 7081\nb0 c720\n37 279d\nc0 7080\n36 279c\n9 a241\n58 396a\n1a 29e4\n0 28a2\n99 6341\n50 392a\n19 2341\nce 5a7e\n3e ad54\n24 ac12\nd9 73eb\nf3 752d\n48 386a\n1b 3e7\nff 755f\nd9 51c1\n59 11c1\nc6 5a3e\nd1 73ab\neb 74ed\n93 6b25\n78 b56a\n78 356a\nf0 752a\n70 352a\n9d 6bf9\n68 346a\n88 e268\n3c a7d2\n80 e228\n6c 9e78\n58 316a\n59 b969\nd0 712a\n50 312a\n14 81b8\n86 6a16\n42 9a04\nd6 d11c\nbc cfda\n36 2fb6\n51 b929\n43 b887\n50 b928\n48 306a\n49 b869\nda 716c\nc0 702a\ne5 5e33\nb 206f\nff 5f75\n5a 316c\n40 302a\n4c 9a78\n65 1e33\n7f 1f75\n4 80b8\n37 5b5\nc6 d01c\nac ceda\n26 2eb6\n41 b829\n40 b828\n59 3969\n14 1b8\n42 1a04\n51 3929\n43 3887\n50 3928\n84 40b8\n68 b442\nc7 5a3d\n4 b8\nd5 51b3\ncd 7851\nc0 7828\n52 b386\n79 b569\n9b 6367\n42 b286\n69 b469\n92 c92e\n8b 6267\n53 b387\n60 b428\n82 6226\n6e 1e76\n38 2fc0\n41 b029\n4f 1a77\n33 af87\n40 b028\n4e 1a76\neb 74c7\nf8 7568\n71 3529\n4c 1a7a\n63 3487\n70 3528\n61 3429\nd3 7387\ne0 7428\n53 3387\n60 3428\nd1 7129\n1c a950\n2 a80e\n51 3129\ne 8ad6\n33 a585\nc3 7087\nd0 7128\n9 8ac9\n43 3087\n50 3128\n41 3029\nb3 6f87\nc0 7028\n33 2f87\n40 3028\nc8 5848\n5a b966\na3 4627\nb6 659c\n58 b962\n90 c1a8\n28 26c2\nb2 6fa6\n4a b866\n48 b862\n80 c0a8\n58 3948\nb3 45a5\na2 6ea6\n42 b826\n48 3042\n70 b7a2\ncf 727f\n50 3922\na 80e4\nd4 f11a\na a2e4\n7a b7e4\n60 b6a2\n38 2f42\n36 2f3e\ne8 5448\n7a b566\n92 418c\n78 b562\n90 4188\n82 408c\n39 2f69\n68 b462\nc8 5048\n5a b166\n19 2b69\nb0 4f08\n42 b026\n11 2b29\n78 3562\n70 3522\n62 9e2e\n7c 9f70\n82 e224\n6e 9e74\n58 3162\nfd 7dfb\n37 a79f\n44 9a32\n5e 9b74\n50 3122\nef fcfd\nd5 fbbb\n38 2742\nc8 d04a\n42 3026\n4e 9a74\ned fcf9\n36 273e\n41 b823\n5b b965\n52 b924\n4b b865\n4a b864\n48 b860\ndf 73fd\nc5 72bb\n42 b824\nf5 7d99\n41 3823\n5b 3965\nf4 7d98\nf 2857\n59 3961\ndd f159\n8d 4a79\n97 419f\nd a79\n3e 8f76\n67 b617\n56 1b9c\n58 3960\n73 bf27\ne1 5e09\ndc f158\n17 19f\n37 af9d\n5 a39\n36 8f36\ncf 727d\n50 3920\nd4 f118\ne5 7c99\n4b 3865\ne4 7c98\n27 aeb7\n4a 3864\n93 4987\nc0 7820\n82 6a8c\n18 816a\n68 b460\n41 b023\n5b b165\n90 c908\n89 6241\na 28e4\n9 28e1\n9c e378\n8f e2d7\n5a 1366\n59 b161\ne9 d469\nc2 d286\ne0 dc80\n58 b160\n31 af23\n4b b065\n48 b060\n29 aee3\n43 b025\nd2 f32c\n3c 857a\n41 b021\n61 3423\n7b 3565\n3b afc7\n48 b068\nad 4679\nd9 d3c9\n53 33a5\nd9 73e3\nf3 7525\nce 5a76\nb3 ef87\nc0 f028\n58 33e2\nf8 d548\n72 3524\nf6 dd36\n25 639\n5 a0b1\n70 3520\n18 16a\n2b aec7\n38 af68\n10 12a\n23 ae87\n30 af28\ne0 7420\nf5 7599\ne2 5624\n41 3023\n5b 3165\n1b abc7\n28 ac68\n59 3161\n8d 4279\n9d 6953\n59 9941\nb9 cfc9\n33 2fa5\nb9 6fe3\nd3 7125\n93 eb87\na0 ec28\nb 8ac5\n9 8ac1\nd6 d936\n5 239\n36 8736\n50 3120\ne5 7499\n31 2f23\n4b 3065\na9 6ee3\nc3 7025\n39 2741\n70 3d2a\n71 958b\nf 2a5d\naa c64c\n2b 8cef\nc1 faa1\nd5 fbb9\n38 2740\nc1 7021\n37 273d\nc0 7020\n78 95e2\n5a b94c\n40 b80a\n5c 395a\nb7 45b7\n4c 385a\na7 44b7\nc8 784a\n5b 3bc7\n68 3c68\nb1 4d8b\n97 6b15\nf8 55e2\n4b 3ac5\nda 794c\nc0 780a\n82 e004\n93 e9af\n7c b55a\n18 ab60\n78 b54a\n34 5b2\n12 2b0e\n2c 2c50\n4c b05a\n7f 3557\n5e b15c\n44 b01a\n77 3517\n5a b14c\n40 b00a\n65 9e13\n7f 9f55\n5 ab9\ne 856\n36 8fb6\nd4 f198\nf8 754a\n78 354a\n59 b961\n74 351a\n10 2b20\nf0 750a\n18 81e0\n8d 62d9\n74 bd92\n90 eb88\ne7 5615\n64 96b8\na8 66ca\n70 350a\n7c 9f58\nfa 754c\ne0 740a\n66 3e34\nec de58\nb0 cf22\nca d064\n37 2f9f\n5c 315a\nd8 714a\n6b 34c7\n78 3568\ne1 f489\na e4\nd4 711a\n90 e302\n99 49c9\naa e444\n54 311a\n93 4325\nde 79fe\nd0 710a\n54 b992\nc7 5215\n74 1f1a\nb a86f\n50 310a\n5c 9b58\n4c 305a\n33 85af\nef f67d\n70 bd20\n48 304a\n70 b7aa\nda 714c\n7c 9ff8\nc0 700a\n7e 1f5c\n64 1e1a\n5a 314c\n40 300a\n4c 9a58\n65 1e13\n7f 1f55\n59 b949\n55 b919\n54 b918\n50 b908\n45 b819\nd4 d192\nbc 65f2\n78 95e0\ned 76d9\nd6 f9b6\n5 22b9\ne 2056\n40 b808\n59 3bc3\n73 3d05\nba e54c\na0 e40a\ndd 7959\nd5 7919\nd4 7918\nd0 7908\n50 3908\ncd 7859\nd5 51bb\n45 3819\n76 bd16\nc4 7818\nf8 55e0\nc0 7808\n3 aa27\nc3 5225\nbf efdd\na5 ee9b\n70 1f2a\n7d b559\n1e 8bfc\n4 8aba\ndc 5172\n93 e9ad\n7c b558\n61 bc81\nd9 5163\n6f b67d\n79 b549\n7a 1f6c\naf eedd\n60 1e2a\n6d b459\n32 d84\n69 b449\n89 40e1\n64 b418\n60 b408\n6e 1e56\n82 6206\n5d b159\n59 b149\n45 b019\n44 b018\n5d 33d3\n77 3515\n41 b009\n4f 1a57\n40 b008\n59 33c3\n73 3505\n4e 1a56\nd4 f19a\n7d 3559\n9e 4bfc\naf e677\n84 4aba\n93 69ad\n7c 3558\n61 3c81\n75 3519\n74 3518\n75 bd91\na9 66c9\n71 3509\n4c 1a5a\n74 bd90\na8 66c8\n11 ab2b\n2b ac6d\n70 3508\n65 3419\n64 3418\ne0 7408\n12 bac\n23 a627\n64 bc90\n60 3408\ndd 7159\nfa df4c\ne0 de0a\n5d 3159\n59 3149\na3 66a7\n55 3119\nc5 78bb\ndf 79fd\nd4 7118\n54 3118\n55 b991\n89 62c9\n7 8037\n75 1f19\n51 3109\nd0 7108\n68 96ca\nca 584e\ncd 7059\nfe f556\nef f67f\n70 bd22\n4d 3059\n7e b556\nd7 793d\n49 3049\n7a b546\nd a2d1\nd3 792d\n71 b7a9\nc4 7018\n45 b891\n65 1e19\n41 3009\n72 b506\nc0 7008\n3 a227\n44 b890\n64 1e18\n40 3008\n5e b956\n94 c198\n8f 42fd\n10 9a0\n56 b916\n54 b912\n90 c188\n52 b906\n4c b852\n4a b846\n48 b842\n84 c098\nb7 4595\n0 8a0\n46 b816\n80 c088\nb3 4585\n42 b806\n95 4139\n4d 10d3\ne3 7e85\n8 8e0\n87 423d\n99 e969\nbd cdd9\n37 2db5\nc1 78a9\n3c a572\n54 3198\nc2 d824\n2c 24f2\n38 8f40\ne6 5e16\nc 2052\n34 a7b2\nd9 7949\n5e 3956\n5c 3952\n74 b792\n78 3d60\n37 59f\nd4 7912\n54 3912\nce 7856\nc4 f832\nde f974\n7 889d\nc5 7ab9\n75 1d99\n6c b6d2\nc9 7849\nd1 51ab\n4e 3856\n45 3ab9\ncc 7852\nc2 f82e\ndc f970\n5 8899\n4c 3852\n31 523\n9c e9fa\n51 b321\nca 7846\nc8 7842\n3e 2f5e\n66 b6be\n8d 68fb\n49 98e9\n34 a5b8\n62 be04\n50 3b22\n6a 3c64\n9a c9cc\n93 6305\n80 c88a\nb3 4d87\n7e b7d4\n64 b692\n2f a4d7\n3c a578\n21 aca1\n41 1229\n72 9726\n68 3c60\n62 9624\na6 6636\n41 3801\n35 a5b9\n63 be05\n2a 8c4c\n10 8b0a\n3c a57a\n25 a4b9\n1a 8b4c\n0 8a0a\n33 f07\n24 a4b8\n32 f06\n63 9627\n76 b59c\nf6 df36\n1c a172\n34 2d98\n79 1fe9\n50 9120\n94 6132\n38 d60\na1 cc81\nde 7356\nb9 efc9\n77 1fb7\n6 80bc\n4e 9856\n46 9ab6\n4e 9a76\n59 b3e3\n73 b525\n93 e9a7\n7c b552\nf a2dd\nd5 7939\ne aa74\nfc fd52\n6 aa34\nf4 fd12\n6c 9672\nc aa70\n5d 19db\n6e b456\n3d 2f59\n65 b6b9\nc7 783d\n59 19cb\n6a b446\n61 b6a9\n39 2f49\nc3 782d\n95 e311\n16 a9b4\n5e b156\n2d 2c59\n35 5bb\nde 5b7c\nc4 5a3a\ne9 74e9\n5a 3964\n40 3822\nde f15c\nc4 f01a\nf7 7517\nc6 5a36\n98 41ea\nd1 73a3\neb 74e5\n5c b152\nda f9c6\n9 22c9\n7c 3758\n93 6bad\n3a a7c6\n5 a833\n1f a975\n17 a935\n94 6390\nf a875\n8c 62d0\n7 a835\n84 6290\ne a874\n77 1f1f\n57 b997\n6 a834\n15 a931\n4e 92d4\n5 a831\n82 628c\n4c b052\nc a870\n75 1f1b\n4 a830\n9b 63cd\n81 628b\n4a b046\n19 2b49\n46 b016\n15 2b19\n42 b006\n11 2b09\nec 5e72\n4d 32f3\ned d459\n67 3435\n54 99ba\nd5 d313\nef d455\n56 99b6\nd9 d969\n5e 9976\n24 a6b0\n86 6834\n77 1db5\n74 95b2\n52 bb0e\n6c bc50\n7c 9572\nd 2a7b\n15 bb9\n26 a634\ne a274\nfc f552\n6 a234\nf4 f512\n2c a670\nc a270\n4 a230\nbc 457a\n27 a4b5\n56 b39e\nd1 5321\n2 8a06\n27 63d\n8f 68f7\n4b 98e5\n66 149e\nf0 5d82\n36 a5b4\n26 a4b4\n6f b4df\nd0 5320\n46 109e\n16 a1b4\n5f b1df\nc0 5020\n6 a0b4\n4f b0df\nb0 4f20\n26 63c\ne 82de\nd4 593a\n35 a5b1\n2a 8c44\n10 8b02\n25 a4b1\nc4 5838\n25 a433\n14 9b8\n3f a575\n15 a333\n4 8b8\n2f a475\ne 287c\n5 a033\n1f a175\n17 a135\nf a075\n7 a035\n77 b597\nd8 5942\n76 97be\n4 a032\n1e a174\ned 7e5b\n23 e25\ne 2de\nc5 d899\n4f 18d7\n5c 1978\n16 a134\ne a074\n77 171f\n57 b197\n6 a034\nef def5\n15 a131\n5 a031\n6e 16dc\n1e 21fc\n4 20ba\nf8 5fc0\n42 3a04\n14 21b8\ne6 de34\nc a070\n75 171b\nff 5fff\n55 b193\n4 a030\n1d 21fb\nf7 5fbf\n7 8ab5\n99 e9e1\n68 b44a\na1 cc03\nbb cd45\n70 3d20\nef 767d\nf9 7549\n7e 3556\n6c 967a\nf7 f51d\ndd f3db\n13 83a5\nb8 65ca\n74 95b8\n96 43b6\nf1 7509\ncc 5a5a\n76 3516\n4e 9276\n66 1e9c\n4c 9272\n64 1e98\n74 3512\n4f 9a75\nea 7c46\ne1 7ea9\n22 ac2c\n8 aaea\n19 81e9\nf0 7508\nf 8a75\n89 c04b\n3 2027\ne 8a74\nfc dd52\n6 8a34\nf4 dd12\n50 39aa\ne9 7449\n6e 3456\n31 8d81\nf a8f7\n8e e254\n65 36b9\n4d 9a71\nd 8a71\n56 9b94\nd a8f3\n8c e250\nc 8a70\n1a 2164\n0 2022\n6f 9cd5\n55 9b93\ne1 7409\n66 3416\n7 a8b7\n86 e214\n4c b85a\ne2 7406\nee de54\n62 3406\n6e 9e54\n8b 42ed\n17 89b5\n7 88b5\nec de50\n8a 42ec\n95 c311\n16 89b4\nde 7156\nd4 f132\n17 819d\nd9 7149\n5e 3156\n58 9940\n9c 6952\n7a 3564\n60 3422\n80 e220\n6c 9e70\na9 eceb\ne6 5636\n7e 375c\n64 361a\n95 6bb1\nb 22cd\n1e abdc\n4 aa9a\nd2 f12e\n15 8199\n5c 3152\nd1 7109\n56 3116\n5a 3164\n40 3022\n4c 9a70\n89 e8eb\nc6 5236\nc1 f203\ndb f345\n42 b8a6\n1a a3c6\nfe dd76\n2d 679\ne7 deb5\nd a0f1\n76 179c\n78 3560\ne1 f481\n54 3112\n25 a43b\n3f a57d\nde 79f6\n52 3106\n45 9833\n5f 9975\n8b 426d\nd4 5390\n5 8833\n1f 8975\n17 8935\n94 4390\n7 8835\n84 4290\n8a 426c\n16 8934\ne 8874\n43 322f\n5d 3371\n57 9997\n6 8834\nc9 7049\nfa f546\n4e 3056\n11 8981\n45 32b9\n89 4269\nd 8871\n42 322c\n56 9994\n8a 42cc\n4c 3052\n6c 3c5a\n14 8930\nc 8870\n4 8830\n9b 43cd\n81 428b\nc1 72a9\n33 85a7\n11 ab03\n2b ac45\nc1 7009\nf2 f506\n46 3016\nce da54\n42 3006\n4e 9a54\n24 e12\n3e f54\n4 a12\n1e b54\n63 be25\n85 e039\n43 1027\n26 86b4\n43 ba25\n74 95ba\n6c bc58\nb9 65cb\n75 95b9\nf1 d583\n8f 6a55\nc7 5835\n88 68ea\n70 bd8a\n25 86b1\n87 4835\n50 b98a\n5 82b1\n68 bee2\n82 c024\n7c 957a\nd2 d386\nf9 d569\ne8 fe6a\n7e 9576\nd 8279\nc6 5834\n61 be21\n5b 1165\n41 1023\n24 86b0\n86 4834\n41 ba21\n4 82b0\na9 64cb\n65 94b9\n3d d59\n87 42b7\n86 42b6\n3c d58\n8f 4277\n8e 4276\n54 91b2\n4c b850\nb2 cf86\nd9 d169\nc8 fa6a\n5e 9176\n76 1d9c\nf2 ff24\n5c 9172\n74 1d98\nba cd4e\n6f 9675\n30 a72a\n9a c94e\n4f 9275\n92 c90e\n8b 6247\n47 9235\n7a bd4e\n2f 8675\n72 bd0e\n27 8635\n5a b94e\nf 8275\n5e b154\n44 b012\nee 5e7e\n6e 9674\n58 3962\n37 af9f\n26 8634\ne 8274\nfc d552\n6 8234\nf4 d512\n4c 1a78\n88 686a\ne8 7442\n78 bd4a\n2d 8671\n76 9794\nc a78\n56 9394\n6c 9670\n4c 9270\n38 27e2\n2c 8670\n75 9793\n24 8630\nc 8270\n4 8230\n1c 8958\nc 8858\n25 c13\n3f d55\nd2 7b0e\nec 7c50\n47 3a95\nf4 55b2\n46 bab6\n27 84b5\n7 80b5\n24 c12\n3e d54\n93 c925\n9 8041\n83 c825\n59 bbe3\n73 bd25\nf0 7780\n36 85b4\nf3 ff2d\n5d 917b\n26 84b4\n53 b925\n3a 5ce\nd0 7380\n16 81b4\n43 b825\n2a 4ce\nc0 7280\n6 80b4\nb9 65c3\n75 95b1\n2d 4d3\nc3 7285\na9 64c3\n65 94b1\n23 c0f\n3d d51\n5e bbf4\n44 bab2\n15 81b1\n5 80b1\n71 bd21\n34 85b0\n51 b921\n14 81b0\n41 b821\n4 80b0\n91 cb09\nb 2ae5\n9 2ae1\nd4 5132\n65 9433\n7f 9575\n86 4a9c\n44 183a\n5e 197c\n55 9333\n6f 9475\n30 a52a\n4e 187c\n28 a4ea\n45 9033\n5f 9175\n4e ba76\n35 8f33\n4f 9075\nea deee\n10 a12a\nfc dff0\ne2 deae\n8 a0ea\n25 8433\n3f 8575\n2e ae76\n1d 83f3\n37 8535\n26 ae36\n15 8333\n2f 8475\nc 827a\ndf 5977\n7d 97f3\nd 82f3\n27 8435\n5 8033\n1f 8175\n17 8135\n6 aa36\nf 8075\n7 8035\nf aad5\n64 9432\n7e 9574\nc5 7899\n5f 3bdd\nd8 db60\n45 3a9b\n77 9597\n16 8134\ne 8074\n6 8034\nd2 d90c\ncb 7245\nba c7ec\na0 c6aa\n53 932f\n6d 9471\n4c ba72\n4b 1ac5\n15 8131\n4b 38e7\nd1 d90b\nca 7244\n52 932e\n6c 9470\n1a 83ee\n34 8530\n4a 1ac4\n8 2a60\nd a8fb\n4a 1246\n8c e258\n14 8130\n4 8030\n85 6ab9\n45 b813\n5f b955\n87 6ab5\n8d 6a79\n43 b80f\n5d b951\n94 69b8\n5a 13c4\n40 1282\n41 b803\n5b b945\n6e 365c\n85 6ab1\n59 b941\n87 c89f\n8d 6279\n57 b915\n56 b914\nb 28ef\n8a 624c\n55 b911\n4f b855\n57 91b7\nf1 d5a3\n8f 6a75\n8c c852\nb5 cd99\ne 80d4\n4b b845\n53 91a7\n8d 6a71\nd6 7b94\nd 2a71\n56 3b94\n5 2a31\n4a b844\n52 91a6\nc 80d0\n49 b841\n51 91a3\n48 b840\n50 91a2\ndf 73dd\nc5 729b\n47 b815\n46 b814\n45 b811\n44 b810\ndc d952\nd 2a79\n71 95a3\nf 2a75\nda 73ec\nc0 72aa\n1c 81d0\n2 808e\n8d c8d9\n7 28b5\nef defd\n15 a139\n4b 92e7\n1b a3ef\n35 a531\n63 3e85\n2a ccc\n3b a747\n10 b8a\n8c eaf8\n4a 1ae6\n8b 4ac5\n5 28b1\ne3 fc2d\nc9 faeb\n2c 2672\n1e 235e\nd5 f919\n97 6935\ne7 5e15\nd 2051\n35 a7b1\n96 6934\ne6 5e14\nc 2050\n34 a7b0\n95 6931\n33 a7ad\n49 3a49\n94 6930\n37 af37\na5 4e19\n14 2930\nc 2870\nc6 d216\nc2 d8a4\nca d864\nd1 f9ab\n34 2532\n3c a7f2\n5a b146\n29 2c49\n31 5ab\nda d346\n85 68b9\n2c a4d2\nbe c5d4\na4 c492\na6 66b4\n9e c1d4\n84 c092\n3b af6f\n28 84e0\n86 62b4\n72 1f04\n56 331e\n87 68b5\n54 b990\nb a86d\n88 62c8\n6 8036\n74 1f18\n50 3108\n8d 6879\nc3 5a27\nd6 799c\na5 66b1\n25 26b1\n40 b800\n5 22b1\na4 66b0\n84 62b0\n70 1f00\nbe c554\na4 c412\nad ccfb\na6 6634\n8c c052\n95 c93b\n8e 6274\n31 da1\nb5 c599\nbe efdc\na4 ee9a\nac ccf8\na5 6631\n8c c8f8\n85 6231\n76 3794\n84 6230\n4 2230\n5 2833\n1f 2975\n15 2939\nad c4d9\n27 24b5\n2 a06\n7b 3fef\n95 4131\n9d c1d9\n17 21b5\n8d c0d9\n7 20b5\nd0 71aa\nd 2879\n85 ea39\n43 1a27\n56 399c\nb a2ef\n25 a431\nc 2878\n84 ea38\n42 1a26\n55 399b\n4b 18e5\nbc c5d8\n36 25b4\nac c4d8\n26 24b4\nf 2875\ne 2874\n57 3997\n98 41c2\ne7 7c97\nf4 7d38\n2e a6dc\n4 2838\nda 71ec\nc0 70aa\n75 1711\ne5 5eb3\nb 20ef\nff 5ff5\n8c c858\n6 2834\nb5 65b1\n6 21e\na5 64b1\nc6 d814\n5 20b1\nd9 f9eb\n3c 2572\nb4 65b0\na4 64b0\naa 46c4\n24 24b0\n54 313a\n54 3992\nb 286f\n9b 63ef\nb5 6531\n93 632f\nad 6471\nf6 7594\n8b 62ef\na5 6431\n13 232f\n2d 2471\n76 3594\n9d c3d9\n17 23b5\n65 be11\n5f 1155\n45 1013\n92 632e\nac 6470\n8d c2d9\n7 22b5\nf5 7593\n8a 62ee\na4 6430\n49 3249\n15 8911\n94 6130\n84 6030\nc9 f8e9\n12 232e\n2c 2470\n75 3593\n70 9502\nb4 65b8\n4c 1052\ne2 7e04\ncb 58e5\n27 ebf\n41 1001\nb6 65b4\n70 370a\na1 6ca1\nf2 5726\n17 23bd\n5d 1b59\n3c 2d58\nb4 ef18\n72 1f06\n64 b4b8\n70 1f02\n85 62b1\n71 1f01\n44 3812\n5e 3954\nc3 780f\ndd 7951\ne8 744a\nc6 5ab4\n96 c93c\n8f 6275\n3c 2f7a\n87 c897\n94 c938\n8d 6271\nd6 7394\nc2 780e\ndc 7950\n42 380e\n5c 3950\nc5 5ab1\n96 61b4\n84 c818\n94 61b0\nc4 5ab0\n35 a51b\n75 b791\n57 3915\n56 3914\nd5 7911\n55 3911\n1e 2956\nf0 5f22\ncf 7855\nd7 51b7\n6d b6d1\nf4 7d90\nf a75\n4e 3854\n98 e1c8\n56 11b6\n48 3860\ne5 7413\nff 7555\ncc f058\n91 4983\n27 ae9d\nce 5a74\n40 3820\ndd 73d3\nf7 7515\nc4 f018\nc6 5a34\n98 41e8\n4e 1a74\n6c bc52\n46 1a34\n18 1e8\ne a74\n19 3eb\n33 52d\ncc 7850\nd4 51b2\ncd 5a71\n9 aac3\n23 ac05\n8d 4a71\nd6 5b94\nd2 5ba4\ned 547b\n85 4a31\n4d 1a71\n45 1a31\nd a71\n56 1b94\n59 bbe9\nfb 5d4f\n5 a31\nda 51e6\n70 b700\ncc 5a70\n8c 4a70\n4c 1a70\nc a70\n58 bbe8\nfa 5d4e\n47 3815\nd6 fb94\n2c 267a\n64 b690\n46 3814\n5f b3dd\n45 b29b\n3e 277c\n24 263a\n87 409f\nc4 7810\n44 3810\nac ced2\nc6 d014\n76 95b4\nba 65c6\n2f 24d7\n3c 2578\n21 2ca1\nb4 e738\na7 e697\n72 1726\nc0 7800\n56 131e\n87 48b5\n4b 3a65\nf2 dfae\n18 a1ea\n46 ba36\n7 8b5\nfd 7553\nc3 f82d\nc 2272\n56 3394\n15 8139\n1e ab7c\n4 aa3a\nbd cf5b\n37 2f37\nc d0\n5 8039\nad ce5b\n27 2e37\nf5 7513\nd5 59b1\nd4 59b0\na cc\n40 1082\n5a 11c4\n97 4935\nd 51\n35 87b1\n5 833\n1f 975\nf 875\n5 839\n36 8d36\n7e 9dd6\n6a 366e\n84 4832\n9e 4974\n22 86ae\n3c 87f0\n16 934\ne 874\n57 1997\n6 834\n55 1931\nba 4766\n11 2ba9\n66 9636\n15 931\n7a 3766\nd 871\n5a b36e\n56 1994\n12 2384\n64 3618\n82 482e\n9c 4970\n54 1930\nd4 f9b8\n37 253f\n4c 1870\n44 1830\n2 82e\n1c 970\n85 c891\n14 930\ne2 f686\nc 870\n55 1993\n4 830\nb7 65b5\n92 4b06\nda d36c\nc0 d22a\nf3 5727\nc2 d226\na5 64b9\n9a 4b4c\n80 4a0a\na4 64b8\n74 b590\ne3 5627\nf6 759c\n65 be19\n5f 115d\n45 101b\ne2 5626\n7 22bd\nf5 759b\n5c 317a\n67 be15\n39 a5c9\n47 1017\nde f1dc\nc4 f09a\nf7 7597\nd2 532e\nec 5470\n97 61b5\n85 c819\n95 61b1\n60 34a0\ne6 56b4\n40 30a0\nc6 52b4\n26 6b4\n6 2b4\nf4 5592\nab 446f\na2 ce26\n3 8a05\nf0 7d8a\na5 46b1\nc0 d800\nd0 798a\n85 42b1\nbb e547\n90 498a\n45 12b1\n87 60b5\n59 1bc3\n73 1d05\n34 2dba\n86 60b4\n85 60b1\n84 60b0\ne4 56b0\nc4 52b0\n9d c1db\n17 21b7\na4 46b0\n2b c6f\nc1 7a21\n3 8805\n84 42b0\n61 3e21\n66 bcbc\ne5 f619\n24 6b0\ne9 f669\n41 3a21\n4 2b0\na9 446b\nf2 558e\n3c 2d7a\n47 ba15\nf3 df8d\n19 a1c9\nbe eddc\na4 ec9a\nd7 7197\n8d 6071\nd6 7194\n45 ba11\n8c 6070\nd5 7193\n61 3421\n41 3021\nc7 5235\n74 1f3a\n7a 3d4e\n2f 675\n5f 93dd\n45 929b\n72 3d0e\n27 635\nf4 7590\n5a 394e\nf 275\n52 390e\n7 235\n53 b3ad\nf5 5513\n60 3420\na9 ece9\ne6 5634\n40 3020\n89 e8e9\nc6 5234\n15 a33b\n2f a47d\nce 78f6\n1b 896f\nf2 dd8c\neb 76c5\n83 e8a7\n6c b452\nc5 7839\n9 a8e9\n46 1234\nc3 daad\n26 634\ne5 7e19\ne 274\n3e 8fdc\n24 8e9a\ne5 5631\n63 1e2d\nc5 5231\nd1 f30b\neb f44d\n52 b9ae\nf8 7d4a\nad 4671\nf6 5794\nb9 e74b\n3a adee\nb a8e5\n2b e6d\n74 1f90\n99 e34b\n1a a9ee\nd6 5394\n76 1794\n58 394a\nd 271\n59 b3e9\nfb 554f\n56 1394\n51 b3a9\nf3 550f\n7d b57b\ne4 5630\n82 e884\ncc 5270\n62 1e2c\n5d b17b\nc4 5230\n86 e896\na6 4e1e\nac 4670\nf5 5793\n2a e6c\n73 1f8f\n8c 4270\n46 b896\n66 1e1e\n19 896b\n64 1630\n44 1230\n3f a57f\nc1 daa9\n24 630\nc 270\n58 b3e8\nfa 554e\n6f 14d5\n55 1393\n4 230\n50 b3a8\nf2 550e\nad ced3\nc7 d015\n77 95b5\nbb 65c7\nb5 e739\n73 1727\n35 2539\nb7 45b5\n5c 3958\n97 41b5\n87 40b5\n34 dba\n5 2033\n1f 2175\n22 8e26\n35 ad9b\n67 14b5\nc 858\n1a abc6\ne9 74e1\n57 11b5\n24 8cb8\ne6 dc1c\ncc dada\n46 3ab6\ne4 d490\n96 e39e\n7b 156f\n47 10b5\n14 8bb8\n27 4b5\na9 64e1\n17 1b5\na6 cc1c\n8c cada\n6 2ab6\n7 b5\n4f 32ff\n69 3441\nc0 f828\nb6 45b4\n2f 2eff\n49 3041\n71 b7a1\n96 41b4\n86 40b4\n16 1b4\n6 b4\nf 2075\ne 2074\n57 3197\nb5 45b1\ncf 50d7\ndc 5178\nc1 58a1\na5 44b1\n95 41b1\n56 3194\n85 40b1\n65 14b1\n55 3193\n45 10b1\n4d b059\n12 984\n35 5b1\n4f 10d7\n5c 1178\nc5 d099\n41 18a1\n25 4b1\n4d 32fb\n67 343d\nb4 45b0\ne1 7c21\na4 44b0\nc1 7821\n37 2f3d\n84 40b0\n41 3821\nc5 f019\nc7 5a35\n99 41e9\n4 b0\na2 ee86\nc9 f069\nbd 4ff3\nd7 5135\n98 61ea\nc6 7a36\n9d 43f3\nb7 4535\n62 9e24\na6 6e36\n7d 3ff3\n97 4135\n42 9a24\n86 6a36\n6d 3ef3\n87 4035\nca 7aec\n34 d3a\n55 919b\nc 8078\n2e 2e76\n60 bca8\n93 41a5\n82 6aa6\n15 333\n2f 475\nd 2f3\n27 435\ne 2a76\n17 135\nf 75\n7 35\n9 88c1\nd4 d19a\n4f 9ad5\n7c 3ff2\n96 4134\n6c 3ef2\n86 4034\n16 134\ne 74\ne0 7ea8\n57 1197\n6 34\ndb 53ef\nf5 5531\n6 8816\n9b 43ef\nb5 4531\n8b 42ef\na5 4431\n6b 3eef\n85 4031\n2c 2e72\nce 7854\nd6 51b6\n6c b6d0\n1b 3ef\n35 531\n3e 2f74\n24 2e32\n13 32f\n2d 471\n76 1594\nb 2ef\n25 431\nd 71\n56 1194\n5 31\na2 442e\nbc 4570\n17 3b5\n8a 42ee\na4 4430\n6a 3eee\n84 4030\n14 130\nc 70\n55 1193\n4 30\nde 5bfc\nef f677\nc4 5aba\naa eee4\n14 8132\n6d 3459\n90 6908\n11 8983\n5f 33fd\n45 32bb\n91 c121\n9a eb64\n80 ea22\nc5 f099\n4f 30d7\n5c 3178\nc7 5ab5\n6 8ab6\n8f c2df\n10 8982\n5e 33fc\n44 32ba\ncc 5a7a\nf1 7529\ncd 5a79\n48 3862\n27 ae9f\nff 7557\n49 3861\n92 4984\ncd f059\ncf 5a75\ne7 fc1f\n3 8aa7\nd1 f303\neb f445\n52 b9a6\nc0 5888\n2f 2cdf\na8 cc62\n5b 33ed\n41 32ab\n5e bb7c\n44 ba3a\nb5 c713\n36 8db6\n5f b3ff\n79 b541\nd 879\n67 b417\n56 199c\n3e 8d76\nc4 5832\nde 5974\n62 96ae\n7c 97f0\ne5 56b1\n11 2309\na3 ec85\ned 5671\nf4 55b0\n8d 6ad1\n57 13b5\nb2 ef0c\n1c 815a\n1b 89c5\n1 8883\n1a 89c4\n0 8882\n4a b2ce\n64 b410\nf 28d5\ne1 5ea1\n45 b013\n5f b155\n3f 2ffd\n25 2ebb\n4f 1a75\ne 28d4\ne0 5ea0\n5e 1b7c\n44 1a3a\n69 34e9\nd 28d1\n5e 1356\n43 b00f\n5d b151\n47 1a35\n19 1e9\nc 28d0\n42 b00e\n5c b150\n61 1e01\nb 28c5\n41 b003\n5b b145\n3b 2fed\n21 2eab\na 28c4\n3a 2fec\n20 2eaa\n31 5a9\n9 28c1\n8f 4ad5\n56 1934\n13 2bad\nd 2ad1\nba 45ee\n35 af13\n4f b055\n32 af0e\n4c b050\n31 af03\n4b b045\n21 4a9\nd3 f925\nd2 f924\n2d aed3\n47 b015\n2c aed2\n46 b014\n2b aecf\n45 b011\nef 5e7d\n2a aece\n44 b010\neb 5e6d\nff f577\nd4 59ba\n83 428f\n9d 43d1\ne2 7e84\n4c 10d2\nba c56c\na0 c42a\n17 831d\ndd 5979\nde 5976\n7c 97f2\na2 c426\nd5 f333\nef f475\nc4 58b8\n6 821c\ncc 5878\nce 5874\nf5 55b1\ne5 54b1\ne4 54b0\n7a 37e4\n60 36a2\nd3 532f\ned 5471\n1e 8b56\n54 193a\n8d 4af3\na7 4c35\n4c b272\n55 1939\nba 476e\n57 1935\n4c 1878\n4e 1874\n44 1838\n46 1834\n7c 1572\n74 1532\n53 132f\n6d 1471\n4d b251\nca d8cc\nc3 7205\n2d 453\n87 e29f\n52 132e\n6c 1470\n4b 12ef\n65 1431\n45 b211\n4a 12ee\n64 1430\n65 3413\n7f 3555\n4c b058\n11 983\na2 ec8e\nbc edd0\nec 567a\na3 ec8d\ned 5679\n68 3462\n88 e260\nb1 ed2b\nee 5676\nd7 5935\n4 8238\n37 735\ne3 740f\nfd 7551\n25 ae99\n63 340f\n7d 3551\n69 3461\nb2 4584\n98 ebe8\nef 5675\nb0 672a\ne2 740e\nfc 7550\n24 ae98\n57 3395\nf4 55b8\n2f ae75\nf 77\ne1 7403\nfb 7545\nc8 f048\n61 3403\n7b 3545\n48 b048\n25 4b9\nf6 55b4\n8d 6ad9\n97 c33d\n18 89e0\ne1 5ca1\n57 13bd\nc6 52b6\n90 6108\n8a eae6\n7c 1d58\n6b 3cc7\n78 3d68\n2a 26ce\ne1 fc89\n41 30a1\nc7 52b5\n74 1fba\nc5 52b1\n5c 33d2\n76 3514\n48 3062\ned 7cfb\n27 a69f\n91 e92b\nce 5276\n49 3061\n92 4184\ncf 5275\naa 646c\n90 632a\n7c 1f7a\ndb f34f\n5c b9f2\ndb 73cf\nf5 7511\n5b 33cf\n75 3511\n83 e885\ncd 5271\nd9 f34b\n5a b9ee\nda 73ce\nf4 7510\n5a 33ce\n74 3510\n5 b9\n36 85b6\nd6 51b4\n5d b959\nd9 73c3\nf3 7505\nce 5a56\nd7 73bf\nf1 7501\ncc 5a52\n9b 49c5\n81 4883\nb2 6f0c\n1c 15a\n1b 9c5\n1 883\n3c af58\n46 9236\n1a 9c4\n0 882\n53 330f\n6d 3451\nd1 7303\nd8 d9ca\n52 39a6\neb 7445\n38 af48\n94 411a\naa 6ecc\n14 11a\n34 af18\ncb 72cf\ne5 7411\n4b 32cf\n65 3411\nca 72ce\ne4 7410\n4a 32ce\n64 3410\nc9 72c3\nd0 d98a\ne3 7405\n90 410a\n9a 49ee\n30 af08\nc7 72bf\ne1 7401\nc6 72be\ne0 7400\n4c b0f2\n7 aa9d\n30 272a\n44 3012\n5e 3154\n1c 2952\n5 aa99\n43 300f\n5d 3151\n28 26ea\n4 aa98\n37 2f95\n42 300e\n5c 3150\n6f 14d7\n7c 1578\n61 1ca1\ne5 d499\nb0 c5aa\n3 aa8d\nf 2ad5\nbc 45f2\n1 aa89\nb3 6d85\nc8 dae8\n2b 66f\n74 1792\n3d 2fd3\n57 3115\n24 ac18\n78 3fc8\na a0e6\n39 2fc3\n53 3105\n20 ac08\n8f 6875\n2d a6f1\n38 2fc2\n52 3104\n3a fee\n54 1130\n6a 96c6\n34 a518\n7c 3d5a\nb0 c5a0\n68 96c2\nca 5846\n33 2f0f\n4d 3051\n37 85b5\n26 aeb6\n27 2e95\n32 2f0e\n4c 3050\n66 96b6\n30 a508\n3e f56\n2d 671\n78 3d4a\nba c5ec\na0 c4aa\n2d 2ed3\n47 3015\n14 ab18\n97 431d\n18 9c0\n2c 2ed2\n46 3014\n2a 2ece\n44 3010\n37 271d\nf7 55b5\n9c 4958\n67 b4b5\nfc 557a\n4f 3a5d\nfe 5576\ne5 54b9\n16 8b9e\n7a 37ec\n60 36aa\n1e 8b5e\nd1 5ba1\nbd e553\n47 12bd\ne8 d6ca\n62 36a6\n18 148\nb0 652a\nd7 51b5\n40 3828\nd5 51b1\nc 2270\n6 8896\n7 8abd\nce 7a76\nc7 50b5\n74 1dba\nc6 50b4\ne2 d40c\nc8 d2ca\n42 32a6\n20 84a8\nb5 4f33\ncf 5075\n90 612a\n7c 1d7a\n5a 33e4\ne0 d408\n40 32a2\n27 eb5\n74 153a\n75 1539\n5d 13f3\n77 1535\n38 25ea\n30 252a\n4f b255\ncc d8d0\n2f 457\n91 e101\n63 9c0f\n7d 9d51\nc7 7a95\n65 1439\n90 e100\n62 9c0e\n7c 9d50\nc6 7a94\n45 b219\n3f 55d\n25 41b\n57 1397\n64 1438\n28 24ea\n47 b215\n4e 3a76\nf2 7f24\n5c 1172\n4c 3a72\n3d ff3\n57 1135\nea 7ee4\n54 1132\n3b fef\n55 1131\n5e 3b74\n44 3a32\n35 f33\n4f 1075\nea 5eee\n10 212a\n2d ef3\n47 1035\n2b eef\n45 1031\nba 4fec\na0 4eaa\nc0 d002\nda d144\n7a 1fec\n60 1eaa\ne9 546b\n3f a5ff\n4a 3844\n94 e1b8\n52 11a6\n21 2ea9\n2a 2c46\n20 2ea8\na1 4ea9\n8 aae0\naa 4c46\nca fa44\nd7 f13f\n61 1ea9\nac ec58\n6a 1c46\n8a ca44\n5c b1f8\ne 28dc\nd6 f13e\n60 1ea8\n96 e13e\n20 ea8\n37 a5bf\n48 1242\nd2 5b26\n6 aa9e\n81 4a21\n87 6835\n25 a6b1\n22 8ea6\n5 20b3\n1f 21f5\n88 40c0\n43 30af\n5d 31f1\n12 a92e\n85 6831\n23 a6ad\na2 4ea6\n58 1948\n8a 62e6\nb8 ef48\n76 1f36\n68 b4e8\na4 eeb8\n62 1ea6\n2c 2cf8\ne 28d6\n74 1f32\nfe d7dc\ne4 d69a\n1a 1e4\n0 a2\na6 6eb6\n62 9ea4\n7d 977b\nec feda\n22 8ea4\n21 8ea1\n20 8ea0\nc9 506b\nb ef\na1 6ea1\n21 2ea1\na ee\na0 6ea0\n20 2ea0\na3 4ea5\n63 1ea5\n44 b0b2\n5e b1f4\n91 c9ab\n8a 62e4\n76 1f34\n62 1ea4\n33 ad8f\n7d 177b\n5d b1f3\nec 7eda\n22 ea4\nf7 dfb7\n1d a1f3\na1 4ea1\n75 1f31\nd7 f137\n61 1ea1\ne5 d699\n6f 16d7\n32 ad8c\n7c 1778\n42 b0ae\n5c b1f0\na0 4ea0\n88 62e0\n74 1f30\nd6 f136\n60 1ea0\ne4 d698\n96 e136\n20 ea0\na4 c698\nb0 e582\n3a afcc\n20 ae8a\n31 8589\nf8 7542\n11 892b\na 2264\nb9 456b\n14 3b0\n7e 9fdc\n64 9e9a\nc5 7819\n4a 1264\n5d 31d9\n3e 2fdc\n24 2e9a\nba 6fcc\na0 6e8a\nb1 4589\n7e 1fdc\n64 1e9a\nfa 5fcc\ne0 5e8a\nba 4fcc\na0 4e8a\n7a 1fcc\n60 1e8a\nb1 452b\n20 ae88\nde 73fe\nf8 7540\n53 3385\n6a 3c66\n8a ea64\n20 8e88\nbc e57a\n53 1385\nde 53fe\nf8 5540\n25 2e99\nd a273\n24 2e98\n15 939\nc a272\n21 2e89\n9 a263\n1e 8974\n4 8832\na0 6e88\n3a 25ee\n20 2e88\nd7 f11f\n61 1e89\n49 9263\n5c b1d8\n72 bfa6\ne0 5e88\n4f 32df\nc8 d262\nf 22df\n88 c262\nd6 f11e\n60 1e88\n48 9262\n96 e11e\n20 e88\n8 8262\n5e bb74\n44 ba32\n5 8b1\nf0 d70a\n6a 36e6\nbb 456d\na1 442b\n49 b069\n22 ae86\n33 8585\nf1 77a1\n31 8581\nc7 7815\n65 b691\ncd 707b\n62 9e86\n2c acd8\n45 3093\n5f 31d5\nc5 7811\n43 308f\n5d 31d1\ne8 d4c8\n62 34a4\n99 416b\na1 6e89\n37 595\ndb f16f\n4 8098\nb5 4591\nf2 7526\n35 591\n1c 952\nb1 4581\nb0 c502\n7a 3dc4\n60 3c82\n80 ea80\nc 20d2\ne6 5e96\nbe e576\n48 12e0\nac 6cd8\nc9 5069\na2 4e86\n2c 2cd8\n91 412b\n20 ae80\n1a 1c4\n0 82\nfc 75f2\nc6 7096\n94 cbb0\n7d 975b\n8d 6079\n22 8e84\na8 44e0\n93 cbad\n7c 9758\nfa 75ee\n21 8e81\nba 65ee\nf5 5d13\n53 bbad\n3c 8758\n20 8e80\n5 8831\n82 428c\n26 2e94\nf df\na5 6e91\n25 2e91\ne de\na4 6e90\n24 2e90\n89 60c3\n45 90b1\nd d3\na3 6e85\n23 2e85\nb cf\na1 6e81\n3d 2dfb\nbc 6758\n21 2e81\n3c 2758\na ce\na0 6e80\n20 2e80\n26 e94\nb 20cd\ne5 5e91\na5 4e91\n94 c998\n8d 62d1\nde 79fc\nc4 78ba\n65 1e91\na4 4e90\n64 1e90\nfb 7f67\n24 e90\ne9 fe49\nc9 504b\na3 4e85\n7b b747\n6a 1ccc\n50 1b8a\n92 c98c\n8b 62c5\n44 9a18\n77 1f15\n52 310c\n38 2fca\ncc f07a\n63 1e85\n44 b092\n5e b1d4\n91 c98b\n8a 62c4\n76 1f14\n69 bc61\nb2 cd84\n62 1e84\n5d b1d3\n7d 1dfb\nfc 5758\na1 4e81\n3d dfb\nd3 7bad\nbc 4758\n90 c988\n89 62c1\n75 1f11\nd7 f117\n61 1e81\n42 b08e\n5c b1d0\na0 4e80\n88 62c0\nb a865\n74 1f10\n68 9e6a\n54 b910\n9 28eb\n88 6248\n28 8e6a\n9d e379\n5b 1367\na8 6e6a\nba 6f6c\na0 6e2a\nb1 4529\nd7 519f\n74 3f3a\n8e 407c\nd4 7910\na8 4e6a\n54 3910\n28 e6a\n87 e23f\n8 a8e2\ne 285e\nda f9e4\nc0 f8a2\n33 5ad\nba 4f6c\na0 4e2a\n9a e9e4\n80 e8a2\n82 c00e\n9c c150\n3a f6c\n20 e2a\n99 e341\n1a a9e4\n0 a8a2\n29 ae69\n9 6b\n52 118e\n21 ae29\n1b 16d\n1 2b\n4 22b8\n29 8e69\n28 8e68\n41 1223\n5b 1365\ncf 70df\n71 9f8b\n21 8e29\n20 8e28\n9e 49fe\n53 1325\na1 6e29\n57 b3b5\nec 547a\n21 2e29\n20 2e28\na9 4e69\nd7 519d\nc6 7a9e\n97 419d\na8 4e68\nf1 5f8b\n5f b3d7\n6c b478\ne1 5e29\nf 285d\n37 afbd\na1 4e29\n6a b44c\n50 b30a\n81 e8a1\n21 e29\nf6 df3c\nf a0d7\n1c a178\n1 a8a1\n52 9326\ne 285c\n16 1be\na0 4e28\n69 b44b\n80 e8a0\n2a 8e66\n42 1a8c\n3d addb\nde 73dc\nc4 729a\n2a 2e66\n51 918b\n8 8068\n21 423\n3b 565\n28 2e62\n39 561\nac e65a\n60 36a8\n0 8028\n31 521\nac ee78\n6a 1e66\nb7 ef15\n68 1e62\n28 e62\nbe c75c\nd5 fbb1\n3f 8dff\na4 c61a\n2a ae64\na 66\n23 ae25\n3 27\n22 ae24\n2 26\n21 ae21\n20 ae20\n1a 164\n0 22\n2b 8e65\n29 8e61\ned fe5b\n23 8e25\nec fe5a\n22 8e24\n35 ad99\n96 6b94\n21 8e21\n20 8e20\naa 6e64\nd1 d189\n29 2e61\nee 5476\nc 72\na2 6e24\nec 5472\n89 e2c9\n47 12b7\n21 2e21\na 6e\na0 6e20\n20 2e20\nab 4e65\na7 ec1d\n8d eadb\n58 1b6a\n36 2f1e\nea 5e64\n18 21c0\nf2 5f84\n69 1e61\n29 e61\n72 1f84\n66 16b4\na3 4e25\na2 4e24\nb5 6d99\n48 b042\nec 7e5a\n22 e24\n35 2d99\nf7 df37\n1d a173\n66 be9e\ne1 5e21\nf 2855\n17 1b7\n64 16b0\n21 e21\n26 8cbc\na5 c619\n51 3323\n6b 3465\n58 99ea\n7 82bf\n21 8401\n7f bfdf\ne0 5e20\ne 2854\n16 1b6\n36 afb4\n20 e20\n25 8cbb\n3f 8dfd\na4 c618\nf5 df33\n1b a16f\n2c ae5a\n3e af5c\n24 ae1a\n47 329f\nda d364\nc0 d222\n3a af4c\n20 ae0a\nd0 5982\n31 8509\n6c 9e5a\n2c 8e5a\n68 9e4a\nb6 cdbe\n6b 96e5\naf 66f7\n28 8e4a\n7e 9f5c\n64 9e1a\n3e 8f5c\n24 8e1a\n2e 8c56\nbd 4559\n2c 2e5a\n3d 559\nb9 4549\n28 2e4a\n39 549\nb5 4519\n3e 2f5c\n24 2e1a\n35 519\nba 6f4c\na0 6e0a\nb1 4509\n3a 2f4c\n20 2e0a\n31 509\na1 4cab\nb2 e726\nbb 4ded\nec 5e5a\nac 4e5a\n6c 1e5a\n1b 29ef\n9a 634c\n80 620a\n4c b8d2\n2c e5a\nc a8d2\ne8 5e4a\na8 4e4a\n68 1e4a\nc7 f21f\n48 b8c2\n28 e4a\n87 e21f\n8 a8c2\n77 159d\nfe 5f5c\ne4 5e1a\nde f9d4\nc4 f892\n3e f5c\n24 e1a\nfa 5f4c\ne0 5e0a\nda f9c4\nc0 f882\nb1 4f21\n36 af9e\nf0 7522\n33 58d\n3a f4c\n20 e0a\n2c ae58\na2 6e0c\nc 5a\n34 87ba\n36 a5be\n28 ae48\n92 492e\n8 4a\n30 87aa\n25 ae19\n1f 15d\n5 1b\n24 ae18\n1e 15c\n4 1a\n20 ae08\n1a 14c\n0 a\n8a 48ee\n6d 9e59\n2d 8e59\n6c 9e58\n29 8e49\n76 95be\nac 6e5a\n68 9e48\nb6 cdbc\naf 66f5\nbe 6f5c\na4 6e1a\n60 9e08\na7 66b5\na3 ec8f\nbd edd1\ned 567b\n20 8e08\n2e 24fc\n14 23ba\nac 6e58\nb4 47ba\n2c 2e58\n34 7ba\n29 2e49\n31 7ab\na8 6e48\nb0 47aa\n28 2e48\n30 7aa\nc8 58c0\na5 6e19\naf c67d\n30 8d20\nc7 521d\n48 18c0\n25 2e19\na4 6e18\na0 6e08\n7f bf77\ned 5e59\nde 7bfe\nf8 7d40\n53 3b85\n3f af77\nad 4e59\n7e bf76\nec 5e58\n3e af76\nac 4e58\n6c 1e58\n4c b8d0\n2c e58\nc a8d0\n69 1e49\n49 b8c1\n7a bf66\ne8 5e48\n68 1e48\nc7 f21d\n48 b8c0\n25 e19\n24 e18\n21 e09\nf6 df1c\n1c a158\n52 9306\n20 e08\n2c ae52\n2a ae46\n1b 3ed\n1 2ab\nf3 dd87\nf9 7761\n28 ae42\n1f 83ff\n39 8541\n1b 83cf\n35 8511\n23 427\nc0 d8a0\ne 85c\n60 b688\n22 ae06\nf8 dde8\nf1 7721\n17 83bf\n31 8501\nbf 4d57\n1d abf1\n3 aaaf\na8 ec6a\n7 aa9f\nac ec5a\ndf 7157\n4b 9a4f\nd7 d915\n12 8926\n2e 8e56\n6c 9e52\n2c 8e52\n2a 8e46\n7e b7de\nf9 5761\n68 9e42\n28 8e42\nd3 f1a5\nf3 572d\n92 e30c\n13 a9af\n1f a95f\n9e 437c\n84 423a\ncf d25f\n50 9902\n2c 678\ne6 deb4\nc a0f0\n75 179b\nd5 71b9\na a86e\n89 e8c9\n47 18b7\nc6 5214\ne a85e\nb6 6f96\n72 9f84\ndd 7179\n8c 4878\n4a 984e\n48 3a48\na3 46a5\nac ecda\n61 b601\n93 692f\n31 a7ab\n35 a79b\n46 9ab4\n8a 6ac6\n3a a7ec\nf3 7da7\n20 a6aa\n3e a7dc\nf7 7d97\n24 a69a\na9 6e49\nb1 47ab\n2e 2e56\nc2 d88c\n25 413\n3f 555\nc 8058\nad 647b\n69 9469\n42 9286\ne6 de9c\nc a0d8\ne3 d4a5\na1 4c03\nbb 4d45\n2c 2e52\n75 b713\n64 1c98\nd3 590f\n71 978b\n16 b16\ne2 de8c\n8 a0c8\n9f 43ff\nb9 4541\n64 3698\nc4 58b0\na1 6e09\n26 2e16\n1d 3d3\n37 515\n4 8018\n92 e924\nfb 5fcf\n7a 97cc\nbe 67de\n60 968a\ne0 7688\n5e 9174\n44 9032\n3a 2f44\n20 2e02\n17 3bf\n31 501\na1 4ca3\nbb 4de5\n3d a7f1\n23 a6af\n3e 5fc\n24 4ba\n1c 2b58\n1f a3dd\n5 a29b\ne1 7ca9\n9f 63df\n5b 93cd\n41 928b\n76 3d3e\n2b 665\n94 6918\n49 924b\n32 8526\n1 29\n68 3c42\n88 ea40\nee 5e56\ne0 f408\nf7 55bf\n7b bf67\ne9 5e49\n25 ae9b\n3f afdd\n3b af67\na9 4e49\n2e e56\nd9 79c3\n20 a408\n4c 90d8\nec 5e52\nc 80d8\n2e 2ed6\nac 4e52\n6c 1e52\n2c e52\nc1 70a3\ndb 71e5\nd1 fb03\neb fc45\n5a 3166\nea 5e46\nbc 45fa\naa 4e46\n7c 35fa\nb3 45af\n2a a6c4\nf0 7d20\n2a e46\nd 2ad9\n3e afd6\n8 80c8\n2a 2ec6\na8 4e42\nb1 45ab\na9 6c49\n28 e42\n7a 3d44\n60 3c02\n80 ea00\n38 2d62\nbc e55a\n7a 3f44\n60 3e02\n57 13bf\n71 1501\ne1 5ca3\n28 86e8\nfb 5de5\n2a a46e\n11 a1ab\n15 a19b\n2 8226\nd5 d331\n9a 69e6\n2 8a0e\n1c 8b50\nf3 df2f\n19 a16b\nf7 df1f\n1d a15b\n4c 1270\n2 a884\n22 e0c\n9f 43d7\nac 4478\n7a 174c\n60 160a\n91 4ba1\nf5 559b\n7 2bd\n65 9e19\n73 958f\n92 c9a4\n7b 954f\n7c 95d0\n62 948e\nd2 5926\n19 836b\n81 c8a3\n9b c9e5\n6a 944e\nbc 6570\na2 642e\n8b e865\n91 612b\n7d 1d7b\ne8 dc6a\n47 9a9f\n66 3c36\nec dc5a\n9a c946\n4f 9a5f\n5a b946\nf3 5d0f\n51 bba9\nf 8a5f\nb 8a4f\n19 a34b\nd2 7906\n87 4a1f\nd2 d30c\n53 99af\n5d 3379\n57 999f\ne 887c\n4a 986e\n4f b2ff\n69 b441\nd9 fbe3\nf3 fd25\n1f 895f\n8 2ac0\nc 2278\n6 889e\nd5 51b9\nde 7bfc\nc4 7aba\nc 2a70\na 884e\nc8 7a6a\n69 be49\n49 104b\n71 97ab\nd7 591f\n75 979b\n97 e31f\n18 a9c2\n38 f4a\nc6 581e\n7e 97dc\n64 969a\n97 491f\n35 879b\n56 119e\n2d ae79\n93 490f\n31 878b\n86 481e\n3e 87dc\nf7 5d97\n24 869a\n15 a1b3\n7d 97f1\n63 96af\n3b f4f\n1b a9c7\nb2 c586\n67 969f\n2f 2edd\na8 ce60\n7d bfd3\n97 c115\n86 ea16\n47 3895\n6b 966f\nba c546\n86 cab4\n6f 965f\n19 b4b\n90 c182\n5b bbcf\n75 bd11\n49 926b\n98 c142\n4d 925b\n64 be10\n60 be00\n50 b182\na1 4ca1\n70 170a\nfa 5fee\ndc f372\n1f 83dd\n5 829b\n1b 83cd\nd8 f362\n1 828b\n58 b142\n8a 4ac6\n98 c96a\nd1 fb23\neb fc65\n57 9935\n9b 6947\nd5 fb13\nef fc55\n9 80e1\n5e 3176\n5c b95a\n95 eb13\naf ec55\nf2 7506\n1e 2176\nd 8271\n58 b94a\n91 eb03\nab ec45\n1a 2166\n77 959f\nf7 fd1d\ndd fbdb\n13 8ba5\n3f 2ddd\n25 2c9b\nb8 cd60\n96 c9b4\n7f 955f\nda 73c4\nc0 7282\nc8 7242\nd8 f360\n1 8289\n51 91ab\n49 b849\n25 8e19\n33 858f\nd1 f301\n52 b9a4\n3b 854f\n28 26c0\n26 849e\nb0 cd82\nfe 77fc\ne4 76ba\n24 26b0\nc9 fae9\n26 8c96\n2c 2670\n15 819b\n11 818b\n9a 49c6\naf eefd\n19 814b\n55 b3b9\nf7 551f\n54 391a\n9 241\n93 4b25\nfc 5570\ne2 542e\ne6 d436\n91 69a9\n38 a5c2\ncb d865\nd2 f9ac\n35 2533\n44 b2b8\ne6 541e\n82 4a24\n95 6999\n4 88b8\n37 db5\nd1 512b\nd9 db49\n53 3b25\n4a bae6\n8b c865\n91 410b\nc3 722d\n2d 47b\nd7 d995\n6c 365a\n9d 6bf1\n83 6aaf\n2a ae44\na 46\n32 87a6\nf6 553e\n8 260\n6b 14c5\n51 1383\nc5 d813\ndf d955\n8b 6a6f\nb1 e5a9\nd2 db8c\n35 713\n28 ae40\n8 42\n92 4926\n30 87a2\n27 ae15\n7 17\n26 ae14\na9 6e69\n6 16\n2a eee\n44 1030\n25 ae11\n20 ae00\n8a 48e6\ncb 5a4f\n2f 8e55\nc0 f880\n23 2407\n2e 8e54\nd9 f9c1\n22 2406\n2d 8e51\n2c 8e50\ne5 d631\naa 6ce6\n61 b681\nc3 7805\n15 8b19\n91 c981\nc4 f032\nde f174\n7 809d\nc5 72b9\n2b 8e45\n3c 85f8\n2a 8e44\nd5 f9b1\nbe c55c\na4 c41a\n1e 23f6\nc2 f02e\ndc f170\n5 8099\n29 8e41\nf1 d5a9\nfa ffec\ne0 feaa\nac 6e52\n68 9e40\nb1 c5a9\nba efec\na0 eeaa\n28 8e40\nde d1dc\nc4 d09a\nf7 5597\n27 8e15\n4 8a18\n37 f15\n26 8e14\nff 7dd7\n2c a6da\n23 8e05\nb7 c51d\n9d c3db\n17 23b7\nb2 ed2c\n98 ebea\nef 5677\n22 8e04\nb6 c51c\n9c c3da\n16 23b6\n68 3460\nb1 4583\n21 8e01\nba 656e\n20 8e00\n80 e822\n9a e964\n1f 295f\n13 292f\n17 291f\n2 2086\ne9 5eeb\na 286e\n6 281e\n53 190f\n2f 2e55\n1e 83fc\nd7 59b7\n4 82ba\nb7 65bd\n2e 2e54\n36 7b6\nf7 d595\ne6 fe96\n2d 2e51\n1 a9\n32 85a6\nb5 65b9\n4d 1053\ne3 7e05\n2c 2e50\n34 7b2\nbf cdd7\nab 666f\nf4 7792\n33 8f2f\n4d 9071\nab 6e45\n9a c3ec\n80 c2aa\nb3 47a7\n2b 2e45\n33 7a7\n32 8f2e\n4c 9070\naa 6e44\nb2 47a6\na9 cec3\nc3 d005\n69 3643\n70 9d0a\na9 6e41\nb1 47a3\n29 2e41\n31 7a3\nfa 77cc\ne0 768a\n27 2e15\n26 2e14\nd5 d191\nde fbd4\nc4 fa92\n9b 63ed\n81 62ab\na1 e403\n90 4988\nbb e545\n8 a840\n10 81a2\n25 2e11\nc3 d00f\ndd d151\ncc fa52\n8d 48d1\nf2 7706\n9d c9d3\n89 626b\n66 3694\n64 3690\n20 2e00\n5a 31e6\ndb 53cd\nc1 528b\nc5 50b9\nd4 fb12\nee fc54\n2a 2e46\n8 8048\nb3 65af\n6f 1e55\n8a c8cc\n83 6205\n4a 304c\n30 2f0a\ne8 56ca\n2f e55\ncc 58d0\ndc d9d2\nc8 726a\nf7 55bd\n6e 1e54\n2e e54\nbb 656f\n9c c3d8\n16 23b4\nad 4e51\n6d 1e51\n2d e51\n9d c9d9\n17 29b5\n8 286a\nba 45c6\nac 4e50\nf5 55b9\n6c 1e50\nb5 45b9\nbe 6ffc\n60 9ea8\na4 6eba\n2c e50\n8a 6866\n28 4c0\nb2 4da4\neb 5e45\nbd 45f9\nab 4e45\n58 1b4a\n7d 35f9\n6b 1e45\n3d 5f9\n46 303c\n2c 2efa\nfe 57fc\ne4 56ba\nd7 d31d\n58 99c0\n9c 69d2\n2b e45\nea 5e44\nbc 45f8\naa 4e44\n7c 35f8\nb3 45ad\n2a e44\n25 ae93\n3f afd5\n6c 16d0\n3 a025\na9 4e41\n69 1e41\n29 e41\nf6 55b6\nc1 7029\n1e 1d6\nb6 45b6\na8 4e40\nc3 d085\n69 36c3\n70 9d8a\n8f e0d5\n40 1022\n5a 1164\n51 9189\n95 619b\n82 4226\ne6 d496\n47 b0b7\nb5 4f99\n99 616b\n26 e14\nb1 cf03\ncb d045\n78 9d4a\n59 9149\n9d 615b\n82 6884\n25 e11\n23 e05\na2 4e04\n74 35b8\n22 e04\nf7 df17\n1d a153\nc2 f00e\ndc f150\n21 e01\n51 3303\n6b 3445\n58 99ca\n36 af94\n20 e00\nf5 df13\n1b a14f\n77 bf37\ne5 5e19\nf3 558f\nb2 e5a4\n99 436b\nd1 518b\n7b 3fc7\n88 4068\ndc f9d8\n3f 255f\n2a 246e\n7f 9fdf\n1 88a1\n2e 245e\nfa f5e4\ne0 f4a2\n1d 8b79\n66 9c9c\n73 150f\ne7 7ebd\n51 110b\n34 a51a\nc7 5a9f\ncb 5a6f\ncf 5a5f\n89 6863\n87 4a9f\n8b 4a4f\n4b 1a6f\n3e ad7e\nf a5f\n94 41b8\nc2 5a04\nb a2cd\nd1 7929\n78 b542\nb a4f\n1d b51\n3 a0f\nd7 599f\n8e 487c\ndf 595f\n7d 97db\nc6 589e\ncf 72ff\n50 39a2\ne9 7441\n97 499f\n4e 387c\n93 498f\n4a 386c\n9f 495f\n9b 494f\n7 aabd\nc5 7033\ndf 7175\nf aa7d\n8a 484e\n5f 195f\n53 192f\n57 191f\n4a 186e\n4 a832\n1e a974\n4e 185e\nd4 7112\n76 9fbe\n5c 1970\n42 182e\n16 a934\n46 181e\n1f 95f\n1b 94f\n17 91f\n13 90f\ne 85e\nda d9e4\nc0 d8a2\na 84e\n6 81e\n57 99b5\n9b 69c7\ne7 569f\n5e 31f6\na1 ec83\nbb edc5\neb 566f\ndf 53dd\nc5 529b\n77 973d\nbb 674f\n3c 2df2\ndb 7bcf\nf5 7d11\n99 e9c1\nc9 526b\n4e 18fe\ncd 525b\n4e 105e\ne4 7e10\nf2 7586\na9 6463\n4a 104e\ne0 7e00\n1a 21e6\nfa 7546\nf1 77a9\n7 ab5\nd0 7182\n9f 43dd\n85 429b\n8 8840\n11 8189\n1a abcc\n0 aa8a\ne 8fe\n8d 425b\n55 9913\n5b 3bcf\n75 3d11\n19 a9c1\n49 126b\n7a 3546\n72 3506\n64 9e12\n7e 9f54\nee defc\n14 a138\n7 a097\nd2 73ac\n3c 5fa\nf3 55af\nf7 559f\n54 399a\n9 2c1\n8c 6ad0\nfb 556f\n56 13b4\n57 19b5\n5d b3f9\nff 555f\n97 e91d\n48 186a\ne6 549e\nea 546e\nee d476\n99 69e9\nd5 519b\n7f 3fd7\n8c 4078\nd6 7bbe\nf0 7d00\nb3 458f\n50 332a\n6a 346c\n17 9b5\n8 86a\n41 3a23\n5b 3b65\na6 449e\nc8 dac8\n2b 64f\n42 3aa4\n4a 3a64\n95 419b\n3f 2fd7\n4c 3078\n91 418b\n3b 2fc7\n48 3068\n62 3484\n99 414b\nc 2ad0\n96 49b4\n7f 155f\n77 151f\ndd 7b5b\n13 b25\n9f e3df\n6a 146e\n6e 9476\n19 29e9\n24 a432\n3e a574\n8e 48fe\n43 1225\na a64\nf8 5d42\n1d 29d9\n97 e39f\n7c 1570\n62 142e\n86 e09c\n51 112b\n91 c303\n12 89a6\nab c445\nee fefc\n58 914a\neb 7ecd\n55 111b\ndf 59ff\ncf faff\ne9 fc41\n4c 9a72\n71 b521\nb4 4d32\n39 af49\nae eefc\n18 814a\nab 6ecd\n15 11b\n9f 49ff\n35 af19\n9b 49ef\n31 af09\nd9 d1cb\n53 31a7\n4b 3845\n95 e1b9\n53 11a7\nb0 6daa\n4b 12cf\n65 1411\n30 2daa\n83 60a5\n55 1311\n70 b52a\n70 1daa\nc3 50a5\n39 7c1\n30 daa\n83 40a5\nf0 d52a\n74 3d9a\n29 6c1\n80 caa8\nb3 4fa5\n6d 16f3\n30 ada8\neb 5467\n21 8ca9\n82 4aa6\n20 8ca8\n53 11a5\ne2 dc0c\nc8 daca\n42 3aa6\ne9 5463\nad 44d9\n3f a5f7\nb1 6da9\n31 2da9\nb0 6da8\n30 2da8\n42 baa4\n2b 864f\nf6 d736\na1 6ca9\nd2 f1a6\nf2 572e\n39 a7e9\nf2 7da4\n68 34c0\n71 b529\n4c 9a7a\n9a c9ee\n93 6327\n7f 1f77\n63 b487\n70 b528\n92 6326\n7e 1f76\n20 2ca8\n28 a6e2\n31 da9\n40 b802\n5a b944\n8e 627c\nf0 5da8\nb 867\nf 2a7f\n70 1da8\ncd 727b\nf2 f724\ne1 5ca9\n6d bc79\n46 ba96\n57 9195\nf1 f723\ne0 5ca8\n41 ba89\n71 b723\n60 1ca8\n4b b2ed\ned 5453\nfb 5565\ne1 5423\n22 a6ae\n3c a7f0\n82 682e\n9c 6970\nb1 e703\na0 4c88\n32 ada6\naf e6ff\n30 ada2\nee 7cfe\na3 4625\nef d6ff\n70 9da2\naf c6ff\n30 8da2\ne1 d603\nfb d745\n62 9ca6\na1 c603\nbb c745\n22 8ca6\ned d6f1\nb2 6da6\n28 24c2\n34 8f10\neb d6ed\nb0 6da2\n26 24be\nb4 e59a\na2 6ca6\n24 8e10\n70 b522\nbe e5dc\na4 e49a\n9a 61e6\n8c 6a70\n8 82c0\nb0 4da2\n26 4be\n98 61e2\nf4 d59a\n6 82bc\n48 b84a\nb4 c59a\nf2 d526\nc1 5029\nf1 df81\n17 a1bd\na2 4ca6\n6b 964f\n82 caa4\nfe d5dc\ne4 d49a\n33 ada5\nb1 e701\n32 ada4\n31 ada1\n51 1329\nb1 e70b\n32 adae\na4 4630\n4b b8c7\n58 b968\ncb 5067\neb fe65\nfc fdda\nb1 c701\n32 8da4\na7 6e9d\n31 8da1\n6e 3476\naf c6fd\n30 8da0\na5 6e99\n6f 94d7\n7c 9578\n9e 4376\n20 8ca0\ned f479\nc6 f296\nc9 5063\ne9 fe61\nb9 cdc9\n33 2da5\n31 2da1\na2 c624\n72 1586\nb4 e598\n58 b3e2\n72 b524\n3d 257b\n4b 38c7\n58 3968\n70 b520\n20 2ca0\nef fe55\n73 1da5\nfd 7ddb\n33 da5\n0 88a8\nd6 d93e\ncf 7277\n8e c856\nf1 5da1\n67 14bd\n4e b856\ne7 5c1f\n45 bab9\nb1 4da1\n27 4bd\nf0 5da0\n66 14bc\n98 61e0\nf 2a77\n2f 26d5\n36 8d9c\nf4 d598\nf 8057\nd4 d93a\ncd 7273\ne3 5ca5\ne4 543a\nfe 557c\n59 13c1\n8b 60e5\n5d 1bf3\n77 1d35\n8a 60e4\n8e 6a74\nf0 d5a2\nce fafe\ne8 fc40\n7d 157b\n89 60e1\n5b 1bef\n75 1d31\n42 3206\ne0 5ca0\n88 60e0\n41 ba81\n60 1ca0\ne4 d498\ned fe51\ndb 5165\nc1 5023\n85 4099\n17 a1b7\nbb 4567\n88 c06a\na 2064\n5e 315c\n44 301a\n9e c9dc\n84 c89a\n97 6315\nb9 4563\na 64\n95 e199\n53 1187\nd5 5b93\nef 5cd5\n30 2d8a\n83 6085\n55 1b93\n6f 1cd5\n8b 4a6d\nd4 5b90\n74 b51a\n10 ab20\n3e 2ddc\n24 2c9a\nba 6dcc\na0 6c8a\n70 b50a\n3a 2dcc\n20 2c8a\n74 1d9a\nc7 5095\n34 d9a\n87 4095\nf0 5d8a\n2 aac\n65 16b1\nb0 4d8a\n70 1d8a\nc3 5085\n30 d8a\n83 4085\n2 822c\naf e6fd\n30 ada0\n7a 1dcc\n60 1c8a\n80 ca88\nb3 4f85\nb1 4523\n34 ad98\n25 ac99\n21 ac89\ne5 f433\nff f575\nd4 59b8\nab 4467\ncb f265\n29 26c1\n30 8d88\n9 2a63\n79 3d69\n52 3b86\nb1 6da1\n1c b50\n2 a0e\n27 24bd\n64 9c98\na9 4463\nf2 5586\nc9 f261\n31 2d89\nf3 df27\n19 a163\n30 2d88\nf2 df26\n18 a162\n75 b519\n99 41e1\n74 b518\n24 2c98\ne6 de36\nc a072\ncf f255\na9 6c61\n71 b509\n4c 9a5a\n7f 1f57\na0 6c88\n88 e062\n70 b508\n7e 1f56\n20 2c88\ne2 de26\n8 a062\nf2 7d2e\n2c a6d2\n35 d99\n34 d98\nb2 ef24\n1c 8172\n68 b6c2\n71 1d89\n59 9163\nf 2a5f\n70 1d88\n58 9162\n30 d88\n18 8162\n49 9063\n7a bfec\n60 beaa\nb5 65bb\n71 95a9\n8b 6a45\n93 43a7\n71 b703\n60 1c88\n48 9062\nb a2ed\nad 4453\na3 4427\n2d 8473\nc3 f225\nbb 4565\na1 4423\nc7 f215\n9b 4167\n68 bc6a\nee 7cde\na3 4605\n26 ac96\n46 121e\nf5 75b1\na6 46be\n66 36be\naf c6df\n30 8d82\n7e 37fc\n64 36ba\n68 3660\n62 9c86\n19 8b63\ne8 dec8\n62 3ea4\na8 cec8\n22 2ea4\ne7 fc95\n3d 277b\ned d6d1\nb2 6d86\n6d 96d1\n32 2d86\neb d6cd\nb0 6d82\nb9 c563\n26 249e\n76 b516\n45 3019\ncf 78fd\nf6 d716\na1 6c89\nd2 f186\nf2 570e\n26 2c96\na2 6c86\n22 2c86\nca 52e4\n4a 12e4\n70 b502\n3 a28d\nc9 78e9\ncd 5079\na6 4e96\n40 10a2\n5a 11e4\nb6 4d96\na8 e6c2\nb1 4d89\nd9 7be9\n36 d96\nd aa71\n56 bb94\nf4 5d92\n6 ab4\n58 11e0\nb4 4d92\n74 1d92\n4b ba6d\n34 d92\nb aa6d\n54 bb90\n39 87cb\nf2 5d86\nc9 fa61\nb2 4d86\n89 ea61\n32 d86\n9 aa61\n52 bb84\n50 bb80\n48 10e0\nee 76d6\nf6 fd36\n25 2639\na4 ec98\n62 1c86\n82 ca84\n19 b63\n75 9d91\n61 3629\nd 27b\n80 ca80\n8e 6ad6\n4a 9ac4\n83 e8af\n9d e9f1\n6c b45a\n8 aa60\n91 4123\nc6 7ab6\n27 a63d\n66 163e\n36 ad94\nc6 5abe\ne0 5c00\n51 1309\n8b 4067\nab ee65\n58 bb6a\nd1 5b01\n47 121d\n26 ac94\nd0 5b00\n46 121c\n40 1208\nca 5aec\n33 8d85\n67 36bd\n32 8d84\naf c6dd\n30 8d80\n64 36b8\n93 c9ad\n7c 9558\n9e 4356\n20 8c80\n8f 4057\naf ee55\n5c bb5a\n48 3a60\n4d b8fb\ncc f258\nde 7376\n7 29f\n37 2d95\n4 a898\n36 2d94\nb5 6d91\n5e 997c\n44 983a\nd 8251\n35 2d91\n27 8cb7\na6 c614\nb4 6d90\n34 2d90\n33 2d85\n32 2d84\nda d96c\nc0 d82a\n69 1443\nb1 6d81\na0 c420\n27 249d\n65 3e99\n4d b273\nb0 6d80\nb9 c561\n26 249c\n5d b3d3\n77 b515\n27 2c95\n5c b3d2\n76 b514\n26 2c94\ndc 7958\na5 6c91\nb9 edcb\nf6 5716\n5b b3cf\n75 b511\n25 2c91\n39 adcb\n76 1716\na4 6c90\n5a b3ce\n74 b510\n24 2c90\na3 6c85\n5 2b1\n50 398a\n59 b3c3\n73 b505\n4e 9a56\n23 2c85\nf0 f5a0\n58 b3c2\n72 b504\n22 2c84\n3d 255b\nd8 7948\na1 6c81\nbc 6558\nb5 edbb\nf2 5706\n90 c320\n17 239d\n57 b3bf\n71 b501\n4c 9a52\na7 4e95\n6e 1cdc\n7f b757\n54 1b9a\n21 2c81\n3c 2558\na0 6c80\na9 c461\n16 239c\n8d 4053\nad ee51\nc9 d069\na2 ce86\n7b b76f\n77 1d95\n44 9898\nad 6473\n69 9461\n7a b76e\n76 1d94\nf9 f76b\nf5 5d91\nb9 e76b\nb5 4d91\nba cfc4\na0 ce82\n79 b76b\n75 1d91\nf8 f76a\nf4 5d90\nb3 4d85\n80 c888\n4d 3251\n54 9918\n73 1d85\n33 d85\n0 8888\n72 1d84\nd 22f3\n14 89ba\n27 2435\nad c459\n29 c61\nd6 d91e\ncf 7257\n99 61c1\n71 1d81\n4f 38f7\nd5 d91b\nce 7254\n31 d81\n95 c91b\nf 28f7\n8e 6254\nb0 4d80\n6a b66e\n66 1c94\n3 a2f\n1d b71\ne9 f66b\ne5 5c91\n8d 60d1\n81 4a2b\n9b 4b6d\n8c 60d0\n7a 376c\n74 9d92\n60 362a\nab c64f\nc2 faa4\n2c 8cf2\n8b 60c5\n5d 1bd3\n77 1d15\n44 9818\n38 2dca\n63 1c85\n8a 60c4\n62 bea6\nb7 65b7\n73 95a5\n18 8948\n62 1c84\n19 b61\n94 49b0\n7d 155b\ne1 5c81\nfc 5558\n57 139d\n89 60c1\n5b 1bcf\n75 1d11\n61 1c81\n8a e2e6\n93 49ad\n7c 1558\ne0 5c80\nde d976\nd 279\nc3 5aad\n3e 8776\n56 139c\nd6 d3be\nf0 d500\na0 4c80\n88 60c0\n5a 1bce\n74 1d10\n7a bfe4\n60 bea2\nb5 65b3\n71 95a1\n60 1c80\n5c 31da\n38 ad6a\n8b e065\n75 1db3\nf4 5710\n28 ac6a\n5b 3167\nd5 5393\nef 54d5\nb0 658a\nf2 7f8c\n5c 11da\n7d 3ffb\n97 413d\n28 8c6a\n9d e179\n5b 1167\n67 9cb7\ne6 d614\nb8 6d6a\nc2 d806\n8b 6065\nb0 6d2a\n30 2d2a\n83 6025\n28 2c6a\nb9 45c1\n39 5c1\n89 62e1\n90 c9a8\n32 870e\n63 bca5\nf8 5d6a\n23 aca5\nb8 4d6a\n22 ae84\n2 86\nad ecdb\n78 1d6a\nb1 4f23\ncb 5065\n38 d6a\n71 3f23\n8b 4065\nb0 4d2a\n79 3741\n30 d2a\n69 3ee3\n83 4025\nbd e75b\n3e adfe\n13 aba5\na8 4c6a\nb7 ed1d\n9d ebdb\n68 1c6a\n88 ca68\na1 4e23\nbb 4f65\nb5 e71b\n36 adbe\n5e b3dc\n44 b29a\n35 8d3b\ncb faed\n2e 2674\n6e 1674\n31 ad29\n23 ac87\n6d 1673\n30 ad28\n9e e95c\n84 e81a\n21 ac29\n13 ab87\n20 ac28\n9b 4b67\n99 e169\n23 e27\n36 2d9c\n9 243\n93 4b27\n31 8d29\n8b e0c7\n98 e168\n8 242\n92 4b26\nd8 db4a\n52 3b26\n4 20b8\n87 6a3d\n87 cabf\na1 cc01\n4b 3a67\nde d95c\nc4 d81a\n8a 4a66\n59 99c9\n9d 69db\n9e c95c\n84 c81a\n15 abb1\nb7 4d17\n86 cabe\na0 cc00\n89 e069\n7c 3f58\ne a076\n83 4a27\n96 699c\n21 8c29\nc9 da4b\n43 3a27\n88 e068\n51 9989\n95 699b\n82 4a26\n39 fe3\n53 1125\nb1 6d29\n31 2d29\na3 6c87\nb0 6d28\n23 2c87\n30 2d28\nef f6dd\n70 bd80\na4 66b8\ne0 7c22\n23 c8d\nfa 7d64\nfb ff6f\n24 8e98\n57 1395\n3 2aad\na1 6c29\n4e 32fe\n68 3440\n21 2c29\n93 6b87\na0 6c28\nb4 45b2\n7 2a95\n13 2b87\n20 2c28\n33 870d\nd2 5b86\nf9 5d69\n74 15b8\na6 c494\n4e ba56\nb9 ed43\n43 1aad\ne1 5c29\n37 adbd\n90 e180\n31 dab\nb0 4708\n64 14b8\n45 b299\na1 4c29\nd2 d126\n46 ba16\n3d 8fd3\n57 9115\nf2 df8e\n18 a1ca\nd3 5b87\ne0 5c28\n9 2869\n70 b5a8\n92 63a6\nc0 f008\n7e 1ff6\n1 2829\n60 b4a8\n38 2d48\n82 62a6\nb0 ef08\n6e 1ef6\n9a e166\nd9 5941\nbb 67cf\n77 97bd\n72 9d26\n41 1829\n8a 42e6\n2a 8c66\n53 b307\n42 188c\n28 8c62\n51 b303\n6b b445\n40 1888\nf8 7f48\n8a e066\n62 9c26\nba 6d66\nd4 719a\n3a 2d66\nf0 75a8\nb 2067\ned d671\nb2 6d26\nc5 f8bb\ndf f9fd\n28 2442\n70 35a8\neb d66d\nb0 6d22\ndd f9f9\n26 243e\nde 71dc\nc4 709a\n2a 2c66\n28 2c62\nac e45a\ncd 78fb\n7 a29f\ne0 74a8\na2 6c26\ncf f8fd\n18 2342\n60 34a8\naf ecd7\nbc ed78\n7a 1d66\nad ecd3\n78 1d62\nc7 5837\ne 827c\n38 d62\n58 bb60\n30 da8\nb0 4d22\ndd d9f9\n26 43e\n50 bb20\nea 5c66\ne8 54e8\naa 4c66\nca fa64\nd4 5932\ne 82d6\nac ec78\n9f ebd7\n6a 1c66\n8a ca64\n85 eab9\n43 1aa7\nd 28f9\na8 4c62\ne7 7c1f\n3 aa7\nc8 fa60\nf 2add\n88 ca60\n47 929f\n28 c62\n48 ba60\nde f376\ncd 58fb\n7 829f\n28 86e0\n8a 4864\n5e b3d4\n44 b292\nba 4d64\na0 4c22\n21 ac23\n3b ad65\nb8 67c0\n96 e916\n20 ac22\n3a ad64\n73 97ad\nb7 67bf\n39 ad61\nb0 6780\n31 ad21\n86 e816\n22 e84\nf7 df97\n1d a1d3\n54 3bb0\n3d 75b\na0 6680\nb2 ef2c\n1c 817a\n55 b333\n44 18b8\n6f b475\n8 aae2\n22 ac24\n21 ac21\n7 2a3d\nf7 57bf\nd4 d198\ncf 52fd\n50 19a0\n96 c916\naf 6e5d\nd4 d912\n5 2a39\n38 8d60\nad 6e59\n81 e023\n9b e165\nf0 5780\nb0 4780\n80 e022\n9a e164\n8a 404e\na7 6e1d\n99 e161\nb5 6d33\n71 9d21\n31 8d21\n8c c0d8\n6 20b4\nc6 d816\n86 c816\na7 46bf\n8a 42c4\n4 20b0\nad 6c73\n69 9c61\ne6 56bc\n87 6a35\n29 8c61\na6 46bc\n9e c954\n84 c812\n86 6a34\n28 8c60\nbf 47fd\na5 46bb\ne0 5680\na0 4680\n42 3284\n21 8c21\n20 8c20\na1 6c23\nbb 6d65\n88 e868\na0 6c22\nba 6d64\n85 c2b1\nd0 f98a\n39 2d61\nb8 6d60\n99 cbc9\n13 2ba5\nf0 d522\n77 359f\n0 a828\nc5 f8b9\ne 22fe\n28 2440\nb0 6d20\naf 667d\n30 2d20\nc5 7099\nf6 f596\n26 8e94\n7 a0b5\n9c 417a\ndc d9fa\nd5 7333\nef 7475\n88 6ae2\na2 6c24\n18 2340\nb2 6f2c\n1c 17a\n8 2ae2\na8 cc48\n22 2c24\nea 56c4\na1 6c21\n6a 16c4\n71 bd09\n44 9830\n88 6842\n26 a6be\n16 914\n85 4039\n21 c03\n3b d45\n8 8848\na7 64b7\n63 94a5\n63 b685\nf8 574a\n75 3d19\ne3 56a7\n88 4a4a\nad 64f9\nf0 570a\nd0 f182\n55 3919\nc3 52a7\n79 1d49\n8d 60f9\na0 e420\nfa 7d4e\naf 4675\nbb e74f\n3c adf2\nf2 7d0e\na7 4635\nb3 e70f\n34 adb2\n21 2421\n98 e140\n4d b259\n12 b84\n5f 13d7\n6c 1478\n51 1ba1\n1f 2977\nda 794e\n8f 4275\n9b e34f\n1c a9f2\n3c f7a\nd2 790e\n87 4235\n93 e30f\nad e451\n14 a9b2\n1 2021\n3f fd7\n4c 1078\n2d ae59\na3 6e0d\nd 5b\n84 4238\nbe e554\na4 e412\n58 b1c2\n31 8da9\n6e 347e\nc9 f2c3\ne3 f405\n5a 39cc\n40 388a\n93 6b85\nc6 5a9e\n12 a32e\n2c a470\n75 b593\ne6 5c3e\n3c add2\nd3 530d\n54 19b0\n67 b495\nfc 555a\n37 ad1f\n98 4b60\ne 27c\n6 201e\n3a 256e\nf0 550a\n2 22c\naa 4e64\nbd 6dd9\n19 21c3\nf3 5f87\n4c 125a\nd6 5b3e\n2c acd2\ne1 74ab\nfb 75ed\nc3 520d\n44 18b0\n21 2e09\n27 ac1f\n88 4a60\n47 129f\n49 98cb\n42 3204\n3 2805\nd0 f120\n7 aa15\n22 2e04\n93 430d\n14 9b0\nd9 f969\n2b 8ccf\nc1 fa81\n58 3b60\n1c a352\nb5 6539\neb 56e7\nd7 f9bf\n3a 2546\n31 27a9\n95 6b39\n7e 1d56\n75 1fb9\n7c 1d52\nb3 6f0d\n1d 15b\n2 884\n3d af59\n9 2ac3\n23 2c05\nf0 f520\nea 7ce4\nd0 7ba2\n31 a729\nc2 788e\ndc 79d0\n9b 6bcd\n81 6a8b\n8d 6af9\n1 8a29\n16 114\nc9 f8cb\n2c 2452\nd0 d982\n46 909e\n1 2aa9\na 2846\na0 4e88\n32 afa6\n33 785\n42 92a6\n1a b46\n25 24b3\n3f 25f5\ne6 debc\nc a0f8\n91 6b01\n7 221d\nd5 f191\nc1 7801\n37 2f1d\n84 4090\n80 4080\ne6 f41e\n2 82a6\n29 4c1\nb3 4da5\n80 c8a8\n1e 9fc\n2f a477\n4 8ba\n9d 4359\n62 94a6\n3a d46\n31 fa9\n40 ba02\n5a bb44\n74 3590\nd 22db\n27 241d\n69 1ceb\n7a b766\ne8 5648\n65 1cbb\n7f 1dfd\n76 b736\ne4 5618\nc4 f090\n8b e2cf\na5 e411\n79 1749\n59 b1c1\n7b 1ded\n61 1cab\n72 b726\ne0 5608\nc0 f080\nc6 d096\n86 e89c\n51 192b\n3b 2d6f\n30 a582\n88 62ea\na2 642c\n80 e080\n52 9b8e\n6c 9cd0\n4c 98d0\n22 84a6\nb3 452f\n68 1648\nbb c54f\nd2 f9a4\n48 b0c0\nf7 df1d\n1d a159\n64 9e90\n60 1608\n40 b080\ne2 56ac\nf3 df0d\n19 a149\n4f 92f7\n4c 9ad0\n48 1248\na7 e61d\n28 acc0\nd3 53a5\na0 cea8\n76 959e\nf6 fd1c\ndc fbda\n12 8ba4\n44 1218\nce 5afc\n27 8637\n28 648\n7b b54f\n92 e9a4\n7e 1dfc\n64 1cba\nfd 5759\n24 618\n4 a090\nfb ff67\n24 8e90\na8 6c60\n89 cac9\n3 2aa5\n20 608\n0 a080\naa 4eec\nc 8ad0\n1a 21c4\n0 2082\n8 248\n36 859e\n36 a536\na4 4418\n68 bc48\n72 95a6\n41 10a9\n50 bb02\n6a bc44\ne7 769f\na7 4e15\n79 35c9\n76 9596\n45 1099\n48 1240\nd2 5b24\nce 52fe\ne8 5440\nac e47a\n43 1285\n8b 60cf\n47 90bd\n83 42a7\n39 d49\n22 8c26\n82 42a6\n38 d48\n87 e89f\n52 192e\nd8 d148\n52 3124\n38 2fe2\n5 aa11\n5a 9166\n72 1d8c\n70 9782\n28 2c60\n6a 1446\nac e458\ne2 d606\n47 9815\n5f 1357\n8 a8ca\nf2 f52e\n35 8599\n15 893b\ne 2274\n3e afdc\n24 ae9a\nfc 7552\nd1 53a9\nb1 c589\n91 c92b\n8a 6264\nba efcc\na0 ee8a\nd9 f3e9\n36 8596\n8e 42fe\na8 4440\n3 285\n2e a45e\n8 240\n54 b3b8\nf6 551e\n92 4b24\n32 8586\nd8 7160\n1 89\nf0 77a2\na9 6e61\nd0 5902\n46 101e\naf e4df\n7a 156e\n91 c189\n9a ebcc\n80 ea8a\nb9 efe9\n16 8196\n43 302f\n5d 3171\n6e 3efe\n88 4040\n14 8192\n8b 6a6d\n3e dde\nd4 7b90\n10 8182\n5f bbf7\ncd 5ad9\nb2 ef04\n1c 8152\n5b bbe7\nc9 5ac9\nae eef4\n18 8142\nf8 5742\n1d 23d9\na7 6cbd\n54 1912\n60 bc88\n93 4185\na9 6c69\n82 6a86\n46 9a16\n18 81ca\n51 b383\n6b b4c5\n14 912\nba 4746\n55 1911\n65 9c99\n3 2087\nea 5eec\n10 2128\n26 243c\nc 22fa\n68 94e8\nac 64fa\nb0 4702\n42 b206\n4b 18cd\nd1 f181\n6a 9e44\nae 6e56\n8 860\n6 2034\n8c c058\n51 1983\n29 a4c1\nb3 eda5\n62 3426\ne8 d44a\n9a 43ec\n80 42aa\ned dcd1\nd3 db8f\n36 716\n1a 2b66\n1b 23ef\n35 2531\n99 49e1\n68 144a\n2c 86d2\nf2 5d2e\n11 a1a9\nad e6db\n78 176a\n58 b1e2\nd0 71a8\ncd d271\n92 6926\ne2 5e06\n8 2042\n30 a7a2\n20 ac88\n77 1715\n44 9218\n38 27ca\n2c 8e58\ne5 d639\naa 6cee\n43 b2ad\ne5 5413\nff 5555\nee 7e56\n48 1860\nfe ff74\ne4 fe32\n27 8e9d\ne3 dc0d\nc9 dacb\n43 3aa7\n2c 652\nb6 4f36\n0 a888\n57 1315\n24 8e18\n60 be88\n93 4385\n37 715\n79 35c1\n52 bbac\nf4 5d12\n6 a34\n22 8e06\n76 b79e\nf1 5721\nc2 580c\n40 ba88\n3a dcc\n20 c8a\n17 315\n50 31a8\n4d 9271\n98 c94a\n12 2926\n95 4199\n7d b573\n44 b238\ne7 fcbd\n30 2702\nba 6fe6\n2 aaa4\nf0 fd82\nd6 7936\n1d a37b\nc0 5880\n1e 8376\n1c 2972\n72 b526\ne0 5408\n36 a59c\n23 8627\n9d 4159\ne7 76b7\na3 4c07\n1 aaa1\n94 6998\n9b 4b65\n81 4a23\n9b e3cf\nb5 e511\n9c 6958\nd2 5b06\nf7 75b5\nf3 57ad\na8 6c62\na3 cc0d\n89 cacb\n3 2aa7\nd5 f939\n1e 237e\n1a a1c4\n0 a082\nc4 5030\naa 4eee\n0 aa80\nef dcfd\nd5 dbbb\n38 742\nc2 5026\n4c 9072\ne2 fe24\n6f 9655\n30 a70a\n2c 24fa\n38 8f48\na5 6439\nc7 f8bf\n2a 2446\nf2 fda6\n21 26a9\na aa64\nf8 fd42\n85 e8b3\n9f e9f5\n6e b45e\n28 6e2\n42 322e\n5c 3370\n56 9996\n2 aa24\nf0 fd02\n53 b1a5\n73 172d\nc0 5800\n6 8014\n69 3ce1\n38 74a\nf2 df86\n18 a1c2\ndc 5170\nc2 502e\n48 1062\n97 e115\nc8 5a4a\ned 74f9\n25 2439\n38 adca\na aa44\nf6 f51e\n12 83a6\n4 8010\nf8 dfc0\n1e a1fc\n4 a0ba\nbd c5db\n37 25b7\nc7 581d\n65 9699\n95 e111\n69 1449\n91 e1a9\n7d 9df9\n8 aa40\nc6 581c\n64 9698\n0 aa00\n53 13a5\n20 8ea8\nf8 5560\nc5 7299\n94 61b2\n50 91a0\n2b 2e65\n50 11aa\na3 44a5\n48 3848\n6 b6\n26 aeb4\n5c 1952\n61 14a9\n70 bf02\n8a c044\n45 3811\nea 5e66\nf 2afd\nd8 d942\n9 2a69\na8 4e48\n3a af66\na7 ec9f\n72 1d2e\n13 a9a5\n33 f2d\n91 e30b\nab e44d\n12 a9ae\n23 e2d\nf8 df40\n4 a03a\n1e a17c\nc 2a5a\n1d 159\ned d6db\n67 36b7\n84 4230\n62 9406\n31 f09\na8 e44a\n6 a894\n26 e1c\n13 a925\n90 6380\nd2 7126\n15 191\n3 a825\n80 6280\n6c 1ed0\n38 56a\n48 1a4a\n6d 34f9\n94 4930\na 4c\n49 1a49\n7a 9f46\nd3 51af\n69 b6c9\nc5 5a39\n83 caa7\n6c 9652\n8c ea58\n4a 1a46\nb2 6fac\n1c 1fa\nde 515e\n3c aff8\n45 9099\ne1 5ea9\n4a 184e\na4 e698\n62 1686\n2c 24d8\nb6 6dbc\n2 a824\n41 1a09\n3c ad58\n72 9f06\n11 a921\nf9 dfe9\n12 a184\n1 a821\ne9 dee9\n2 a084\n22 60c\nac 4ef0\nb4 6db8\nf0 5782\n0 a820\n6 2b6\n81 42a9\n8a 4046\naa ee44\nc2 da8c\n25 613\n3f 755\n76 1d3e\n5c 1b52\n3 a27\n16 299c\nb7 e53f\n41 12a9\n6a be44\n72 97a6\n3c a5f8\na8 e4c8\n66 14b6\n44 3a12\n5e 3b54\ne1 54a9\n90 41a8\n2f a45f\n32 d26\n52 bb24\nc aa7a\n70 15a8\n41 b289\n60 14a8\nc0 5080\n85 6899\nb6 ed96\n21 a681\n83 6805\n20 a680\n82 6804\n0 a280\n23 2485\n22 2484\nd 8079\n56 919c\n2f 2e77\n43 1a0d\nf2 7f2c\n5c 117a\n95 4333\naf 4475\n4a 12c4\n87 4a35\n59 31e9\n1 2821\n43 1007\n85 e019\n0 8000\n60 3c20\nca 78ec\n4 a290\nfb f547\nd0 598a\nb3 458d\na3 4e05\n75 35b9\n2b cef\nc1 7aa1\naa 464c\n36 a594\n40 1200\nca 5ae4\n3b a54f\n93 6ba7\n7c 3752\n74 3d12\nad 64d3\n69 94c1\nf3 dda5\nc7 7295\n50 1908\na a244\n29 2cc1\n5c 9972\nc8 7860\n2 a204\n1 a221\n82 e00e\n9c e150\na1 4e01\n3a 2f6c\n20 2e2a\n31 529\n30 f08\n28 a640\n92 4126\n9d 6979\nd3 5b27\n20 a600\n8a 40e6\n8 a240\n0 a220\n78 1742\n27 2cbd\na 8a66\n1d a9db\n3 2005\n7a 1dec\n60 1caa\nf9 5749\n7e 1756\n13 2b8f\n2d 2cd1\n7 a35\nda 53ec\nc0 52aa\nd9 7b6b\n2d a651\n97 4137\n5f 997f\nb0 45a2\n3 2a85\n28 ae4a\n8 2a40\n14 2918\n8c ead8\n4a 1ac6\n8e 4274\nbe cfdc\na4 ce9a\ne4 5438\nd7 5397\ne2 5e24\n8 2060\n43 9a8f\n5d 9bd1\n84 4a30\n2 2026\n88 c04a\n70 15a2\n4e 3afe\n68 3c40\ncf 5255\n7c 1f5a\n5c b9d2\n68 bc4a\n9b 4147\n50 11a2\n48 3840\n96 4136\n26 8c1c\nc 8ada\n31 a589\na2 4686\n22 6a6\n2 aa04\n48 b04a\n30 5a2\n84 e238\n42 1226\n55 319b\n8f e25d\n10 a900\na2 44a6\n4 a30\n50 bba8\nf2 5d0e\n12 1a6\na 2844\n32 afa4\nfe 7f54\ne4 7e12\ndb 53cf\nf5 5511\n77 3595\n44 b098\n81 c089\nb c7\n18 168\na9 eee9\n6 8096\nde 73f4\nc4 72b2\n1e 2b5c\n4 2a1a\n15 119\nee ded4\n14 a110\n1d 2959\n95 eb19\n53 1b07\n45 b0b9\n7 8095\n2f 4ff\nc5 72b1\n8b 68c7\n98 6968\n47 98b5\n32 a584\nf9 ffe9\n56 9196\na a264\nf8 f542\n22 a4a4\nc8 7060\na9 cec9\n23 2ea5\n44 9290\n5a b3e4\n40 b2a2\n18 2b42\n40 1080\n2 a0a4\n22 62c\n15 2919\n31 a581\n11 a1a1\n31 729\n1a 83cc\nd3 5987\n0 828a\n33 787\nb8 6d62\n2e 247e\nb3 cd0d\n99 cbcb\n13 2ba7\n10 a182\nd4 5130\nba 4fee\n2e 2efe\n48 3040\n70 b7a0\n0 8008\n19 3c3\n33 505\n22 2e06\n94 41b2\n8c 6850\n0 2828\n20 a480\nca 52ec\n0 a0a0\n20 628\n45 1211\ncf 5af5\nca 7ae6\n2b a66d\n74 b790\n5 2819\n36 ad16\n48 b260\n28 462\n37 a595\n1 aa21\ndf 515d\nc5 501b\ne5 fe19\n42 1286\n84 e298\n69 1469\n30 2522\n22 8e2e\n3c 8f70\n8f 68d7\n9c 6978\n4b 98c5\n4a b246\n53 190d\nb8 4742\n43 180d\na8 4642\ncd 7ad9\n65 1631\nb0 4d0a\nfd dff9\n16 a194\na6 4ebe\nc0 5000\n53 3b05\n1e 8b5c\n4 8a1a\n29 a4c9\n37 f17\ned def9\n6 a094\n26 61c\nb0 4f00\nb2 cdac\nab 66e5\na8 6e4a\n64 9e38\n45 1231\n90 490a\nc9 7ac3\ne3 7c05\n22 a484\ncc 52f0\n8d 68d3\n49 98c1\n45 1039\n76 9536\n34 a590\n48 b242\n51 1909\neb 56c5\n8f 40d7\n9c 4178\n55 9b19\nd0 79aa\n24 a490\n41 1809\n72 9d06\na6 463e\n90 c900\n6 801c\nc4 7238\n45 9a19\n14 a190\n34 718\nd2 712e\n15 199\nf6 dd1c\ndc dbda\n56 3bb6\nef 7655\n28 8e48\nd6 d91c\ncf 7255\n7 abf\n21 c01\n6f 3cf7\nf5 dd1b\nee 7654\n26 ebe\n40 1000\n94 e918\n52 1906\ne1 74a1\n39 ad69\n12 ab86\n56 9b16\n61 b483\n7b b5c5\n1 a021\n6a 16cc\n54 9910\n1a 2b6c\n0 2a2a\n11 129\nd6 73be\nf0 7500\n12 32e\nc9 d8e9\n2c 470\n75 1593\nd a251\n8a e064\nc a250\n5a bb6c\n40 ba2a\n95 613b\n51 9129\n40 1808\ne7 fc1d\ncd fadb\n3 8aa5\n67 949f\n70 3fa2\n8a 40e4\nce dafe\ne8 dc40\ndc 71f2\n53 938f\n6d 94d1\n54 1918\nd aad1\n29 ccb\n3a a746\n93 6b2d\n3 8a85\n8e cafe\na8 cc40\naa 66e6\neb 76ed\n14 918\n3 228d\nc aad0\n7b 956f\n8 2248\n86 6ab6\n42 9aa4\n5d 937b\n4 2ab0\nc 28d8\n3e a57c\n24 a43a\ne6 fc1c\ncc fada\n2 8aa4\n66 949e\nf0 dd82\nb0 65a8\n0 8a80\ne5 5eb9\n52 1984\n9 861\n7 2035\n8d c059\nb6 cd3c\n9c cbfa\naf 6675\n1 21\nae ccde\n63 9605\na7 6617\n58 1942\n64 b498\n5 a231\na 804e\na0 ee00\nb0 e520\n9f e3ff\nb9 e541\n41 1881\n5c 1158\na6 46b6\nee 7c56\ne4 fc32\nfe fd74\n27 8c9d\n8f 6a57\n4b 9a45\n26 ac3c\nc aafa\n1d 81f9\ncd fa5b\n3 8a25\n67 941f\n77 bfbf\n91 c101\n9a eb44\n80 ea02\n41 3881\n5c 3158\nc7 5a95\n3e a55e\n92 e92c\ncf 5277\n2 8a04\ncc fa5a\n2 8a24\n15 a999\nf0 dd02\n97 c9b5\n66 941e\na8 c442\nec 7c52\ne2 fc2e\nfc fd70\n25 8c99\n54 3b38\n47 3a97\n3d a55b\n6c 1670\n22 ac84\ncc 5af0\n42 120c\n20 e80\n96 e116\n9b 49e5\n81 48a3\n6a 144e\n6e 9456\n19 29c9\n2e 86d6\nf4 5d32\n3e ffe\n58 1140\n6 a2b6\n43 3805\n6 94\n1 8a21\nc5 5a91\nc8 58e0\n2 8284\n8 a60\nf6 5d3e\n1e ab54\n4 aa12\n15 8111\n0 8a20\nc4 5a90\n9e 43dc\n84 429a\n9a 43cc\n80 428a\n1d 8971\n3 882f\n7e 17dc\n64 169a\n5d 1979\n3a 856c\n20 842a\nbe edfe\n73 b725\n5a 13cc\n40 128a\n50 118a\na3 4485\nd8 716a\na7 c695\ndb dbed\nc1 daab\n24 632\n3e 774\nb 22c5\n12 898c\n3e ad56\nd 2859\n15 1bb\n3e 7dc\n24 69a\nf7 559d\n51 b323\n6b b465\n40 18a8\n28 26e0\n11 b01\nff 5ddf\n19 89e1\n34 59a\n90 e1a2\nb0 472a\nfb d74f\n7c 9df2\n41 1089\n72 9586\n29 8463\n90 c180\na2 6e86\nc9 7069\n45 9299\n23 a4ad\n1e 3dc\n4 29a\n7d 3df3\ne2 760e\nfc 7750\n1a 3cc\n0 28a\n79 3de3\nf8 7740\nc0 f288\nf3 7785\na7 cc97\nb4 cd38\nad 6671\nf6 7794\ncd f8db\n3 88a5\n4 803a\n1e 817c\na8 c46a\n7a 3fc4\n60 3e82\nf0 d502\ncc f25a\n2 8224\n15 a199\na 8a44\n5c 9158\n80 4a28\n1 8881\n1c 8158\n3e 2f56\n66 b6b6\n4e 3076\n5f 1957\n6c 3658\n83 6aad\nf0 7d22\n33 d8d\n2a a6c6\nc3 f80d\nc 2252\n46 9894\n5e 935c\n44 921a\n3a adcc\n20 ac8a\n77 1717\ne 8a54\n3a a56c\n20 a42a\n45 9233\n5f 9375\ncd f85b\n3 8825\n80 4280\ncb f26f\nc7 5895\n4b 3a6f\ne2 7e26\n25 e91\nb0 4f0a\nca 504c\nda f1ec\nc0 f0aa\nf3 75a7\nf1 5501\nd7 53bf\ncf 7a5d\ncd d8d1\n16 316\nfd d759\n77 3735\n64 9cba\n7e 9dfc\nf1 d58b\n8f 6a5d\nf 8d7\n1c 978\n85 c899\n1b 83ef\n35 8531\n87 6a1d\n31 5a1\nbe e7dc\na4 e69a\n56 13be\n70 1500\ne0 5ca2\nfa 5de4\n62 3e84\n85 6833\n9f 6975\n41 9821\n14 219a\n22 2e84\n3d 275b\n1 8821\n39 afe3\n53 b125\n89 c8c9\n3 28a5\nc9 f26b\nc5 5891\n30 8588\n10 892a\n9 2263\n79 3569\n52 3386\n9a 436c\n80 422a\ncb d24f\n4c 98f2\n68 3c4a\ne3 f6a5\na1 6e03\nbb 6f45\n8f c27d\n10 8920\n85 6a19\n98 c9c8\n91 6301\n12 29a4\nd8 f36a\nd4 5990\n8b 486d\n8 8860\na5 c413\nbf c555\nae ee56\n9f 43fd\n85 42bb\n51 9983\n79 97eb\n0 8820\n9d c3d3\nb7 c515\na6 ee16\n88 c8c8\n81 6201\n2 28a4\n9a 4146\n91 43a9\nd0 7180\n35 f11\n61 1689\n1a 146\n11 3a9\n20 ae02\n3a af44\ne4 5e90\na 20cc\n1e 8356\nf9 55e1\n2a 8cc6\nc1 7809\n46 3816\n16 a1b6\n84 4098\n6c b472\na 22c4\n11 898b\n65 b41b\n7f b55d\n42 3806\n70 3580\n95 693b\n51 9929\n42 1006\n84 e018\n0 2820\n32 a5ac\n5a b3ec\nfc 5552\n40 b2aa\n18 2b4a\n2 806\n40 1088\n28 8462\n22 a4ac\n51 3909\n72 972c\nb6 673e\n14 198\n11 189\n1a 2bcc\n0 2a8a\n83 422d\nb0 65aa\nd5 53b3\nef 54f5\n76 3f96\n9d 4179\nbc cf5a\n36 2f36\ne4 5cba\nfe 5dfc\ne6 7496\n59 9361\n9d 6373\n54 1118\nef f477\nc4 58ba\nde 59fc\n89 eac3\na3 ec05\n9e c3fe\nb8 c540\nd9 59e1\n13 8385\n32 2d0e\n6d 9659\nf0 5702\n15 2399\n6d 9edb\n72 1da4\nf1 5701\nd4 7192\n8b 606f\n8a 4a46\n5c 31fa\n86 62b6\n42 92a4\nec f6da\n22 86a4\ne6 f41c\ncc f2da\n2 82a4\n8e 6a54\nf0 d582\n29 aec9\n65 9499\n9b 636f\nc3 5825\n39 f41\n19 b41\n21 86a1\n83 4825\n92 49ae\n8 ca\n28 aec8\ne2 f40e\nfc f550\nec 545a\n57 b395\nd4 f932\n17 899d\nde 7956\n38 f40\n40 3088\n28 a462\n2 2806\n12 838e\n2c 84d0\nd8 59ea\n44 b298\n77 3795\n93 4b87\na0 4c28\n6 aa16\n17 8115\nb7 4db5\n90 c302\naa c444\nea 7ecc\n54 111a\nde 59fe\n20 8680\n82 4804\n18 b40\n20 86a0\n82 4824\n84 6a30\n92 61a6\n98 e96a\neb f66f\ne7 5c95\n9a c94c\n80 c80a\n90 61a2\n45 1a91\nef fefd\n59 914b\n43 1085\n10 8b88\nb0 4582\n2a 4c4\n10 382\nfa d5cc\ne0 d48a\ne0 76a8\nf9 f563\n22 848c\nb0 6f22\nca 7064\nfa fdcc\ne0 fc8a\n18 29ea\n46 9294\n42 b2a6\n1a 2b46\n42 1084\n7d b759\n90 c1a0\n1d b59\n4e 9056\ne9 7461\n1a ab46\n65 9691\nc7 5815\n44 98b8\n88 68ca\n61 1481\nf2 5fa4\n18 21e0\nc9 7061\n25 263b\n3f 277d\n45 9291\nc9 d2c9\n43 32a5\n64 9690\n60 1480\nee 7456\ne4 f432\nfe f574\n27 849d\nb1 cd81\ne5 76b9\n96 c91e\n8f 6257\n4b 9245\n23 2c0f\n3d 2d51\n65 b4b1\n56 b91e\nb 8245\n12 8984\ned f65b\n6e bcfe\n23 8625\n89 c2c3\na3 c405\ne6 febc\n50 910a\n8a 484c\n59 bbe1\nfb 5d47\n28 864a\n39 a561\n56 b93e\nb 8265\ndc f9da\n91 c301\n12 89a4\ne4 7498\n27 a6b7\n93 4187\n7a bdcc\n60 bc8a\nc4 7098\n7 a2b7\n5a b9cc\n40 b88a\n7c 1f72\n60 3480\ne6 5694\na9 e64b\n2a acee\n38 8548\n5a 3346\ned f4db\n23 84a5\n40 3080\nc6 5294\n89 e24b\na a8ee\n9b 496f\nfe 7554\ne4 7412\n35 27b3\n88 624a\n44 9238\n20 aca8\n77 1735\n38 27ea\nb0 6782\n23 2685\nb4 cd3a\nad 6673\n69 9661\n84 6292\n40 9280\n9e 63d4\n94 c93a\n8d 6273\n49 9261\n72 3526\nf8 d54a\ncf d2fd\n94 69b2\n50 99a0\nc5 7a99\n74 bd3a\n29 8661\n87 c2bf\na1 c401\ne3 54a5\ne5 5691\n4e b2fe\n68 b440\nd8 fbe2\nf2 fd24\na7 c41d\n8d c2db\n7 22b7\n9a 496e\n38 87ea\na2 ee0c\nc 805a\n45 b213\n5f b355\n0 a800\nf3 fdad\n3c 27f2\n28 8640\nd8 5b42\n4e 125e\n2e acd6\ne3 74af\nfd 75f1\n20 8600\n8 8240\na7 ec97\nb4 ed38\n72 1d26\n9 2a61\n52 3b84\ncb 5a4d\n85 6a39\n37 8595\n8c 6272\n48 9260\ncf 5a7d\n28 8660\n71 9783\n5 a011\ne4 5690\n7f 1777\n28 acea\n7e bf54\n64 be12\n5b 93cf\n75 9511\n8 8260\n7e 3ddc\n64 3c9a\n69 1c69\n42 1a86\n84 ea98\nb7 6f95\ncb 5a6d\n7 aabf\n21 ac01\n4f 9255\n31 f03\n4b 1045\n18 8b48\nf 8255\n46 983e\na a866\n4e 9254\n49 b241\n94 e91a\n2b acc7\n38 ad68\n1 a0a1\ncc 5870\n6 8214\ne2 5686\nac 64d8\n48 10c0\nf6 fd16\n25 2619\n19 14b\naf 6efd\nd0 5182\n4a 9a4e\n55 b3bb\n6f b4fd\n8 a862\n73 b7a7\ne1 5689\n58 31e0\n66 1696\n74 1592\nc8 d8e8\nc1 7221\n2b 46f\n75 959b\n4e 3276\n70 1582\n31 85a9\n74 9792\n2b 866f\nbd 4d59\ne6 5496\n45 1833\n5f 1975\n22 8426\n98 69c8\na8 c4ca\n22 24a6\n2e 8ef4\n50 1182\n1 20a9\n11 81a9\nd3 d10d\nb9 cfcb\n33 2fa7\nb 826f\n83 caa5\n6c 9650\nea 74e6\n3 882d\n4e 12fe\n68 1440\n52 9926\nc7 7a1f\n0 82a\n1a 96c\n22 ae26\n22 686\n75 9599\n7e bfdc\n64 be9a\na4 4438\n97 4397\naa 64e6\n2d cf3\nc3 7aa5\nac 4650\ndc 5b52\na2 64a6\n5 8031\n26 2694\n34 592\nb a26d\n54 b390\n24 e10\n1f a15f\n32 586\n9 a261\n52 b384\n8 22ea\n22 242c\n77 9f9d\n29 ce3\na8 4640\nc 8250\n8a 60e6\ncf 5a5d\n48 1842\n29 469\n2 286\na2 4486\n14 192\n34 af90\n9 8e3\n88 4240\n2c 2c5a\n10 182\n30 af80\n70 1702\nfa 5fe6\n90 c102\nc2 d006\n23 ac27\n91 4b09\n38 256a\nf1 d703\n72 9da6\n88 c2e2\na2 c424\n51 9109\n95 611b\n18 942\nfd f5db\n33 85a5\n22 aea6\n30 858a\nf4 5590\n29 8461\n96 6196\n52 9184\n9 8061\n32 8584\nf0 77a0\n55 1b91\nc 8af0\n12 8184\n99 c9c9\n13 29a5\nbd cfd9\n37 2fb5\nc2 702e\n5 99\ndc 7170\nd9 f36b\nd5 5991\n11 8921\n51 3921\nd5 f119\n14 1b0\nb2 ef86\nd9 f169\n17 935\nd0 51aa\nc8 7848\nb aa67\nd0 f1a8\n3 288d\n52 b3ac\nf4 5512\n6 234\ne7 769d\n34 25ba\n59 13c3\n73 1505\n62 3e06\n20 c02\n3a d44\n14 21ba\n39 fc3\n53 1105\n42 3a06\n20 8c08\n86 60b6\n42 90a4\n0 802\n1a 944\n40 1a00\n73 b5a5\n18 a948\n4e 9af6\nfc f5da\n32 85a4\na7 669d\ne6 7eb4\n50 1102\n59 916b\nec f4da\n22 84a4\ndc f1da\n12 81a4\n6a 946e\n87 629d\n8c 4872\n2a 86ee\n2c 265a\n72 9f2c\nb6 6f3e\ncc f0da\n2 80a4\nf0 5582\nc5 5833\ndf 5975\nc 8278\n6a 14c4\n50 1382\n7a b74c\n91 eba1\n60 b60a\n2f ac77\n9d 4b59\nce d056\n35 2519\na5 6cbb\n61 9ca9\nbf 6dfd\nb1 e521\na 8244\ncd 5a51\n80 4228\ncb d24d\n90 6902\n4c 98f0\n11 8181\n66 be16\n38 a5ca\na 8264\nf8 d542\nf7 df9d\n1d a1d9\n9a 4b66\n19 941\n31 85a1\n7f 1557\ne2 fe0c\n4c 905a\ncd fad3\ne7 fc15\n2b 8cc7\n38 8d68\n5a 3b66\n1 80a1\nf5 fd31\ndb fbef\n3e 2776\nd8 73e2\nf2 7524\n21 2429\n6b b647\n5a 1bcc\n40 1a8a\nab 64c7\n67 94b5\nb8 6568\n13 23ad\nc8 5042\n1e a1d6\ne8 fe40\n38 a5e2\n42 3824\nc8 d848\n1e bfe\n38 d40\n18 940\ne6 f696\ne9 5e69\n2 2004\n20 84a0\n0 80a0\na6 e696\nfa d56c\ne0 d42a\na6 649c\n3e 8576\nc3 58ad\n6a 94c6\n39 fc9\nfd dffb\n16 a196\ned dcd9\n67 3cb5\n36 71e\n53 1b05\n2 4\nac c6d8\n26 26b4\n3e 85d4\n24 8492\nfc 77f0\ne2 76ae\n0 aa20\n2d a671\n76 b794\na8 4442\n49 b8e3\nc8 f240\n83 c805\n46 9094\n53 b905\n8 28e0\n87 623d\n16 8194\n43 b805\n6 8094\n2e 4fe\nc4 72b0\nc 2a78\n25 a631\n0 a0\n8a 4ae6\n4d 18d1\nb2 4706\ne 8254\n13 92d\na a266\na 44\nfc f7da\n32 87a4\nc2 d826\n38 8f42\n64 9490\n81 c801\n44 9090\n41 b801\ndb f167\n4 8090\nfe d55c\n5e 33f6\ne4 d41a\n7 2a9d\n80 ca20\ne7 7e95\nef 76dd\n70 3d80\nfc 7f70\ne2 7e2e\n25 e99\n7d 9579\n56 9396\n3a 2dce\n75 9719\nf3 75af\nc7 589d\n98 e16a\ne7 5495\n47 1295\nd2 530e\nec 5450\n20 2e08\nb4 4792\nce f2fc\n38 854a\na aa66\ndd f15b\n13 8125\n2 aa26\n87 c01f\ne8 5e60\n1e 1f6\n9e ebfe\nb8 ed40\na5 c499\n2f 4d7\n3c 578\n21 ca1\nd4 7338\nc7 7297\nb 8065\ncd f05b\n3 8025\n19 8341\n89 cae3\na3 cc25\n68 1442\n9 88e3\n88 c240\n17 29bd\n4a 1a6e\n19 96b\n83 4a05\n55 31b9\n46 1294\ndc f15a\n12 8124\n1f a3fd\n5 a2bb\nd6 5194\n73 3f2f\n8d 4071\n52 3906\n7 a1f\n66 bc16\na 8064\n53 9187\ncc f05a\n2 8024\nc6 5094\n1b 89e5\n1 88a3\n80 c200\n31 872b\nea 5ce6\n68 9e60\nac 6e72\n3a 8f4c\n20 8e0a\n95 e319\n16 a9bc\n53 1307\n30 27a2\na a86c\n96 e31c\n17 a9bf\n62 3684\n85 6033\n9f 6175\n41 9021\nb0 65a2\n61 3c23\n7b 3d65\n48 b868\n6 281c\n8e c8fc\n87 6235\n4e 307c\n34 2f3a\n96 691c\n34 a798\na9 c4c9\n23 24a5\n45 1291\nd0 530a\nea 544c\ne5 5491\n4c 9252\n61 1c03\n7b 1d45\n3c 2dfa\n1 aa81\nd5 7913\n1c a358\n19 a96b\nac 6472\n68 9460\n59 91c1\n9d 61d3\n9c 6172\n58 9160\n4e 38fc\ncd 7259\n97 eb1d\n48 1a6a\nff 575f\n5a 1b6c\n8f eadd\n40 1a2a\n94 c112\n38 8560\n2e 2cfc\n14 2bba\nad 6659\n9e c154\n84 c012\n8d c8fb\n86 6234\n28 8460\nf 2a55\n71 9583\nc3 d805\n73 9da5\nb7 6db7\n74 bd12\n64 9638\na8 664a\n18 8160\ne 28fc\n8d 6259\n8 a6a\nd6 7bb4\nbf 475f\n7e bd54\n64 bc12\n8 8060\n51 9183\neb 7e65\n11 2381\n85 4a39\nf ad5\n3a a54c\n20 a40a\n48 9a6a\nbd ef79\n7b 1f67\naa 664e\n66 963c\n35 719\n15 a191\n74 3598\n48 3842\n6a 364c\n81 6aa1\n1 2aa1\n43 1287\n50 1328\n85 e299\nf 22d7\n16 899e\n1c 2378\n0 8280\n0 2a80\n83 e887\n90 e928\ncd 5273\n0 8a00\na8 c6ca\n22 26a6\nda 53ee\nf4 5530\n68 9c62\n85 6a91\n5a 13ee\n74 1530\n65 9c91\n61 1409\nc 2850\n14 1b2\nfb 7dc7\n28 a6ca\ne7 dcbd\n30 702\nba 4fe6\n6c 3ed2\n86 4014\nf a855\ne1 de21\n46 3294\n83 6a05\n8c 60f0\n9a 4bcc\nab e647\n80 4a8a\n79 9563\n8 20c0\ne2 5e84\n7e 1dfe\nfd 575b\n8f c2dd\n10 8980\n44 32b8\n3 2285\nd a8d9\ne8 dc42\nb 2a45\nb8 4562\nf7 751f\n13 3a7\nf 8fd\n6 a236\n6 14\n46 92b6\nea decc\n10 a108\n1e b56\n66 3494\n9d 415b\n82 4884\n5 231\n50 390a\nce 5076\ne a854\n16 81b6\n67 3e9d\ne0 de20\n8c 4a5a\ne7 56b7\nb1 6509\n8a 6a44\n92 43a6\nb4 65ba\n70 95a8\na0 4ea8\nd a851\n88 c842\n8a 6a64\nb1 cd89\n2b 86ef\n9a c944\n80 c802\n82 6a24\n2 2a6\ne6 741e\n0 8220\na2 64a4\nc4 5290\nda 51c6\ncc 5a50\n9 8243\n32 2786\nd2 d106\n33 ad27\na1 4c09\n82 4886\n44 3290\n81 6a01\nfa d546\n74 3790\n9 2a41\n4 10\n8e 48f4\n1c b52\n64 3490\ncc 5072\n1 2a21\n2 2284\nc a8d8\nad 6c7b\n69 9c69\n42 9a86\n25 2c93\n3f 2dd5\na2 6484\n0 8200\n90 41aa\n88 6848\ne3 74a5\n2e 45c\n45 38b1\n14 31a\n92 4986\na a844\n12 81a6\n43 928d\n87 629f\n88 6a60\nd8 5940\n17 395\n35 8519\nd4 5992\ne2 5ca6\n29 86eb\n8b 486f\n58 b148\na2 e6a6\n21 a481\ncb 52ed\n80 6a00\n6d 1cfb\n7e b776\nec 5658\ncd f0db\n3 80a5\nb9 cfe3\nd3 d125\nf5 5791\n7c bdfa\n31 8721\n54 1992\nb 86f\n6 221e\n36 ad96\n5 2899\ndd f1db\n13 81a5\n2 aaa6\n88 e06a\nbb 6567\n51 3181\nd7 5395\n9a e34c\n80 e20a\n1b a9ef\na4 ce98\n7 a815\n28 2ec2\n42 3004\n1b 894f\n6 a814\n5 a811\n4 a810\n61 3c21\n24 4b0\ncb 78ed\n5 a291\n67 b41f\nc8 5260\n44 1a30\n3f ad7f\n50 3180\nc2 722e\ndc 7370\nd6 d996\n5 299\n50 b12a\n89 e2e3\na3 e425\n14 a910\n2 a804\n22 4a6\n42 b2a4\n0 2a02\n1a 2b44\nb5 6d39\na8 e448\n66 1436\n15 29b1\ne1 5429\n37 a5bd\n89 6ac3\na3 6c05\n45 9ab1\n4e 1076\n4c 9258\n88 e2c8\n46 12b6\nea 5ecc\n10 2108\n80 68aa\n9a 69ec\nc1 52a9\nea fe44\nb0 e580\n65 b699\nc7 781d\ndd f9d9\n26 241e\nda d966\n9 269\n3a 8766\nc3 d8af\ndd d9f1\n26 436\n46 b234\na1 4429\n5 8ab1\nd2 d926\n1 229\n32 8726\n1 a801\n6d 9459\n8f 4257\n8f 685d\n97 41bf\ne 76\n2e ae74\nd3 d30f\ned d451\n54 99b2\n9f e1dd\n85 e09b\n50 112a\ne3 f407\nd2 598c\n89 4869\nba cd66\nf3 df05\n19 a141\n60 1600\n10 2120\nea 5ee4\n57 119d\nd9 db63\n46 3a9e\n3f 8577\n68 1e68\nc7 f23d\n48 b8e0\nde 5b5c\nc4 5a1a\ne9 74c9\n56 9914\nb 8ef\n8a 424c\n13 12d\n38 a5c0\n97 6395\n7a 9566\n70 3d02\nef 765f\n89 68c3\n45 98b1\n30 a580\n41 1029\n72 9526\n68 3cc2\ne7 761f\nf2 df04\n18 a140\n8 2a4a\n19 149\ne9 d6cb\n63 36a7\n5 8a39\ncf f27d\n50 b920\n64 9c38\n57 9b97\na8 6c4a\n7b 9567\n52 330e\n6c 3450\n83 68a5\n97 e3bf\nb1 e501\n8c ca52\n90 41a2\n88 6840\n26 a6bc\n73 3f8f\n8d 40d1\n19 2943\n47 1a9f\ne7 7e15\nb9 65c9\n24 2490\n77 bd37\ne5 5c19\n91 6981\n40 b2a0\ne2 5406\n80 c020\n7 209d\n28 ae6a\n81 6881\n95 e9bb\nd2 5306\n9c 6158\n67 349f\ne0 d422\nfa d564\n51 b121\n87 4ab5\n0 8080\nfa d54c\n5a 33e6\ne0 d40a\n42 1806\n84 e818\n1f a3ff\n39 a541\neb dec5\n11 a101\ne a254\n4d 9871\nca 52cc\n1e a3fe\n38 a540\n46 3a94\nea dec4\n10 a100\n1a 2b4c\n0 2a0a\n11 109\nba 6d4c\n5c 9bf8\na0 6c0a\nff 5fdf\n82 4086\nf7 75bf\n83 6885\n48 986a\nbd ed79\n96 eb96\n7b 1d67\n31 a701\n9b 41e7\n95 6991\n47 1a3d\n92 4924\n8 40\n30 87a0\nb a045\na 20ce\ne4 5e92\nfe 5fd4\na a044\nc a5a\n31 2509\na9 e6c9\n67 16b7\na1 6cab\nbb 6ded\n2 800e\n1c 8150\na4 6630\n7d 97d9\n88 4268\nde 7b76\n7 a9f\ncc fa58\n40 b820\ndd f3d3\nf7 f515\nda f3ec\nc0 f2aa\n6c 3652\n83 6aa7\n61 bca9\n5a 936c\n40 922a\n9e 637e\nce fa54\na5 4eb9\nf4 d590\nba 67ec\na0 66aa\n7 8a95\n92 cb0e\nac cc50\n83 6825\n21 a6a1\n8 a4a\na5 e6b9\n63 16a7\n2d 24f9\n4e 1ad4\n18 8140\n7 a015\n40 32a8\ndd 715b\n13 125\n47 1ab5\n70 1d02\nef 565f\n11 8121\nbd c7d9\n37 27b5\n51 3121\n2c a458\n62 9606\n11 ab81\nc3 7887\na a2cc\nd0 7928\na a4e\nb9 6de1\n12 8104\n3a 56e\nd0 7320\n4 aa38\nbd cf59\n37 2f35\nd5 5911\n22 2684\n1 8021\n67 3695\nf2 5f0c\n18 2148\n9 8861\n86 42bc\na5 6c99\nd6 f196\nf6 571e\nb 2865\na 8044\nc2 d886\nc8 7260\n5a 3bc4\n40 3a82\n51 1181\n2f 2cf7\nb5 cd1b\nae 6654\ncd 5851\nc5 5811\na7 669f\n63 968d\n46 b294\n18 340\n38 a7e0\n92 6924\ne2 5e04\n8 2040\n30 a7a0\n6 8a94\n82 6824\n20 a6a0\nd4 5190\n71 3f2b\n8b 406d\n98 c1c8\n12 21a4\n62 bc0e\n7c bd50\n1a b6c\n0 a2a\n49 1049\n7a 9546\nb4 6d98\n98 c948\n12 2924\nd4 5910\n11 832b\nca 58e6\n2b 846d\n74 9590\n0 8020\n88 c0c8\n2 20a4\nc4 5090\na4 6c98\na 2864\ncc 5850\n88 c848\n2 2824\nc4 5810\n26 4b6\n46 b2b4\na1 44a9\nb0 ef02\nca f044\nac e452\na5 4e11\nc5 d8b9\ne 2fe\n28 440\n81 c823\n9b c965\nd8 db48\n52 3b24\n92 6184\nd0 7122\n13 18d\nca 52c4\n81 6821\n82 6084\n54 1b92\n6e 1cd4\n39 8fe3\n97 6137\n53 9125\n42 ba26\ncd 78db\n3 8a5\n4 3a\n1e 17c\n75 1791\nef ded5\n15 a111\nda 7bce\nf4 7d10\n98 e9c0\nc8 526a\n3 a005\nad 4e71\n39 2f6b\n6e bede\ne9 5e61\n25 aeb3\n3f aff5\n10 a1aa\n33 a5af\nc0 fa08\n8f 4a75\n84 6832\n9e 6974\n40 9820\ndd d3d3\nf7 d515\ne6 fe16\n3 aa85\n98 4b4a\neb 7e45\nda d3ec\nc0 d2aa\nbd 65f9\nf3 57a7\n22 a40e\n3c a550\ncb 5ae5\na4 4e10\n1b 2967\nb2 4586\n89 e261\nf a2ff\n29 a441\n99 ebe3\nb3 ed25\n58 9960\n9c 6972\n3a a7ee\n98 6960\ne8 5e40\ne 207c\n90 6920\ne0 5e00\n6 203c\n88 ea4a\n1e 8156\n15 83b9\n61 1489\nb0 c580\nc2 7286\ne9 7469\n80 6820\n1 a001\nab 4e6d\n18 2960\n33 af27\na1 4e09\ned 7edb\n23 ea5\nc8 5060\n8f 627d\n10 2920\nd2 590c\n8e 4a74\n8 2860\n4a 1046\n8c e058\nc2 d206\nae ccfe\na7 6637\n63 9625\n86 4a34\n58 31e8\ne6 5416\n44 b2b0\ne a2fe\n28 a440\n98 ebe2\nb2 ed24\na8 e462\nc0 7088\n3 a2a7\n5d 1359\n54 3190\n91 6901\n7 201d\n42 90a6\n1a 946\nf a055\ne1 d621\nf1 7da3\n38 a7e8\nc1 5229\n70 b70a\na1 eca1\nd3 d9af\n36 536\n6 236\n81 4229\n85 e011\n59 1349\nda 7946\nd1 7ba9\n8f 4a5f\n40 1228\n20 aca0\n17 91d\ne a256\n9b 43c7\na8 4468\nf1 558b\n3 2ad\n7e 3f5c\n64 3e1a\n83 e2a7\n75 1519\ne5 5cbb\nf6 f736\nff 5dfd\n8 268\n13 898f\n19 2369\n38 2dea\n66 9694\n19 29e1\n9f e3d7\nac e478\n6a 1466\n8a c264\n62 b6a6\n18 8148\n3a 2f46\n62 1484\n19 361\n4 8ab0\n0 228\n6 2a3c\n61 34a1\ne7 56b5\n8b 40c7\n98 4168\n9c 41d2\n7 a2bf\n21 a401\n81 e881\ncb 526d\n58 b9ea\nb8 6dc8\n4f 30fd\n35 2fbb\n76 9594\n13 832f\n2d 8471\n99 69c3\n55 99b1\n89 42e9\n69 364b\n80 6aa0\nca 5a46\n9c 41fa\nd5 73b3\nef 74f5\nf3 552f\n84 6a90\n36 8594\n15 89b1\n49 32e9\nb1 edab\nee 56f6\nc5 5291\nd aad9\na3 6685\n7a bfce\n94 c110\n44 3890\n68 966a\n2a 866e\n6 2894\n4 8810\nba c5c4\na0 c482\na2 66a4\n68 94c2\n21 6a9\nf2 dda6\n65 1c13\n7f 1d55\n4c 9858\n81 c021\na2 6684\nd9 7b61\nd4 5912\n72 978e\n3b afcf\n55 b111\n5 2891\na1 66a1\n21 26a1\ne3 568d\n40 30a2\n5a 31e4\n4e 1876\n29 669\nfa dd66\na1 6681\n19 8941\n21 2681\n8c 60d8\na0 66a0\nd8 7148\na1 6481\n80 62a0\n6c 1ef0\n61 1421\na 286c\n20 26a0\n0 22a0\na0 64a0\n88 ea48\n46 1a36\n18 1ea\nda 514e\n38 afe8\nc1 5a29\n68 9642\n17 abbd\n68 be4a\n9b 4347\n48 3a40\n20 2680\na0 6480\n7 2295\n92 630e\n4e 92fc\nac 6450\ncb 586d\n5 8211\n83 60a7\naf 6e57\n6b 9e45\n3d 85f9\n46 b03c\n2c aefa\n6 2294\na6 6494\n82 60a6\n13 8905\na5 6691\n25 2691\n3d 2d59\n43 92a5\n87 62b7\nb5 ef19\n73 1f07\n65 b4b9\n5 2291\na5 6491\nc4 5032\nde 5174\n6 8abc\n11 8901\n45 3239\n13 2985\nac 4c5a\n17 ab95\nc1 5a01\na2 ec0e\nbc ed50\na4 6690\nad 6e53\n69 9e41\n4 2290\n65 9439\na9 644b\na4 6490\n57 bb37\nc5 5a19\n3 2a2d\n96 4996\nb0 4d02\nfb 75e7\ne 8854\ncc 7a70\nd3 5127\nba cd6c\na0 cc2a\n25 c19\nf9 ff69\n56 9116\n96 6936\n52 9924\nc7 7a1d\n26 694\nb2 458c\n30 8580\n83 4a07\n55 31bb\n53 b3a7\nc1 5289\n10 8180\nce 587c\nee fcde\na3 c605\nca f2c6\nd3 598d\n5c 3970\n42 382e\n56 1914\n58 314a\n42 b2ae\n5c b3f0\nfe 5556\nf5 57b9\n26 8e9e\n27 a49d\n54 b3b0\nf6 5516\n62 bc84\n19 ab61\n94 e9b0\n7d b55b\n18 8962\n50 b90a\n5 8231\n8c 425a\nc 8850\n30 2502\nf5 d731\nba 6de6\n22 8e0e\n3c 8f50\nb6 4716\n85 e891\ncf 527d\n50 1920\n2b 2ce7\nb1 cd0b\naa 6644\n32 8506\n1 9\nd6 d336\n81 68a9\n28 a4c2\nb2 eda6\n29 8ccb\n22 2604\n5c 1972\n9 88cb\n2 2204\na8 ccc8\n22 2ca4\na1 6601\n80 6220\n6c 1e70\n13 ab85\na8 4c4a\nc8 fa48\naa 446c\n90 432a\nea dcec\nd0 dbaa\ne3 7625\ndb d34f\n5c 99f2\n7a 3d6c\n60 3c2a\n80 ea28\n49 12cb\n63 140d\n98 c148\n12 2124\n4 aa30\n80 4088\n12 a1a6\ne9 dce9\n32 72e\n54 1910\ndc 73d2\nf6 7514\n25 2419\n5b 196f\n27 417\nc4 d890\n0 2220\nee 74fe\n47 1095\neb fc6f\n14 8b98\n2e 4d4\n14 392\ncd 5af1\n43 120d\n1e 837c\nd7 5937\n4 823a\n2c 2458\n25 acbb\na4 e618\n3f adfd\n62 1606\n11 2b81\n11 121\nbe cdde\n73 9705\nb7 6717\nb1 e589\ncc 5078\nbf 4fd7\n36 a596\n5 2099\n24 2418\nf 8d5\n85 4839\n1 8aa9\n5e b1de\nd9 5161\na 8846\nac ce50\n44 9810\n73 970d\nb7 671f\n38 2dc2\ne a5e\nda dbe4\nc0 daa2\n19 23cb\n33 250d\na3 6caf\nbd 6df1\n8a 4266\n97 6b35\n11 1a9\n73 978d\nb7 679f\n93 630f\n4f 92fd\n14 29b2\nad 6451\n5f 33ff\n79 3541\nc3 f887\nd0 f928\n48 1a60\n28 662\naa 4466\nca f264\n3f 2fff\n59 3141\n47 181d\n8 262\na8 4462\ne7 741f\n3 2a7\nc8 f260\n86 4a9e\nc 2250\n96 6b34\nd3 51a5\na0 cca8\nba 45e4\na0 44a2\n98 6b40\ne 225c\n60 3e00\n10 a32a\n2a a46c\n2 aa84\nd6 7916\n1d a35b\n8d e8db\n58 196a\n35 539\n3e 2f7c\n24 2e3a\n5e 39dc\n44 389a\na 8c4\n3a fec\n20 eaa\n2d 651\nb7 4f35\n4a 1a4e\n34 a59a\n23 2ead\n44 9298\n77 1795\n91 63a1\n5c 195a\n36 a59e\nb1 4521\nb9 c5c9\n33 25a5\n0 a0a8\ne af6\n55 1391\ne0 540a\nfa 554c\n4f 1ad5\n2a 2ccc\n10 2b8a\n19 8141\n8 aa42\n30 582\n50 b380\nb8 6548\n67 9495\nb8 65e8\n87 e897\n94 e938\n52 1926\n82 60a4\n77 1597\nf a255\n4c b252\n55 1919\nd1 7ba3\neb 7ce5\nba 474e\n90 6322\naa 6464\nda f1cc\nc0 f08a\n4d 18f3\ncc 5250\na8 c4c8\n22 24a4\ne4 5490\n81 4a01\n44 1290\ne9 544b\n43 1a2d\n3e 8554\n24 8412\nd1 f323\neb f465\nc0 58a8\n93 6185\n45 3211\n4c 98d8\n65 1c93\n7f 1dd5\n5e 13dc\n44 129a\n12 a924\nac e6f8\n6a 16e6\n49 1869\n97 6b95\n7a 9d66\n47 1a95\n11 8101\n1a ab44\n0 aa02\n36 5b4\na2 e40e\nbc e550\n46 1a1e\n16 8114\n6 a1e\nb5 6db1\n11 238b\n2b 24cd\n17 a9bd\nc1 5829\n68 9442\n21 629\nf2 dd26\nb0 478a\ne3 548d\n50 1308\n40 1828\n80 c808\n8d ca5b\n7 2a37\n90 61a0\n15 2bb1\na0 6c2a\nba 6d6c\ne1 5629\n99 6941\nc1 f0a1\nf 205d\n37 a7bd\n90 e922\nfa df44\ne0 de02\n6 a03e\n45 1819\nc1 7aa3\ndb 7be5\naa 464e\n76 9d16\n44 1818\n22 2486\n2e 8ed4\n13 8105\n2 aa06\nd8 d9e8\n3b 56f\nd1 7321\n54 1192\n2b ae6d\na1 6e21\nb 6f\nf6 753e\na6 46b4\n20 24a0\n41 1a29\n2f acd7\n3c ad78\n72 9f26\ne2 548c\n99 4369\n98 e948\n56 1936\n78 9542\nda d16c\nc0 d02a\nf3 5527\nb1 6581\n48 3048\nc a2d0\nd2 792c\n2d c73\nc3 7a25\n86 42b4\n0 20a0\n65 be99\n88 e848\n46 1836\n91 6181\n63 1c8f\n7d 1dd1\n1e 83fe\n38 8540\n98 e160\ne8 fe48\n71 3d21\n34 5b0\nd2 f386\nf9 f569\nc1 78ab\ndb 79ed\n15 a391\n25 611\naf 4ef5\na2 ee2c\nc 807a\ndc d9f0\nc2 d8ae\n3f 577\n6e 9c54\n54 9b12\n79 b5c1\na8 6e60\n97 6195\nd0 7902\n29 e69\n9 a8e1\n5a 9366\n1 881\n1c 158\nec d6da\n66 36b6\n27 2495\n65 3699\nc5 58b1\nb0 6580\nd 8a51\n9 841\n8f 60f5\n31 8fa1\n6e 3676\n7 2095\n45 3299\n76 b796\n7 2a17\n90 6180\n80 6080\n45 18b1\naa 46e6\ned 545b\nd2 5b84\n30 2580\n51 1b09\ncd 505b\ned fe59\n10 2180\n56 1916\ncb 58c5\nb6 6594\n96 6194\na5 6433\nbf 6575\n61 9421\n99 cb63\n6 2a9e\n9e 4b5c\n84 4a1a\na9 64c9\n35 8f13\n4f 9055\nea dece\n10 a10a\nc2 f824\n38 af40\nf 8055\nc7 d897\nd4 d938\ncd 7271\n4b 18c5\n36 2594\n1a 83ce\n34 8510\n5a 1b4c\n40 1a0a\n8e e054\n65 34b9\n23 60d\n98 414a\n3 a085\nad 4ef1\nfa 5d4c\ne0 5c0a\n26 2494\ndc 5952\n7a 97ce\n5b bb67\nc9 5a49\ne 8054\nc6 d896\ncc 7270\ne2 7e2c\n4c 107a\nb5 6591\n4c 3058\nd6 793c\n15 2391\n39 25c1\n48 906a\n96 e396\nbd e579\n7b 1567\n95 6191\n33 8f0f\n4d 9051\n35 2591\ne6 54b6\nc4 7a12\nde 7b54\n49 3243\n50 990a\n89 cac3\na3 cc05\n25 2491\n50 31a2\n3 2085\nf3 7f2d\n5d 117b\nc1 5201\n42 18a4\n10 92a\n26 4b4\n1 8001\n15 2191\n6f b477\n5e 19fc\n44 18ba\ndd 5359\n55 1b11\n32 a5ae\n32 8f0e\n4c 9050\nc 8050\nef 7e55\nf7 57b7\neb f647\nda 5bcc\nc0 5a8a\na3 468d\n0 20a2\n1a 21e4\n97 43bf\nb1 4501\n33 2585\n0 a088\ne ad6\nf 8a55\nea 5e6c\n3 2007\nc9 58c1\nb4 6590\n67 bc37\nd5 5b19\n6c 1c7a\na5 4e33\nbf 4f75\n94 6190\n93 692d\n31 a7a9\ne3 5e0d\n9 2049\n3a a546\n72 3706\n2 2884\ne6 debe\nc a0fa\n18 8342\na2 cc26\nb 8a45\na5 6eb9\n57 b937\nc5 5819\n1b 29c5\n1 2883\n3e 25f6\n1d 23fb\n37 253d\n18 2b62\n1e b5c\n4 a1a\n29 24c9\nb3 6dad\n14 8110\nce f256\nd7 591d\n75 9799\n28 a660\n74 9592\nc1 f221\n2b 846f\n1d a979\n53 b985\n87 62bd\n73 1f0d\n5a 134c\n40 120a\nd1 d1a9\n56 91b6\n4e b854\n3c a55a\n5b 134d\n41 120b\n93 c987\n99 6361\ne2 7484\nd a879\n43 9a27\n56 b99c\n63 1e0d\n46 18b4\nc5 5211\n9 a86b\na7 6e15\n6e 3c5c\n54 3b1a\n8d 6859\nc3 5a07\n48 1ac0\nbe ed56\n95 41bb\n99 e141\n46 1296\n6d 1479\na5 4639\n19 949\n9f 61fd\n85 60bb\n41 90a9\nfe 7dde\nb3 4705\n1d 8b59\n9b 69ef\neb 5ecf\n11 210b\n4d 1251\nd7 5b35\n7a 37c4\n60 3682\nd3 530f\n54 19b2\ned 5451\n88 e0c8\n46 10b6\ncb 52cf\ne5 5411\n54 133a\n6e 147c\n53 1ba5\na6 463c\n54 b11a\n8e 685c\ne5 7c93\nff 7dd5\n2c a6d8\n96 41be\ne3 dc0f\nfd dd51\n86 423c\na8 cc42\na6 6e14\nd2 d924\n3c 25f2\nc3 5aa7\n34 a512\n8d 68f9\nc a252\n15 919\nd5 7199\nc2 5224\n21 2c23\n3b 2d65\n8 a868\n53 9905\n97 6917\nfb 7def\n35 a793\n53 930f\n6d 9451\n82 6024\naa 46c6\nf6 7d9e\nab 46c5\n25 24b1\n1a b44\n0 a02\n2d 8ed3\n47 9015\nfc dfd0\ne2 de8e\n8 a0ca\n7 8015\ncc d8f8\n2f 47f\nc5 7231\n88 c048\n2 2024\ne2 dc0e\nfc dd50\n50 1188\n38 8562\n12 906\ncb 70c7\nd8 7168\n33 2fad\na1 64a1\n10 1a8\n72 978c\nb6 679e\nd4 7198\n85 4299\n17 a3b7\n52 9904\n96 6916\nfa 7dee\n34 a792\na9 4c69\n82 4a86\nc aa50\n52 930e\n83 c8a5\n6c 9450\n3a 25c6\nff 75ff\n88 e040\n70 3d8a\n25 6b1\n54 31b2\nf7 753f\n52 3384\n73 152f\n4 2a90\n1b 9ef\n9a 434c\n80 420a\n4c 98d2\n81 6021\n86 489e\n65 1691\n9 c1\n5e 9bfc\na2 6c0e\n44 9aba\nbc 6d50\n17 2b95\nde 7bf4\nc4 7ab2\n25 a639\n62 968c\na6 669e\n0 a8\n38 25c2\ndd dbf1\nc3 daaf\n26 636\nce d8dc\nc7 7215\nd9 dbe3\nf3 dd25\n1c a9da\ne 5e\na4 6e10\nb2 6586\n80 6020\n79 956b\n5 8a19\n6c 345a\n9d 69f1\n83 68af\n86 4234\n0 2020\n55 9b91\n96 4934\nc 50\n34 87b0\n4b 1a4d\n5e 1b5c\n44 1a1a\n69 34c9\n58 33c2\n72 3504\n27 61d\n9c 415a\n7 a095\nb1 4f01\nbc c5da\n36 25b6\n2a 2c64\n10 2b22\n17 a99f\n35 25b3\nb0 6582\na a064\n73 170f\n4c 92d2\n66 9414\nea 5c4c\nd0 5b0a\nf5 75b9\n89 e841\n3a 876c\nf3 5d27\n20 862a\na8 4c60\n67 149f\nfb 576d\nb1 ed81\ne1 562b\n55 9931\n99 6943\n37 a7bf\nf 205f\n84 6812\n40 9800\n9e 6954\ne6 563c\ne8 7cea\n22 a68e\n3c a7d0\nd9 dbc1\n22 606\nc3 5a85\n83 4a85\nac ec7a\n43 1a85\ne3 5c85\nd9 d3c3\nf3 d505\ne2 fe06\nbc 67da\n78 97c8\na3 4c85\n7b b547\n50 198a\nc1 5aa1\n72 958c\nb6 659e\n49 b861\n92 c984\n0 2a20\n5 a8bb\n42 1206\n1f a9fd\n84 e218\n42 1a84\n5d 135b\n83 eaad\n6c b658\nd6 513e\n4b 1a6d\nca f2cc\n34 851a\n7b 1def\nfa 574c\ne0 560a\nda f1c4\nc0 f082\nc0 5aa0\n31 a50b\nb7 ed17\n41 1a81\n5c 1358\ncc 5afa\ne6 5c3c\n22 ac8e\n6c 167a\n3c add0\nc8 f060\n3 a7\nb8 c5c8\n32 25a4\n30 8520\n7e b7dc\n64 b69a\nc6 781e\nf0 df82\n16 a1be\n54 3918\nc2 52a6\n78 1d48\n8c 60f8\n6a 16e4\ned 5479\nc6 5296\n1 2881\n94 e318\n15 a9bb\n52 1306\nf6 5f1c\n1c 2158\n87 4a95\n68 16e0\n75 3f33\n8f 4075\nd2 7b2c\n3c d7a\na2 c40c\n88 c2ca\n2 22a6\n19 a9e1\n39 f69\n19 8361\n34 259a\n0 a80\n8b c2cf\na5 c411\n1e a976\n8c 4858\ne7 54b5\n42 982e\n5c 9970\nf3 550d\nd9 53cb\n21 421\n13 2905\n17 ab15\n85 4a91\n15 ab11\n8f 625d\n10 2900\nc1 d821\na 266\nbe 47dc\na4 469a\n74 b512\n7 a29d\ncd 78f9\n8b 4a65\n83 4a25\n26 696\nf0 7d80\n17 899f\n1d 2379\nf9 d561\n66 349c\n37 afb7\na5 4e99\nb a65\n86 48b4\n6f 145f\n89 6a41\nab 44e5\n91 43a3\n6a 3ece\n84 4010\n67 141f\nd2 f30e\nec f450\n66 14b4\n19 341\n85 e89b\n9f e9dd\n50 192a\n58 3142\nd 25b\nb1 cd2b\naa 6664\nda f3cc\nc0 f28a\nf6 5596\nad 4473\ne9 dcc1\n32 706\nca 5a64\ndd 79d9\n8 60\n51 1183\n19 bc3\n33 d05\n0 8808\n38 2d60\ncf d25d\n50 9900\n94 6912\n8 c0\n7b 154f\n92 49a4\ncb 5a45\n9d 41f9\n5 2831\n8b 4a45\n5d 31f9\n52 91a4\n96 61b6\n88 cac2\na2 cc04\n88 6a40\naa 44e4\n90 43a2\n2c ae78\n8f c25f\n10 8902\n5e 337c\n44 323a\n4d 92d1\n12 2986\nb0 4da8\ne5 74b1\n4b 1a45\n1d 1f9\n61 3621\n68 9ce8\nac 6cfa\n35 8d9b\n2e 26d4\nd 8071\n56 9194\nde 53fc\nc4 52ba\ne4 d412\nfe d554\nb a45\n58 194a\nca 5a44\n9c 41f8\n75 9591\n7e bfd4\n64 be92\n81 e823\n9b e965\n4b 32c7\n58 3368\n52 998e\n9 886b\n82 4a04\n54 31b8\n4a 1a44\n1c 1f8\n11 89a1\n3e 255e\nf0 f5a2\nc 8070\n55 9193\n24 a630\nc2 fa0c\n2c 8c5a\ne8 7e68\n5f 1157\n89 e061\nd2 f184\n96 6994\n82 4284\n34 25b2\ne0 7e28\n57 1117\n88 4262\n2 a04\n9 a61\ne 88fc\n7 2235\n8d c259\n52 1b84\nf7 5d3f\n1 a21\n25 c93\n3f dd5\nd9 514b\n34 f90\na2 4484\n45 b291\n3f 5d5\n25 493\n64 14b0\n26 ac9e\na1 4c21\naa 4ee6\nc9 5a41\nae c456\ncc 705a\n2 24\n79 9549\nbd 655b\na2 6c84\n76 bd1e\n2b 8645\n48 9a42\n53 b3af\n6d b4f1\nac 66d0\n15 ab33\n2f ac75\n31 afa3\n4b b0e5\nd0 f18a\n4c 12d0\nd6 5bb4\n3 282d\n89 4a41\nd aa51\n8a e864\n1f bff\n39 d41\n5f bbdf\nc0 5a20\n16 abb4\n49 1a41\ne4 54b8\n2b ae65\n8c ea7a\nf0 55a8\nb 67\n3 88d\nc0 7822\nda 7964\n2c 26d0\nb6 6fb4\n41 1a01\n22 ac0e\n3c ad50\nc7 5abf\ne1 5c01\nfe 555c\ne4 541a\n88 e060\n51 9981\n95 6993\n9 a41\n55 bbb9\nf7 5d1f\n52 138e\n6c 14d0\nda 53ce\nf4 5510\ncb d845\nd2 f98c\n35 2513\n9c e3d2\nb6 e514\n68 1eca\n1 a01\n8d 60d3\n49 90c1\nd3 d9a5\na7 6e95\n98 436a\n3 a2a5\na8 e460\n2e aed6\n0 a20\nb7 4715\n1f 89fd\n5 88bb\n84 c218\nee 5cfe\n44 b290\n3e 5d4\n24 492\nc8 5a40\n94 e998\n52 1986\n9 863\nf5 fdb1\n3e 27f6\n1c 235a\n62 9c2c\n48 9aea\na6 6c3e\n7d 9d79\n56 9b96\nd 8a73\n2a 8644\ned 5e51\n98 6362\nc0 5a00\n16 ab94\n15 293b\n87 6837\n43 9825\nc0 5280\na0 6420\n8b 4a6f\n31 2f29\n85 4833\n9f 4975\n23 86af\n3d 87f1\nb2 cd0c\n98 cbca\nab 6645\n12 2ba6\na 844\nfc ffda\n32 8fa4\nee 7efc\n58 114a\n1 2801\n87 4a15\n59 31c9\n8c e2d8\n4a 12c6\n84 68ba\n40 98a8\n9e 69fc\n0 a00\nee 5cde\n47 1a15\n19 1c9\n22 2c0c\n8 2aca\n92 6126\n7e 1d76\n54 191a\n50 3102\n1f 35d\n5 21b\na9 4c41\n8f 4aff\n99 e9cb\nd6 5316\n85 6891\nba c544\na0 c402\nc aa78\n25 2e33\n3f 2f75\na9 cceb\na2 6624\n18 162\n38 af60\nc0 d0a0\n98 4940\ne 5c\n6 2814\n46 1a14\n18 1c8\n6 a14\n8c 4ad2\na6 4c14\nbe e75c\na4 e61a\n3f adff\nec 565a\ne0 5ea8\n13 2185\n9a 6b44\n80 6a02\n77 3fbf\n91 4101\ncc 525a\nc8 524a\n15 2911\nd8 5160\n0 8aa8\nfd 7fdb\n33 fa5\nda 736c\nc0 722a\nd4 d992\n5 2ab9\ne 2856\nd0 510a\n4 ab0\n26 8c16\nc9 fa69\nf2 5d8e\n7c 3dda\n31 701\nb7 ed9d\n68 1cea\na1 4ea3\nbb 4fe5\n7 283d\n8d 4a51\n85 4a11\n5 a11\n68 3e6a\n94 e398\n52 1386\n79 1569\n94 6910\nb5 e539\n73 1527\n31 2581\nb7 4795\ne4 7c18\n0 aa0\n84 c298\nd3 5b85\n65 141b\n7f 155d\ne2 740c\nc8 72ca\n45 9a91\n41 1209\n3c a558\n6b 166d\n21 ac81\n72 9706\ncb 5aed\n57 b137\nc5 5019\nf6 d516\n48 124a\na7 e61f\n28 acc2\nc2 fa26\n5 8a91\n89 6861\nd2 d906\n1 209\n8b 4aed\n0 808\n1a ab6c\n0 aa2a\n11 8129\nb9 cf4b\n33 2f27\n3a a56e\n44 3090\n81 6801\n90 e122\n7c 9d72\n45 1019\n76 9516\nc6 f236\ncf 58fd\n45 9891\n41 1009\n72 9506\n76 bfbe\n90 c100\n40 3880\nc6 5a94\na 28ce\n45 9219\nc 25a\n83 420d\n4 8b0\nc9 f869\n83 4027\nd 8073\na3 ee25\n8 24a\nac 4c70\n92 4b2e\n12 186\n32 af84\n4 a10\n37 a5b5\n66 b49e\ne1 5421\n12 8b06\n22 ae04\n2 6\n80 6800\n4c 1258\n12 ab8e\n2c acd0\nb5 4791\nd1 5b81\n47 129d\n2f 8677\n10 232a\n2a 246c\n65 9e9b\n7f 9fdd\n22 2c26\na8 cc4a\n26 a496\n35 8f3b\n4f 907d\nf4 551a\n2f acdf\n90 4b20\n6 23c\na a2ee\n24 a430\na7 4615\n1a a36e\n16 994\ndb f96f\n4 8898\n43 1aa5\na3 e60f\nbd e751\n24 acb2\n3e adf4\nc3 58a5\nc4 503a\nde 517c\n39 fc1\nd7 d3bf\nf1 d501\nfa ff44\ne0 fe02\na1 4c81\nd8 5948\n1b 8b67\n97 e137\n21 ea1\na5 c699\n2f 6d7\n3c 778\n4c b0d2\n90 41a0\nd4 511a\n6b 14c7\nad e4d9\n78 1568\ne1 d489\n57 999d\n9 8261\n54 b93a\nd1 59a1\n47 10bd\n61 3689\n96 4316\n74 9518\n5a b144\n40 b002\nea 5e6e\n7d b5d9\n19 2bc3\n33 2d05\n0 a808\n5b 33c7\n68 3468\nb1 458b\nde 715c\nc4 701a\n21 8e09\n2f 24fd\n15 23bb\n31 27a1\nd 8879\nb9 4569\n92 4386\n50 992a\n49 3263\n60 3688\nc0 58a0\n49 b8eb\nc8 f248\n1d 3d1\nda 7366\n3 28f\ne1 7603\ne8 dcca\n62 3ca6\nfb 7745\n71 170b\nfb 5fef\n93 4985\n87 6895\ne1 7c2b\nfb 7d6d\n35 a711\n13 985\nfb fd6f\n24 8c98\n57 1195\n6d 3c79\n46 3a96\nf1 550b\n3 22d\n38 8fc0\n96 6114\n8 2840\n10 1a2\n8e 4a54\nbb cfcf\nd5 d111\nde fb54\nc4 fa12\n95 c993\n9b 636d\n81 622b\n6d 1e7b\n89 e26b\n85 4891\nea 76c6\n5e 937c\n44 923a\n51 3b81\n35 2791\n80 e2a0\ne6 56b6\nb0 6508\na3 ce05\n75 b5b9\n4 2830\n8a 4a44\n5c 31f8\nc1 5881\ndc 5158\nf4 7f32\n37 f9d\n98 4b6a\n3 aaa5\na8 ec60\n34 27b2\nb7 cfbf\nd1 d101\nda fb44\nc0 fa02\n81 4881\n9c 4158\ne6 76b6\na a44\n90 4b02\naa 4c44\n48 b86a\n11 981\n88 e8e8\nc5 5233\ndf 5375\n8e 4a7e\nba 6566\ndc 5352\n8b 68cd\n70 9788\nb4 679a\n7c 9f78\n6f 9ed7\n90 692a\ncf 5875\nb8 cdca\nb1 6703\n32 2da6\n6d 96f1\nb6 cfbe\nd0 d100\n80 4880\n55 1319\nf5 5519\nfe 7f5c\ne4 7e1a\n5c 1352\nb 28cd\nda 5946\n78 97c2\n9e e3fe\nb8 e540\n5a bbe4\n40 baa2\n95 61b3\n51 91a1\n6d b659\n32 f84\n40 1880\n44 9a90\n98 4b62\nc5 d839\ne 27e\n10 292a\n4f 1875\n0 208\n8a 4aec\n88 6860\ndb fb67\n4 8a90\nc7 fabd\n31 8d0b\n2a 2644\n39 ad6b\nb8 4d62\nf7 7d1f\n13 ba7\nc4 7230\n2e 47e\n18 b62\nc8 70ea\n17 a997\n37 f1f\ncd 785b\n3 825\n6 8236\n13 230f\n2d 2451\n61 be29\n5b 116d\n41 102b\nf7 5db5\n24 86b8\n86 483c\ne 2054\n7f b7df\ne0 5620\n36 a7b4\n4f 1a7d\n38 87e0\n83 4aa5\n6c 1650\nf5 f713\n76 bdb6\ne4 5c98\n4a 1864\nb5 e713\n36 adb6\na4 4c98\na 864\n95 e999\n53 1987\nce 52d4\n91 e989\n16 a996\n36 f1e\ncc 785a\n2 824\nc3 f8a7\nac c452\nc9 f8c9\n12 230e\n2c 2450\n2c 8ed0\na6 4696\nc5 fab9\n28 2640\n1c 172\nb2 6f24\n81 c889\nb 8c7\n18 968\n7a 9f4c\n60 9e0a\nbe 6f5e\n82 4084\n39 2f61\n31 589\n3a 2fcc\n20 2e8a\n9 24b\n9d e9fb\nda 5346\n89 68c1\ndb f9ef\n3e 2576\nf2 f72e\n35 8799\nfc 7752\nbc 65d2\n78 95c0\nfc 7df2\n36 a796\n23 84ad\n13 38f\n2d 4d1\nea 7466\n81 6281\n6d 1ed1\n80 4288\n12 a3a6\n12 184\n17 2395\n42 9826\n34 f3a\n4e 107c\n46 3896\n20 8422\n3a 8564\n6 8ab4\nfa 776c\ne0 762a\nf4 dd92\n28 a44a\n86 c016\n55 3b19\n73 9587\n72 3d06\nb0 4588\n64 be90\n60 3608\n12 8b8e\n2c 8cd0\n45 1a39\n76 9f36\n90 4920\n6 3c\n77 95bf\n4 2298\n22 84ac\ne3 5427\n38 a54a\n77 9595\n8d c079\n66 be96\n9 8869\nd 22d3\n14 899a\n27 2415\n8 8868\n21 c23\n3b d65\nd 8fb\n1e a376\n8c 4258\n55 3313\n6f 3455\n5c 99da\na 204c\ne4 5e10\n5b 3967\nb 22cf\n25 2411\n87 68b7\n43 98a5\n44 903a\n5e 917c\n21 8c81\n5e 3356\na2 cea6\n85 60b3\n41 90a1\n9f 61f5\n3 8d\nc0 7022\nda 7164\na5 c6b1\nf0 fd8a\na8 4448\n3a a566\n80 4a80\nbc cfd2\nd6 d114\n8a e26e\n86 4894\n5 8019\n27 2e17\na 22ce\nc1 f889\n24 2410\n31 ad0b\n6e 1656\n3 2a8f\n1d 2bd1\n6a 1cec\n7b b767\n50 1baa\ne9 5649\n79 1741\nb0 65a0\nd1 5b29\n78 9742\n27 acbd\nbc 455a\nd3 79af\n27 a495\n95 c99b\n8e 62d4\n66 1e94\n16 83be\n30 8500\n47 18b5\n32 2584\nca 726c\nde d9d4\nc4 d892\n90 e120\nf9 57cb\n62 9c2e\n7c 9d70\ne0 fe08\n87 e89d\n52 192c\na5 ec13\nbf ed55\nc9 facb\ne3 fc0d\n2c 2652\na 8a64\n1d a9d9\n85 c8b3\n9f c9f5\n6e 945e\nf8 dd42\n4f 90d7\n5c 9178\n4 2010\n8e 68f4\ncc d850\n36 251e\n63 b4a5\nf8 556a\na7 e41d\n8d e2db\n58 136a\nf6 7d3e\nab 4665\nb7 e73f\n38 ade2\n0 aa88\n33 2f85\ne 8fc\n1f a377\n8d 4259\na 8864\n59 3361\n53 9987\na1 4401\n87 42bf\n5e 13fe\n78 1540\nc 878\nb 82ef\n25 8431\n95 4119\n7 aa35\n82 4806\nd 2a5b\nf4 5518\n9a 43ee\nb4 4530\n69 1649\n49 b0c1\n65 9e91\n10 2320\nfe 75fe\n88 4842\n26 86be\n95 41b9\nc3 5a05\nd4 5118\n9 e1\nb6 cd9e\n6b 96c5\nbc 6778\naf 66d7\n52 b126\nc0 5008\n16 a19c\n3 8227\nb4 4518\ndc 7952\nd2 f92e\n15 8999\na a2ce\nd0 792a\n24 a410\n4d 9ad1\n7a 3fee\n94 4130\n49 1249\nd3 5b2d\n29 acc1\n6e 1456\n65 16b9\nf 88f7\n8e c254\nb0 4508\n3c ad7a\n61 3481\ne7 5695\n98 4148\ne2 76a6\n43 1207\n85 e219\n6 a8bc\n42 b80e\n5c b950\n7 22bf\n21 2401\n58 39ea\nab 4c47\n9 aae1\n94 4118\nb0 452a\n3 2a0d\n20 2482\n3a 25c4\n54 9930\n98 6942\nda f1e4\nc0 f0a2\n36 a7be\ne 205e\nb3 4f2d\n56 33be\n70 3500\nf6 dd16\n25 619\n5 a091\n80 6a80\n1 a081\nab 4eed\n7c 9dd2\n68 366a\nca fae4\n34 8d32\n36 8fbc\n94 6110\n93 69af\n7c 355a\n81 ea81\n18 2b60\n54 1b12\n6e 1c54\ne 2a56\n5 13\n1f 155\n89 6a49\n50 b9aa\ne9 f449\n3e 2ffe\n58 3140\nde d956\nd 259\n60 3628\n74 9d90\n11 8b2b\n2b 8c6d\n70 1508\n9d c15b\n17 2137\nd7 f317\nc6 589c\nca fa66\nd 8ad1\n3a 8746\n93 4b2d\n8 848\n63 14a5\n8e 60fc\n30 8fa8\n8 aa6a\n19 8169\n3b 2f67\n31 8f81\n8f 60d5\n3c 2dda\n18 a96a\n97 631d\n18 29c0\n60 16a8\nd4 d312\nee d454\n8e 40f6\ncc d2d2\ne6 d414\n8f 62dd\n10 2980\n93 61af\ndc d9d0\nc2 d88e\n3f 557\nc8 7268\na 804c\nb7 659d\n6f 9477\n0 28a8\n8 842\nc 8a52\n17 a3bf\n31 a501\n4c 9250\nca 70e6\n37 a597\nfb 5545\ne1 5403\n2e efe\n48 1040\n86 e016\n86 e014\n23 685\n2f cf7\nae 4654\n23 8ead\n81 6001\n44 1018\ndf f377\nce 58fc\nd3 7925\n0 a228\n4f 985d\n5a b36c\n40 b22a\n44 9890\n40 1008\n98 c9e8\n91 6321\n46 10b4\nb2 ef0e\ncc f050\n90 6182\n7c 1dd2\n15 21b3\nd0 d902\n46 901e\n37 afb5\ncc 507a\n1 2a29\na0 4e08\n32 af26\n88 e860\nf1 5f0b\nd3 5905\n0 8208\n7e 3dde\n33 705\n26 ae94\n14 21b2\n76 3d1e\n2b 645\n48 1a42\n53 33af\n6d 34f1\ne8 5c42\nd 28d9\n3e add6\n78 97ea\n97 e317\n86 489c\n8 2842\n80 ca28\nb3 4f25\n9 2841\n11 1a3\n8f 4a55\nc2 700e\n7e 9ffc\ndc 7150\n64 9eba\n29 ae61\n94 e198\n52 1186\n9 63\n2f ae55\nf4 5598\nf 57\n2e ae54\ne 56\nd6 d9b6\n5 2b9\n36 87b6\n2d ae51\na3 6e05\nd 53\naf 4e55\n5c 1b5a\nf 2aff\n29 2c41\n31 5a3\nf3 5507\n51 b3a1\n27 e15\nfa 57cc\ne0 568a\n85 4231\nd0 790a\nc3 52a5\n70 1faa\n6b be65\n18 8b6a\n8d e079\n4b 1067\n1e a1d4\n4 a092\n3e 75c\n55 3bb1\n24 61a\n90 c9a0\n79 954b\n9 2a49\n3a af46\n6e 16d4\n31 ad89\n5e 997e\n6d 16d3\n30 ad88\nba 47cc\na0 468a\n69 3649\n9a 63ee\nb4 6530\n5 a8b3\n84 e210\n1f a9f5\nd3 590d\nca f246\ne a054\nc3 5aaf\ndd 5bf1\nf4 f71a\n2a 86e4\n29 866b\ne2 5c26\n58 1342\n7 28bd\ne9 7c61\n23 a605\n19 161\n8 2a62\n8c e25a\n25 ac13\n3f ad55\n7a 17cc\n60 168a\n99 4b63\nb a8c7\n18 a968\n43 12a5\ne8 5460\n24 a4b2\n3e a5f4\nf8 5548\n3b 8767\nb0 47a2\na8 6e40\n50 b108\n5e 1b56\n5d 197b\n9c 4b5a\n7 aa95\n92 eb0e\nac ec50\nfe dddc\nf7 7715\ne4 dc9a\n1 8801\n39 afc3\n53 b105\n3 2885\nd0 f1a0\n4 201a\n1e 215c\ne3 5687\nf0 5728\n5c 197a\n95 4b33\naf 4c75\n6 aa94\n0 8800\n3a 7cc\n20 68a\n8f ea5f\nf3 558d\n7a 37cc\n60 368a\n54 19ba\n7f b577\ned 5459\n14 118\n62 9484\na6 6496\nc9 58e1\n3 8285\n8e c2fe\na8 c440\nf2 55a6\n3e 2d56\n35 2fb9\nb9 6d49\ne1 f4a9\nd3 7ba7\nbc 4752\n71 9789\nb5 679b\n55 9393\n6f 94d5\n30 a58a\n56 191c\na9 6461\nf2 7584\n87 4815\n9a 63ce\n56 93bc\nb4 6510\nf aa55\nd4 5198\nb 2845\n1 8089\nd8 f160\n13 1a7\nc8 506a\n33 afa5\n14 a99a\n8 8c0\n87 421d\ne aa54\n16 83b6\n98 63c0\na 8a4e\nd9 5369\n4e 38de\n3 205\n3 aa25\n92 638e\nac 64d0\nc1 5801\n47 123f\n17 a995\n37 f1d\nf2 dfa6\n18 a1e2\n8 8a4a\n63 96a7\n2d a4f9\n3b f47\nb7 e51d\n9d e3db\n68 146a\na1 4623\nbb 4765\n88 c268\nb8 4548\nf7 579f\nae 467c\ne4 f418\n0 82a0\n23 2e05\n35 2599\nec 765a\n22 624\n70 3f82\n8a 40c4\n62 9ea6\n45 30b3\n5f 31f5\n25 84b1\n83 6285\n6f 1ed5\n4a 30cc\n30 2f8a\n5a bbce\n74 bd10\n29 2ceb\na8 6648\n48 926a\nbd e779\n7b 1767\n43 1825\n77 95bd\nbb 65cf\ne7 f615\n68 1642\n17 2bbd\n9b 43cf\nb5 4511\n37 2595\n4 a098\nec 5652\n4f 32d7\n5c 3378\n56 999e\n58 9962\n3 a25\ncd 7a5b\n2a ae66\n31 2589\n16 136\n36 af34\n91 4129\n9a 6b6c\n80 6a2a\n35 a799\n97 691d\n56 b334\n45 18b9\naa 46ee\nf5 d713\n76 9db6\n30 2588\nd8 59ca\n20 8488\n69 3469\n42 3286\ne7 fe15\n38 762\n60 3c08\n6e 14d4\n54 1392\nb 26f\nb8 c5ca\n32 25a6\n8d e8fb\nca 5246\n17 23bf\n31 2501\nc a52\na1 6ca3\nbb 6de5\nf9 75c1\n98 cb4a\n12 2b26\n8c 4870\ne3 5ca7\n2a 86ec\n2c 2658\nd0 7b22\n13 b8d\nea 7c64\n66 9e94\n4c 9870\ne3 540d\nc9 52cb\n5c 39fa\n11 321\nff 55ff\nd9 d341\n5a 99e4\n40 98a2\n9e 69f6\n43 120f\n5d 1351\ncd 5af3\ne7 5c35\na8 6cea\ne5 7c33\nff 7d75\n2c a678\nd7 791f\ne3 7487\nf0 7528\nf1 df2b\na a0c6\nce 5074\nb4 4f32\n62 bc86\n19 ab63\n59 3be1\n28 64a\n92 e9a6\ncc 5070\nb2 4f2e\n5b 194f\n7a bdc4\n60 bc82\nc2 f086\na3 64ad\nb2 4786\n70 9d2a\n69 3663\n18 142\nae 6ef4\nd1 7901\n94 4190\nd8 d142\n29 ceb\na8 4648\n3a a766\n6f 3cdd\n55 3b9b\ne8 dc60\ncb f8ef\n2e 2476\n70 b580\ne2 f62e\nfc f770\n25 8699\nec 7652\n87 481d\n5e 197e\n4 2210\n8e 6af4\n95 e9b1\n64 b41a\n7e b55c\n2d cdb\nc3 7a8d\n3e a756\n97 6b3d\n43 1885\n44 101a\n5e 115c\ncb 7865\nb4 edba\n69 b6e1\nd2 db0e\nec dc50\n7f 957f\nc 2258\n83 e8ad\n6c b458\n4a 9244\n8e 6256\n51 bb81\ne1 7cab\nfb 7ded\n35 a791\n97 6915\n16 a19e\n91 4121\na0 e402\nba e544\n9a 63ec\n80 62aa\n6c 1efa\n42 baa6\n97 61b7\n53 91a5\n42 1884\nf3 7f0d\n5d 115b\nac 6458\na5 ecbb\nbf edfd\ne2 5606\n91 6b81\nca 7864\n68 b6e0\n46 9a94\n6 8814\n2e c7e\nc4 7a30\n13 2385\n9e 63fe\n5a 93ec\n40 92aa\nb8 6540\nc0 f808\nb6 4594\n53 332f\n6d 3471\ne5 7c13\nff 7d55\n2c a658\n96 413e\n34 2d9a\n87 6095\na6 6c96\n62 9c84\n94 c9b0\n7d 955b\n19 8b61\nd5 5391\n98 e348\n56 1336\n19 a9eb\n11 8183\n99 c3c9\n13 23a5\nb8 6560\n70 9520\nb4 6532\n5c b9fa\n11 8321\n24 a4b0\na7 4695\nbc cdd0\na2 cc8e\na8 6668\n30 852a\n42 b884\n62 1e0c\n5d b15b\n8 a86a\n3b 2d67\n86 689c\n71 b781\n96 4194\n93 61a5\n65 1cb3\ne4 5610\n7f 1df5\n92 692c\n30 a7a8\nc5 7813\ndf 7955\nc a258\n4d 32d1\n54 9998\n7 a15\nda 53cc\nc0 528a\n5d 9971\n43 982f\n21 2403\n3b 2545\ne2 de0c\n8 a048\n4e b056\n1d 2b59\ne7 541f\n45 b2b9\nb1 45a1\n29 ae49\n93 492f\n9 4b\n31 87ab\n3b fc7\n48 1068\nc4 fa18\nf5 7519\n11 3a1\n8 a260\n86 4094\n19 2361\n13 8987\nec 747a\n5e bb76\ncc 5a58\n93 4927\n29 ae41\n9 43\n31 87a3\n48 1a40\ne a76\n0 a028\ndd 7bf3\nf7 7d35\n24 a638\n4d 9079\n26 8e96\nf5 57b1\naa 6c66\nd9 dbc9\n53 3ba5\n3c 750\n22 60e\ne9 deeb\n2 a086\nc6 5034\nac 4ef2\nf5 7d93\n3c a7d8\n48 3a6a\n47 12bf\n61 1401\nd1 5ba3\neb 5ce5\nfc 7d52\nf2 fd2e\n35 8d99\n70 95a2\ne 2a74\n5f 197f\nc 88d8\n5 2211\n8f 6af5\n16 a39e\n91 4321\ndc 79fa\n13 927\n7e 3f54\n64 3e12\n5b 13cf\n75 1511\ne5 5cb3\n2c 86f8\nff 5df5\na5 6e13\nbf 6f55\n61 9e01\na8 4460\nf1 5583\n43 322d\n57 9995\n8b 42cd\n53 19a5\nb9 6d61\n31 709\n11 a181\na1 4eab\nbb 4fed\ncc 5852\n6a 96ce\n96 6914\nfa 7dec\ne0 7caa\n34 a790\n9c 4152\na5 ec9b\nbf eddd\n70 1d2a\n39 741\na9 4ee3\nc3 5025\nfc 575a\n67 b695\n47 90b7\n98 616a\n7a 3fcc\n60 3e8a\n7f bd77\ned 5c59\n61 1681\n8 8a48\na7 66b7\n63 96a5\n21 e03\n3b f45\nd6 5196\n8d 4073\n88 c042\n12 8126\n47 38b7\ncd d8db\nc6 7214\nd8 dbe2\nf2 dd24\nee 5efe\n14 213a\n1a 8966\ne d4\n6a 9c46\n86 6094\n70 15aa\n68 3c48\n86 c896\n8c 6270\nb4 e5b8\n72 15a6\nc2 7226\n5 291\nca 5a6c\n27 e95\nb2 4f0e\ncc 5050\n77 9715\n38 a7ca\n21 2e01\n4a b2ec\nec 5452\n4c 1252\nd6 5b36\ne1 74a3\nfb 75e5\n97 eb15\n48 1a62\n46 3236\ncc d25a\na8 ecca\nff 5757\n49 1a61\n6e bcde\n23 8605\ne9 5c61\n38 856a\na 2a44\n4c 9af0\n90 6b02\naa 6c44\nb2 45a6\n76 1536\nb8 e548\n5a bbec\nfc 5d52\n40 baaa\n44 9a98\n77 1f95\n2 a024\n31 8521\n84 c898\nbb e76f\nb7 4d95\naf e4d7\nbc e578\n7a 1566\nd 2a59\n3e af56\nad 6c59\nb5 45bb\nb 2a67\n2b 26c5\n32 8d8c\ncc 5252\nbe 67dc\n60 9688\na4 669a\n31 858b\n8c 6a50\n83 4aa7\n6c 1652\n1c 2172\nf6 5f36\n30 8522\n4f 1a7f\n9a 4966\n38 87e2\n6 214\n8c 42d2\na6 4414\n66 1496\n86 c294\ne8 7ce0\n22 a684\nad e4db\n78 156a\n4c 9850\nff 55df\n97 e99d\n48 18ea\n5a b16c\n40 b02a\n65 9e33\n7f 9f75\nf9 d54b\n73 3527\n72 3f8e\n8c 40d0\nc9 7841\nd1 51a3\n67 b6bd\nd5 511b\n8f e055\n3c ad5a\n11 ab21\n8c 6072\n48 9060\n45 1831\naa 4666\n79 95c9\nbd 65db\n16 23be\n30 2500\na0 6ca2\nba 6de4\n73 1785\n5 a019\n86 6894\n58 b3e0\nfa 5546\nf1 57a9\nd 88f3\n8c c250\n3d 877b\nf6 5d36\n90 4902\n6 1e\nca 72ec\n34 53a\n41 3aa9\n4a 3846\ndb f9cd\nc1 f88b\n24 2412\n3e 2554\n80 60a8\n7d b773\n6c 1cf8\na7 e497\nb4 e538\n72 1526\na5 e493\nbf e5d5\n70 1522\n61 bc21\n24 84b0\n82 6284\n6e 1ed4\n81 6a21\n3e ad5c\n24 ac1a\n57 3117\n42 1824\n76 95bc\nba 65ce\nec d452\n61 be01\n5b 1145\n41 1003\n12 832c\ncb 58e7\na8 6460\n89 c2c9\n3 22a5\n86 4814\n1d 2973\na6 4e1c\ncc faf8\n2f 267f\n87 e097\n94 e138\n52 1126\na4 4490\n41 3a01\ndb 7367\n4 290\n82 6806\n3a a7c4\n20 a682\nd3 59a5\n0 82a8\nfd 77db\n33 7a5\n75 1533\n6 2a94\n48 1260\nca 5266\nb 2acf\n25 2c11\n6d 145b\n84 48b0\n24 2698\n45 3a19\n90 6900\n6 201c\nce f2fe\ne8 f440\n62 14a4\n9 8e1\n9d 6173\n59 9161\n48 ba62\n64 1490\nd0 512a\nc2 d026\n38 8742\n91 4b29\n97 6997\n53 9985\n87 42bd\nd6 7914\n1d a359\nb1 47a9\n66 1e16\n38 5ca\nc1 582b\ndb 596d\n15 8311\n93 61a7\n1b 296f\n36 af36\na4 4e18\nce 527c\n84 e890\n13 298d\n35 873b\nee 5cf6\nd9 7161\na a846\n2c a452\n85 6839\n57 3bbf\n71 3d01\n34 590\nf9 f549\n20 ae28\nfd df79\n16 a114\n6 2a1e\n17 11d\n98 e162\nb0 6d88\nef d67f\n70 9d22\nb1 c703\n32 8da6\n10 8122\nc0 da28\n19 2161\nf3 5f25\nf8 7548\n9a 4bec\nab e667\n80 4aaa\n8 20e0\ne2 5ea4\nb3 ed8f\nfd 577b\n19 a941\n80 6880\n9e c15c\n84 c01a\n1a 964\n0 822\n56 b3be\n70 b500\na6 4e94\n20 2c80\n54 b198\n16 a916\neb 54c7\nf8 5568\n53 13ad\n35 2d19\nac 6cf8\n68 3448\n31 2781\n9e 4956\na6 e634\n95 4bb9\n3c 87d2\ncf 52f5\n7c 1ffa\ncf 725d\n50 3900\n5 a19\n36 8f16\n37 ad37\na5 4c19\nd6 d116\n53 b127\nc1 5009\nf2 d506\n17 a19d\n97 c3bf\nb1 c501\nba ef44\na0 ee02\nd 20d1\ne7 5e95\nd2 f126\n15 8191\n1e abd4\n4 aa92\n96 c3be\nb0 c500\n60 3c80\n2a 2cce\n65 9619\nc 20d0\ne6 5e94\nd0 79a8\n87 e895\n52 1924\n4a 18e4\nc9 5241\ncb 78ef\n5 a293\n1f a3d5\n2a e66\nde 53dc\nc4 529a\n28 2448\n98 6bea\nb2 6d2c\nd0 dba0\n33 727\n9a 4be6\ndb 79c7\n22 a40c\n8 a2ca\n72 9784\nb6 6796\n10 1a0\n5c 3172\n3 88ad\nd 8d1\nca 7866\n71 1da9\n68 b6e2\nd0 5120\n12 38e\n2c 4d0\nb6 4db4\n4f 3aff\n69 3c41\n71 15a3\nef 5e55\nca 704c\n6c 9ef8\nb0 6f0a\n9a 6366\n6b b4c7\n78 b568\n5 19\nd9 f369\n36 8516\n90 4b0a\naa 4c4c\nd3 71a7\n23 2c2f\n3d 2d71\nee 5e54\ncb 78c7\n12 a30c\nd8 7968\n2b 8647\nc3 5aa5\n1a a3ce\n34 a510\ne0 fca8\n48 9240\n8c 6252\nc 2852\n41 b8a9\ne2 d406\nc1 522b\ndb 536d\n91 e981\n7 a09d\n43 b027\nb1 4f09\nb9 4d69\n92 4b86\n86 e814\n5d 9979\n6b 1ce7\nad ecf9\nea 5644\nf0 5720\nf5 dd3b\nee 7674\n42 9aa6\n49 1243\nc a8f8\nc 8d0\n21 ac03\n3b ad45\n1b 2bed\n1 2aab\nd2 5186\n89 4063\na9 ee61\nc4 5a10\n39 2561\n37 8fbf\n51 9101\n95 6113\n39 749\nf3 df85\n19 a1c1\n57 1915\n18 29ca\n5d 99d9\n4d 9879\n38 a548\n6e 96f6\nb0 4722\nd 88db\n6 2214\n48 92c0\n8c 62d2\na6 6414\n3d 87db\nf6 5d96\n16 893e\nf 2277\n36 859c\nc aa72\n70 15a0\n98 4362\n96 c99c\n8f 62d5\n56 311c\n3c 2fda\nc6 78be\n67 1e95\n18 ab6a\nc3 5885\nfa d544\ne0 d402\n5 a099\ne9 dceb\ne2 7624\n6 29e\n7f 3df7\ne4 7612\nfe 7754\n84 e810\n95 c9b1\n64 941a\n7e 955c\ne0 7402\nfa 7544\n16 bbe\n30 d00\naf 465d\n5b 1bcd\n41 1a8b\n3c adda\n29 2449\n2 226\n15 219b\nc 887a\na2 4426\nce 72fc\n38 54a\n3 a805\n42 b204\nd9 d9c1\n22 406\n6d bcf1\n53 bbaf\n3c 875a\n69 9463\n76 1d96\n4d ba71\ne8 f6c2\nf1 5d89\n3 2825\n89 c849\n99 61e1\n71 1da1\nf5 d599\nfe ffdc\ne4 fe9a\nd5 d93b\nce 7274\n62 b604\n51 1b89\n0 88\nc3 5a0d\nd1 5183\n88 4060\n67 b69d\nb9 c7cb\n33 27a7\n2f eff\n49 1041\n6d 3cf3\nec 7650\n86 6896\n42 9884\nf3 ff0d\n5d 915b\n14 299a\n2c 26fa\ne5 f419\n1 82a1\n9d 697b\n59 9969\nec 5e70\n24 2e18\n1e a3de\n99 4361\ne2 5484\n81 4081\ndd 517b\n38 fc0\n6 a94\n7f 1dff\nfe 575c\ne4 561a\nde f1d4\nc4 f092\n9c 61f2\n58 91e0\ncd 72d9\nb0 450a\ne3 7caf\nfd 7df1\n37 a795\n62 1ca4\ne1 5601\n25 8e33\n3f 8f75\n18 8940\n95 61b9\n2d c53\nc3 7a05\nc 2a50\n17 81b5\n6 aab6\nf5 7d31\ndb 7bef\n2f a6d5\n13 2387\n20 2428\n75 9f99\nda db44\nc0 da02\n90 e98a\ne7 5417\n45 b2b1\n4 ab8\n2f a675\n8 a2e0\naa 4446\na1 46a9\n4b b8e7\nca f244\n55 9999\n35 f13\n4f 1055\nd5 d339\n9a 69ee\n1c 8b58\nbb 67c7\n77 97b5\n77 3d9f\nf0 dd22\n66 943e\nd3 d90d\n1c 352\na6 4c36\ne1 d429\n11 29a9\n66 9436\n15 a9b1\n35 f39\neb dee5\n11 a121\n88 e862\n1a 834c\nd3 5907\n0 820a\nd0 db80\n33 707\nff f557\nd4 599a\n88 6a68\n87 4017\na7 ee15\n97 e915\n48 1862\n43 b2af\n5d b3f1\nff 5557\nfe ff76\n27 8e9f\n44 9832\n5e 9974\ned 745b\n9 2e3\n23 425\n55 b3b1\nf7 5517\n8d c2d3\na7 c415\nea fecc\n54 911a\n8e 485c\n32 5a6\n10 2b02\n2a 2c44\n52 b3a4\ncc 5272\n82 e886\na8 4660\nf1 5783\n12 a904\n28 ae68\n8 6a\n59 1b41\n52 9984\n96 6996\n37 a5b7\na5 4499\nb 65\nfe dffc\ne4 deba\n17 a197\ndb 5145\nc1 5003\nd4 db98\n37 71f\ncd 705b\n3 25\na5 4439\n2e 8656\n87 4a3d\n42 b20e\n5c b350\na8 e6e2\nb1 4da9\n94 491a\n49 1241\nd3 5b25\ncf 52ff\n50 19a2\ne9 5441\na6 4436\n7e 1576\n59 1369\n39 ade1\n70 950a\nc8 5a6a\n38 ad40\n1e abfe\n15 a911\n1d 297b\n97 49b5\n66 141e\n15 2999\nf0 5d02\ncc 7a5a\n2 a24\n18 8b62\n12 986\n9a 6966\n38 a7e2\n56 1996\n29 a4e1\n96 4916\nb8 6562\nb3 c50d\n99 c3cb\n13 23a7\n18 2362\n1c 297a\n8e 6876\n4a 9864\ne1 5401\nc7 52bf\n3f 2ddf\nb8 cd62\nf7 fd1f\n13 8ba7\nc4 f230\n2e 847e\n59 1341\nb6 4536\n82 4aa4\n6b 164f\n8 e0\n6a 96c4\nae 66d6\nb4 c512\n7e 3dd4\n64 3c92\n69 1c61\n84 ea90\n1b 2b6f\ncb 5a65\n5f 135d\n45 121b\ne9 5c41\ncf 5aff\n90 4102\nec 7c78\ndf 7bd7\n26 a61c\n38 85c2\nbd 6579\n52 9384\n96 6396\n74 b598\n9 ac3\n23 c05\nd7 fb95\n2d 267b\n85 e093\n9f e1d5\n50 1122\n49 3841\n6a 9664\nae 6676\n51 11a3\ncf 5a55\naa 6c4c\n4c 9af8\n90 6b0a\n13 a125\n69 1e69\n17 bbf\n31 d01\nce 5a54\nb5 4539\nbe 6f7c\n60 9e28\na4 6e3a\nd7 d9bf\n3a 546\n31 7a9\na ac4\n2c 245a\na0 6ca0\n3a fcc\n20 e8a\n86 4ab6\na8 6cca\n64 9cb8\n50 b122\n2d 2459\nd4 51b8\nf aa75\n96 4936\n2c ae50\n34 87b2\n4b 1a4f\n96 e9b4\n7f b55f\nb8 edc0\ne8 566a\n3e a7fe\n1c 2958\n94 eb18\n52 1b06\nfd d5d9\n77 35b5\n44 b0b8\n1b a3cf\n35 a511\ndb d9cf\n3e 556\n35 7b9\n4d 18d9\n44 b212\n5e b354\n3d 5f3\nd3 73a5\n40 3800\nc6 5a14\n98 41c8\n6a be66\nbf 6577\n61 9423\n7b 9565\n82 4a8c\n0 a020\n19 21eb\nf3 5faf\na9 44c1\nad e4d3\n78 1562\n8a c8ee\n83 6227\n61 b429\n6f 1e77\n6d 9e51\n2 a8a4\n81 e201\n4f 1aff\n69 1c41\nc7 d8bd\n10 302\n2a 444\n65 9491\n7a 95c6\nca f2e6\nd3 59ad\n49 10c9\n6c 9e50\n1b a9e5\n1 a8a3\n80 e200\n68 be62\nbd 6573\n79 9561\n84 6090\ncc 52d0\n4f 9875\n10 a92a\n83 682d\n21 a6a9\n19 9cb\n2a a446\n42 1224\n55 3199\n3d a573\n4c 9878\n93 6125\n65 1c33\n7f 1d75\n11 329\nc8 52e2\ne2 5424\n9b 69e7\n78 1548\n48 b24a\n30 7a2\n28 2e40\na2 c406\nc5 5813\nc 8258\ndf 5955\n63 968f\n7d 97d1\ncc fa50\n13 8187\n5 8a11\n6c 3452\n83 68a7\n8d e0db\n58 116a\n39 85c1\n42 b004\n28 aec2\nd9 d14b\n53 3127\n89 4863\naa 4e66\n5 2811\na8 cce8\na1 6621\n7e bfde\nf9 5f61\n35 afb3\n4f b0f5\nd0 d18a\n33 8fad\n91 6101\n63 1c0f\n7d 1d51\n66 be94\ne9 7ee9\n46 1096\na0 4480\n0 280\n70 150a\n34 8792\nfa 5dee\n3e fdc\n24 e9a\na5 ccb1\n74 971a\n19 349\nd0 5302\nea 5444\nf 88d7\n1c 8978\n67 163d\n76 bf9e\nf1 5f21\nb6 ed36\n40 1aa0\nf7 5795\nc4 d298\ne8 564a\ncb 5265\nc7 f01d\nad eedb\n78 1f6a\nd7 f33f\n58 b9e2\n2 a806\nd1 7121\n36 afb6\na4 4e98\n3a 87cc\n20 868a\nf3 5d87\nb9 e561\n67 3e95\nf0 7580\nf9 5543\nca fac4\n34 8d12\n9d e353\n8c 48d8\n1e a9f6\n48 9a62\n76 bd3e\n2b 8665\n4b 18c7\n8d e8d9\n58 1968\n12 a124\nd2 592e\nc 82d2\n26 8414\ne4 7630\nde 5956\ne6 f634\nd5 5bb9\n7c 97d2\n6b 164d\n31 af83\n4b b0c5\n59 13e3\n73 1525\n84 603a\n9e 617c\n33 8f87\n40 9028\nab e66f\na7 4c95\n7c 97d8\n78 3fe2\n92 4124\n13 a325\nec dc52\nf4 7d30\nda 7bee\n2e a6d4\n70 3f22\n8a 4064\nd3 5187\nba cdcc\nb3 6705\na0 cc8a\n49 12e3\n63 1425\n30 722\n7c 157a\nec 7cf0\nd2 7bae\n26 a694\n68 3ee2\n82 4024\nef f657\nde 5bdc\nc4 5a9a\n12 a1a4\nfc 5752\n8a 4866\n28 86e2\nad ecfb\nea 5646\n99 6bc1\n0 2a8\nd3 d98f\nd9 7369\n36 516\neb 7c6d\nd1 7b2b\n25 a611\nc 2a58\n2e 4fc\n14 3ba\ned d6d9\nb2 6d8e\n67 36b5\nd1 7b23\neb 7c65\n3 a8a5\n98 496a\n39 8d61\n14 a1b2\n34 73a\n4 8218\nd7 5915\n75 9791\n2d efb\n47 103d\nc9 d8c1\n12 306\n60 1e20\n82 c886\n88 6260\n78 15e0\n40 3808\nd4 5192\n8b 406f\n4 2a30\n98 c1ca\n12 21a6\n2a 8c4e\nc0 fa00\nb 8247\n61 9c03\nbf 6d57\n7b 9d45\n3c adfa\n30 d20\naf 467d\ne4 7698\nb0 4502\nd8 7940\n1f ab77\n8d 4a59\nef 7efd\n59 114b\n22 484\n22 a6a6\nfa 55c4\ne0 5482\n8d 68f3\n49 98e1\n34 a5b0\n1 809\n32 8d06\n66 363e\na 8246\n69 344b\n80 68a0\n9 aa69\n32 d8e\nc 8878\n25 c33\n3f d75\n3e ad5e\n13 ab25\n14 2190\nca f86e\nd 88d9\nd 88f1\n36 f9e\nd2 7326\n15 391\nd1 79a1\na0 440a\nba 454c\nd2 598e\n89 486b\nb6 cd3e\naf 6677\n6b 9665\n3b a56f\n40 1220\n34 792\ne 885c\ncc 7a78\n11 909\n8 a242\n91 e189\n7d 9dd9\n1a 3ec\n0 2aa\ne3 5c0f\n41 baa9\nfd 5d51\n77 9d15\n5d 9bd3\nd0 5900\n61 be09\n5b 114d\n41 100b\n9b 63c7\n57 93b5\na8 6468\n3 22ad\nfb f76f\nf7 5d95\n24 8698\n86 481c\na5 e49b\nbf e5dd\n70 152a\nff d577\n90 69a8\n32 2506\n24 8e12\n3e 8f54\n17 915\nd0 518a\nb aa47\na 80ee\na0 eea0\n28 c48\n45 3a99\n90 6980\ncd f2d3\ne7 f415\n2b 84c7\n38 8568\n5a 3366\nf2 fd26\n21 2629\n35 8d91\n69 36c9\naf 66dd\n30 2d80\n87 489d\nec 76d2\n67 bc1f\nc8 5a60\n4 aab2\n1e abf4\n28 4e0\neb 5e65\n9e 41d4\n84 4092\ne 80de\na4 ee90\n3b 2f6f\n32 506\n41 1889\n52 b304\n61 3e89\n49 b263\nd6 d916\n5 219\n43 b085\nd8 514a\n4d 10d1\n33 f8f\n88 4062\na8 ee60\n67 b69f\ne8 d6c8\n62 36a4\n1e 2b56\n46 b2b6\nc2 d00c\na8 ceca\n22 2ea6\n0 80a8\n58 13c2\n72 1504\n8c 4052\nac ee50\nb1 67a9\n4 8a12\n1e 8b54\ndf 5177\n21 8c01\n7 8abf\n12 8904\n6b 3ecf\n85 4011\nf5 5fb9\n44 3818\n3e adde\nb9 4d61\n15 33b\n2f 47d\n4e b0de\nc9 5061\n5 a0b3\n1f a1f5\n3c ad52\n5b 1b45\n41 1a03\n93 6985\nb5 4f93\ncf 50d5\n90 618a\n7c 1dda\n58 996a\n91 cb23\nab cc65\n45 1239\n21 2ca9\n76 9736\n25 acb1\n90 4120\nf5 f71b\n76 bdbe\n2b 86e5\n38 ad4a\n8b e045\n1e 954\n4 812\n44 3018\nce 78fc\na8 6e62\n77 1537\nb9 e549\n90 c980\ndd f173\n6 809c\nc4 72b8\n45 9a99\n84 68b2\n40 98a0\n9e 69f4\nd 8a59\n90 4322\naa 4464\nda d1cc\nc0 d08a\nf3 5587\n30 85a0\na5 6699\n7e 1556\n75 17b9\n7 835\n57 9197\ne0 fea8\n1e 895e\n29 ee3\n43 1025\n3 8a87\n10 8b28\n1d 2953\nb0 4522\n3 2a05\ne5 5439\n4 a010\n1d 21db\nf7 5f9f\n1 22a1\n93 49a7\n7c 1552\ne8 f6e2\nf1 5da9\n9c e958\n5a 1946\n65 b419\ne0 7422\n23 48d\nfa 7564\n7e 3756\n9a 4b46\nc2 d2a6\na5 64b3\n61 94a1\nbf 65f5\n11 2121\neb 5ee5\n59 33e3\n73 3525\nf9 d549\ne8 fe4a\neb 7ced\nd1 7bab\n25 a691\n87 6815\n6 a09e\n81 4021\n59 b36b\n55 1991\nba 47c6\n41 10a3\n5b 11e5\nea dc4c\nd0 db0a\n4a 3ae6\nc8 5a62\neb 5665\n60 b488\na9 6469\n82 6286\n6e 1ed6\n1a abec\n0 aaaa\nbc 4d52\na0 6c20\n7 95\n6e b4de\ne9 5461\n1a 8b46\n25 a4b3\n3f a5f5\n94 49b2\na6 6696\n62 9684\n34 279a\n4c 1850\n6 294\ndd 515b\na6 4494\n80 e888\nd7 5315\n1b a96f\na4 ce18\n6c 1672\n22 ac86\nf1 75a1\nb9 4fe3\nd3 5125\n5 a91\nc2 7a26\n93 cb87\na0 cc28\nba 4564\na0 4422\n4c 1250\nd6 5b34\n10 81a0\n68 946a\n85 6299\n78 3548\nee fcfe\na3 c625\nb7 479f\ne 8d4\n3e ffc\n24 eba\n4e 1a5e\nb aac5\n56 3bbe\n70 3d00\nef 765d\n86 4896\nba 4de6\n50 b300\n8d 4af9\n92 61a4\n13 1ad\n9a 4b6c\n80 4a2a\n51 1921\nde fb5c\nc4 fa1a\n96 e314\n17 a9b7\n85 4899\n7e 155c\n64 141a\n95 49b1\n35 2799\n1 801\nec d65a\n66 3636\naf 6e55\n5c 3b5a\n16 8b36\n3 a85\n83 4aad\n6c 1658\n32 af8e\n4c b0d0\n0 2a00\n18 296a\n46 9214\n4b 18ed\n42 b226\nbd cfd3\nd7 d115\nc6 fa16\n8b e26f\n87 4895\n18 2b6a\n37 2795\nd7 7995\n4 a298\nb3 65a7\n9a e1ec\n80 e0aa\n64 b698\nc6 781c\n26 ae16\nf0 df80\n16 a1bc\n47 1a1f\n92 4906\n30 8782\nf6 5f3e\n1c 217a\n1b 29e5\n80 6200\n1 28a3\n47 1835\n8 28ea\n18 23c2\n32 2504\nc1 52a1\nb2 4da6\n28 4c2\n48 b2c0\n23 c85\nf0 d5a0\n7 bd\n31 2789\n1a 23ec\n0 22aa\n9 aa6b\n9 849\n3a 8d46\n8f 60fd\n31 8fa9\n6e 367e\nc0 5802\nda 5944\n9d 6b59\nce f056\nbf 45fd\na5 44bb\n95 4191\n5a 19e4\n40 18a2\nd9 5341\nc2 d00e\ndc d150\n8c 48d0\n9c c9d2\n88 626a\n54 b932\n1e 976\n99 4969\n3c 27fa\n8e 6854\nf2 7d2c\nd8 7bea\n2c a6d0\n96 41b6\n25 ac93\n3f add5\n6f 167f\nba 4566\ncb d26d\n90 6922\n4a 104c\n30 f0a\nc2 5284\n8c e05a\n8 2862\n4 22b0\na9 646b\nad 6e73\n69 9e61\n40 9a80\n84 6a92\n9e 6bd4\n39 87e1\n6d 1651\nd2 712c\nb8 6fea\n28 8ce8\n21 2621\n8c 6a58\nae 44fc\n94 43ba\ne7 76b5\n9b c3cf\nb5 c511\nbe ef54\na4 ee12\n65 3c91\nca 7844\nd2 51a6\n68 b6c0\ncb 50c7\nd8 5168\n33 fad\na1 44a1\ne5 7419\n1 2a1\n2d 479\n6 296\nf1 7d21\n2b a6c5\na6 4496\nc6 f294\n4c 1852\nd2 7106\n74 9fb2\n1e bfc\n4 aba\n2f a677\n8 2e0\nec 7458\n3b dcf\nd1 7b81\n15 313\n2f 455\n67 1e1d\ne8 5662\nde f9f6\nd 22f9\n91 63a9\n7d 1ff9\n8c 4852\nf0 5d2a\n2a 86ce\nd9 fbcb\nf3 fd0d\n3c 2752\n16 394\nf1 552b\nf a55\nac c6da\n26 26b6\na1 66a9\n49 3069\n22 2e86\n33 585\n0 8088\n47 1a3f\nd0 51a8\nb aa65\n67 9c95\nb8 6d48\ne0 f4a8\n34 253a\na6 6436\n48 92e2\n62 9424\n53 9185\n97 6197\nbc 65fa\n78 95e8\n9a 43e6\n3a 8d66\n9 869\n63 b407\n52 198c\n56 11b4\n93 4925\n31 87a1\n9 41\n16 2994\nd a51\n31 581\n23 8cad\n66 9e96\nbe 6574\na4 6432\n60 9420\n51 9181\n95 6193\n38 8d62\n61 b403\n7b b545\n50 1988\n69 9e49\nad 6e5b\n34 859a\n2e ae56\n1f 3fd\n5 2bb\n99 e143\n23 ead\n91 43a1\n88 4840\n26 86bc\n91 c989\ncd 52f1\n35 a591\n82 6a04\n23 a4a5\nb8 456a\nb 2a4d\n42 12a4\n3d a5f3\n97 4915\ne1 5cab\nf2 f726\nfb 5ded\n35 8791\ndb d9ef\n3e 576\n20 4a2\n3a 5e4\n96 4196\n4d 32d3\n67 3415\n54 999a\na7 cc95\n8 aa68\n21 2e23\n3b 2f65\n43 1a25\n38 ad48\n74 1518\n82 e2a6\n6e 9ef6\n84 e010\n21 681\n96 4914\ne0 5caa\nfa 5dec\n34 8790\nc 8a7a\nd0 79a2\n31 a529\na2 4626\n71 9589\nb5 659b\n20 8420\ne4 7e30\n4e 107e\nae ccfc\n94 cbba\na7 6635\n8c 485a\nc5 7a13\ndf 7b55\ne7 54b7\n9c 4972\n3a 87ee\nae 6656\n6a 9644\n3c 275a\nd4 d190\na6 c41c\n8c c2da\n6 22b6\n81 62a9\n6d 1ef9\n9e 6bdc\n84 6a9a\n40 9a88\ndc f17a\n73 1f85\n3d afd3\n57 b115\n7 2895\nab 6e65\n58 3b6a\n49 1061\nf6 dd1e\nef 7657\nd7 7915\n4 a218\nd5 f9bb\n38 2542\nbe 4756\n15 8b13\n2f 8c55\nb a065\n74 1710\nf aff\n29 c41\n41 1021\nee dcde\ne7 7617\n3f a5df\na0 4420\n0 220\nee 54fe\n54 1138\n47 1097\naa 4c64\n90 4b22\n6 23e\n1 281\n0 880\n11 81a1\ne2 d40e\nfc d550\na8 6448\n57 9395\nbc cdd2\na8 666a\ne8 5642\nde f9d6\nd 22d9\n97 6bbd\n3e a7d6\n1d 8979\n46 103e\n4e 9874\n5e 39fe\ndd 735b\n13 325\ne1 5481\n2b ce7\naa 4644\n83 c20d\n4 88b0\nd1 5123\n0 28\n62 960c\na6 661e\nc1 7803\ndb 7945\n8 a248\n86 6814\n1f a1df\n80 4020\n28 ac4a\nf0 5daa\nb9 47c1\nbe 657c\n60 9428\n53 9387\na4 643a\n1 8229\n60 1680\nc7 5a1f\n4d 1a59\n7e 9f56\nb7 ef1d\n68 1e6a\nc7 f23f\n48 b8e2\n94 4910\n22 8c0e\n3c 8d50\ncc 585a\nc5 5831\na7 66bf\n63 96ad\ne2 d426\nb1 4f29\n92 4ba6\n22 404\n8 2c2\n39 2549\nb7 6d97\n73 9d85\na7 46bd\nc8 d2c8\n42 32a4\n5a 91c4\n9e 61d6\n40 9082\n5b b36f\n57 1995\n1 2a01\nec fe50\n3f a55f\n44 1210\nce 5af4\n65 1e11\n9 8a61\n84 c8b0\n6d 945b\nd 2851\n15 1b3\na3 cc87\nb0 cd28\na9 6661\nf2 7784\n71 3d09\n34 598\n74 1712\nf0 f5a8\n79 3f49\nb a067\nfe 5ff6\n21 ae89\ndf 73ff\nf9 7541\n40 10a8\ne6 769e\n2 826\n57 bbb7\nc5 5a99\naa eec4\n14 8112\n21 c81\n43 182d\na8 4662\n51 1901\nb6 4736\n55 9b11\n1a a3ec\n0 a2aa\nbc 4552\nd3 79a7\n7 215\n40 30a8\n88 c84a\n2 2826\n0 a88\n2b a645\n4 a2b0\na6 4416\n47 b8b7\nc6 f214\n4d 98d1\n6 216\n16 a914\n31 ad2b\n6e 1676\n3 2aaf\n1d 2bf1\ne9 5669\n3a ad4c\n20 ac0a\n92 4306\n43 9aa5\n87 6ab7\nbd 4fd3\nd7 5115\nc6 7a16\n54 91b8\n98 61ca\n46 b09e\nc1 5021\n17 a1b5\n91 e301\n12 a9a4\n3 a225\na7 4ebf\nc1 5001\ne4 deb8\n17 a195\n37 71d\n6f 96f7\n3e 275c\n24 261a\n3d 2559\n5a 934c\n9e 635e\n40 920a\nb5 e719\n36 adbc\n73 1707\n59 13cb\n73 150d\nab 46cd\n56 bbb6\nef f655\nc4 5a98\na2 448e\nbc 45d0\ndf 7bff\nf9 7d41\n55 331b\n6f 345d\n0 8a28\nfd 7f5b\n33 f25\n46 9216\n1 a09\n32 8f06\nea 7ec4\n54 1112\n80 4002\n9a 4144\n8c c0da\n6 20b6\n12 8b04\n48 3ac2\n62 3c04\nf2 7f0c\n5c 115a\n5b 19c5\n41 1883\n50 11a8\nd0 5122\n4d b27b\n62 9426\n11 a9a1\n31 f29\nc a50\n92 4b0e\nac 4c50\n7 a95\nba 47ce\nae 4656\nc7 dabd\n2a 644\nae 6e5c\nc1 f8ab\ndb f9ed\n3e 2574\n24 2432\naa 4646\n45 1811\n13 a30d\nd9 7969\n55 9b99\n0 2028\nc3 580d\nf4 dfb0\n1a a1ec\n0 a0aa\nb9 c5cb\n33 25a7\n91 4901\n7 1d\n4a b266\n53 192d\nb8 4762\n97 4bbf\nb1 4d01\nd 2db\n27 41d\nc5 5a11\n13 a1a5\n33 72d\n80 4800\na4 4690\nd 8af3\n27 8c35\n68 36ca\nc6 72bc\n30 50a\nba 4dee\n65 bcb1\n34 871a\n13 2b85\ne0 f4a0\na 244\n90 4302\naa 4444\n44 b812\n5e b954\n2c a6f2\n35 db9\n9 22c3\n10 898a\n23 2405\ned 7c71\nd3 7b2f\n27 a615\n4a ba64\n2a c66\n1b 23cf\n35 2511\nc2 daa6\na5 6cb3\n61 9ca1\nbf 6df5\n64 1412\n7e 1554\n2f 867f\nb2 6f86\nd9 7169\na a84e\n55 9399\n36 2d96\nb1 6d89\n99 6b61\nf 227d\n19 3e3\n33 525\nfd 755b\n61 b409\n8a c8ce\n83 6207\n6f 1e57\n88 42e8\n85 e099\n43 1087\n50 1128\n8 8248\nc1 5803\ndb 5945\n91 41ab\n89 6849\n44 1ab0\nba ed46\n8 a42\n13 23af\n2d 24f1\na8 4c42\nc8 fa40\na7 4495\n54 119a\n7 295\n92 430e\nc3 78a5\nac 4450\n90 6120\n97 eb9f\n62 1c2e\n7c 1d70\nb1 ed0b\nee 5656\n88 40e0\nfe 7dd4\ne4 7c92\n9b 6b6f\n5c 9358\n35 a59b\n22 8626\n44 b898\n77 3d95\n3a 566\n15 311\n4c 18fa\n94 e11a\nd2 590e\n70 978a\n81 62a1\n6d 1ef1\n3e a5dc\n24 a49a\na aa46\nd3 d987\nd9 7361\n4a 1a64\n5d 39d9\n76 bfb6\ne4 5e98\ndc 59da\n61 bc09\n83 6a07\n8e 6a76\n4a 9a64\n5d b9d9\n1b 3cf\n35 511\na5 4cb3\nbf 4df5\n52 190e\n5e 9b54\n44 9a12\nf3 fda5\n69 b4c1\n3 a05\n66 1494\n50 190a\nd9 f9e9\n3c 2570\n22 242e\n77 9f9f\n48 9860\n8c 6872\n2a a6ee\n1e 35c\n4 21a\nb3 65ad\na8 4c40\n8e 4afe\n28 6e0\n2 2a84\n1d 235b\na6 e494\n71 1523\n92 49a6\n8 c2\n28 aec0\na5 441b\nbf 455d\n8 868\n1a 296e\nc2 da06\n80 c0a0\n58 3940\n27 4b7\n66 1694\nb9 65e1\nae 4c74\n94 4b32\n9b e36f\n97 4995\nc3 78a7\na a2ec\nac 4452\n4d b8f3\ncc f250\n6 ab6\n64 3412\n7e 3554\n8f 42df\n10 982\n81 4aa9\n28 86c2\n8a 4846\n60 1402\n7a 1544\nf4 571a\nd4 f192\n89 4841\n27 86bd\nee 7676\na 2046\n32 584\n66 163c\n8f c0df\nf0 5f20\n46 b0b4\n1f a977\n8d 4859\nbe cd56\nb cd\nc8 7062\n1 80a9\nf5 fd39\n3e 277e\nc3 d00d\na9 cecb\n23 2ea7\n7a b564\n60 b422\n83 482f\n9d 4971\n21 86ab\n3b 87ed\n3d 2759\n4f 1a5f\n30 2d0a\n3c 255a\n77 b537\ne5 5419\n65 b413\n7f b555\n54 1998\nd2 fb24\n3c 8d72\n30 250a\nf5 d739\nba 6dee\n3c 8f58\nf0 7520\n75 9d99\n4e 3a74\n75 3f93\n8f 40d5\nd2 7b8c\n3c dda\n1b 2947\n50 b3a0\nf2 5506\n90 c120\n17 219d\n5c 995a\n95 cb13\naf cc55\nde 51d6\n49 b2c3\n63 b405\na6 eebc\n10 810a\naa 46e4\n9a 61c4\n80 6082\n6c 1cd2\n86 4036\nb a265\n68 9e62\n48 b262\n51 1929\n6f 3eff\n89 4041\n28 8e62\na0 eea8\n17 8197\n7 a03d\ne1 de01\nbb 65e7\n81 6a81\n1d 29fb\n9c 6358\nf0 5520\n98 e3e2\nb2 e524\na5 6499\nb 2065\n5a 33c4\n40 3282\n9e 6956\n40 9802\n5a 9944\n8e 427c\n28 24ca\ned d6f9\nb2 6dae\n34 8f18\nf2 ff04\n5c 9152\n9e e154\n84 e012\n89 48e1\n58 134a\nf6 7d1e\n12 ba6\nab 4645\nb7 e71f\n38 adc2\n28 a460\n74 3f12\n8e 4054\n1b 8947\n91 4181\na7 6c97\n63 9c85\nb4 6d38\n10 2122\nea 5ee6\n46 b2be\n60 b400\ncd 5a5b\nc0 f220\n2a 846e\n44 9230\n88 6242\ne3 fc87\nf0 fd28\nc 8a58\nc5 d239\n8a 68ee\n25 e13\n3f f55\n23 ca7\na2 4604\nfa 5566\n52 1184\n9 61\nb6 cd1e\n6b 9645\naf 6657\n20 c80\nf4 559a\n2f ae57\n6 2bc\n8f e0d7\n9c e178\n5a 1166\n16 8914\nc8 fae2\ne2 fc24\n58 b340\n55 9313\n6f 9455\n30 a50a\n95 4b39\nad 4ed3\nc7 5015\n74 1d1a\n44 90b8\n88 60ca\n82 4206\n1 8281\n9d 695b\n59 9949\nba 67c6\n76 97b4\ne5 dc31\ncb daef\n2e 676\na9 4669\ndb fb6f\n4 8a98\n37 f95\n6a 3c6c\n50 3b2a\n7e 95d4\n64 9492\na1 6ea9\n4c 9af2\n66 9c34\naa 6c46\na a864\n43 92ad\na1 6401\n87 62bf\n73 1f0f\nff d557\n90 6988\n3d fd3\n57 1115\n46 3a16\n24 8c18\nb2 e526\n81 6029\n4d 12db\n67 141d\n92 c90c\n8b 6245\n7a b7ec\n60 b6aa\n38 2f4a\n5 819\nd9 fb69\n36 8d16\n1d 8359\nd6 5914\nca 5ae6\n2b 866d\n74 9790\n96 43be\nb0 4500\n90 e1a8\n9e 4bf6\n75 3731\n7c 9df8\n2d ed3\n47 1015\ncd d2f9\n92 69ae\n14 8b18\n12 a3ac\nb4 4512\n2 206\na 2a46\n88 4848\n1a a966\n4f 3277\n56 993e\n8a 686c\ne1 7ca3\nfb 7de5\n28 a6e8\nea f66e\n2d 86d9\ne6 5c94\n83 4a2f\n9d 4b71\n65 9419\n87 4217\nec 7c70\nd2 7b2e\n26 a614\n15 b99\ne9 fee9\n46 9096\nbe 4ffe\nd8 5140\n0 8a88\n9c e17a\n33 f85\n3e 257c\n24 243a\n55 3333\n6f 3475\n5c 99fa\na2 6626\n74 159a\nf4 7d18\n10 ba0\nb9 cf61\n26 2e9c\ne 54\n36 87b4\nbf e577\n94 49ba\n49 12e1\n47 ba95\ndc 5b5a\n4d 1259\nd7 5b3d\n13 ab8f\n2d acd1\n7e 3ffe\n98 4140\n24 ae10\n8e 48f6\nf0 7d02\n97 69b5\n66 341e\n49 1269\n7a 9766\n29 ace1\n6e 1476\nc2 5286\ne9 5469\n25 a4bb\n3f a5fd\n1a 8b4e\n8e e8de\n43 b205\nc0 d880\n23 407\n23 8c0f\n3d 8d51\n99 6b41\n2b 84ef\nc1 f2a1\nbb 45e5\na1 44a3\nf 225d\n7a 35cc\n60 348a\nb3 6785\n80 e288\n6c 9ed8\ne6 569e\n65 1639\n45 b0b1\nb0 4520\n4e b2de\nc9 5261\n2a 8e64\n3d add9\ne 2a7e\nd 2d3\n27 415\n11 b21\n75 151b\nff 5dff\n74 15b0\n36 ad9e\nb1 4d21\nd 2fb\n27 43d\nf 8f7\n8e 4254\n72 3f0e\n8c 4050\n42 9226\nc2 f8ac\n25 2433\n3f 2575\ne6 de3c\nc a078\n55 b19b\ndd f35b\n13 8325\n5e b9fe\n52 b986\n9 a863\n72 1f0e\nde fb76\n7 8a9f\n83 688d\n1c a97a\nb2 6526\nc1 580b\ndb 594d\nbd 67db\n79 97c9\na0 6620\n4 2810\n85 e8bb\n9f e9fd\nc2 5206\n8c 6058\n56 b314\n45 1899\naa 46ce\na8 e6c8\n66 16b6\n30 2508\na0 6caa\nba 6dec\ne1 56a9\n99 69c1\n88 c060\nf 20dd\n9a 4166\n54 1312\n6e 1454\n22 ea6\ned de79\n6 a014\n57 33bf\n71 3501\n4c 1a52\na0 ec88\nf7 5715\n40 1a20\n74 97b8\nb8 67ca\n3b ad6f\n20 2600\n14 132\naa 6ee4\nd3 5185\na0 cc88\nba 45c4\na0 4482\n9 88e1\nd0 5920\na 82c4\n9 824b\nc2 5806\n0 8288\nd3 5985\n1e 29fc\n4 28ba\n9d 6359\n1 82a9\na 8046\n25 8631\n70 bd0a\na9 eec3\nc3 f005\n6d 3e59\n75 17bb\n9a 43ce\nb4 4510\n2 204\n32 2526\nb8 c54a\n8f c2fd\n10 89a0\n68 9c6a\n85 6a99\n1a 3ce\nd1 d989\n34 510\n70 3f88\n2 a0a6\ne5 5639\n5c 9b52\n3b 745\n21 603\nab 4ee7\n22 ae0e\n3c af50\n62 9e26\n45 3033\n5f 3175\n70 3f02\n8a 4044\nba 6564\na0 6422\nb7 ed3f\n41 1aa9\n8c e858\n4a 1846\n72 9fa6\nc1 d88b\ndb d9cd\n24 412\n3e 554\nc8 d84a\n42 3826\n80 40a8\n80 40a0\nf1 5729\nd1 f1a1\nb9 cf63\n26 2e9e\nbe 4f5c\na4 4e1a\n48 1268\na7 e63d\n28 ace0\n25 2c19\n56 b116\n9a 4346\n95 6131\nd0 5328\nc3 5287\n8d 60d9\n19 2961\n75 9d19\n97 4b17\n5 a8b9\n42 1204\n3d a553\n4b 32c5\n52 998c\n96 699e\n2 a224\nf0 f502\nb4 e590\nc5 5039\n4e 9256\n9 a49\n3a 8f46\nf2 7f04\n5c 1152\n48 9840\n8c 6852\nf0 7d2a\n2a a6ce\n4f 9855\n10 a90a\n1c 8b52\n6c 1ef2\n4d 98d9\ne3 5485\nb0 4782\n30 5aa\n28 2c48\na6 64b4\ncb 5245\n78 1f4a\nd7 f31f\n58 b9c2\nca 5a66\n13 9a5\ndd 79db\nce 7af6\n2f a67d\n99 4163\n3d af79\na2 c40e\nd3 f9a5\nbc c550\n57 1937\n99 e949\n5f 9355\n45 9213\n1e a956\n66 963e\nbb e567\n90 49aa\nb2 470e\ne3 7ca5\n7a bf6c\n60 be2a\n93 4327\na6 66b6\n62 96a4\n20 e02\n3a f44\n45 1219\n21 2c89\n76 9716\n6f 167d\n25 ac91\ncf 5afd\n76 3fbe\n90 4100\n31 252b\n3d 8f79\n4f 98d7\n5c 9978\n83 6a85\n21 429\n42 180e\n5c 1950\n91 c1a9\n9a ebec\n80 eaaa\nb3 6fa7\n8 8a40\ne0 d480\nf2 5f26\n18 2162\n30 8702\n89 4ae9\n11 2981\n74 1512\nfe 5df6\nd a59\n3e 8f56\n21 84a9\ne3 d40d\nc9 d2cb\n43 32a7\nd 825b\nc6 5816\n7a 15c4\n60 1482\n80 c280\n57 399f\nd0 d922\n46 903e\nde d156\n99 c149\n13 2125\nd7 7935\n4 a238\na4 6498\n42 b886\n68 1660\nc a87a\n3f 2d77\na2 6426\n5d 13d3\n77 1515\n66 3e16\n44 9018\n38 25ca\n8d e8d3\n58 1962\n37 8f9f\n73 b527\ne1 5409\n37 a59d\n66 1416\n15 2991\n7 88b7\n86 c214\ne2 f4a4\n9f 49f7\n35 af11\n14 110\n84 48b2\n9e 49f4\ne1 f4a1\n51 b901\n85 6239\n14 8190\nce f2d6\nd7 599d\n46 383e\n7b b567\ne9 5449\n50 19aa\n72 170e\na3 4ca5\n19 3c1\n49 1841\nae 4676\n4d 9a51\n21 aca9\ndf dbfd\nc5 dabb\n28 642\nb2 4f26\n53 130f\n6d 1451\n1a a164\n0 a022\n8f 42ff\n10 9a2\na9 4441\n7f 3fff\n99 4141\n44 9a30\n88 6a42\n4b 1acf\n65 1c11\n2f 8efd\n8d 6051\n0 a802\n1a a944\na6 663e\n62 962c\n19 141\n8 2a42\n18 a962\n84 6a98\n63 b42f\n7d b571\ndd 73db\nf7 751d\n13 3a5\nb8 4560\n46 10be\n10 122\n30 af20\n7 8a15\n3 805\n57 319d\nd0 d120\n1 22a9\nd2 f9a6\n48 b0c2\n28 ae60\n8 62\n14 910\nfc 5550\ne2 540e\n40 b2a8\n18 2b48\nf9 d7c9\n73 37a5\n2 804\n6 8a14\n91 4189\n9a 6bcc\n80 6a8a\n79 b563\na a8c4\n2a e4c\nd 8f3\n8c 4250\n8d e2d3\na7 e415\n58 1362\nbe 457c\na4 443a\n26 8ebc\n84 6010\n69 bec3\n83 c005\nc6 fabc\n29 2643\n30 8d0a\n80 60a0\n5a 13ec\n40 12aa\ncc 5872\n6 8216\n9e 49f6\n34 af10\nd8 d9ea\nd1 7323\neb 7465\n3 a0a5\n98 416a\n63 1485\n30 782\nae 6e7c\n5 2011\n5e 335e\n8f 68f5\n18 a362\n30 2f88\n18 140\n13 105\n2 2a06\n50 b1a8\na0 ec08\n5e 1bf6\ncd d25b\n47 3237\n4e 98fe\n78 3fc2\n92 4104\n7a 97ee\na 82ee\n24 8430\na6 6694\n0 200\n8a 4ae4\nee 54de\n2c a4fa\n18 ab62\n66 3ebe\n80 4000\n28 84c0\nb2 cda4\n86 6294\n88 eac8\n46 1ab6\n10 2908\ncf 52df\n50 1982\ne9 de6b\n2 a006\nac 4e72\n51 1381\na0 4e00\n33 a7a5\nb 2045\n15 111\n1e 2b54\n4 2a12\n85 48b3\n6e 145e\n9f 49f5\n3 5\n2 a826\n89 6ae9\n44 1012\n5e 1154\n2b a665\n0 aa8\nb7 479d\n65 3619\nfe d556\n2f 267d\nb9 6f61\n17 115\n6 2a16\n8c e8da\n41 b201\n3b 545\n21 403\n95 e391\n60 1420\n8 804a\nd8 d9c0\n3b 547\n41 b203\n5b b345\nf 82ff\n29 8441\n99 cbe3\nb3 cd25\n8e c8dc\n87 6215\n4e 305c\n34 2f1a\nb 2cf\n25 411\n5c 19fa\nf4 5d9a\n6 abc\n8f e8d7\n9c e978\n5a 1966\n95 e31b\naf e45d\n16 a9be\nd4 791a\na 8e4\n89 4241\n84 6032\n9e 6174\n40 9020\na5 e61b\nbf e75d\n26 acbe\n6e 9676\n19 2be9\n7c 3fd2\n96 4114\n53 b927\nc1 5809\n17 a99d\n21 609\nf2 dd06\n46 1816\n6e 9656\n19 2bc9\n4a b0c6\n9c 4952\n3a 87ce\n82 480c\nd8 51e0\na 846\n1 aa9\n32 8fa6\ne 8a56\n19 a3c3\n33 a505\n2a ece\n44 1010\n51 1b21\n53 1925\n68 b46a\ne aa56\n72 1584\n29 461\n1e a176\n8c 4058\n64 1610\n14 2130\nee 5ef4\n19 894b\nc5 f091\n42 302e\n5c 3170\na2 4c86\nc5 f831\ne 2276\n35 859b\n83 c88f\n9d c9d1\n89 6269\n55 b931\n8f e8d5\n40 1822\n5a 1964\nf4 5d98\nf 857\n5 211\n8f 4af5\n8b 42cf\na5 4411\n16 839e\ndc 59fa\nc4 52b8\n1a 2964\n0 2822\n9e e15c\n84 e01a\nff 5577\n49 1a6b\nd1 f98b\n34 2512\nca d844\nc3 da8d\n26 614\n8c e272\n95 4939\n15 1b1\n3a fce\n54 1110\nc4 58b2\n25 8439\nde 59f4\n1c a9d8\n9 8a63\nc7 58b5\nfa 7f66\n23 e8f\n3d fd1\nb2 6584\n11 2b21\n75 351b\nb 2ac5\nb8 45e2\n34 afb0\nd6 5116\ne3 d485\nad 4ef3\nc7 5035\n88 60ea\n74 1d3a\n19 8969\nc8 5062\ne8 fe60\n23 ea7\n41 1a23\n5b 1b65\n67 3ebf\n81 4001\n29 84c1\nb3 cda5\n10 908\n6a 9466\n87 6295\n4e 30dc\n34 2f9a\n4 8a30\n8d 6073\n49 9061\nf3 7d2d\nd9 7beb\n2d a6d1\n8f 6855\n97 41b7\ne a0de\n89 4061\nd2 5184\n3a d66\nd4 519a\n9c c9d0\n82 c88e\n88 6268\n54 b930\n10 812a\na0 64a8\ne 854\n36 8fb4\n0 22a8\nc5 d831\ne 276\n81 6aa9\n31 d89\n28 a6c2\n3c fd2\n56 1114\n8a 4844\n28 86c0\n82 480e\n9c 4950\n52 9b26\nf a8d7\n1c a978\n18 2962\n9c e15a\nda 594e\n78 97ca\ned 7c5b\n9 ae3\n23 c25\nc a2d2\nd2 792e\n26 a414\n15 999\nb2 65a4\n81 4a81\n1d 9fb\n2e a476\n9c 4358\n7 2015\n55 33b3\n6f 34f5\nf5 d519\n71 1d21\nfe ff5c\ne4 fe1a\nb 865\ncf 72dd\n50 3980\n1a 29ce\n55 9319\n5d 9179\n36 8f96\n26 a49e\na1 4421\n52 9186\n9 8063\n16 2914\n87 c09f\ne8 5ee0\n7e 3d5c\n64 3c1a\n42 1a06\n84 ea18\nb7 6f15\n8 68\n51 118b\nb6 ed1e\n40 1a88\n6b b645\n49 b8e1\n18 834a\n9 a8cb\n46 1216\nf1 7d83\n38 a7c8\nc aa58\n2e 84fc\n14 83ba\n25 2e13\n3f 2f55\na9 cccb\n23 2ca7\na2 6604\n98 c14a\n12 2126\n4 8a32\n1e 8b74\n3c 2d5a\n4d 1ad3\n67 1c15\n28 2cca\nb0 4daa\n5a 13ce\n74 1510\n4d 1a5b\n38 2d6a\n66 9614\n9e 4156\n95 43b9\n96 493e\n4b 1265\n77 b7b7\ne5 5699\n42 30ae\n5c 31f0\na2 4ea4\n13 8b0f\n2d 8c51\nd2 71a4\nf2 5784\n13 a905\nd 851\n93 6105\n35 8fb1\n4e 92fe\n68 9440\nac 6452\n54 b91a\n9 8241\nb5 ed39\n73 1d27\n47 1215\n56 9316\n1 2889\nb4 4712\n46 b216\n4f 18dd\n64 94b8\na8 64ca\na3 4c0f\n1 aaa9\nbd 4d51\n20 8688\nf3 5d85\n2 84\ndc 7bd2\nf6 7d14\n3d a759\n2 a2a6\nbc ed7a\n53 1b85\nde 5bfe\nf8 5d40\nda 51c4\nc0 5082\n4a 90ce\ne0 fe80\n36 79e\n3a 8d64\n20 8c22\n5b 9947\n10 922\n8f 427f\nde dbfe\nf8 dd40\ne3 5caf\nfd 5df1\n37 8795\n11 89a9\n20 a420\n4b 18e7\n8d e8f9\nca 5244\nb9 65e9\nae 4c7c\n94 4b3a\n95 e939\n53 1927\n12 832e\n2c 8470\n75 9593\n62 1e04\n34 5b8\n5d b153\n91 c90b\nb 28e7\n8a 6244\n3e 8756\nc3 5a8d\n12 8106\n13 38d\nea 7464\nd0 7322\n51 31a9\n99 c94b\n13 2927\n68 1640\n18 2160\nf2 5f24\nee 7e54\nf6 57b6\n5a bb64\n40 ba22\n95 6133\n51 9121\n40 1800\n43 b0a5\nd8 516a\n40 1002\n5a 1144\nee 545c\nd4 531a\nb2 652e\n79 3d61\n4 2818\n15 911\n7a 3746\nc5 dab9\n28 640\nb2 4f24\nd2 7386\nf9 7569\naa 64ee\n9c 49fa\n51 1321\n99 e3e3\nb3 e525\n64 1e10\nd 28f3\n8c 6250\n8c 405a\n6a 1e46\nac ee58\n33 85ad\n76 9796\n45 1299\n2d 8673\nc2 5084\n65 be91\n61 3609\n17 a9b5\n3a 2fce\n54 3110\nd2 db84\n28 66a\ne1 542b\nfb 556d\ndb d3cf\nf5 d511\nfe ff54\ne4 fe12\na9 e66b\na5 4c91\ndc 5958\n0 282a\n1a 296c\naa cccc\n90 cb8a\na3 6605\n8 226a\n1c 89d2\nd aa59\nc0 7802\nda 7944\n8f 4a5d\n9 2849\n3a ad46\n11 1ab\nc8 7a60\na 8844\n87 429f\nb7 6d95\n11 901\naa eecc\n14 811a\n28 24e0\n9e 61d4\n40 9080\n84 6092\n15 8b11\n7c 3552\n93 69a7\n4 12\n1e 154\nda f96e\n1d 89d9\n4e 98d4\n13 385\n9e 43fe\nb8 4540\ne0 5e28\n13 2105\ncd f25b\n3 8225\n4e b8fe\n87 60b7\n43 90a5\n21 8481\na2 c6a6\n38 25c0\ne1 7c09\n66 3c16\n86 ea14\n36 a5b6\na4 4498\n41 3a09\ndb 736f\n4 298\nd1 5383\neb 54c5\n55 9919\n2b 2ecf\n45 3011\n9e 435e\ncf 78f5\nea 5666\n68 364a\n99 6be1\n33 5a5\nfd 75db\n27 2695\n52 130e\n83 48a5\n6c 1450\nc 78\n55 119b\ndd 5173\n1f 8bfd\n5 8abb\n8f c25d\n10 8900\n44 3238\n12 2984\n1 2a81\n1c 2358\na5 e491\n70 1520\n1e a3f4\n4 a2b2\ndb 7167\n4 90\nc9 f049\nc7 5a15\n99 41c9\na2 6c0c\n44 9ab8\n88 6aca\n6f 9e55\n83 e205\n4a b04c\n30 af0a\nd8 5142\n1a 8bcc\n0 8a8a\n0 2\n1a 144\n9 8eb\n88 4248\n1a a366\n2c 2cfa\n6e 9654\n6d 1659\n1d 2179\nf7 5f3d\n33 af8f\n4d b0d1\nae 4674\nde d3dc\nc4 d29a\nf7 5797\nca 7a66\nd ad1\ndb 71e7\na0 6ca8\n41 baa3\n5b bbe5\n2a 864e\n9a 6166\n4f 1a55\n2a 2c4c\n10 2b0a\n89 4069\n62 3e86\nf aa57\n73 1585\n9e 61dc\n84 609a\n40 9088\nf a277\ncc 7058\nf 55\n37 87b5\nea 56c6\nb4 6518\na7 ce15\n79 b5c9\nbc 6d58\n90 cb20\n17 2b9d\n18 2142\nf2 5f06\n85 6091\n48 b06a\n7b 3567\n11 181\n8f e2ff\na9 e441\n10 a9a2\nce 78fe\n83 4225\n7e 9fd4\n64 9e92\n1a 8146\n11 83a9\n6a 3466\n8a e264\n5 2031\n57 bbbf\n71 bd01\na5 6639\n34 8590\ne4 fe98\nfa fdc4\ne0 fc82\naf c67f\n30 8d22\nd 59\nc3 588d\n3e 8556\n35 87b9\n8e e276\n97 493d\ndd fb73\n6 8a9c\n8 aa62\n19 8161\n8 840\n1a 81c6\nc 8a50\n1a 2144\n0 2002\nc5 d231\n8a 68e6\n5 2a99\ne1 7423\nfb 7565\nbb efc7\nc8 f068\n77 9795\n65 1611\n15 2131\nef 5ef5\nca 70ec\nb0 6faa\n9 22eb\n23 242d\naa 646e\nec 5cfa\n26 869e\nf 7d\n39 2749\n4c 1a50\n99 43c1\nb 28c7\n18 2968\n81 e889\nfa 55c6\nec 5e50\n29 8643\n69 3449\n8c 68f8\nb 22ef\n25 2431\n41 1083\n5b 11c5\n21 2601\n28 8cc8\n4a 3ac6\nc8 5a42\n1e abd6\nd3 73af\ned 74f1\n1 a0a9\n88 c86a\nbb 4d67\n51 1981\nae 6e54\nb6 47b6\n50 1180\n32 a5a4\n72 9fae\nd0 7102\nc9 f269\n26 8416\n93 c9a7\n7c 9552\nc0 5022\nda 5164\ne9 76c3\nf0 dd8a\n2 8aac\nd0 718a\n17 2915\ne9 5ee1\n95 e139\n53 1127\n11 2181\n97 4395\n64 be98\n5d 9959\n13 8927\nd8 5162\n1a 8bec\n0 8aaa\n33 fa7\n52 1904\nef 5ef7\n15 2133\n7 8ab7\n0 8828\n19 be3\nfd 7d5b\n33 d25\n38 562\n58 b360\n71 1781\n84 6890\n68 36c8\n30 508\na0 4caa\nba 4dec\ndc 53d2\nf6 5514\n25 419\n9a c1c4\n80 c082\n82 62a4\n6e 1ef4\n17 a915\nca 5a6e\n3a ad44\n20 ac02\n10 8100\n46 1a94\n3a 7ec\n20 6aa\n0 aa08\n33 2f05\n6a 1e44\n3c 5f8\n8f ea7f\nf3 55ad\na5 6eb3\n61 9ea1\nbf 6ff5\n49 3ac3\n63 3c05\n10 90a\n26 a6b6\n26 494\n40 1802\n5a 1944\n4f 1255\n62 3686\n36 253e\n52 11ac\nd2 5126\n68 b640\nd9 f949\n5b 19ef\nda 534c\nc0 520a\n23 e0d\n6 8b4\n85 4211\n5c 137a\n12 a98e\n9a 6964\n80 6822\nac 6e50\nb4 47b2\n9c 495a\n7 a895\n27 e1d\n74 3592\nc8 f8e8\n2b 246f\nc1 5223\ndb 5365\na8 ce68\n50 b188\n5e 1bd6\n12 a906\n95 c9b3\n7e 955e\nc 852\n97 6995\n0 800\ndf 5155\n23 aead\nc5 5013\n84 e290\n69 1461\nba 47ec\na0 46aa\n8a 42ce\na4 4410\n4 210\n8e 4af4\n2 80e\n1c 950\ne0 5480\nb6 e516\n40 1280\n3b a5cf\nff 557d\ne5 543b\n98 43c2\nb2 4504\nb a8c5\n2b e4d\n85 4291\na6 ecbc\ne3 5607\n9a 69e4\n80 68a2\n2 8a0c\ndd 5959\n4a 12ce\n64 1410\nba cd46\nee 767e\n91 c901\n7 801d\nc5 7239\nd6 519c\n54 9190\nb 806d\nc5 52b3\ndf 53f5\n26 6b6\nbe 4574\na4 4432\n29 a661\n72 b784\ne3 5e05\n9 2041\n93 6925\n31 a7a1\n9 2ac1\n32 afac\nd4 5112\n93 e38f\nad e4d1\n78 1560\ne1 d481\n8a c8ec\n83 6225\n6f 1e75\n4a 306c\n30 2f2a\n5f bb77\ncd 5a59\n9 249\nda d946\nb 2a6d\n54 3b90\n8b 6ac5\n9f 415d\n85 401b\n63 1e07\na5 ee19\ne0 5422\nfa 5564\n80 6000\n22 8eac\ncb daed\n2e 674\n5e 93dc\n44 929a\n77 1797\n1e 2154\n4 2012\nf0 758a\n2 22ac\n1d 2bd3\n37 2d15\n4 a818\n35 711\n6c 1cfa\na5 4eb3\nbf 4ff5\n6e 3cde\n23 605\n74 1d30\n5a 1bee\n65 34b1\nc0 52a0\na6 4616\n2a 24c4\n10 2382\n48 9a40\n8c 6a52\n71 3f03\n8b 4045\nce 7afc\n38 d4a\n6 2a14\nc9 5263\ndc 71d8\n48 9ac0\n8c 6ad2\na6 6c14\n7a 3fce\n94 4110\n48 b0e2\nd7 7bbf\nf1 7d01\n4d 32db\n67 341d\nb4 4590\n51 332b\n6b 346d\n30 270a\n5c 997a\nf2 5526\n7 a8bd\n58 9342\ne2 dc26\n55 9911\nc5 d811\ne 256\nb1 65a9\na6 4c3c\n8c 4afa\ne2 74a4\n11 23a9\n1a 2146\ne7 74bd\n9a 41c6\n6 283c\n8c 4a50\n88 e2c2\na2 e404\n91 4989\n16 996\n11 8929\n43 18a5\n44 103a\n5e 117c\nad 64f3\n69 94e1\n85 e899\n43 1887\n50 1928\n57 9315\n32 a50c\n18 a3ca\n98 e148\n56 1136\ne1 7421\n12 ab06\n6f b655\n44 1a98\n2c 8e72\n1a 34c\n0 20a\n88 6862\nd3 53ad\n36 594\n11 2b01\n3a afec\n20 aeaa\ndc 5152\n1e 8bdc\n4 8a9a\n44 1038\n37 f97\n76 1516\na2 4c26\ncf d8fd\n18 342\ne0 54a8\na2 ce06\n74 b5ba\n59 1969\nd 22fb\n27 243d\nb1 6d21\n66 3c14\n4c 3ad2\nca 5a4e\nd5 73bb\nef 74fd\n3 aad\nf1 5d8b\nb5 e519\n73 1507\n1c 295a\n5e b1fc\n44 b0ba\nfd d5db\n77 35b7\n8e 6856\n4a 9844\nc7 529f\n5 8011\n49 b049\n35 a713\nfb 7d6f\n24 c98\nf0 5d20\n2a 86c4\n27 495\ndd 79f3\n24 a438\n17 a397\n5e 19d4\n44 1892\n1c 29fa\n78 3f48\na a066\nbe 6fdc\n60 9e88\na4 6e9a\nb5 4599\na 2a66\n1 23\n1b 165\n2a 26c4\n31 8d8b\n45 1093\n5f 11d5\n25 2611\n2c 8cd8\n4e 3ad6\nc0 5a80\n45 9019\nad 6479\n86 6296\n42 9284\n99 6949\nc1 f0a9\ncf 5af7\n98 4162\ne7 7c37\n2e a67c\n34 27ba\n12 904\ne4 7c12\nfe 7d54\n6 89e\ncf f27f\n50 b922\n27 615\n78 1d40\n5e 1bfe\n8e 4854\n69 be41\n1a 836c\n0 822a\nd3 5927\n49 1043\n71 97a3\nce d854\n11 2b89\n66 9616\n15 ab91\n6c 96f8\nb0 670a\n74 3792\n6e be56\n5f 13fd\n45 12bb\nee 5ef6\n14 2132\n45 9899\n4e 38fe\ncd 725b\n3 225\nf1 5503\nec f458\n8 82e0\n7a 374c\n91 6ba1\n60 360a\ne1 54ab\nfb 55ed\nc2 fa84\n2c 8cd2\n95 e313\naf e455\n84 4898\n16 a9b6\n7a b76c\n60 b62a\n64 9c90\n60 1408\nfb f767\nd0 5baa\n24 8690\nea 5cec\nac e6d2\nb5 4d99\nf4 5712\nc0 50a8\n82 4826\ne9 fce1\nb8 c74a\n32 2726\n4f b077\nbd 4f59\n23 ae05\n3 7\n69 16c1\nb4 4d9a\n1a 966\n45 3219\n32 8fac\n90 6100\n1 8221\n4c b8fa\n28 2460\n26 8ebe\n40 9000\n9e 6154\n84 6012\n17 2b15\n77 bfb7\ne5 5e99\ndb 734d\nc1 720b\n42 38ae\n5c 39f0\nca f2c4\n34 8512\n9e e374\n84 e232\n8d 48f9\n4b 1a65\n46 12be\n60 1400\n71 1589\nd aa5b\n8d 6871\n2b a6ed\n36 2fbe\n50 3100\n37 a537\na5 4419\n19 1c1\nc9 d063\n36 2f9e\nad 667b\n69 9669\n90 69aa\n2b ecf\n45 1011\ncf 58f5\n27 a4b7\n95 4399\n17 b95\nd3 5305\na0 ce08\n61 1c21\n89 6061\nd2 7184\n4f 9a55\n2a ac4c\n10 ab0a\n18 b42\n3a 8544\n20 8402\n1e 2954\n4 2812\nf2 df84\n18 a1c0\n38 748\ne0 5c22\nfa 5d64\n83 c8ad\n6c 9458\n8e 4256\nb9 6561\nc6 7a14\n98 61c8\n5c 31f2\nd9 7be1\na8 464a\n74 9d12\ndb 594f\n79 97cb\n27 695\n97 6937\n53 9925\nd0 5380\nf0 552a\n2f 4d5\n15 393\n26 ae96\n4d b079\n41 1a21\n46 98bc\n8a 68ce\nc5 d219\n22 ac2e\n3c ad70\n42 1284\n3d a5d3\n66 bc9e\ne1 5c21\n37 adb5\nc 250\n96 4b34\n19 961\n17 2135\n9d c159\n62 9626\n1f a3d7\n2c a478\nb3 4d07\n11 aba1\n75 b59b\nd3 79a5\na2 440e\nbc 4550\n0 a2a8\nb9 c7c9\n33 27a5\nd3 5325\na0 ce28\n28 a448\n80 4a00\na8 446a\n13 a3a5\nb8 e560\n12 238e\n2c 24d0\nb6 6db4\n41 1801\na6 4636\n72 3f2e\n8c 4070\nd5 5193\n45 9a11\n1a 894e\n2c a650\nd8 7b6a\nc4 7812\nde 7954\ne5 fc31\ncb faef\n2e 2676\na3 cc8f\nbd cdd1\na9 6669\n2a accc\n10 ab8a\n67 1617\n1c 8952\n22 a6a4\n5d 9359\n8a 6a46\n46 9a34\n18 81e8\nda d14c\nc0 d00a\n3a 2fe6\nf6 7dbe\nab 46e5\nfa f5ec\ne0 f4aa\n8c 4850\ne3 5c87\nf0 5d28\n2a 86cc\n42 9a26\nc a878\n25 2c33\n3f 2d75\n55 b99b\ndf f15f\n69 1ec9\n61 b423\n7b b565\n50 19a8\n69 9e69\nad 6e7b\n12 106\n32 af04\n41 3a89\ndc 5972\n16 8316\n4a ba66\n42 9224\n86 6236\n57 b397\n64 b438\n8e e0dc\n59 116b\nec 74da\n22 4a4\n5 2a19\n36 af16\nb7 459d\n11 a323\n2b a465\n0 8a8\nc8 5268\nb9 4561\n3e a5de\n66 3e94\n89 6843\n45 9831\naa c666\nc2 528c\n16 a3be\n30 a500\nca 5264\nfa dfcc\ne0 de8a\ndd 71d9\nda 73cc\nc0 728a\n7 2a15\nb4 4532\n2f 655\n66 1c3e\n4a baec\nec 5c52\n1c 897a\nec 76d0\n37 ad95\n67 163f\n1f 2957\nb2 4526\nc8 d868\ne1 5c23\n28 8668\nfb 5d65\nc9 d8e1\n12 326\n20 8c88\n53 1185\n69 3c69\n42 3a86\n93 41af\ne2 7c84\n29 a6c9\n8b 684d\na a2c4\nd0 7920\na a46\nb5 c519\n15 23b3\n2f 24f5\nd6 d194\n2b 86e7\n15 319\ncc 52d2\ne6 5414\n5a 91e6\n1d 3db\n37 51d\n26 2e1e\n3 85\n5a b964\n40 b822\nbd 6d59\nda db4c\nc0 da0a\nc5 5239\n63 b485\nf8 554a\na 26c\n2d cd3\nc3 7a85\n61 1429\n17 2917\n51 b9a9\nf8 5542\na 264\n3a 8fcc\n20 8e8a\nd7 d9bd\n20 402\n3a 544\n68 3ec2\n82 4004\n93 49af\n7c 155a\n81 ca81\n18 b60\n2e ac54\n14 ab12\nc9 72c9\n91 4109\n9a 6b4c\n80 6a0a\n36 af14\nb0 4f22\nca 5064\nfa ddcc\nf3 7705\ne0 dc8a\nda 71cc\nc0 708a\ne5 5e93\nb 20cf\nff 5fd5\n7 2815\nd 28fb\n8c 6258\n8e 6af6\n4a 9ae4\n5a 19c4\n40 1882\n1 88a9\n3e 2f7e\n5e 9bd4\n44 9a92\n6 823c\n27 261f\nc4 fa98\na 866\n19 2bc1\ne 254\n18 8960\n8d 6a59\nae 4cfe\n63 1625\neb 5cc7\n32 870c\nf8 5d68\n53 1bad\nc1 50a1\n8a 4264\n9d 61d9\nba cfcc\na0 ce8a\ne0 5428\nd3 5387\nd0 5322\nea 5464\nfa 75cc\ne0 748a\nd 2ad3\n27 2c15\n44 1090\n81 4801\ne6 7636\n25 439\n6a 9c66\n87 6a95\na1 6ea3\nbb 6fe5\n85 e239\n43 1227\n98 6162\n4d 1a51\n12 2904\n2e 8c5e\nc4 fa10\n48 1060\nee 7656\n25 8c13\n3f 8d55\nac e678\n6a 1666\n19 2be1\ned 74db\n23 4a5\nb8 e562\nd0 7188\n81 4289\n13 a3a7\n6d 1459\n96 4336\n51 b109\n5f 1b57\n87 4a9d\n53 3905\n16 194\n89 6ac1\n9 26b\nda 5366\n58 334a\n89 68e1\n1b 2bcf\n35 2d11\n6c 3cda\n21 601\nab 4ee5\n99 41c1\n9a 696c\n80 682a\na5 e413\nbf e555\n94 4998\n58 1940\n83 62a5\n6f 1ef5\n4a 30ec\n30 2faa\n5c 9bf0\nba 6d44\na0 6c02\n42 9aae\n8c e2d2\na6 e414\n95 4999\n6f 165d\n35 af93\n4f b0d5\na0 4402\nba 4544\n84 eab8\n42 1aa6\nc 28f8\n42 1804\n46 9a14\n18 81c8\n3a 2fc6\n9f e35d\n85 e21b\n6 a8be\n88 424a\n54 9912\n68 b662\n71 1d29\nd2 f32e\nec f470\n15 8399\ndc 7352\n99 6169\n5e b3fe\n78 b540\n5d 1bf9\n6e b674\n6c 1e72\ne2 7426\n25 491\n46 9a9e\n59 1941\nbe 4776\naa 4664\nbd 65d9\nda d3cc\nc0 d28a\nf3 5787\n6c 1c52\n8c ca50\n6 8a9e\nd5 53b9\n13 2b05\ne0 f420\n23 ac8d\n6d 1679\nb0 4580\n65 1699\n99 4149\n44 9a38\n88 6a4a\ne3 76a7\n1e 156\n15 3b9\n24 ae12\n3e af54\nbe c556\n1f a177\n8d 4059\ndc 715a\n12 124\nd9 5b41\n4f 125d\n77 1595\n8d 4079\n66 3e96\n44 9098\n9c 4352\nb5 4f13\ncf 5055\n7c 1d5a\n1e 2174\n4 2032\n8a 4246\nd1 d901\n1a 346\n89 c861\na9 eccb\ne6 5616\na4 ee38\n62 1e26\n1f 2bd7\n2c 2c78\nd 8051\n8a 6266\n5b b3c7\n68 b468\n8f c27f\n10 8922\n2 aaac\na4 4c12\nbe 4d54\n49 1ae3\n63 1c25\n53 bb27\nc1 5a09\n17 ab9d\n46 1a16\n18 1ca\n38 afc8\n51 3383\n6b 34c5\n6b be45\n18 8b4a\n3d a5f9\n8d e059\n4b 1047\n73 97a7\n66 b694\nd5 dbb9\n38 740\n8 2868\n21 a421\n6d 3ed3\n87 4015\nca 7acc\n34 d1a\nc 850\n92 6104\n34 8fb0\n68 1e40\n51 13a9\n9c e158\n5a 1146\n22 684\n29 a461\n72 b584\n4d 9859\n53 1985\n5a b164\n40 b022\n1d 2359\n38 2562\nd1 5301\n52 19a4\nf8 55e8\nba 4d66\n1a b66\n44 1a90\na0 4c20\n3f addf\nce 7256\n28 c60\n49 18e3\nc8 5240\n44 1a10\n3f ad5f\n13 81ad\n64 3c18\n26 e16\nd1 7983\n18 a3c8\n64 1698\n5a 3bce\n74 3d10\n97 e31d\n18 a9c0\n48 126a\na7 e63f\n28 ace2\n3e a5fe\n23 ac8f\n3d add1\n6d 167b\n31 f23\n4b 1065\n18 362\n25 631\n70 3d0a\n63 16a5\n8 a48\ne6 5494\na9 6449\n10 29aa\n4f 18f5\nf9 df69\n12 a104\n7e bffe\n98 c140\nce 5ad4\n61 3e01\nfb 7767\n24 690\n6a bcec\n50 bbaa\ne9 f649\n2a 8444\n10 8302\n4d 1851\nb9 6fc3\nd3 7105\n75 9fb1\n25 a6b3\n3f a7f5\n8e 6276\n4a 9264\n5d b1d9\n4d 1a79\n7e 9f76\n98 4960\n57 119f\ne 7c\n90 4980\n45 1a99\n76 9f96\nd3 f9ad\nbc c558\n1c 23f2\n36 2534\n4b 1865\nf6 f714\n77 bdb7\ne5 5c99\n90 61aa\nb5 4fb3\ncf 50f5\n7c 1dfa\nb a245\n10 322\n2a 464\nb5 e599\n73 1587\n86 4016\na6 ee14\nb8 cfe2\nd2 d124\n1 8029\na9 ce4b\n23 2e27\n9d 417b\nda 536c\n90 e980\nc0 522a\nba ede4\na0 eca2\n88 4868\n85 e213\n9f e355\n6 a8b6\n88 4242\ne3 dc87\ne9 7661\nf0 dd28\n7c 9570\n62 942e\nc1 5081\nf4 7732\n37 79d\nf8 5d48\n53 1b8d\n3b 8f67\nd9 f9cb\n3c 2552\n29 a6e9\ne2 7ca4\nfd 757b\n65 1491\n88 42c2\na2 4404\ncb dacd\n2e 654\n5b 9147\n96 e91e\n4b b245\nc8 d8c0\n2b 447\n9f e3d5\n85 e293\n50 1322\n6a 1464\ne6 fe14\nd3 f9a7\nbc c552\n10 120\n9c 497a\n7 a8b5\n85 6a11\nc4 5a30\n0 aa82\n1a abc4\nac c4da\n26 24b6\n32 8f04\na1 64a9\n73 15a5\nf aa77\ncc 7858\nd4 51ba\nef d6fd\nb4 6db2\n70 9da0\ne5 7e99\nf 855\nd0 d182\n8c 4252\n46 32be\n60 3400\n67 1cb7\na9 ecc9\ne6 5614\n2a ac6e\n95 4911\nfa 7746\n46 1094\n83 4805\n21 8681\nd3 7905\n0 a208\n52 398e\n7 2b5\nf5 5593\n92 432e\nac 4470\n14 291a\n86 6816\n42 9804\n1b 8147\nb7 e715\n68 1662\nb0 45a0\n5 11\n5e 135e\n8f 48f5\na4 e638\n62 1626\n11 2ba1\n1f 23d7\n2c 2478\n75 359b\n89 4049\n1b a167\n21 ae01\n1b 145\n1 3\n8b 48e7\nc9 5a69\nd1 fb8b\neb fccd\n34 2712\nca da44\n2e 245c\n14 231a\n86 6216\n42 9204\nbc c7da\n36 27b6\n99 e36b\n95 4991\nb1 652b\n88 e04a\n70 35a2\nb9 cfc3\nd3 d105\nc2 fa06\n83 4885\n33 2785\nd3 7985\n0 a288\n7c bdda\n31 8701\nb 2a6f\n6e 3cd4\n54 3b92\na1 4681\n19 a949\n4f 9af7\nf5 5799\nd0 5ba2\nea 5ce4\n31 8729\nc2 588e\ndc 59d0\n2f cfd\n15 bbb\n26 a636\nd0 f320\n3a 856e\nfe fd76\n27 8c9f\n2d 2679\n85 e091\n50 1120\nf6 7716\nc 2a52\n16 2996\nc1 7023\ndb 7165\na5 4c13\nbf 4d55\n3 aaad\n9b ebc7\na8 ec68\n1d bfb\n37 d3d\n2e a676\n3a dce\nd0 7b80\ncb d8cd\n14 312\n2e 454\n4b b86d\nc8 72c8\n90 4108\nc9 fac9\n2c 2650\nb6 6f34\n14 293a\n86 6836\n42 9824\n20 8480\nb7 6f35\n16 a136\n84 4018\n59 1361\n8d e0d3\n58 1162\n67 161f\n6f 1cdd\n55 1b9b\n66 b616\n28 2662\n1a 234e\nd1 f909\n42 1aa4\nea 564e\ne9 74c1\n72 95a4\nb6 65b6\na8 cec2\nc2 d004\n4b b0c7\n58 b168\n21 a4a1\n80 6a20\n30 a522\n91 e90b\nce 5256\n3f a7fd\n25 a6bb\nf6 5594\n93 432f\nad 4471\nd2 5384\n4b 12c5\n96 499e\nc9 d8cb\n43 38a7\n2c 452\nc2 7204\n4a 92ee\na8 6442\n64 9430\ned 7cf1\nd3 7baf\n27 a695\nbc 475a\n28 2e6a\n12 386\n39 569\n46 3094\n93 49a5\n7c 1550\n62 140e\n15 ab99\n97 cbb5\n66 961e\n33 a5a7\na1 4489\nd2 d986\nd8 7360\n1 289\n17 2995\n65 9e99\n3 2287\n10 2328\n13 2925\n99 c949\n1e 8956\n15 8bb9\n10 188\n9a c9ce\n93 6307\n4f 92f5\n87 6abf\na1 6c01\n43 9aad\ndb dbc7\ne8 dc68\nf 2055\n37 a7b5\nd6 599e\n22 8404\n8 82c2\nf6 559c\n86 609c\n0 8880\nb5 45b3\n70 9582\ne 2a54\n16 3b6\n1a a964\n0 a822\n1b 34d\n1 20b\n14 2910\n2 2804\n6 aa14\n9b 696f\nf2 7da6\n39 a7eb\n93 698d\nd9 f9e1\na8 c44a\n22 2426\n2e 8e74\nc8 704a\n77 9f97\n92 41a4\n69 b461\nb2 c584\n62 1684\n9 8a4b\n64 1418\n4 2a98\n2b c4f\nc1 7a01\nb0 e522\n65 1419\n45 bab3\n5f bbf5\n2e 865e\nd3 5385\na0 ce88\nc2 5a26\n8c 6878\nd5 799b\n8 a62\nbf 4757\n8f 4875\n78 bdca\n2d 86f1\n2f 265d\ne1 f6a1\nb9 6f41\na2 cc0c\n88 caca\n2 2aa6\ndd 71db\n13 1a5\n58 3148\na2 66a6\n21 2481\nae 4e54\n1a 8b64\n0 8a22\nda 5966\n78 97e2\n81 42a1\n77 b795\n5e 1976\n13 830d\nd9 5969\nc8 5242\n1e a3d6\n53 b9a5\n22 840e\n3c 8550\nef d477\n80 68a8\n66 b4b4\naa 64e4\n90 63a2\n7c 1ff2\nc3 5a25\n84 6838\n5a 994e\ne4 7e98\n48 9842\n2f 8cf7\nae c654\n23 2c8f\n3d 2dd1\nc7 7a15\n99 61c9\n4 2090\ne6 569c\n27 263d\n1b 16f\nb1 6f21\na6 4c96\nc6 fa94\n9 2061\ne3 5e25\n1 a281\n20 4a0\n5c 9952\n1a a1e4\n0 a0a2\n3a 76c\n20 62a\naf 6c57\n51 9b03\n6b 9c45\n2c acfa\n17 abb7\n85 4a99\n15 8313\ndb 596f\n2f 8455\ne7 dc97\nf4 dd38\ned 7671\n4c 9852\n8 a2e2\n22 a424\n11 9a9\n5 ab1\nf3 5d8f\n90 4b2a\naa 4c6c\n21 ae81\nf5 7d13\n3c a758\n31 a5a1\n26 8c34\nc 8af2\n9 22e3\n10 89aa\n23 2425\na9 c449\n2e 8456\n25 86b9\n87 483d\n84 6898\n71 1789\n47 b01f\na8 4e60\n67 169f\ne1 5681\n1e a1f4\n4 a0b2\n3e 77c\n24 63a\n88 4048\n1a a166\nce 7af4\n38 d42\n58 bb40\n41 b281\n3b 5c5\n21 483\n60 14a0\n33 a5a5\ne 8af6\n55 9391\ne8 5cea\n3c 87d0\n22 868e\n9e 4954\n84 4812\n4d 9ad9\n87 4097\n94 4138\ne3 5685\n62 340c\n48 32ca\ndd f95b\n13 8925\n90 4380\n57 93bf\n71 9501\nb5 6513\n21 ae09\n1b 14d\n1 b\n8b 48ef\n33 f87\n40 1028\ne6 761e\na0 44a0\n2e cdc\n14 b9a\n3f a757\n26 a4be\nea 546c\nd0 532a\nb0 eda2\ne4 7418\n0 2a0\n16 293e\n9c 4b52\n2d 659\nfe dd56\nb 286d\n54 3990\ne0 54a0\nb6 e536\n40 12a0\n3b a5ef\n83 4285\n4a 10cc\n30 f8a\n96 c93e\n4b 9265\n8f 6277\nfc 7dda\nb1 4701\n32 da4\n46 b296\n6d b479\n8c ea72\nf0 55a0\n1 2081\n87 4295\n4e 10dc\n34 f9a\n16 3be\n30 500\na0 4ca2\nba 4de4\n95 6b91\n78 9d62\nfb 554d\ne1 540b\n48 1048\n11 381\n9e c35c\n1f 89ff\n84 c21a\nb7 4717\n1e 8bf4\n4 8ab2\n62 b40e\n93 e9a5\n7c b550\n1a 36c\n0 22a\nba 456c\na0 442a\n19 169\n8 2a6a\ne8 5660\n24 a6b2\n3e a7f4\n9c c158\n16 2134\nfc 7770\n25 699\ne2 762e\nf6 dd96\n1 8829\n1a 896c\n0 882a\n33 d27\n53 bb25\na7 449d\n47 9215\n19 296b\nca fa6e\nd 8ad9\na3 4685\n98 4342\nb1 4f03\ncb 5045\n78 1d4a\nd 8a79\n58 b960\ne1 de09\n35 f3b\n4f 107d\n41 b209\n3b 54d\n21 40b\n95 e399\n53 1387\n60 1428\n88 6a62\n1e a1de\n99 4161\nc8 5262\nd 8851\ncb 7a6d\n2c 84d8\n4e 32d6\n69 bc49\nf 2a7d\n71 95ab\n47 9a35\n8b 6a47\n51 11a1\nb5 cd3b\nae 6674\nde f3dc\nc4 f29a\na4 e6b8\n62 16a6\n2c 24f8\n8c 6a72\n48 9a60\n19 3cb\n33 50d\na3 4caf\nbd 4df1\n23 262d\n37 8d95\n71 150b\n9a 434e\ncb 78e5\nb 88c7\n18 8968\n3e 8d54\n24 8c12\n61 1e21\n42 b02e\n5c b170\n83 c887\n90 c928\n89 6261\na 8ac4\n2c a45a\n79 15e1\n8e c8de\n87 6217\n43 9205\nbd c7db\n37 27b7\ndb 716f\n4 98\nc7 5a1d\n95 411b\n1a 166\n20 ae22\n3a af64\nb4 459a\nef 567d\n70 1d20\na5 ec91\ne5 f413\nff f555\nd4 5998\ne8 7c42\na0 e422\nba e564\nee d456\n99 69c9\n68 14e0\nde 51d4\nc4 5092\n4e 90de\ne4 fe90\n1a 8164\n0 8022\n88 c0ca\n2 20a6\ne 8af4\n98 c940\ne 805c\nc6 d89e\ncc 7278\n4d 9a59\n34 2518\nac e6d8\n6a 16c6\n49 1849\n7a 9d46\nae 467e\n66 1c96\n86 ca94\nf2 f704\n73 bda7\ne1 5c89\nb7 ed1f\n41 1a89\n3c add8\ndd 717b\n72 9f86\nec f6d2\nf5 5d99\nc 8af8\n25 eb3\n3f ff5\n32 858c\n12 892e\nb 2267\nd0 5108\n9a 6bec\n80 6aaa\n91 41a9\nf 8855\ncd 7a71\na a046\n6e bc56\n5f 11fd\n45 10bb\nd1 51a1\n8 2260\n2 8886\n1b a3c7\n28 a468\nc1 5a81\n53 93ad\n97 63bf\nb1 6501\n8c 4a52\n18 89c8\n11 2301\nff 75df\n48 38ea\n8e c056\n5d 3b59\n51 13a1\n45 9811\naa c646\n7e b554\n64 b412\n57 1b95\nc1 7823\ndb 7965\n8 a268\ncf f2f5\n39 8543\n3b 8547\nc3 50a7\n4d 90f3\ne3 fea5\n21 2ca3\n3b 2de5\na0 6600\nfe 5fdc\ne4 5e9a\n8c ea78\n4a 1a66\n9b 63cf\n57 93bd\nb5 6511\n1c 89d8\n15 2311\n1 221\n4c 38fa\na0 4ca0\n3f af57\n2 2206\n46 9814\n75 9711\n3a 2dc6\n56 93be\n70 9500\nb4 6512\ncb f8cd\n2e 2454\n14 2312\n81 60a1\n53 1baf\n6d 1cf1\n27 cb7\na6 4614\n1b a36f\n17 995\nf5 551b\n7 23d\n59 1b61\n4 2030\nb 8e7\n8a 4244\n68 bec2\n82 c004\n93 c9af\n7c 955a\n18 8b60\n64 3e18\n5e b3de\nd9 5361\na 8a46\n8f e25f\n10 a902\n21 623\n3b 765\nc1 5823\ndb 5965\n8 8268\n8d ead3\na7 ec15\n58 1b62\n19 81c1\n22 ac04\n8 aac2\nb9 cd4b\n33 2d27\n36 afbe\n50 b100\n0 2880\n86 4a94\n5 8219\na7 649d\nf 8077\n7d 1f59\n43 b88f\n5d b9d1\ncd f8d1\n16 2316\nf6 7d34\ndc 7bf2\n3d a779\nd5 d199\n51 19a1\nde fbdc\nc4 fa9a\nae 6e74\n21 8c23\n3b 8d65\nb8 47c0\n8 8062\n31 a703\n20 c88\n65 3491\nfa 5d66\n9c eb78\n8f ead7\n5a 1b66\n9a c144\n80 c002\n89 c8eb\n82 6224\n6e 1e74\n5b 996f\n1 a201\n20 420\n6a 1444\n50 1302\nf0 5502\ncc 725a\n2 224\n15 2199\n18 3c2\n32 504\n3b ded\n21 cab\na0 4608\n32 a726\n74 151a\n10 b20\nfe 5dfe\naa eeec\n14 813a\na1 e421\n88 6868\nb 265\n56 393e\n61 1ca3\n7b 1de5\ne0 5600\nfc 7df0\ne2 7cae\n36 a794\nc0 5002\nda 5144\nd9 fb63\n2 8a8c\n87 621d\n8 28c0\n8e 4ad4\n3e affe\n58 b140\nc6 5814\nd 8259\n77 1d3f\n25 2cb3\n3f 2df5\na4 6610\n70 1502\nfa 5de6\n92 6124\n3d 77b\ne7 dc95\n74 9712\ncd 5af9\n27 2cb7\nad ccdb\na6 6614\n4d 125b\nd7 5b3f\n98 4142\ne7 7c17\n2e a65c\n65 349b\n7f 35dd\nf8 d560\n97 63b7\n53 93a5\n6a 1c44\n50 1b02\n75 35b1\nae ccdc\n94 cb9a\na7 6615\n5d 3bf1\n43 3aaf\n2c 65a\n93 6905\n31 a781\n48 10c2\ne5 541b\nff 555d\n48 1868\n92 6904\n30 a780\nb4 e518\n72 1506\n4d 1079\n26 e96\n54 131a\n6e 145c\n85 48b1\nea 76e6\nf6 fd96\n25 2699\nf9 7561\n2a ac46\n0 20\n95 6911\nf a275\nae eefe\nc8 f040\n42 10a4\n39 a56b\n9c c1d8\n16 21b4\nf8 57c0\na5 4491\nba ef4c\na0 ee0a\ne aa76\n72 15a4\n2 284\n34 2792\nd4 d112\n9 aac1\nd6 f936\n5 2239\nbc 6572\n78 9560\n6e 3cfc\n54 3bba\ned 7659\ne a85c\n13 a105\ne0 de28\n16 81be\n58 1b42\n63 34af\n7d 35f1\n80 6802\n9a 6944\nc2 f0a4\ne2 562c\n38 a7c0\nac 4e70\nf5 5539\nbc e558\n7a 1546\n71 17a9\ne4 5492\nfe 55d4\n82 40a4\nf2 df0c\n18 a148\n4e 92f6\n1f 83d5\ncb 58ef\n5 8293\nef f6fd\n70 bda0\nb2 cd2c\n98 cbea\nab 6665\nd9 516b\n34 fb0\na2 44a4\n38 25e2\nc 52\na2 6e04\n7 2bf\n21 401\n58 19ea\n7 15\nb2 45a4\n5e 9b56\ne5 dc93\nff ddd5\neb 766d\naa 6666\n45 3831\n12 8906\nc2 50a4\n38 7c0\nd2 51a4\n9d 6959\nd3 5b07\n5c 9b5a\naf ce55\n3b 74d\n21 60b\ne8 dee8\n1b a1c5\n1 a083\nc5 5031\nab 4eef\n49 18c1\nae 46f6\n34 2590\n55 1b19\n3e f7c\n24 e3a\nc 8a78\n25 e33\n3f f75\n40 100a\n5a 114c\n23 485\n23 a6a7\ne0 7488\n7f bddf\ne0 5c20\nb5 e711\n36 adb4\ne3 5ea5\n9 20e1\n58 9948\n9c 695a\nf7 75b7\n31 2523\n23 8e2f\n3d 8f71\n1d 295b\nc a8fa\n18 8b42\n4d 3a59\n6f 14fd\n55 13bb\n98 6940\ne0 5628\nc0 f0a0\ne 205c\n0 a000\n19 21cb\nf3 5f8f\naa 4e6c\n78 1542\na 8866\n93 cba7\n7c 9752\nd5 5b39\n8e ea76\nf2 55a4\n8 2262\n86 4816\na8 6462\na3 c40d\n89 c2cb\n3 22a7\n31 8f23\n8f 6077\n4b 9065\n18 8362\n30 f88\n19 23c3\n33 2505\ne a56\n0 a008\ne3 7c2f\nfd 7d71\n37 a715\n91 4b81\nc 258\ne 8afe\n28 8c40\n86 6a14\n58 3160\nc1 f081\nfd 7df9\n37 a79d\n29 accb\n66 1616\n15 2b91\nec de7a\n1f a157\n6c 3cfa\n21 621\ne9 fcc1\n32 2706\nd0 51a0\nf 8aff\n29 8c41\na6 469c\n87 6a15\n93 c905\n99 c1c9\n13 21a5\n8c c2d8\n6 22b4\nf4 7592\nab 646f\n1e 81d4\n4 8092\naf 6e77\n6b 9e65\n69 3ec3\n83 4005\nc6 7abc\n30 d0a\n4d 18fb\n5e b376\ncc 5258\n5e 91d4\n44 9092\n83 6a25\n39 87eb\nf2 5da6\n68 14c2\n94 ebb8\neb 5645\n52 1ba6\n1c 29f8\n3 8f\nda 7166\n1d 1d1\n1e 297c\n4 283a\n8a 4a4e\n37 597\nc0 72a8\nd9 f163\n2 808c\nf3 7d85\n20 a688\n82 680c\n36 8fbe\n50 9100\n94 6112\nd5 5139\n6 881e\nde 7b7c\nc4 7a3a\nd a8d1\n2d e59\n8b 6a65\n20 ac00\n6 aabe\n17 81bd\n7 9f\nde 7176\n8e 4874\n2c 86f0\n89 6a69\n18 36a\nc2 d884\n8e e8d4\n59 1963\n41 3229\n55 9991\n2 aa8c\n51 1ba9\n62 b624\n6d 1cd9\n64 b612\n7e b754\n8d 6a51\nb 886f\ne6 7ebc\n50 110a\n2 222c\n16 8994\n85 c8b1\n6e 945c\n54 931a\na7 c615\n30 58a\n67 94b7\nb8 656a\n34 879a\n4b 1245\n96 491e\n67 949d\n7e 1ddc\n64 1c9a\n84 ca98\nb7 4f95\n89 6a61\n4 8a10\n82 68a6\n12 8186\n2c ac5a\n5f 3157\n19 8b41\n78 9d40\n5e 9bfe\nbc 6d52\nd3 d1a5\nc2 faa6\n1c 2b52\n5e b3f4\n44 b2b2\n70 158a\nf8 756a\n74 979a\nd6 591e\nb0 458a\n97 c31d\n18 89c0\n4c 32f8\n7 97\n14 138\n63 1685\n8 8a60\n83 c8af\n9d c9f1\n6c 945a\n95 69bb\n51 99a9\n8e 407e\n21 6a1\n7 8815\n2f c7f\nc5 7a31\n7c 1752\n93 4ba7\nae 447e\na1 4481\nd8 5148\n1b 8367\nf0 7f22\n33 f8d\ne 2876\n89 6869\nba ed66\n1d bd3\n37 d15\n4 8818\nf0 558a\n2 2ac\n20 a4a0\n51 1b29\n8e ea5e\nf2 558c\n82 4286\na9 4469\ne7 5497\nf4 5538\n2d a6d9\ne6 7c94\n83 6a2f\n9d 6b71\n6c 3650\n83 6aa5\n45 9819\n5d 935b\n2e 2cdc\n14 2b9a\n5e 135c\n44 121a\n6e 167e\n3e add4\n24 ac92\ne8 5c40\nce 5afe\nf3 75ad\nc9 d861\nd0 f9a8\n33 252f\n69 b649\nd3 512f\n26 a6b4\nad 6e51\n20 600\naa 4ee4\n28 460\n71 1583\n9e 4154\n84 4012\n48 1840\n5a 91c6\n4c 9a50\n5a 3144\n40 3002\n11 a32b\n2b a46d\nca 78e6\nef 5eff\n15 213b\n29 8ee3\n87 6037\n43 9025\nb2 65a6\n40 3a08\nee 54d4\nd4 5392\n8b 426f\n57 9937\nb2 c50c\n98 c3ca\n12 23a6\n4d 1871\n1e 23fe\nd5 f9b9\n38 2540\n2a 8464\n10 8322\n1b 9e5\n1 8a3\n80 4200\neb f667\nda 5bec\nc0 5aaa\n51 33a3\n6b 34e5\nf1 d509\nfa ff4c\ne0 fe0a\n60 3e08\nf4 5792\nab 466f\nd0 5300\n51 19a3\n6a 9e64\nae 6e76\nb8 c7ca\n32 27a6\n30 8722\n82 48a6\nb 2a65\n21 409\n5c 9958\ne6 de1c\nc a058\n42 9206\n91 61a9\n74 b732\n7d 1df9\n4c 985a\n35 8fb3\n4f 90f5\n93 6107\n7f 1d57\n68 9660\nac 6672\n47 383d\n81 c203\n9b c345\n2 88a6\n9f cbff\nb9 cd41\ne7 dc9f\ned 7679\n5b 19e5\n41 18a3\nc0 5200\n0 a82a\n1a a96c\nae 4ef4\nc9 dac9\n43 3aa5\n2c 650\nb6 4f34\nef fef5\n59 9143\nb0 45aa\n57 9b95\na8 6c48\n3 2a8d\n52 3924\nd8 d948\nbe 4df6\n54 b310\n9 8c1\nf4 d71a\n6e 36f6\n1b 2167\n1e 176\n24 ae32\n3e af74\n72 3f86\n99 4169\n88 6a6a\n22 aea4\n2 a6\nf6 7d36\n3d a77b\n59 1949\na3 4ea7\ne2 de2e\nfc df70\n8 a06a\nd8 f9e0\n3b 2567\nf3 5705\n70 97a8\nb4 67ba\n5d 9b59\n5a 194e\n1e 2974\n4 2832\n29 641\n74 3d1a\nc a58\ndd dbd1\nc3 da8f\n26 616\nf0 5508\nba 6fec\na0 6eaa\nb1 45a9\n2a cec\n10 baa\n3b a767\na9 4649\n2d 265b\n73 9f2d\nb7 6f3f\na0 4408\n32 a526\n1 2029\n6e 9454\n54 9312\n18 a940\nbe 677c\na4 663a\n60 9628\n7e 1dd4\n64 1c92\n84 ca90\n1b b6f\n30 2520\nd8 53ca\nf2 550c\n47 909d\ncc fad8\n2f 265f\n94 e118\n52 1106\nee 7ef4\n58 1142\n5c 9352\nb a8cd\n23 a485\nb8 454a\n62 3c06\n82 ea04\n32 a5a6\na0 4488\n88 68e0\n0 288\n84 4a90\nda d3ce\nf4 d510\n92 4106\n7 a235\n82 4006\nc 8052\na2 ee04\n94 4112\nb0 e5a0\nd ad3\n27 c15\n3 8827\n73 158d\nf aa5f\nf2 75a4\n21 24a9\n3a 85e4\n20 84a2\n18 ab40\nf1 7d09\n76 3d16\nb4 4598\n1 8a81\n69 36e3\n70 9daa\nc3 d0a5\n39 87c1\n12 104\n26 86b6\n49 1ac3\n63 1c05\n9b 4b6f\nb0 6520\n8d cad9\n7 2ab5\n30 afa0\nd2 5106\n14 2392\n2e 24d4\n6 82b6\n74 3f32\n8e 4074\nd7 5197\nbe cddc\nb7 6715\na4 cc9a\n47 9a15\n19 81c9\n22 ac0c\n8 aaca\n35 25b1\n36 5b6\n28 e40\ne2 54a4\n20 aea0\nc2 5006\n4c 9052\ne2 fe04\ne4 de38\n17 a115\nfa 57ec\ne0 56aa\nd3 d30d\n54 99b0\n98 69c2\n1b ab67\n89 4a49\n6a 1664\n7d 35d9\n89 e8cb\nc6 5216\nbb ed4f\n45 1ab9\n4e 1856\n76 9fb6\n6 bc\n77 b715\nd1 d129\nda fb6c\nc0 fa2a\n56 9136\n25 c39\n92 e324\n81 48a9\n28 84c2\nb2 cda6\nf8 55ca\na 2ec\n22 604\n5c b1d2\n88 6262\n2 2a04\n44 9ab0\n88 6ac2\na2 6c04\n5e 1156\n55 13b9\nd a271\nd9 73e9\n36 596\n56 b394\n88 4042\na8 ee40\n13 8185\n29 ac69\n2 aa86\n3b 5ef\nd1 73a1\n2d ac59\n35 85bb\n84 4810\na8 64ea\nb2 458e\n89 e269\n63 bca7\nd1 5b89\ne2 f604\na8 c6c8\n22 26a4\n3a 85c4\n20 8482\n14 8312\nda 596e\n2e 8454\n7e 3d54\n64 3c12\n84 ea10\nd0 59a0\n46 10bc\n46 b29e\nc1 5221\nac 447a\n17 a3b5\nb 8c5\n0 2000\n8a 68e4\n7 8b7\n86 4214\n6e 1c56\n8e ca54\ndb f14f\n65 1eb9\n53 b30f\n6d b451\n6a 9666\nbb 4d47\n19 abe1\n7d b5db\n4f 18f7\n91 e909\nce 5254\n52 b90e\n7 8235\n5e 39de\n13 305\nc9 524b\n4a 18ee\n4e 9854\nd3 f90d\n1c 2352\ne6 d636\n91 6ba9\n56 9934\n9a 6946\nc2 f0a6\n38 a7c2\n45 1a11\n90 6122\n7c 1d72\n4e 18fc\n5f b377\ncd 5259\n11 a92b\n4e 1276\nc9 5269\n90 4182\nec 7cf8\n26 a69c\n31 a721\n11 212b\neb 5eef\nc6 d016\n27 ac37\n95 4b19\n3c 257a\nee ded6\n14 a112\n51 9323\naf 6477\n6b 9465\n38 8762\n50 1388\n76 1596\n98 e94a\n4d b271\nca d8ec\nc3 7225\n2d 473\n3 2025\n89 c049\n51 1389\n39 8763\nba e5e4\na0 e4a2\n55 1399\nd0 59a2\n31 8529\n3a af6c\n20 ae2a\n43 baa5\n2c 8650\nd8 5b6a\nad e65b\n2e acfe\n4b b26f\n47 1895\na5 4631\nf0 7d0a\n2 2a2c\ne3 56a5\n1a ab66\n88 4a48\n48 12c0\nbe e556\n8d 6059\n86 e8bc\nc3 5207\n21 689\nf8 7760\nf2 dd86\n46 1896\n74 3d18\ne2 56a6\nac 64f8\ne 807c\n57 919f\n4d 9a79\nd0 5922\na 82c6\n47 1815\n8 28ca\n8 8242\n9 2c3\n23 405\n44 9812\n5e 9954\n20 620\nf4 7d12\n1e 8954\n4 8812\ndc 7b70\n5 a99\nc2 7a2e\n60 1620\nf3 df25\n19 a161\nd8 dbc0\n3b 747\n11 ba9\n22 a624\n9 2a6b\n10 ab88\n67 1615\n28 26ca\n2 880e\n1c 8950\n1f a3df\n80 4220\n91 490b\n4 810\n5f b3df\nc0 5220\n16 a3b4\n27 a635\n0 a2a0\na2 4406\n43 b8a7\n2c 8452\nc2 f204\nc 885a\nc8 7a68\n3f d57\n45 ba13\n5f bb55\n83 caad\n6c 9658\nea 74ee\nb3 cd87\nb9 6761\n44 1810\n2a ae6e\nc3 d80f\ndd d951\n89 6a6b\n12 abac\nb4 4d12\n78 154a\nad ccf1\n93 cbaf\n7c 975a\nc 227a\n2a ac44\n10 ab02\n4c 987a\n93 6127\n7f 1d77\ne2 5426\n7a 354c\n60 340a\n91 69a1\ne1 5e81\n7 20bd\n17 8915\n5c 13da\n76 151c\n56 9916\n8a 424e\n9 8841\n86 429c\n5d 13db\n77 151d\nc 8852\nc1 502b\ndb 516d\n5e 1956\nd9 5949\n81 4281\n4a 92c6\nee dedc\n14 a118\n2d 2cf3\nac 6650\nfe 57dc\ne4 569a\n8c e278\n4a 1266\nea 5466\nb3 450f\n11 a3a9\n1a a146\nfa 55e4\ne0 54a2\nd8 7b40\n1d 8959\n24 2690\n98 e36a\n94 4990\n4b 386d\n7 8895\n2f cff\nae 465c\nc5 7ab1\ne2 fc0e\nfc fd50\nec 5c5a\n57 bb95\ndb 73c7\ne8 7468\n26 8e16\nf5 5731\nb1 67a1\n97 e315\n48 1262\ne8 5462\n85 e2b9\n43 12a7\nac 44d8\n3e a5f6\nca 72cc\n34 51a\n19 8961\nad 66d9\n38 85e0\nd0 5180\n47 1a17\n39 afc9\n53 3925\nd9 d949\n5e 9956\nb3 6d0d\n99 6bcb\n55 9bb9\n59 994b\nfe 7f74\ne4 7e32\n27 e9d\n95 4391\nbd 6559\nb6 edbc\nf3 5707\ncb d2ed\n90 69a2\nfa 5fc4\ne0 5e82\n6 20be\n7a 974c\n91 cba1\nbe 675e\n60 960a\n84 e2b8\n42 12a6\ne2 54a6\nc0 7a02\nda 7b44\nd 22d1\n14 8998\ndd f9db\n13 89a5\n2e ae5e\ncf d27f\n50 9922\ndf 7b5d\nc5 7a1b\n42 baac\ne4 5c12\nfe 5d54\n2d c59\n5e 9156\nd9 d9c9\n53 39a5\n3c 550\n22 40e\nac 4cf2\n9 261\n54 393a\nc0 d802\nda d944\nf3 5fa5\n19 21e1\n9 8849\n6e b67e\n74 9512\nde f374\nc4 f232\n7 829d\ncd 58f9\n93 4387\na0 4428\n2d 26d1\n34 8d98\nd 2a73\n7d 3d79\n56 3b96\n25 cbb\n3f dfd\n36 a736\na4 4618\n84 e090\nf5 5f33\n1b 216f\nd1 d301\n96 69b6\n52 99a4\nc7 7a9d\nf2 5f2c\nb 20c7\n18 2168\n81 e089\n6d 9cd9\n8e c8fe\n87 6237\n43 9225\nf2 f526\n35 8591\n3e afd4\n24 ae92\nb2 67a6\n97 631f\n53 930d\n18 29c2\n83 e28f\n9d e3d1\n68 1460\n22 aeac\nde 5154\nc4 5012\n4e 905e\ne4 fe10\n57 1b15\n32 2d0c\n18 2bca\n34 a5b2\nd1 7909\n56 3916\n94 4198\n7c b572\nc8 7840\nd0 51a2\nfd fddb\n33 8da5\nd1 7b03\neb 7c45\nf3 55a7\n4d 1271\n3 a885\n98 494a\nda d1ec\nc0 d0aa\n1a 81e4\n0 80a2\ncf d2ff\ne9 d441\n50 99a2\ndf 7bdd\nc5 7a9b\n11 a901\n4e 1a54\n9e 6976\n5a 9964\n40 9822\nd4 5b12\nee 5c54\n85 4239\n60 1e00\n9 28e3\n88 6240\n53 bba7\n3c 8752\nc1 5a89\n6d 1c79\n46 1a96\na6 eeb4\n10 8102\n4 2a10\n4 8a92\n1e 8bd4\nb0 45a8\n52 3926\nd8 d94a\nd f3\na3 6ea5\n2b a4c7\n38 a568\n0 2080\n86 4294\na8 ce48\n22 2e24\n3a 8d44\n20 8c02\n29 2ce3\na8 6640\n4a 92c4\n8e 62d6\n6c b4d8\n73 bfa7\ne1 5e89\nd7 733d\n58 39e0\n66 1e96\nc6 f2b4\n30 8502\n24 2e10\n32 2586\n24 8e92\n3e 8fd4\nd8 7942\n11 8989\n80 e880\nca 526c\n8d 4a5b\n6 2216\n6d 1e59\n4d b8d1\n57 9915\n49 bac3\n63 bc05\n9 2243\n10 890a\ne4 76b0\n5 2091\n5 88b1\na2 46a4\nd3 59af\n27 8495\ne5 76b1\nea dee4\n10 a120\n4a 326c\n5e 99d4\n44 9892\n0 2800\n86 4a14\n58 31c8\n1c a9fa\n10 920\n8f 427d\nc4 7298\n28 8c42\na8 e442\n82 4224\n95 6199\n6 814\ne1 54a1\n39 8d69\n12 8b86\nf9 75c9\nb7 e537\n41 12a1\n22 a4ae\n3c a5f0\n70 b5a0\n46 1814\n4e 1854\ncc da50\ned fcd9\n36 271e\nea 5664\nfd 75d9\n67 3c95\nf5 5d93\n3c 87d8\n9f 63ff\n5b 93ed\n41 92ab\nb9 6541\n46 1036\n88 e048\n52 392e\nfb f567\nd0 59aa\n24 8490\nc9 f849\n3a 256c\n20 242a\n8 a8e8\n45 1233\n5f 1375\n2c 8e78\n75 9f9b\na0 46a0\n92 4186\n84 4a10\nc2 7826\n9 a26b\n5 891\n6a 36c6\n98 e14a\na1 46a1\n77 959d\n87 6a37\n43 9a25\n11 8323\n2b 8465\n48 9862\ne8 7460\n11 389\ne6 7416\nc7 5ab7\n91 6909\nb2 6786\nda 51e4\nc0 50a2\n4a 90ee\ne0 fea0\n2e 2e5c\n94 e910\n68 be6a\n52 9386\nbd 657b\n79 9569\n9b 4367\n68 1c48\n8d 6a7b\n49 9a69\nd6 793e\n8b 4265\n97 e33f\n18 a9e2\n87 e01d\n38 f6a\nd9 dbe1\n22 626\n35 259b\nbb 65ef\n43 9827\n6a 9e66\n80 62a8\n6c 1ef8\nb6 4596\n98 6948\n47 9895\n6 aa9c\naf e67f\n30 ad22\n98 6962\ne8 5e42\ne 207e\ne3 54ad\n64 1690\n90 e1a0\na3 4687\nb0 4728\ne1 d60b\nfb d74d\n62 9cae\n7c 9df0\ne0 fe88\n7 815\n4a 9846\n83 c8a7\n6c 9452\nc5 5839\nda 7346\n5a 9bec\nb8 6d40\n9e 6bfe\n40 9aaa\n3d 2df9\nb5 efb9\n73 1fa7\n68 bc42\n20 680\n20 2480\na6 4694\n5a 33cc\n40 328a\n11 8b29\n62 1e86\na4 ee98\nd7 7395\n33 70d\ne1 5ea3\nfb 5fe5\n84 6810\ne8 7ce8\n22 a68c\n2a ac64\n10 ab22\n8f 425d\n10 900\ne2 7c26\n29 a66b\n25 c91\n5c 1958\na6 4eb6\n94 4992\n2d 2e59\n4d 18db\n5e b356\n45 1a19\n76 9f16\n90 4900\n6 1c\n85 6811\ne9 7ce9\n23 a68d\n5e 1176\nb0 e5a2\n65 1499\nc3 fa8d\n2d 8cdb\n26 2614\nab 44c7\nb8 4568\nd 8db\n1e a356\n8e 6a56\n4a 9a44\n1c 81f8\nde d15c\nc4 d01a\n3e 2ff6\n1c 2b5a\n5e b3fc\n44 b2ba\nc1 5281\n47 1a1d\n92 4904\n30 8780\nd8 d34a\n52 3326\nc2 d80e\ndc d950\na5 4691\n8d 4871\n2b 86ed\nfe fd56\n2d 2659\nb7 6f3d\n36 fbe\n50 1100\n11 1a1\n73 9785\nb7 6797\n1d a959\n49 1a69\n7a 9f66\na 24e\nc1 d809\n71 9da9\nb5 6dbb\nd1 71a1\n4c 1272\n2 a886\n3c f50\n22 e0e\n46 9816\n47 9a95\n98 6b48\nf3 77a5\nf1 57a1\n13 905\nb0 c508\n10 23a2\n2a 24e4\n42 9084\n86 6096\n17 8b15\n24 2492\n3e 25d4\n53 1905\nad 6459\n14 29ba\n70 3f08\n2 a026\n57 9b15\n32 ad0c\n6f 1657\n18 abca\n93 4905\n31 8781\n0 2200\n8a 6ae4\nee 74de\n8f e8df\n5a 196e\n1b 23ed\n1 22ab\n37 8fbd\n95 6111\n1f 23fd\n5 22bb\nb 80e7\n79 1fc9\n74 959a\nd1 53a1\n29 8c69\n2 8a86\na9 e461\nf2 f584\na2 4684\n91 6123\n5e 915c\n44 901a\n77 1517\nda 7be4\nc0 7aa2\n21 a629\ncc 78d0\ne2 5684\nde 597e\n7c 97fa\n90 49a0\n79 154b\n94 6990\n58 91e2\ndf 597f\n7d 97fb\n8c c8d8\n6 28b4\n85 6211\n32 7a6\n2a 2e44\n60 140a\n91 49a1\n7a 154c\n5f 995f\na 2ce\nc1 d889\n24 410\ne 82fe\n28 8440\n98 cbe2\nb2 cd24\n8d c8db\n7 28b7\n86 6214\n88 c8e8\n81 6221\n6d 1e71\n2 8a84\nd6 5916\n1d 835b\n3e a55c\n24 a41a\ndb f9cf\n3e 2556\n35 27b9\nb9 6549\nb2 edac\nef 56f7\n19 2349\n80 40a2\n9a 41e4\n3d a7db\nf6 7d96\n44 3298\n12 30e\nc9 d8c9\n43 38a5\n2c 450\n66 1e14\n38 5c8\n9a eb4c\n80 ea0a\nc7 5a9d\nb9 ef69\n16 8116\n13 30f\n2d 451\n9d 4bf3\nb7 4d35\n4e 9a5e\n67 1e15\n39 5c9\n42 300c\n28 2eca\n96 c91c\n8f 6255\n3c 2f5a\n7e b7fc\n64 b6ba\nc6 783e\n9f 4155\n85 4013\nf 805f\na5 ee11\ncb 72cd\n35 51b\n4a 9a6e\n82 c80e\n9c c950\n47 32bf\n61 3401\n98 49ea\n75 9519\n7e bf5c\n64 be1a\n97 4317\nfa f76e\n3d 87d9\nf6 5d94\n93 4b2f\nad 4c71\n33 8587\n63 1e25\n92 c92c\n8b 6265\n38 2f6a\n8c 60fa\n48 90e8\ne8 5e62\nd 2af9\n9c c3d2\nb6 c514\n66 3c94\n3 2a2f\n1d 2b71\n9d 495b\n6d 3659\n7 8215\ncd 5871\n2d c79\n6 a96\n13 aba7\n81 4a89\n39 2fe3\n53 3125\nd9 d149\nc8 fa4a\nee 76fe\n78 b548\nd6 f334\nc5 58b9\n6c 94d2\nb0 6588\nf2 7d84\n39 a7c9\n36 2596\n11 2389\nec 56f2\nc3 5285\n70 1f8a\n43 3825\nc9 d849\n1 2023\n1b 2165\nb5 6599\na2 4624\ne6 7494\n41 1081\n2c 658\n43 3aad\n64 9e98\n52 3126\nd8 d14a\n4d 90d1\n33 8f8f\na3 66a5\ne3 76ad\n30 2782\n7e 9554\n64 9412\n3c 2d72\n69 1669\n49 b0e1\n85 6031\ne8 7e40\nf0 57a2\n1e 21d6\n5d 9bd9\n8b 4247\nec 7452\ne2 f42e\nfc f570\n25 8499\n54 3338\n47 3297\n1d 23d3\n37 2515\n4 a018\n34 251a\nf9 d749\n60 9caa\n73 3725\n7a 9dec\nbe 6dfe\n19 a961\na6 6416\n48 92c2\n62 9404\ne9 fc49\n5a 93ce\n74 9510\n1d a95b\n70 9522\n66 3cbe\nff 775d\ne5 761b\n80 e280\n6c 9ed0\ne6 5696\n12 a38e\nd8 79ea\n2c a4d0\n7a b544\n60 b402\n83 480f\n9d 4951\nf8 f762\n21 868b\n3b 87cd\nb5 6533\n71 9521\n7a bf64\n60 be22\nc7 f8bd\n2a 2444\n10 2302\n9a 6be6\n1b 2b6d\n1 2a2b\n59 19e3\nd8 5340\n15 291b\n51 198b\n62 b406\n31 2f09\n85 4813\n9f 4955\ne9 5ceb\nfa f766\n23 868f\n3d 87d1\n6d 36f3\n74 9dba\n39 2d69\n12 2b86\nc 27a\n20 6a0\n28 8e60\n17 8195\n30 522\n50 b320\n60 16a0\n32 526\n41 18a9\n52 b324\nc6 529c\nf1 d521\nfa ff64\n23 8e8d\ne0 fe22\n12 830c\ncb 58c7\nd8 5968\n80 42a0\n4d 9851\n5a 336c\n54 9992\n40 322a\n8b c24f\nc 88f2\n48 b26a\n44 1890\n8d 6851\nf1 7d29\n2b a6cd\n95 41b3\n99 6149\n92 e9ac\ncf 52f7\n1e 2156\nf0 5722\n15 23b9\n8e e2d6\n97 499d\n2b ae45\n8c ea5a\nf0 5588\nb 47\n33 87a7\nc6 fa14\n27 86b7\n12 830e\n43 b8a5\n2c 8450\nd8 596a\n8d 4879\ne7 f417\nd6 599c\n50 1108\ne7 7695\ncb facd\n35 8d1b\n2e 2654\nb0 4da0\n26 4bc\nc9 f8c1\n12 2306\n5a 9966\ncf 7a5f\n4f 1a5d\n18 a34a\nb4 4d98\n97 69b7\n53 99a5\n66 9494\nf3 5f2d\n19 2169\n0 8a2\n1a 9e4\n99 4341\na 82ce\n24 8410\nd0 592a\n8c e050\n58 31c2\n65 941b\n7f 955d\nd1 d303\neb d445\n52 99a6\nc7 7a9f\ne0 d488\nf2 5f2e\n18 216a\n49 32e3\n63 3425\ne9 d449\n50 99aa\n72 970e\na3 cca5\n19 83c1\n61 16a1\n8 8a68\n21 e23\n3b f65\n9e 4174\n84 4032\n92 c904\n9e 41d6\nb9 c54b\n33 2527\nd0 f9a0\nba edc4\na0 ec82\nea 566e\nee d676\n99 6be9\n6f 14f7\nb1 e509\n6 2a36\n8c ca5a\nbf 4f57\n36 a516\n5 2019\n8f 68fd\ne a54\na0 66a8\n1a 8964\n0 8822\n94 4b12\nae 4c54\na3 e40f\nbd e551\nb6 e51e\n40 1288\n28 8662\n0 aaa0\na2 4c06\n2c 8c52\nc2 fa04\nfa 5f6c\ne0 5e2a\n13 2107\n93 c30d\n14 89b0\n48 32e8\n3 87\n10 128\n72 970c\nb6 671e\n44 1092\n5e 11d4\n81 6081\n11 2929\n90 412a\n10 8108\n32 2f06\n87 4215\n5e 137e\n14 a992\n4e 105c\n34 f1a\nc0 70a8\n82 6826\n8 2a68\n21 a621\n87 e2bf\na1 e401\nda 594c\nc0 580a\n39 2569\n12 2386\n2a 664\n9e 63de\n5a 93cc\n40 928a\n3d 25d9\nb5 e799\n73 1787\n86 4216\n64 9418\n5b 994f\n6 2be\n20 400\n90 4ba2\naa 4ce4\n14 8392\nda 59ee\n2e 84d4\nec 76f0\n51 bb03\n6b bc45\n18 894a\n73 95a7\n15 91b\nb8 cf4a\n32 2f26\n4b 1845\nc 28fa\n19 23eb\n33 252d\ne a7e\n14 a912\n1e 95c\n4 81a\n57 3b15\n62 16a4\nb a845\n13 81a7\ncd 70db\n3 a5\n68 3648\n1a 8bce\n34 8d10\nb3 652f\n2 8204\nc8 5860\n83 e20f\n9d e351\n4 a8b2\n1e a9f4\n82 42a4\nc9 7861\n3 a205\nc2 52a4\n33 f0d\n1c a95a\ne5 5499\n77 b5b7\n4d 12d3\n67 1415\n10 a988\n4a b26e\n46 1894\ne2 56a4\n8 a40\n54 bbb8\nf6 5d1e\n4e 12d4\n11 a989\n93 c9a5\n7c 9550\n62 940e\n34 2d1a\n2a 2466\n7f 9fd7\naa 6466\nec 5cf2\n26 8696\n57 93b7\na8 646a\n40 b288\n73 3785\n50 3188\n38 a562\n4d 9251\n12 2906\n30 85a8\na3 4c87\nb0 4d28\n16 ab16\ncb 72ef\ne5 7431\n83 ca85\n0 a22\n1a b64\n30 a782\nd5 5311\n56 19b4\n5c b3f8\nfe 555e\nd4 5930\ne 82d4\nc0 f2a8\n37 8597\n4a 9266\n62 1e8c\n5d b1db\nd d1\nca 7066\n3 80ad\n6d 9479\n46 9296\ne 876\n1c ab52\nd1 732b\neb 746d\n55 1b99\n66 b614\nd7 7b95\n9a 4366\n74 9598\n54 993a\n4d 3273\n95 6939\nc1 7889\n3c a552\n4 a8b8\n5b 1345\n41 1203\ncb 5ae7\nab 4cc7\nb8 4d68\n13 bad\n81 40a1\n1e ab56\nd3 732f\ned 7471\n32 8d26\n1 829\n63 9e0d\na7 6e1f\n9d cbf1\n83 caaf\n6c 965a\n94 e112\n38 a560\n23 aca7\n91 4b89\na2 e604\n66 9696\na4 e498\n62 1486\n82 c284\n19 363\nf a5d\nee 765e\nc1 daa1\ne8 746a\n30 a5a0\nb3 4785\n80 c288\n19 9e1\n62 96a6\n2c a4f8\n3a f46\n31 2f03\n4b 3045\n18 ab48\n73 b7a5\n1a 8166\n32 d8c\n4a 3046\n41 32a9\nca 7a44\nd2 53a6\n9c 61f8\n7c 3dfa\n31 721\n7e 3dfe\nfd 775b\n33 725\nd3 5925\n0 8228\n85 ea93\n9f ebd5\n6a 1c64\n50 1b22\n39 761\n64 3612\n7e 3754\n84 6830\n22 a6ac\nbe 4dfe\n73 1725\n9e 637c\n84 623a\n40 9228\n8c 6870\ne3 7ca7\n2a a6ec\n4 218\n79 1761\n8e 6874\n2c a6f0\n61 1623\n7b 1765\n8c 627a\n48 9268\nd4 f310\n55 b9b3\n3e 855e\nfc 7dfa\n36 a79e\nb1 4721\nfe 7dfe\nb3 4725\n95 e39b\naf e4dd\n7a 156c\n60 142a\n80 c228\n16 a994\n46 123e\n44 3218\n3e a7de\nb9 4761\n20 8620\nd8 5b62\n1e a9d4\n4 a892\n4e 127e\n3e ddc\n24 c9a\n44 ba98\n74 951a\n10 8b20\nf3 5725\nc0 d228\n6e 14de\nf8 5dc2\naf e65f\n30 ad02\n2e 8654\nda 5b6e\n4b 9ae5\n8f 6af7\n70 b588\nb9 6569\n92 6386\n7e 1fd6\n32 ad06\n1 2809\ndc 5b72\n9 88eb\n88 c248\n2 2224\nf0 7502\n1a 8144\n0 8002\ne8 dc4a\n62 3c26\n82 ea24\na0 44a8\n3f a75f\n67 363d\n49 98eb\nc8 d248\n42 3224\n81 e003\n9b e145\n34 adba\n42 9006\n11 b09\n12 a926\n80 4808\nd 88fb\n8c c258\n6 2234\nf4 7512\n1e 8154\n4 8012\n2 82ac\n5e 9154\n44 9012\n85 e013\n9f e155\n44 1098\nc2 f224\n2c 8472\n6 816\nb2 cd06\ne6 763e\n54 11b0\n16 a99e\n91 4921\n7 3d\n31 2709\n71 9d2b\n6a 3664\n9a c3cc\n80 c28a\nb3 4787\n16 a936\n84 4818\n17 a937\n85 4819\nb6 cd16\n58 11c0\n35 2719\n81 c009\nde 7374\n7 29d\nc4 7232\ne9 fe69\n46 9016\n15 b19\n54 1198\nd2 f324\nc1 58a9\n3c 8572\n16 916\na4 6698\n21 8c03\n3b 8d45\n1b bed\n1 aab\n6f 367d\n4a 9046\n19 b49\n9f 63fd\n85 62bb\n41 92a9\n29 2ee3\n43 3025\nc9 d049\n79 95e9\nbd 65fb\nf6 7d94\n3d a7d9\n7 8a37\n88 4a40\n2 282c\n1f 235d\n5 221b\nda fb4c\nc0 fa0a\ne6 76be\n6 896\n6b 1cc7\nad ecd9\n78 1d68\n2a 6ce\ne1 dc89\n41 10a1\nf4 7738\ne7 7697\nd4 591a\n98 4b42\nda d3e4\nc0 d2a2\na3 64af\nbd 65f1\nc5 d819\ne 25e\n4d 9279\n12 292e\n0 828\n62 9e0c\na6 6e1e\nee fef4\n58 9142\n11 8ba9\n1a 8946\n5a 9946\n8e 427e\n96 e334\n85 48b9\nc2 f284\n2c 84d2\nea 76ee\n5a 9146\n29 c49\nca dacc\n2d 653\ne9 dce1\n32 726\ne9 56e3\n1a 8944\n0 8802\ne3 d487\nf0 d528\n9 acb\n23 c0d\nb1 e723\na0 4ca8\n3f af5f\n52 b106\n21 2c09\nfc 5f72\n2 2886\nbf e7d5\na5 e693\n70 1722\nad e6d3\n78 1762\nbc e778\naf e6d7\n7a 1766\n29 2ce1\n92 4326\nb2 4726\n4d 18f1\n85 68b3\n6e 345e\n41 98a1\n9f 69f5\ne2 de2c\n8 a068\n39 8fc3\n53 9105\n97 6117\n42 9806\n48 3240\ndb 5167\na8 cc6a\n1b 36d\n1 22b\n63 b40f\n7d b551\nd8 5362\n3b 76d\n21 62b\n1b a1e5\n1 a0a3\n57 b995\n77 1f1d\n8b 62cd\nf8 5762\n1d 23f9\nd3 db85\n29 66b\n72 178e\n3b 2fcf\n55 3111\nfa 5766\n78 374a\na9 6ce1\na5 ec93\nbf edd5\n70 1d22\nef 567f\n30 8582\n4b 1ae5\n34 8592\na4 6e98\n8 8842\nd8 53c0\n76 151e\nbe 4f56\n9b 6167\nd1 510b\n19 8949\n7d 1751\n63 160f\n33 a725\n98 6160\n52 9906\n86 423e\n1f 237d\n5 223b\n6e 3cfe\n23 625\ned 765b\n19 969\n11 309\n81 4aab\n9b 4bed\n99 6961\ne9 5e41\nf 207d\nd2 fb26\n15 8b91\nc8 52c2\ne2 5404\n8d 687b\n49 9869\n1f 9ff\n9e 435c\n84 421a\naa 466e\n45 1839\n76 9d36\nf3 df2d\n19 a169\n5f 19ff\nde 535c\nc4 521a\nbe edd4\nee 567e\na4 ec92\n68 1668\n48 b0e0\n43 380f\n5d 3951\n3a a544\n20 a402\n9a e9c4\n80 e882\nca 526e\na8 4668\nf1 578b\nb8 6d42\n5a 9bee\n74 9d30\nc0 5228\na0 eca0\n79 1769\n59 b1e1\ne8 5668\n91 4329\nb9 4769\n45 3813\n5f 3955\n2d 26f3\n34 8dba\n62 94a4\na6 64b6\nf9 5769\n2a 8e4e\ned f6db\n23 86a5\n1a a144\n0 a002\naa 4e6e\n19 abc3\n33 ad05\n3e a554\n24 a412\n9e e9d4\n84 e892\nce 527e\n2e 8674\n77 9797\n1e a154\n4 a012\n1d abd3\n37 ad15\n5f 13f7\na1 e409\naf 4e57\n3f ff7\n81 e009\n6d 9c59\n8f 4a57\ned de7b\n6 a016\n90 4908\n28 a442\n81 6829\nb2 ed26\n94 4918\nf3 57a5\nc0 d2a8\n2a ac66\n98 4b48\n13 ab27\n81 4a09\n21 84a1\n4b 90c7\n9c 617a\n58 9168\nd0 7900\n17 ab37\n85 4a19\ne1 7e89\n3c a752\nc1 7a89\nea dec6\n10 a102\nba 4f6e\n32 a506\n1 2009\n8b 68ed\n92 e986\ndc 5372\nf9 df6b\n12 a106\nbc 4f72\n4f 10f7\n91 e109\n7d 9d59\n9f 4b57\nfd df7b\n16 a116\nd4 5918\n6 8a36\n12 292c\nf2 df06\n18 a142\n38 a542\n91 6929\n5a 3944\n40 3802\n6a 1e64\nc6 5a16\n98 41ca\nd1 7383\neb 74c5\nf6 df16\n1c a152\n1a a946\n53 11ad\n5c 3bf0\n42 3aae\nda 5b6c\nc0 5a2a\n30 72a\n10 a1a2\n49 12eb\n63 142d\n82 408e\n9c 41d0\nd9 7941\n43 b2a5\nd8 536a\n2e a4fe\nf0 572a\nd0 f1a2\n10 2928\n3 2887\n1e 215e\n63 b6a5\nf8 576a\n28 8642\n81 4a29\nb ac5\nfe 7556\nf4 f532\n37 859d\na1 4e21\n26 ae9e\n1e a954\n4 a812\n27 8ebd\n85 6011\nf6 7596\n97 6397\na4 6438\n53 9385\nde d3fe\nf8 d540\n81 e809\n6 a816\n8 a842\n9 22c1\n10 8988\n68 9c48\nac 6c5a\n9 8a49\nc a852\na2 4606\nc 8a5a\n3f f57\n8 84a\n83 c2a5\na5 e4b9\n63 14a7\n41 3a03\n5b 3b45\na 2866\nd5 53b1\n2f a65f\n90 43a8\n52 9106\n21 c09\n2 886\n3d af5b\n47 1837\n89 e849\n71 3da1\ne2 d624\nf5 f599\ne a856\na7 4c1f\n5 aab9\nfa df64\n67 3e9f\ne0 de22\nc3 702f\ndd 7171\n68 9662\n38 f42\n9 a69\n3a 8f66\n18 a942\n7a 976c\nbe 677e\n60 962a\n4b 98c7\n9c 697a\n58 9968\n18 a960\na1 ce09\naf 64fd\n51 93a9\n95 63bb\n48 184a\nc3 d2a5\nce 70fe\n70 9faa\n1a 83ec\n0 82aa\nd3 59a7\n6d 9c79\n46 9a96\nc 225a\n52 9b2c\n96 6b3e\n44 12b0\nba e546\n89 6049\n84 e898\n42 1886\n5e 9bdc\n44 9a9a\n2e e74\n77 1f97\nb8 e54a\n8f e2fd\n10 a9a0\n23 e87\n30 f28\n93 4b85\n1e 835c\nd7 5917\n4 821a\nd4 db90\n37 717\n94 e110\n64 1c90\n68 b66a\n68 1448\nd8 5bea\n2c 86d0\nf2 5d2c\n31 781\n96 e114\n50 130a\n81 48a1\n6a 144c\nda 5bee\n2e 86d4\nf4 5d30\n3f df7\na4 4612\nbe 4754\n83 48ad\n6c 1458\n51 1b81\ndc 5bfa\nf6 5d3c\n37 795\ndb f36f\nd7 5995\n4 8298\na6 4634\n20 2420\n75 9f91\ndc dbd0\nc2 da8e\n3f 757\n0 80a\n1a 94c\n91 4381\nb1 4781\n46 129e\nd1 5381\nf 28dd\n88 c860\n47 909f\n92 412e\nf1 5781\nf3 5785\nc0 d288\na8 e46a\n71 3581\nfd f75b\n7e bdfe\n33 8725\n99 c3c3\nb3 c505\na2 ee06\n63 3c85\n95 69b1\n64 341a\n7e 355c\nf1 5d83\n38 87c8\n3e 875c\n55 bbb1\nf7 5d17\n24 861a\nee def4\n14 a130\nf2 df24\n18 a160\n4c 925a\nc5 5a31\nd3 51a7\ncb 7845\nba cdec\nb3 6725\na0 ccaa\nb4 ed9a\n69 b6c1\n6a 366c\n7e 9dd4\n64 9c92\n1b 8b6f\n30 a520\n95 cbb1\n7e 975c\n64 961a\n1a a3ee\n34 a530\n8d 6873\n49 9861\nc6 52bc\n5 a239\na0 ee08\n88 404a\nc1 7203\nc8 d8ca\n42 38a6\ndb 7345\na4 6418\n4 2218\n8e 6afc\na5 6419\n3f ddd\n25 c9b\nfc 7d72\n36 a716\n8f 6afd\n93 6387\na0 6428\na6 4c16\n4 aab0\nc3 f8af\ndd f9f1\nac c45a\n26 2436\na1 6429\nd0 d102\na7 4c17\n5 aab1\nb5 6519\nf8 5768\neb 56c7\n15 2319\n85 6abb\n41 9aa9\n9f 6bfd\nd3 f9af\nbc c55a\n36 2536\n11 2329\n70 35a0\n82 4a06\na7 64b5\n54 31ba\n70 1782\nce 5ad6\n2f 865d\ne6 fc1e\n2 8aa6\n17 8b95\na2 cc0e\nbc cd50\n39 a7e1\n4e 985c\nf1 5789\n76 1796\nca daec\n2d 673\n53 9927\n50 3182\n42 9a8e\n5c 9bd0\nfd 5579\nd6 5396\n8d 4273\nd7 f9bd\n20 2402\n3a 2544\n12 8326\n70 3582\nf6 5796\nad 4673\n89 406b\nd2 518e\n6 8016\na9 ee69\n50 9182\n78 954a\n76 1d36\nb8 ed48\n87 60bf\n43 90ad\ne 2a5c\n70 958a\n14 a192\n65 3cb1\n34 71a\na4 6492\n60 9480\nbe 65d4\nb 267\nf0 57a8\nf0 7582\n88 c2c8\n2 22a4\n63 9607\n24 a498\n25 a499\n20 a4a8\n2e ef6\nb8 cd42\n20 8c28\n13 8b87\nec 767a\n21 a4a9\n38 a56a\n35 a599\nec f65a\n22 8624\n31 a5a9\n26 8c3c\nc 8afa\n30 a5aa\nb3 6507\n55 93b3\n6f 94f5\n20 688\n61 3e09\nfb 776f\n24 698\n31 789\n22 e06\nb6 451e\n14 a3b8\n60 1688\nf2 772e\n35 799\nc1 7a09\n16 a3b6\n84 4298\n7 883d\n2c a472\n44 3098\n32 a7a6\na0 4688\ne1 7e09\n90 c122\n17 219f\n36 a7b6\na4 4698\n52 b3a6\nc0 5288\n2f 26df\na8 c662\n87 683f\n43 982d\n2a e64\n3d 2dd9\nb5 ef99\n73 1f87\n1a 2944\n0 2802\n86 4a16\n91 6383\nab 64c5\n64 9c18\n58 31ca\n56 b3b6\nc4 5298\na2 64ac\na2 4e06\n74 35ba\n72 b7a6\ne0 5688\n75 1799\n3d 579\n16 396\n36 796\nb1 4789\n1 223\n1b 365\nb5 4799\n85 683b\n9f 697d\n41 9829\n9d 4979\n2a 2cec\na9 6649\n10 2baa\n4f 1af5\n7d 1579\n56 1396\nd 273\n63 b4a7\nd1 5389\n67 b4b7\nd5 5399\nc8 d2e2\ne2 d424\nca 58e4\n11 8329\n22 8c86\n28 2660\n28 8442\n81 4829\nb2 cd26\n29 649\nfa dd46\nc8 50c0\n3f 2dff\nbe 675c\n60 9608\na4 661a\ndd fbf1\nc3 faaf\nac c65a\n26 2636\n30 78a\n67 969d\n34 79a\n70 178a\n81 e021\nea 56cc\n53 9b2f\n6d 9c71\n74 179a\nd1 d921\n1a 366\nb4 479a\n31 2d81\n23 8ca7\na2 c604\ne7 549d\na0 e4a0\n17 b15\n53 992f\n38 2548\n2a 846c\n10 832a\nae ecfe\n63 b625\nb2 65ae\ned 5cd3\n34 8718\nf0 578a\nf4 579a\n99 e941\ne1 d629\nf a05d\n30 872a\n48 9242\na9 444b\nc0 78a0\n4a 9246\ne0 7ca0\n15 2bb9\n6a 9646\nc3 5a2d\n19 abc1\n2e 2cfe\nad 665b\n69 9649\n99 cbe1\n68 964a\n19 9c1\n62 9686\n2c a4d8\nb a4d\nc5 7019\nf6 f516\n7a 3d64\n60 3c22\n80 ea20\nca 78ee\n1e a3d4\n4 a292\nad 4479\n86 4296\n64 9498\n8f 6ad5\ne4 5432\nfe 5574\n17 8bbf\n31 8d01\n65 3639\nb5 cd9b\nae 66d4\n0 80\n8d 68db\n49 98c9\n34 a598\n30 a5a8\n80 e008\n3e ff6\nff 7d57\n2c a65a\n6 36\n26 ae34\n25 a699\n87 681d\na7 469d\n4 20b2\n1e 21f4\n21 2489\nfc 57f2\na6 469e\n45 1293\n5f 13d5\n2c 8ed8\n49 b249\n40 38a0\n29 44b\nb3 4d2f\n5b 13c7\n9d e3d9\n68 1468\nde 515c\nc4 501a\ne4 fe18\n31 7a1\n9f e3dd\n85 e29b\n50 132a\n6a 146c\n7d 1759\n43 b08f\n5d b1d1\na4 4632\nbe 4774\n6d 345b\n84 68b0\ne 8256\n71 17a1\n5 a039\n8a c0e6\nf8 5fc8\n6f 345f\n86 68b4\n73 17a5\n9e 63fc\n84 62ba\n40 92a8\n18 b48\n57 191d\n4e b256\n3f 7fd\n25 6bb\nb9 e543\n43 12ad\n24 a4ba\n3e a5fc\nb1 47a1\ne2 de06\n8 a042\n92 e926\n52 b926\nc0 5808\n16 a99c\n3 8a27\nd0 590a\n99 43cb\nb3 450d\nb0 6522\n2a 2464\n10 2322\n55 333b\n6f 347d\n84 6818\n85 6819\n40 1a80\nb6 ed16\n80 6828\n8c c85a\n6 2836\n95 6919\nc2 d804\n2c 24d2\nf1 d701\n72 9da4\nb6 6db6\nd2 798e\n87 42b5\n4e 10fc\n34 fba\n2b 2cc7\n38 2d68\na1 ec89\n1 20a1\n49 3241\n50 9908\n94 691a\n9c c95a\n16 2936\n58 994a\n91 cb03\nab cc45\na0 4400\n86 42be\n9 8863\n3a 25e6\n65 96b3\n7f 97f5\n8a e8ec\nc7 5237\n1c a35a\n1b abc5\n1 aa83\n49 32c3\n63 3405\n50 998a\n20 c22\n3a d64\na3 cc85\nb0 67a2\n34 a592\n51 190b\n30 a5a2\nd a859\n43 9a07\n9 a869\n5 a899\nfa dd44\ne0 dc02\n1 a8a9\n19 a969\n20 6a8\n22 e26\n35 2d9b\n80 42a8\na0 46a8\n84 68b8\n33 af0f\n4d b051\na2 4e26\n71 9d89\nb5 6d9b\nd0 5908\nda 5b4c\nc0 5a0a\n91 4b21\n16 ab9e\ne5 74b9\n28 2e68\n81 6a29\nd4 d910\n3e 25de\n4a 9a46\nb2 efac\n1c 81fa\n55 b3b3\n6f b4f5\n8d 6a5b\n49 9a49\n48 9a4a\n96 c9be\n4b 92e5\n8f 62f7\nbd ef59\n7b 1f47\n6d b4f9\nca 5ac6\n2b 864d\n5 8239\n50 b120\n6f 165f\n86 4ab4\n64 9438\n57 9397\na8 644a\n8 224a\nac 6c70\n92 6b2e\n4b 9a6f\nfe 7ddc\ne4 7c9a\n4a 3866\n9f 4957\n3d 87d3\n31 d29\n28 a662\n40 3288\n1d 95b\n20 480\n1c a958\n52 9b06\n77 b5b5\n47 12b5\n92 498e\n14 a998\n1b 8b65\n1 8a23\n4d 12f3\n67 1435\n10 a9a8\n4c 3278\n46 989e\n13 185\n29 2c69\n2 2a86\n28 e60\n2d 8cfb\nc3 faad\n26 2634\nac c658\n17 195\n1 83\n1b 1c5\na 2ac6\n1c 2970\n2 282e\n88 4a42\ne 2ad6\n6 283e\nc8 d268\n0 2008\n8a 68ec\n17 397\n24 438\nf5 5533\n86 6a94\n5 a219\n20 2408\n90 6baa\naa 6cec\ndd 7bd3\nf7 7d15\n24 a618\na0 6408\n4 aa90\n0 2208\n8a 6aec\n5c b958\nd8 73c2\nf2 7504\n21 2409\nd2 d9a4\na6 6e94\nde 7bd4\nc4 7a92\n25 a619\nc3 f88f\ndd f9d1\n26 2416\na1 6409\n5a 93ee\nb8 6542\n74 9530\n5 aa91\n3b dcd\n21 c8b\nf8 7d62\n32 a706\n8b 6aed\n79 3561\n4 2018\n8e 68fc\n84 6018\n5e 137c\n44 123a\n14 a990\n34 f18\nde 537c\nc4 523a\n94 e990\n11 2989\n66 9416\n35 f19\n27 497\n34 538\ndf 537d\nc5 523b\n95 e991\n91 6989\ne6 d416\n47 b037\nb5 4f19\n7e 177c\n64 163a\n34 ad90\n54 1318\n9c 61d2\n58 91c0\nee 7cd4\nd4 7b92\n35 a719\n87 689d\nd3 f98f\n36 2516\n85 68bb\n41 98a9\n9f 69fd\n38 5e0\n94 4192\n0 2808\n4 aa18\n37 2f15\nb8 45e0\n80 6808\n5 aa19\nb9 45e1\nc2 7024\na8 6ee2\n6 2816\n81 6809\nb2 ed06\n11 2909\n89 eac9\n47 1ab7\n26 8616\nec 5c72\nb2 cd8c\nab 66c5\n64 9e18\n15 ab19\n16 2916\ne8 5ee2\nb8 cfc2\nd2 d104\n1 8009\n35 25bb\n43 b885\nd8 594a\n91 6121\n63 1c2f\n7d 1d71\ned 7cf9\n27 a69d\n48 3060\n91 4183\n90 49a2\n26 aebc\n6 be\nd4 731a\na 2e4\nee 745c\n3d dd3\nd3 7b85\n49 10e1\nf6 dd9e\nfc 7778\nef 76d7\n47 b895\ndc 595a\n28 24c0\nb2 6da4\nae 46d4\ndc d3d2\nf6 d514\naa e66e\na6 4c94\ndd 595b\n8a 48c4\n9 8049\n99 6161\nc4 d012\nde d154\nbe 4ffc\na4 4eba\n8e 48d4\nd 8059\n2f 2e57\n15 8319\nce 58d4\nc2 5aa6\n23 862d\n4d 9059\n11 a303\n2b a445\n0 888\n11 8109\n1a ab4c\n0 aa0a\n30 8508\n52 3306\n14 8118\n36 2f16\n2b cc7\n38 d68\na1 cc89\n1 a1\n19 ab49\n9a e36e\n96 4994\n4d 3871\n15 8119\n1e ab5c\n4 aa1a\n37 2f17\n1f abfd\n5 aabb\n6 9c\ndd 7173\n3e 25dc\n24 249a\n59 11e1\n34 8518\n56 3316\nba e76e\nb6 4d94\n80 4882\n9a 49c4\n19 8149\n8 aa4a\n63 b6a7\n84 4892\n9e 49d4\n1d 8159\n3f 2f57\n67 b6b7\n12 926\n3a 76e\nf1 dd29\nb6 e53e\n40 12a8\n2 a26\n15 299b\nbe 4fdc\na4 4e9a\na a66\n10 21a0\n50 31a0\n9c c15a\n16 2136\n91 6129\n56 1b96\n7d 1d79\n9a 4b64\n80 4a22\nc9 d8eb\nc2 7224\n2c 472\n4c b270\nd a253\n1 a3\n1b 1e5\naa cc4c\n90 cb0a\na 2ae6\n88 4a62\n5e 995e\nd2 d904\n3c 25d2\n11 21a1\n1 8809\n51 31a1\n7a b54c\n91 e9a1\n60 b40a\n9d 4959\n14 21b0\nbd 6d73\n79 9d61\n91 6921\ne1 5e01\n7 203d\nc9 7ae3\ne3 7c25\n90 492a\n59 3341\na6 44b4\n9 2241\n10 8908\ne6 54b4\n5 a8b1\n1 28a9\n56 9336\n25 e39\n11 8909\n95 691b\n51 9909\n90 4382\naa 44c4\nd 2251\n14 8918\nd0 5382\nea 54c4\n91 4383\nab 44c5\n58 11ca\n15 8919\n21 8421\n29 84e1\n87 62b5\n9e 6b5c\n84 6a1a\n40 9a08\n73 1f05\n4e 30fc\n34 2fba\n3 887\n10 928\n72 9f0c\nb6 6f1e\n80 4828\na1 c421\n83 4887\n90 4928\nb1 c521\nba ef64\na0 ee22\n30 70a\n61 3ca1\ne5 f499\n6f 34d7\n7c 3578\n8b 48c7\n98 4968\n1 a009\nf a57\n86 c0b6\nf4 5f98\n86 c094\n23 ae2f\n3d af71\nb3 6f25\n1d 173\n8 a262\n11 929\n73 9f0d\nb7 6f1f\n1 a29\n32 8f26\na2 448c\ncb 52e5\nc7 f09d\n78 1fea\n8d 4271\nd8 794a\n54 11b8\n16 936\n88 e262\n91 4929\nce f2f4\n38 8542\n79 b561\nef d657\n80 6a88\nba e54e\n44 12b8\n89 4a69\n2f e57\n12 924\ndc 795a\ndd 79d3\n24 a418\n48 ba6a\n48 1848\n11 b81\n43 38ad\n2c 458\na6 6c94\n8a 68c4\ne3 de0d\n9 a049\n8e c0f6\nfc 5fd8\n25 691\ne2 7626\n4c 1858\n4b 92cf\n65 9411\nb0 edaa\nd2 7b26\n15 b91\na0 4c0a\nba 4d4c\n8e 68d4\n9d 6b73\n59 9b61\ne7 de1d\nd a059\n43 9207\ncf 5a7f\n92 6984\nb2 6d84\nef dedd\n15 a119\nb6 6d94\nd4 7992\n35 a519\nc0 5822\nda 5964\n44 10b8\n6 836\n31 2729\n94 c192\n88 4a6a\n39 85e1\n42 b024\n28 aee2\n1 a809\n79 bd61\n5 a819\n4e 1254\n11 a909\n98 6b6a\n47 9ab7\n15 a919\ncb d8c7\nd8 d968\n59 1be3\n73 1d25\n9e 697c\n84 683a\n40 9828\nf 85d\n61 b689\n95 e393\naf e4d5\n7a 1564\n60 1422\n7 229d\n80 c220\n9d e3d3\nb7 e515\n68 1462\nf 22dd\n88 c260\n76 15b4\nec 7cf2\n26 a696\n82 4026\nc 8072\na2 ee24\n8a 4066\naa ee64\n53 1b25\n66 b696\nca 5066\nea fe64\n14 992\n12 ab0e\n2c ac50\n34 85b2\n54 9118\n93 6b07\n4f 9af5\n55 b313\n6f b455\n44 1898\nc2 fa24\n2c 8c72\n1e 2976\na7 4e1f\nd2 fb2e\nec fc70\n15 8b99\ndc 7b52\n69 1ce3\ne8 5640\nea 7cee\n24 a692\n3e a7d4\n47 98b7\nc6 d214\n98 696a\n47 989d\n6d 1cf3\nec 5650\n88 42ea\na2 442c\n60 3402\n7a 3544\ne6 7c1e\n2 aa6\n72 9f8c\nb6 6f9e\nab e4c7\nb8 e568\n91 e323\nab e465\n80 48a8\n1f ab5f\na1 e423\nbb e565\n90 49a8\n92 e386\nb9 e569\n15 b11\n51 1b01\n0 0\n8a 48e4\n91 4b01\n7 21d\n93 4b05\n5a 194c\n40 180a\n95 4b11\n11 2901\n97 4b15\n13 292d\n99 4b41\nc1 d2a1\nf 25d\n15 2931\n17 293d\n83 4a0f\n9d 4b51\n4c 185a\nd2 710e\n74 9fba\n12 18c\nd5 5b11\n9a 6164\n80 6022\n6c 1c72\nde 5176\n20 8c00\n6 8abe\n99 cb49\n13 2b25\n77 351f\n51 3901\n14 190\nd9 f149\nd7 5b15\n82 6026\n6e 1c76\n18 1c0\n88 6062\n96 69b4\n7f 355f\n2 8e\n1c 1d0\n8a 6066\n10 aba0\nb2 4d06\nf8 55c8\n3b 87e7\nb0 67aa\nef 56f5\nc2 5aa4\n93 e98f\ndd 537b\n33 a50f\n9f 637d\n85 623b\n41 9229\nfc 55d8\nb5 4fb9\n90 c920\n17 299d\n63 bc27\nd1 5b09\n27 ac9d\n56 1b16\n61 3483\n7b 35c5\n48 b0c8\n54 bbb0\n3d 875b\nf6 5d16\n9c eb58\n5a 1b46\n65 34b3\n7f 35f5\n4c b0f8\n58 bbe0\nfa 5d46\nf1 5fa9\n8f e27d\n10 a920\n5e 9b5c\n44 9a1a\n77 1f17\n69 b4c9\n27 8c95\n9 2cb\n23 40d\n5 aa39\n9a 494c\n80 480a\n3d d53\nd3 7b05\n90 4922\n6 3e\n90 e920\ne0 de00\n6 a03c\nf9 5fcb\n8e 6ad4\n1f 3d7\n2c 478\nf5 7d19\n11 ba1\n75 159b\nfd 5573\nc6 7814\nd a259\n16 8bbe\naf c65d\n30 8d00\n64 3638\n89 42cb\na3 440d\n1b b4d\n1 a0b\ncf 725f\n50 3902\nd6 5b16\ne1 7483\nfb 75c5\ne8 5c60\n22 8604\n9 a4b\nda 5b46\ne5 74b3\nff 75f5\nec 5c70\nd2 5b2e\n26 8614\nd a5b\nde 5b56\nd0 f108\n14 a930\n1b bc7\n28 c68\n71 1d8b\n9 aa49\n9e 495c\n84 481a\nd7 7b15\n49 ba49\n5b 1bc7\n9d ebd9\n68 1c68\nde 595c\nc4 581a\nd aa79\n36 d9e\naa ccec\n90 cbaa\na3 6625\n9b c34f\n1c 89f2\n4d 1a7b\naa 6646\n66 9634\n98 4962\n2e ae7c\n38 274a\nff 57d5\ne5 5693\n98 e960\ne8 de40\ne a07c\nc8 584a\n99 4961\n1e a9de\n1e 8bfe\n38 8d40\nb5 479b\nc 85a\n45 3a13\n5f 3b55\na9 e4c9\n67 14b7\na5 ce19\n0 a08\n33 a5ad\n32 ad26\na0 4c08\n4 a18\n6c bcda\n21 8601\n72 9d2c\n6b 3665\nb6 6d3e\n58 9bea\n3a ad66\na8 4c48\na3 64a7\n3e ad76\nac 4c58\nde 7b74\nc4 7a32\n7 a9d\n9e 63dc\n40 9288\n84 629a\ncc 5ad2\n2d 8659\ne6 5c14\n40 1a08\n72 bd26\ne0 5c08\n36 ad9c\nd0 5b02\nea 5c44\na9 466b\nf2 578e\n44 1a18\n76 bd36\ne4 5c18\n48 1a48\n7a bd66\ne8 5c48\n43 1a8d\n2b 8e67\n24 ac9a\n3e addc\n44 9838\n88 684a\n4c 1a58\n7e bd76\nec 5c58\n47 1a9d\n12 ab26\n80 4a08\n92 418e\n52 bb26\nc0 5a08\n85 e811\n59 1b49\n56 bb36\nc4 5a18\n3a 874c\nf3 5d07\n51 bba1\n20 860a\n77 9d1d\n5d 9bdb\n55 931b\n6f 945d\ndf dbff\nf9 dd41\nc1 d203\ndb d345\n42 98a6\nfd 5d59\n3e a75c\nf7 7d17\n24 a61a\n6b bc67\nd9 5b49\n6f bc77\ndd 5b59\n9 a861\n52 b984\n86 62bc\n2 a84\n1d 35b\n3e 255c\n24 241a\n1e 235c\n4 221a\n1e a35c\nd7 7917\n4 a21a\n6c 1c5a\n6 2a34\n8c ca58\na5 4e13\nbf 4f55\ne0 542a\nfa 556c\n55 13b1\n1b 23c7\n28 2468\n7d 9fd9\n40 9008\n9e 615c\n84 601a\n30 a7aa\ncd d279\n92 692e\nba cdce\nb3 6707\n6f 96f5\n48 3260\n42 9886\nc2 fa8e\ndc fbd0\n3f 2757\n85 6a31\n38 254a\naa ecec\n90 ebaa\ne7 5637\n3c a75a\n18 234a\n2e a45c\n14 a31a\nde 537e\n94 e992\n2b 24c7\n38 2568\na1 e489\n50 9108\n94 611a\nb3 47a5\n80 c2a8\n4b 10c7\n8d e0d9\n58 1168\nc1 d089\n21 4a1\n69 1641\nb4 4d1a\n4f 9a5d\n6d 1671\n23 ac85\nb8 4d4a\nfa d5ec\ne0 d4aa\n9a e966\n4f 185d\nbb 6fcf\nd5 7111\n77 9fbd\n29 4e1\n60 b680\n27 ac95\nbc 4d5a\nee 7c76\n17 b9f\n20 ac20\nf0 5d0a\n2 a2c\n61 14a1\nf4 5d1a\n6 a3c\n9e e954\n84 e812\n63 bc85\nf8 5d4a\na a6c\n6d 1cd1\n53 1b8f\n69 14e1\n67 bc95\nfc 5d5a\n57 1b9f\ne a7c\n9a 694c\n80 680a\n1e 295c\n4 281a\n1b 2bc7\n28 2c68\n40 9808\n9e 695c\n84 681a\nf4 ddb8\ned 76f1\n16 91c\nc 285a\nb7 6f17\n73 9f05\n4e b0fc\n34 afba\n48 9848\n8c 685a\n6c 147a\n10 290a\n4f 1855\nbb 6fc7\nc8 7068\n77 9fb5\n83 42a5\nf9 55c3\n8a 6846\n46 9834\nb9 6743\n75 9731\n3a 2de6\n1e abde\n99 4b61\n29 86c9\ne2 5c84\nfd 555b\nf 27d\n42 b804\nd2 fb0c\n3c 8d5a\n75 bf13\n8f c055\n5d 1959\n51 b9a1\n3a 854c\n20 840a\nbe edde\n73 b705\nec 7cfa\na1 4621\n26 a69e\n56 bb9e\nd1 5b21\n47 123d\n28 ac48\n30 85aa\n41 3003\n5b 3145\n5e bbde\nd9 5b61\n5 a891\n4f 127d\n82 c804\n18 1e0\n74 3d92\nc1 5a23\ndb 5b65\n6 aab4\nf4 fd92\n9e e9de\n53 b305\n99 c9e1\n68 944a\n48 904a\nbd e559\n7b 1547\nc6 f2bc\n30 850a\n99 6163\n86 e096\nd7 7bb5\na6 461e\n11 292b\n12 b26\n3a dec\nb9 4749\n20 caa\n23 ac0f\n3d ad51\na9 c6c9\n23 26a5\n63 bc0f\n7d bd51\n1b b6d\n1 a2b\n9 a6b\n6c 1cd0\n52 1b8e\nda 5b66\n1e 895c\n4 881a\nc0 7a28\n37 d17\n57 bb15\n64 9618\nb8 cde8\nb1 6721\n5e 995c\n44 981a\n77 1d17\n48 bac2\n62 bc04\nad c6d9\n27 26b5\nf2 ff0c\n5c 915a\n95 c313\n16 89b6\naf c455\n5b 99c5\n9f 69d7\n41 9883\n23 a625\n8 884a\n63 94a7\n3b d47\n41 ba03\n5b bb45\n41 ba01\ndb f367\nca 58ec\n4 8290\n88 6060\n48 984a\na8 6662\n43 382d\n2b a6e5\na6 44b6\n8d cad3\na7 cc15\n57 91b5\n9b 61c7\n6c bcfa\n21 8621\n8 a68\n26 cbe\na5 461b\nbf 475d\n6b 1ccd\n62 b606\n51 1b8b\n58 1340\n11 b29\n42 9026\n40 1a28\nd7 f195\nd0 5b22\nea 5c64\n19 b69\n4a 9066\n73 b707\n62 1c8c\n96 6394\n48 1a68\n66 1cbe\ne5 561b\nff 575d\n88 4a68\nc0 5a28\n59 1b69\nc8 5a68\n5d 3959\n63 9ea5\na7 6eb7\nd9 5b69\n25 8e13\n3f 8f55\n1e a15c\n4 a01a\ne2 de0e\nfc df50\n8 a04a\nd8 f9c0\n3b 2547\ne6 de1e\nc a05a\nc2 f88e\ndc f9d0\n3f 2557\nee dede\n14 a11a\n18 b6a\n6b 3e65\n1c 23fa\n36 253c\n4b 186d\nc3 5027\n4d 9073\ne3 fe25\nd6 dbbc\n39 743\nf2 df0e\n18 a14a\n1e a95c\n4 a81a\n37 2d17\nf6 df1e\n1c a15a\n3b f4d\n21 e0b\n8 a84a\nae ce54\nc a85a\n3f 2d57\n52 b98e\n7 82b5\n14 a91a\nd5 f31b\nef f45d\n56 b9be\nb 82e5\n18 a94a\ne1 5489\n73 b5a7\n35 251b\n4a 184c\na7 4417\n5 a2b1\n9a 41c4\n80 4082\na 80ce\na0 ee80\nc7 d2bf\ne1 d401\nc9 d2c3\ne3 d405\nca 584c\n71 9509\nb5 651b\ncb d2cf\ne5 d411\nf 8a77\n95 4b91\ncd d2d3\ne7 d415\nce 585c\n6c 96d8\n97 4b95\nd5 5b91\n11 8b21\n75 951b\n9a 61e4\n80 60a2\neb 564f\n6c 1cf2\nbc 45d2\n1a 8b6c\n0 8a2a\n33 f27\n88 e86a\nbb 6d67\n51 3981\n72 97a4\nb6 67b6\nd7 5b95\ndd fb5b\n13 8b25\n77 951f\nd2 d984\n28 46a\n48 b268\n88 406a\n4d b25b\n1c 29d8\n9 a63\nea 7cec\n24 a690\nd0 7baa\ncf 72df\n50 3982\n88 62e2\na2 6424\n8d 4a73\na1 6421\ndb 7b6f\n2f a655\n4 a98\n83 4207\ne8 7c60\n22 a604\n11 b89\n42 9086\nc a872\n2f 2edf\na8 ce62\n47 9a9d\ne1 d421\n2 820c\nc8 5868\n9d e359\n4 a8ba\n5b 1347\n1e a9fc\n79 3549\n33 872d\nd2 5ba6\n62 1404\n48 12c2\n9c 69f8\nc0 50a0\nd9 fbc1\n22 2606\nc8 50e0\nc7 fabf\ne1 fc01\n2a 2646\n45 183b\n5f 197d\n3e 8574\n24 8432\n46 9234\n8a 6246\n68 b448\na2 6606\n64 9630\na8 6642\n44 10b0\n25 ae91\n1f 1d5\n5 93\n6 a89e\n81 4821\nf2 fd06\n21 2609\nfa fd46\n29 2649\n1d 17b\nb3 6f2d\n82 eaa6\n74 1d18\n88 60c8\n10 8120\nc4 50b0\nc3 fa8f\ndd fbd1\n26 2616\nf4 5d18\n7a 15e4\n60 14a2\n80 c2a0\n58 3b40\n1f 29ff\n9e 635c\n40 9208\n84 621a\n48 9248\n8c 625a\n70 9702\nc9 5ae9\n3b 2def\nba 674c\na0 660a\ned fcdb\n23 8ca5\n24 843a\n3e 857c\n9e 697e\n40 982a\n5a 996c\n83 e20d\n4 a8b0\n24 e38\n93 e30d\n14 a9b0\n27 e97\n34 f38\neb 5eed\n11 2129\n41 ba09\n95 eb99\n53 1b87\n60 1c28\n37 8fb5\n88 6068\n2e 6de\ne5 dc99\n6f 1cd7\n7c 1d78\n8b 60c7\n47 90b5\n98 6168\n8c c25a\n6 2236\n95 c991\n81 6229\n6d 1e79\n1c 835a\n4d b8f1\n65 1c19\n8c c05a\n6 2036\ne 2076\n50 b180\n70 1708\n61 1c29\n89 6069\nba e566\nba cfce\nd4 d110\n9a 636c\n94 c992\n80 622a\n6c 1e7a\ncb f24f\n4c b8f2\n7e 15d4\n64 1492\n84 c290\n86 4096\n71 1f23\na6 ee94\nc2 5086\n50 13a2\n6a 14e4\nc6 5096\ne6 fe94\n70 1f08\n2 8026\n10 2182\n2 8a8e\n1c 8bd0\n2a 84e4\n10 83a2\nf4 f51a\n6 a23c\nda 79ee\n2e a4d4\n14 a392\nd1 590b\n14 2192\n94 eb98\n52 1b86\n79 1d69\nd5 591b\n73 3f0f\n8d 4051\n1a 23c4\n0 2282\nba 65c4\na0 6482\n68 96c8\nac 66da\nbc ed5a\n17 ab9f\n29 2469\n2 2286\na2 6486\nb6 4796\n74 9d3a\n6d 3673\n30 2582\n22 8e8e\n3c 8fd0\n5f 137d\n15 a991\n45 123b\n90 4122\ndf 7bf7\n26 a63c\n34 2592\n93 430f\n14 9b2\nad 4451\n1 a81\n1c 358\n22 2686\n43 1a0f\n5d 1b51\nf6 7516\n9e c9de\n97 6317\n53 9305\n84 6a10\n92 6186\n7e 1dd6\n3 a827\n6c 1ed2\n26 ac1c\nc aada\nda f16e\n1d 81d9\na2 6686\n0 2088\n53 3b87\n60 3c28\n35 afb1\nd7 5117\n10 2188\n20 2488\n73 3f87\n80 4028\nf7 7d95\n24 a698\n86 681c\n24 2498\n11 2189\nec 54f2\n71 3d29\n15 a399\nb8 65c2\n74 95b0\nf8 7de2\n32 a786\n26 2496\na1 6489\n84 6098\na0 6488\n73 95af\n0 2288\nd1 d121\nda fb64\n3 8a8d\nc0 fa22\nf2 fd86\n21 2689\n91 6189\n74 b712\n7d 1dd9\n81 6289\n6d 1ed9\n26 2696\na1 6689\n1 2089\n32 a586\nf6 5534\ndc 53f2\n24 4b8\n5 a299\n25 2499\n1a 23cc\n0 228a\nc5 5293\ndf 53d5\n1e 23dc\n4 229a\n28 a642\n31 d09\n34 a79a\n57 319f\nd0 d122\n3 807\n17 39d\nd4 7332\nee 7474\n3f a577\n14 9ba\nad 4459\n3a 27cc\n20 268a\n10 3a2\n2a 4e4\nf4 751a\n90 6b20\n6 223c\nd8 7b60\n1 a89\n9d 617b\n59 9169\n32 8f86\n31 25a1\n21 8c09\n15 21bb\nc2 d084\n8e e0d4\n59 1163\n50 9188\n94 619a\n25 8c19\n47 3a17\n9a 63cc\n80 628a\n6c 1eda\n1d a971\n3 a82f\nba 67cc\na0 668a\n1a 894c\n0 880a\n33 d07\n53 bb05\n10 21a2\n2 8aae\n1c 8bf0\nb 82cf\n25 8411\nd1 592b\nba 65e4\na0 64a2\n76 bf36\ne4 5e18\n5b 396f\ne5 7c19\n1 aa1\n85 c299\nf 2d7\n1c 378\n2a cee\nc0 7aa0\na9 464b\nfc 57d0\ne2 568e\n0 20a8\n10 21a8\n90 430a\naa 444c\nc1 78a1\n37 2fbd\ncf 70d7\n71 9f83\ndc 7178\n4b 9247\n20 24a8\n30 25a8\nb0 470a\ne1 7ca1\nef 74d7\nfc 7578\n6b 9647\n11 21a9\n31 25a9\n26 c3c\nc afa\n7 2a3f\n90 61a8\n7c 1df8\n10 21aa\n35 fb3\n4f 10f5\n1c 8bf8\n30 25aa\n55 13b3\n6f 14f5\n9c c9d8\n16 29b4\n95 6311\ne a2de\n89 4261\nd4 793a\n3 ad\nb8 cdc8\n32 2da4\nb1 6701\n2 820e\n1c 8350\nc8 586a\n9d e35b\n1e a9fe\n5 b1\n1d ab59\n2e 8efc\n8c 6050\n4a 1ace\n64 1c10\n99 69e1\n68 344a\n9 a8eb\n46 1236\n88 e248\n30 8f00\n8e 6054\n4c 1ad2\n66 1c14\n13 8985\ndf 73d7\nec 7478\n3b def\nba 474c\na0 460a\nd1 7ba1\n7c 1f52\nca f2e4\n34 8532\n6c 96f0\nb0 6702\nc7 5a37\n99 41eb\nc6 5016\n24 aeb0\n1c ab5a\n4d 38d1\ncf 5a77\n1e 81d6\nc1 f029\n57 391d\n9a 49e6\n30 af00\n3a 274c\n20 260a\na1 44ab\nbb 45ed\n9a 4964\n80 4822\n30 2722\na8 64e8\n61 be89\n84 e838\n42 1826\nd4 f31a\nee f45c\na 82e4\n28 264a\n88 4862\n38 2762\n8c e878\n4a 1866\n24 ae90\n1e 1d4\n4 92\n20 2608\n14 13a\naa 6eec\n84 4830\n22 86ac\n24 2618\n18 14a\nae 6efc\n88 4860\n47 109f\n28 2648\n1f 29fd\n5 28bb\n84 6218\n3b 2ded\n21 2cab\na0 6608\n25 2cbb\n3f 2dfd\na4 6618\nd8 51c0\n1 8081\n3a 2dec\n20 2caa\nb9 6749\n64 1c18\n3a 2564\n20 2422\n2c 8e70\n28 2462\n40 b020\n96 4bbe\nb0 4d00\nc9 5a4b\n80 4a20\n1f abdf\ncd 5a7b\n60 b420\nc4 5892\n25 8419\nde 59d4\ne7 f695\n1d 359\nd4 5312\nee 5454\n9e 6176\n5a 9164\n40 9022\n85 6291\n68 9462\nf9 7d63\n33 a707\n22 c8c\ne6 7c36\n2d a67b\n2d 459\n42 b2ac\ne4 5412\nfe 5554\nfd ff73\n26 8e9c\n50 9122\ndf 735d\n46 38be\nc5 721b\n2f a67f\n3d af7b\n52 9126\n21 c29\nb0 6722\nb2 6726\n1c 35a\n4d 38f1\nf3 dd8f\nf9 7769\nb8 6762\n53 392d\nba 6766\n55 3931\n8 8862\na3 e487\nb0 e528\nbe 4f76\n4a 9866\n34 8d90\n20 2628\n94 c990\n80 6228\n6c 1e78\ncb f24d\n4c b8f0\n91 6329\n7d 1f79\n60 b480\n93 c98f\n99 6369\n8c 6a5a\n48 9a48\n96 c9bc\n8f 62f5\n61 1e03\n7b 1f45\n56 313c\n3c 2ffa\nb3 cd8f\nb9 6769\n3a a564\n20 a422\n5f 1bff\n79 1d41\n8d 60f1\nea dee6\n10 a122\n8d e051\n80 4008\n12 a126\n7f 1757\n28 acca\nd 225b\n53 9b2d\n97 6b3f\n50 bb00\nf9 fd49\n88 486a\nc1 7a23\ndb 7b65\n82 488e\n9c 49d0\n47 92b7\n98 636a\nb8 e76a\nb4 4d90\n67 96b7\nb8 676a\ndd 5b51\nc3 5a0f\n37 8fb7\n88 606a\n1e 83dc\nd7 5997\n4 829a\n37 797\n10 a1a0\n14 a1b0\na7 441f\n5 a2b9\ne a056\nfa d764\n67 369f\ne0 d622\na0 ee88\n3d 5d3\nd3 7385\n77 9f95\nc8 7048\nb a267\n91 6381\n35 85b1\n3e aff4\n24 aeb2\n83 4827\n58 936a\nf6 fd3e\nab c665\n78 9562\n95 6391\ne8 7448\n1a bec\n0 aaa\n2b a667\nb1 6781\n78 976a\nb5 6791\n32 85ae\naa 64c4\n90 6382\n81 4809\n13 a927\n7c 1fd2\n1f ab57\n20 2688\n6a 9e46\nd2 f3ac\n3c 85fa\n80 6288\n3 a82d\n6c 1ed8\n6e 9e56\n84 6298\n7 a83d\n1e 8174\n4 8032\na0 6688\n3d 2579\n16 2396\n91 6389\nf 80f7\n7d 1fd9\n78 956a\n95 6399\n36 2796\nb1 6789\naf eef5\n19 8143\nb5 6799\ncf 52d5\n13 a92f\n7c 1fda\ndf 5bfd\nc5 5abb\ne5 dc13\nff dd55\nb0 678a\nef 56d5\nc2 5a84\n5e 19fe\ndd 535b\n35 27bb\n20 26a8\n14 b12\n2e c54\n96 4396\nbd 4579\n68 9e68\nac 6e7a\n30 27aa\n29 86e9\ne2 5ca4\nfd 557b\n58 13c0\nd0 d12a\n1d 951\n3 80f\nd4 733a\nee 747c\n50 9988\n94 699a\n49 32c1\na 2044\n32 a7a4\n1d 2bd9\n4e b0d6\n5e 9bf4\n44 9ab2\na2 6c06\n91 e929\nce 5274\nfe dfdc\ne4 de9a\n1 8a01\n9a 616e\n91 61ab\n5 8a31\n7 8a35\n51 91a9\n95 61bb\n18 8942\n4c 327a\n9 8a41\nc0 7288\nb1 4d29\na8 e662\n46 1ab4\nee 565e\n9d 6bd9\n28 2e60\n2d acfb\n6a 1646\nac e658\n40 9a00\n9e 6b54\n84 6a12\n7b 3fcf\n95 4111\n37 af95\ncc 505a\nec fe58\nfe 7f76\n27 e9f\n1 2a09\n32 af06\n81 6a09\n7e bd5c\n64 bc1a\n97 4117\n47 309f\nc0 d022\nda d164\n9 8069\n2b 2e67\n8d 607b\n22 8e86\n49 9069\n45 12b3\n5f 13f5\n29 8641\n74 bd1a\nb 80c7\n18 8168\n3a 2f66\n80 48a2\n9a 49e4\n10 100\n47 389f\nc0 d822\nda d964\n1 a029\nf4 5fb8\ne3 de2d\n9 a069\n12 a30e\n2c a450\nd8 796a\na7 ce95\nd9 79e3\n13 a387\n20 a428\n2e e76\nc0 78a2\nda 79e4\n21 a429\n77 1d97\nea deec\n3 a087\n10 a128\n1e b76\neb deed\n11 a129\ne2 5486\n99 4363\nf2 df2c\nb a0c7\n18 a168\n3e f76\n1 a829\n4e 1274\n11 a929\n95 6b11\na4 e4b8\n62 14a6\n82 c2a4\n40 3a02\n5a 3b44\n82 40a6\nc2 50a6\n38 7c2\ne3 74ad\n8d 4851\ncd d2d1\n92 6986\ncf d2dd\n50 9980\n94 6992\n3a afce\n54 b110\n4 2890\nfb 5d67\n28 866a\n54 b118\n4 2898\n14 2998\n1b b65\n1 a23\nef d457\n80 6888\n1 2a89\n59 b169\n32 af86\n81 6a89\n55 b119\n6 2896\nd6 d316\n81 6889\nfc 5772\nb2 ed86\n69 3ce3\ne8 7640\n10 298a\n4f 18d5\n43 b087\n50 b128\n5e 1b76\n7f 9577\n10 29a8\n4b 9a47\n10 8188\n59 3169\n32 2f86\n48 30c0\n51 b129\n5f 1b77\n15 2b11\n89 426b\n55 9933\n8d 405b\n93 6b05\n5a 394c\n40 380a\n1f 8bff\n39 8d41\n67 9c9f\n6d 3679\nbf 45f5\na5 44b3\n12 2b06\nf0 7588\na1 4689\n33 a7a7\nb 2047\n2e 2c54\n14 2b12\n11 8b01\ne2 f4a6\nb1 6fa9\nae 44d4\n94 4392\n17 8937\n0 2a08\na0 6c08\n4 2a18\na4 6c18\n21 a601\nda f3e4\nc0 f2a2\nc ad2\n26 c14\n80 6a08\n84 6a18\n31 8583\n47 b095\ndc 515a\n37 f9f\ncc 7ad2\n2d a659\ne6 7c14\n16 2b16\n91 6b09\nc2 f006\n95 6b19\nc6 f016\n7d 9fdb\n19 2b61\n62 3c84\n94 69b0\n7d 355b\n91 6b21\n7 223d\n2b 4e5\n11 3a3\nf5 751b\n72 bd8e\n27 86b5\n54 9192\n63 1ca7\na5 ecb9\ne2 5604\nf2 ff84\n5c 91d2\nbe 657e\n7a 956c\n60 942a\n28 846a\n9a 69c4\n80 6882\n70 952a\naa 6c64\n90 6b22\n6 223e\n98 6b62\nc5 f839\ne 227e\n8 886a\n3b d67\n41 ba23\n5b bb65\n0 2a28\n80 6a28\nc8 7ae2\n29 a669\ne2 7c24\n58 3340\n47 b0b5\ndc 517a\n65 9c19\n87 4a17\n91 6b29\n38 a742\n60 bc80\n99 6b69\nd 80d1\nca f066\n67 1495\n34 8f98\nf2 df2e\n18 a16a\n28 a46a\ndd 5b71\nc3 5a2f\ndc 51d2\n47 b2bf\n61 b401\n4b b2cf\n65 b411\ncb d2cd\n90 6982\n99 c163\n6 209e\n58 9b6a\nab ce65\n99 c363\n6 229e\n14 2990\n38 876a\n51 932b\naf 647f\n6b 946d\n30 52a\n55 199b\n66 b416\n35 2f19\n78 9d6a\n95 6b99\ne6 561e\nc6 f096\n48 3868\na2 e406\n91 498b\n61 b421\nd2 f926\n1 2229\n15 8991\n49 32c9\n92 6ba6\n70 bda8\n22 2404\n8 22c2\n6f 9677\n0 2aa8\nb7 679d\nc8 5862\n2 8206\n89 ca4b\n3 2a27\n3a 2d4c\n20 2c0a\ne8 5c62\n22 8606\n89 e861\n28 246a\nd2 f984\n82 4a84\nbe 655c\n60 9408\na4 641a\n1 8209\n8a 4ac4\n68 9448\nac 645a\nc2 5804\n9 8249\n73 1d2f\na2 4e84\n3e dfe\nd4 7bb0\nbd 475b\nca 5ac4\ne 28fe\n8d 625b\n49 9249\n48 924a\na 8266\nf7 df9f\n1d a1db\nf9 7f63\n22 e8c\nef 7c5d\nd5 7b1b\nb ae5\n2a 8666\n3d a5db\n42 128c\n7a 374e\nab 6ce5\n91 6ba3\nc2 5824\n9 8269\n8d 627b\n49 9269\nbd 65f3\n79 95e1\n41 b809\n51 b909\n14 8198\n5d 3179\n36 2f96\n81 c809\n91 61a1\nb 28e5\n91 c909\n16 8916\n54 9198\na 2066\n6c 9c78\n5f 9bd7\nbe 65dc\n60 9488\na4 649a\nb2 cd2e\nab 6667\n4a 3066\n9f 63dd\n85 629b\n41 9289\na5 6c19\n60 1e80\nd6 f116\na4 4492\nbe 45d4\ne1 7c03\n28 a648\nfb 7d45\nd0 d1aa\nc8 f848\nda 7966\n1d 9d1\n3 88f\n30 85a2\n34 2d3a\n22 86a6\n10 81a8\nd2 d10c\nb8 cfca\n32 2fa6\n2 8826\n94 61ba\n50 91a8\n11 2b03\n2b 2c45\n21 8489\nf8 f560\n33 5a7\ne8 546a\n53 b3a5\nd2 d1a4\nf2 d5a4\n85 6039\n5e 93fe\n78 9540\nbc 6552\n24 2438\n17 2397\n7a 9544\n60 9402\nbe 6556\nb5 67b9\n4b 3267\n52 992e\n7e bdde\nf9 5d61\n33 8705\nab e64f\n2c acf2\n6a 9c44\nae 6c56\n50 9b02\n75 b5b1\n58 9b42\n63 b4af\n7d b5f1\n9 ac1\nf7 5d9f\nc9 dae3\ne3 dc25\n8c e850\n8e e854\n43 ba85\nd8 5b4a\na9 4c61\n2e acde\nfd 75f9\nd8 5962\n12 8306\ne1 5c03\nfb 5d45\n28 8648\n79 354b\n90 69a0\ne0 5e80\n6 20bc\n1a 8346\n70 9780\nb4 6792\nb0 6da0\n26 24bc\nae 6456\n6a 9444\n50 9302\n29 c69\n2 a86\nac 665a\n68 9648\n49 1069\n22 e86\nf0 7da0\n66 34bc\n82 6086\n6e 1cd6\nf4 7598\nfb 5765\ne1 5623\nf 2057\na5 4699\n37 a7b7\n13 8b05\n1e 21d4\n4 2092\nea fec4\n54 9112\n1e 9d4\n4 892\n99 e961\nc8 d2c2\ne2 d404\n70 9508\nb4 651a\nd0 d302\nea d444\n93 630d\n14 29b0\n78 9548\nbc 655a\nd2 5904\n19 8349\n83 488d\ne8 76c2\n98 6b60\ne 227c\n98 e962\ne8 de42\ne a07e\n69 bce1\n38 874a\na9 cce1\n78 974a\n1a 81c4\n0 8082\n50 b102\nfa 5f6e\n1a 29c4\n0 2882\n1a 234c\n0 220a\n81 40ab\n9b 41ed\n82 6886\n53 3b0f\n6d 3c51\n75 15b3\nf1 f5a1\n54 b112\nfe 5f7e\n1e 29d4\n4 2892\n78 b560\n9e 69d4\n40 9880\n84 6892\ne1 7c23\n28 a668\nfb 7d65\nc8 f868\n7a 9564\n60 9422\nbe 6576\n39 8761\n21 8623\n3b 8765\n58 9b62\ned 7c59\n9 ae1\nf7 5dbf\n22 8486\nfa 77e4\ne0 76a2\n76 3dbe\nf5 771b\n2b 6e5\n62 9486\n19 8363\n31 f89\nb aa6f\nf 8a5d\na1 c489\n2b 4c7\n38 568\nc9 f2e9\n26 8496\nfe 77f4\ne4 76b2\n67 3615\n54 9b9a\n6e 9cdc\n66 9496\nf2 7f2e\n35 f99\n73 15ad\nf aa7f\n25 ac19\nac 667a\n68 9668\n1a 8366\n5e 33fe\n78 3540\n9e c3dc\n84 c29a\nb7 4797\n75 9d3b\n6e 3674\nae 6476\n6a 9464\n50 9322\n58 9362\n2 80a6\n70 1f88\nbe c7dc\na4 c69a\n70 9722\n52 1104\n38 fc2\n78 9762\ned defb\n6 a096\ndd dbd9\n57 3bb5\n26 61e\nca 5044\nb0 4f02\n68 9640\nac 6652\n47 381d\n4 aa10\nf9 dfeb\n12 a186\ne9 dcc9\n63 3ca5\n32 70e\nd6 5134\nbc 4ff2\n57 339f\nd0 d322\nea d464\n6b 94c7\n78 9568\nbc 657a\nd2 5924\n19 8369\na6 649e\n62 948c\n30 8f88\n8e 60dc\n3a a5c4\n20 a482\n22 a486\ne6 5434\ncc 52f2\n3e a5d4\n24 a492\n70 972a\n28 660\n71 1783\nc9 dae9\n2c 670\n75 1793\n6 a896\n26 e1e\nd5 71b1\n82 682c\nf3 7da5\n20 a6a8\nae 4454\n94 4312\n4c 1050\n32 f0e\n11 8381\n72 9584\nb6 6596\ne0 7c02\nfa 7d44\n1c 9d0\n2 88e\n89 4a6b\naf 64d5\n95 6393\n51 9381\n80 6008\n6c 1c58\n28 2c4a\na3 e6a5\n39 8fc1\n97 6115\nf2 5d84\n39 87c9\ne2 d484\n94 e392\nae e4d4\n79 1563\n70 9588\nb4 659a\ne8 f460\n11 8389\nd8 7342\n40 3aa8\nda f9cc\nc0 f88a\n3d 2553\n3a 2566\n31 8789\nf8 7742\nf4 759a\n79 374b\n90 6ba0\n6 22bc\n7a 3566\n95 6119\n30 878a\n47 1235\n92 490e\nb5 6d19\na3 c685\n20 622\n3a 764\nd5 7119\nf5 f519\n11 83a1\n49 38e3\nc8 7240\nae 647e\n50 932a\n6a 946c\nfd f7db\n33 87a5\nb 45\n7d 9759\nfb 75ef\n80 6028\n5f 1bd7\n6c 1c78\n2 8a26\n15 a99b\n72 b7a4\ne2 d4a4\na0 4c02\nba 4d44\ne8 5e68\n1 2003\n1b 2145\n95 6139\ncb 52e7\n2b 4cf\nc1 7281\na7 64bf\n63 94ad\n21 c0b\n3b d4d\n5a 3146\n8 8a42\n13 a3af\n2d a4f1\n11 23a1\n1 8a09\n8 8a62\n1 a089\n10 a188\nc1 7a29\n1e bd6\n11 a189\n14 a198\n1b 8365\n1 8223\n9 8a69\n20 a488\n2e ed6\n95 e119\n53 1107\n21 a489\n89 68cb\n45 98b9\n30 a588\ne1 7e29\n3e fd6\n8 8a6a\n3b f67\nfc 55d2\ne 2f4\n16 2b96\n3d 2d79\nb5 ef39\n73 1f27\n61 3489\ne7 569d\n1 a889\n9 22e1\n10 89a8\n5e 195e\n8f c2ff\na9 c441\n10 89a2\nb5 6fb9\n3 a88d\n4d 1279\n98 4160\n3d a75b\nf6 7d16\n80 4022\n9a 4164\n97 6b17\n53 9b05\nfe 7d56\nf4 fd32\n37 8d9d\n36 ad3c\n1c abfa\n83 e88f\n9d e9d1\ncd 527b\n3d a551\n23 a40f\n0 8a08\n33 f05\n74 3d3a\n29 661\n72 1784\n9f 4355\n6 8b6\n85 4213\n88 e8c8\n46 18b6\ndf 5355\nc5 5213\n5a 9b46\n65 b4b3\n7f b5f5\nc 8ad8\n25 e93\n3f fd5\n79 15c1\nc9 72e1\n33 52f\nd0 d9a8\n1e a3dc\nd7 7997\n4 a29a\n37 2797\n31 8d09\n6c 1cda\na5 4e93\nbf 4fd5\n4b 30c7\n58 3168\nc1 f089\n21 24a1\n69 3641\n70 9d08\nb4 6d1a\n11 8b09\n1e 21dc\n4 209a\n35 8d19\na3 448d\n78 9d48\nbc 6d5a\n19 8b49\n70 95aa\ne 2a7c\n3a 25e4\n20 24a2\n58 9b4a\nab ce45\n7d b5f9\n43 9aa7\n6f 3cdf\ne8 dc62\n4a 1244\nd a8f9\n33 587\nde f3fe\nf8 f540\ne8 544a\n53 b385\n91 61a3\n83 6a2d\n9a 69cc\n80 688a\n1e 29dc\n4 289a\n9e 69dc\n40 9888\n84 689a\n73 b785\na8 6e68\n1f 157\n25 ae13\n3f af55\ne2 548e\nfc 55d0\n9e 6b7c\n84 6a3a\n40 9a28\n73 1f25\n8c 6a7a\n48 9a68\n61 1e23\n7b 1f65\n1a 8b66\n5a 9b66\nf4 df90\n1a a1cc\n0 a08a\n33 2587\n25 8e93\n3f 8fd5\n1e a1dc\n4 a09a\n37 2597\nde fbfe\nf8 fd40\n33 d87\n85 e8b1\n54 b31a\n6e b45c\ne8 5c4a\n53 bb85\n14 a19a\n19 8b69\n8e 68dc\n3a a5cc\n20 a48a\n45 9293\n5f 93d5\ne3 5e85\n9 20c1\n62 340e\n7c 3550\n93 69a5\n8f 687f\n4b 986d\n5a b3e6\nc8 52c8\n41 3081\nc7 5295\n74 1f9a\na3 ce25\n1a a9cc\n57 1317\n0 a88a\n1e a9dc\n4 a89a\n37 2d97\nef d6dd\n70 9d80\nb4 6d92\n72 9d84\nb6 6d96\n8e 685e\n4a 984c\n13 8b85\n9e cbfe\nb8 cd40\ne6 dc9e\nec 7678\n98 41e0\nf4 7d92\n8c cad8\n6 2ab4\n2c 2c52\n74 3dba\n29 6e1\n9f 43d5\n85 4293\n1a 2966\n69 36c1\n70 9d88\nb4 6d9a\ne8 fc60\n11 8b89\nd8 7b42\na9 46c1\nf4 7d9a\n6 2abc\n5a 3966\n8e 425e\na3 ce85\n20 e22\n3a f64\n8c 687a\n48 9868\n61 1c23\n96 eb94\n7b 1d65\n8e 687e\n4a 986c\n38 25e0\n50 9180\n94 6192\n87 68bf\n43 98ad\na3 cea5\n81 c821\nc d2\na2 6e84\n3e 2dfe\n79 9749\nbd 675b\nda 7bc4\nc0 7a82\n21 a609\nc8 7ac2\ne2 7c04\n29 a649\n93 412f\n5b 13ef\n75 1531\nd3 7907\n1a a34c\n0 a20a\ndb 7947\n8 a24a\nf3 7d07\n3a a74c\n20 a60a\nf 28df\n88 c862\nfb 7d47\n28 a64a\n9a c9c4\n80 c882\nd4 f312\ndd 59d9\nee f454\n6b 364f\n82 6aa4\n59 9369\n9d 637b\ne6 749e\n1 a229\n9e 417c\n84 403a\nc2 7824\n9 a269\n32 58e\n73 bf0f\n8d c051\n8 2062\ne2 5e26\n7 2abd\n1a a3c4\n0 a282\n93 c30f\n14 89b2\nad c451\ne8 7ce2\n22 a686\nae 645e\n50 930a\n6a 944c\n81 c8a1\n21 a689\n83 680d\n75 15b1\nd3 7987\n1a a3cc\n0 a28a\nf3 7d87\n3a a7cc\n20 a68a\n9c 6950\n82 680e\n10 a1a8\n1e bf6\n30 520\n11 a301\n48 b8ea\nd9 7961\n13 a305\nc9 f24b\n4a b8ee\n96 4116\nf9 7d61\n33 a705\nb 8a65\n86 c8b4\n6f 945f\n7 289d\n80 c820\nd9 7bc3\nf3 7d05\n20 a608\n4d 3851\n55 11b3\n4f 3855\n99 e1c9\n57 11b7\n3e 8dfc\nbd c759\n37 2735\n24 8cba\n18 a342\na2 ec26\n9 8cb\n1a a346\nc1 d823\ndb d965\n4f 9a77\n84 e8b8\ndb 5345\nc1 5203\n42 18a6\n20 40a\n51 39a1\n3a 54c\n23 487\n30 528\n91 c921\n7 803d\nf 807d\nb4 451a\n50 3b20\n6 803e\nd9 7be3\nf3 7d25\n20 a628\n11 a321\n94 4132\ne3 7c07\n2a a64c\n49 124b\n86 4836\n1e 3fe\nd5 d9b9\n38 540\n38 a762\n60 9ca8\nbe 6dfc\na4 6cba\n3a a5e4\n20 a4a2\n90 c982\n6 809e\nde 73fc\nc4 72ba\n2f e75\nca 78e4\n11 a329\nb4 453a\n7 2a1d\nd8 7be2\nf2 7d24\n39 a769\n10 108\nab e467\n80 48aa\n9a 49ec\n0 2228\n14 8990\n48 32c8\n61 1c09\n2 888e\n8 2268\n1c 89d0\n67 1695\n14 990\n18 a36a\n95 e911\n69 1c49\n38 548\n34 d90\n38 a76a\n6 8894\n2e cfe\nad 465b\nc4 7ab0\n31 afa1\nd3 5107\n30 5a0\n11 a381\nf9 7de1\n33 a785\n83 c00f\n9d c151\n2a a4c4\n10 a382\n85 c013\n9f c155\nf2 5584\n8e ea56\ne9 7ce1\nb8 474a\n23 a685\n94 e39a\nae e4dc\n79 156b\na3 c40f\nbd c551\nac ee52\n30 5a8\n11 a389\nbe 655e\n7a 954c\n60 940a\n91 c9a1\n7 80bd\n12 126\n32 af24\n31 a789\n93 690d\n1d 23f3\n37 2535\nbd c559\n39 d61\nac ee5a\nb3 4507\n11 a3a1\n5d 997b\n14 8b12\n2e 8c54\n10 180\nd3 5b05\n2a a4e4\n10 a3a2\n30 580\n30 588\n7c 95d8\n9e 43d6\n3e 8d56\nd 859\n85 cab3\n9f cbf5\n6e 965e\n1d abd9\n13 b87\n20 c28\n1 aa09\nba cf64\n27 2e9f\na0 ce22\nda d3c4\nc0 d282\n1 aa29\n8d c851\n8f c855\n31 a723\n20 ca8\n7 2835\n8d c859\n13 ab05\n32 8706\nf8 5d62\n6e 147e\n1d 29f9\n95 ebb9\n53 1ba7\n23 8c87\n30 8d28\n29 2661\n72 3784\n8c c8da\n9f 6355\n41 9201\n6 28b6\n85 6213\n18 ab42\n43 10a5\n10 8ba8\nb8 e56a\nf9 5d49\n23 c87\n30 d28\n11 ab09\n94 4932\n32 87ae\na0 6e00\na 4e\na6 6616\n62 9604\n49 1a4b\n34 271a\n7a 9fec\n60 9eaa\nd8 7140\nbe 6ffe\nd1 5901\n2d edb\n47 101d\n0 aa28\nb9 cf49\n33 2f25\n21 8c89\nf8 fd60\n54 b33a\n6e b47c\n45 92b3\na3 6407\n5f 93f5\n11 ab29\n49 1261\n94 493a\n43 320f\n5d 3351\n19 ab69\nf0 5da2\n66 14be\n83 c80f\n9d c951\n85 c813\n9f c955\n30 8da8\n29 26e1\n11 ab89\n17 2935\n9d c959\n2 82c\n9b 49e7\n31 af01\n13 107\n33 af05\na0 6e28\n17 117\n37 af15\naf 6ef5\n19 143\nc3 f825\n39 af41\n1b 147\n21 ae03\n3b af45\nb3 6f05\n1d 153\n23 ae0f\n3d af51\nc7 72bd\n31 50b\nbb 4def\nc0 7228\nd4 d990\n37 517\n1e 815c\n4 801a\n57 b315\ncf 72fd\n50 39a0\n39 54b\n29 2ec3\n43 3005\n10 ab08\n22 c84\nef d677\n80 6aa8\neb 7ec5\n55 1113\n43 b8af\nc2 f20c\n5d b9f1\n2c 845a\n86 e094\n51 1123\nf3 7f05\n5d 1153\n31 fa3\n4b 10e5\n18 8be8\nde 595e\n7c 97da\na6 e49c\n71 152b\n99 4143\nce 7ad6\n2f a65d\n9d 4153\n21 4a3\n3b 5e5\n8 80e8\nca d04c\nb0 cf0a\n2a 2ee6\na8 4e62\nd0 79a0\nb9 454b\neb 7467\n14 390\n15 9b1\na2 4c84\n59 3b61\n1b 167\n21 ae23\n3b af65\nb5 459b\n5f 33d7\n6c 3478\n3a 74c\n20 60a\n51 3ba1\ndc fbf0\nc2 faae\n3f 2777\nc 58\n34 87b8\n96 493c\n55 19b1\nba 47e6\n10 32a\n2a 46c\n73 158f\nf9 556b\n54 13b0\n68 1e42\n1b 345\n1 203\n8b 4ae7\nad 6cfb\n69 9ce9\n62 1406\na4 e418\n20 2c20\n3 207\n7 217\nb 247\nf0 5788\nd 253\nb9 6d4b\n75 9d39\nbc 65da\n78 95c8\n9a 43c6\nf 257\nf4 5798\n70 3502\n62 9e0e\n7c 9f50\n3f 75d\n25 61b\nec def8\n1f a1d5\n5 a093\nc8 dac0\n2b 647\nbd edfb\nfa 5746\na9 6cc1\n40 3aa0\n29 64b\ncd 5071\nb3 4f2f\ncc dad0\n2f 657\n78 3542\nfe 5756\n93 6b8f\nad 6cd1\n24 eb0\n9a e146\nc1 700b\ndb 714d\n7d 9ff9\n71 35ab\n10 a908\n4d 1253\nd7 5b37\n7 2a1f\n90 6188\n7c 1dd8\n49 1263\n5c 31d8\nc 2ad2\n26 2c14\na5 e639\n63 1627\n6 2296\n2d 2479\n76 359c\nce 5a5e\nb8 4542\n84 4ab0\n6d 165b\n1d 217b\nf7 5f3f\nad e679\n6b 1667\n39 adc1\n69 166b\nab 4647\naf 4657\n9b e3c7\na8 e468\na5 4413\nbf 4555\n3 a2ad\n92 e90c\ncf 5257\ncb 5267\nb6 4dbe\n6b 16e5\nb2 ed0c\n98 ebca\nef 5657\nbb ed47\n45 1ab1\n24 8610\nd0 5b2a\nea 5c6c\n6e 1cfe\ned 565b\neb 5667\ne4 749a\nfe 75dc\nb9 edc1\ne9 566b\n3f a7ff\nf4 5738\ne7 5697\n8d e279\n4b 1267\nbb 4de7\n9c e9da\n51 b301\n4a 9a66\n5d b9db\n55 b311\nb6 ed3e\n6b b665\n40 1aa8\nf7 579d\nda d9cc\nc0 d88a\nd3 7305\n3d 553\n43 b20f\n5d b351\n13 907\n10 8382\n2a 84c4\ne8 76e0\n11 90b\n17 917\n1b 947\n43 90a7\nf0 7720\n19 94b\n1f 957\nb1 65ab\n17 197\na0 6ea8\n75 1513\n1e 295e\nd0 f9a2\nf0 5f2a\n7d 1553\na 2ac4\n57 1917\n54 9392\n6e 94d4\n55 191b\n5d 195b\n9d e979\n5b 1967\nec 7cda\na1 4601\n22 ca4\nd3 732d\n3d 57b\n68 3cc0\ne7 761d\n8e e8dc\n59 196b\nb9 4543\n1b a3ed\n1 a2ab\nbd 4553\nb0 4d20\n91 e303\nab e445\n80 4888\n12 a9a6\n4c 1070\n32 f2e\n9b 4947\n39 87c3\n98 c3c2\nb2 c504\n99 494b\n97 4997\n4e 3874\na6 e416\n95 499b\n4c 3878\nff 7d77\n2c a67a\n8a 6ac4\n1b 3c7\n28 468\n71 158b\n9e 415c\n84 401a\n62 1e06\na4 ee18\nde d9dc\nc4 d89a\nd7 7315\nc2 7804\n9 a249\nf9 5563\n20 4a8\n1 a289\na 86c\n53 198f\n79 97e3\nd8 d3e2\nf2 d524\na2 4ca4\nbd 457b\n18 3c0\nc0 58a2\nda 59e4\n21 8429\nc9 d24b\n4a 98ee\n43 3227\n13 830f\n2d 8451\nd9 596b\nc6 dabc\n29 643\n9a cb6c\n80 ca2a\nb3 4f27\n3 a07\n90 4180\n7 a17\nb a2c5\nd1 7921\n94 41b0\nb a47\n82 c0a6\nf0 5f88\n98 41c0\nf5 d591\nfe ffd4\ne4 fe92\ned f453\ndc 59d8\n1d 2173\nf7 5f37\n69 1663\n7c 35d8\na9 66e9\nb1 6729\n8d ea79\n4b 1a67\n43 b285\nd8 534a\na9 4461\n2e a4de\n47 b295\ndc 535a\nbf 47d5\na5 4693\n8b 4a47\nad 6c5b\n69 9c49\n89 4a4b\n31 a509\nd0 7982\nbd 677b\n79 9769\nc8 fae0\n32 8d2e\n2b 2667\ne9 5663\nad 46d9\n3f a7f7\nfc 75d8\ncf 5a57\n7f 1ff7\nc1 f009\ncb 5a67\nc4 789a\nde 79dc\nc9 5a6b\n39 ad41\n1f abff\nb5 47b9\ndb 5bed\nc1 5aab\ne1 dc03\nfb dd45\ne7 7eb5\n51 1103\nef 7ef5\n59 1143\n10 9aa\na9 4449\n3b a567\n9d e159\n5b 1147\n28 8c4a\neb 5ee7\n11 2123\n66 9c94\nd3 f307\nc2 588c\naa cc66\n99 c14b\n13 2127\nd4 fbb0\nbd c75b\n3e 8dfe\n37 2737\n4 18\nf1 5723\nef fc5f\nb 8ae7\n3e a77c\nf7 7d37\n24 a63a\n24 418\n91 c109\nb 20e5\n40 980a\n5a 994c\n9e 695e\n3c a7da\n10 b22\n2a c64\n39 256b\nd8 fbe0\n3b 2767\nd9 5143\n6f b65d\ne 88d4\n36 d3e\ncc 7af0\n26 c94\n2a a66e\n41 38a1\n10 30a\n2a 44c\n9a 4bee\nb4 4d30\nf9 554b\nb 26d\n54 1390\nb 204d\ne5 5e11\n10 a180\n30 708\n3f 8fff\n59 9141\n9d 6153\ne2 deac\n8 a0e8\n16 b36\n80 6088\n7d b753\n6c 1cd8\nb9 656b\n14 23b0\n68 9c4a\na5 6c3b\n61 9c29\nbf 6d7d\n39 a761\n80 4aa0\n69 164b\n19 216b\nf3 5f2f\n16 831c\ncf 58d7\ndc 5978\n14 891a\nd 2253\n53 9b25\n97 6b37\nc aa52\n70 1580\nc5 50b1\n3f 275d\n25 261b\n44 1812\n5e 1954\ncc fad0\n36 8d1e\n2f 2657\n3d 8dd3\nd3 fb85\n29 266b\n8e e8fc\ncb 5247\nae ecfc\n94 ebba\neb 5647\nb7 ed37\n41 1aa1\nc5 d299\n6a 1cee\ne9 564b\na0 4620\n3f a7df\n94 413a\nd2 7924\n19 a369\n58 31e2\n8c ea52\nf0 5580\n5 2ab1\n90 6b2a\naa 6c6c\nd0 5102\ne3 d40f\nfd d551\nec fe52\nbd cdd3\na9 666b\n71 1503\n28 86ea\nfb 5de7\n95 e919\n53 1907\n9d e959\n5b 1947\n59 194b\n4a 1844\nfb 7d67\n24 c90\n28 a66a\n28 448\n98 4bea\nb2 4d2c\n20 488\na 84c\na aa6e\n8 824a\ndb 5947\n79 97c3\nd8 d3c2\nf2 d504\nc0 5882\nda 59c4\n21 8409\nd9 594b\n37 d97\nc0 7aa8\nd9 f963\n2 888c\n5f 93ff\n79 9541\nbd 6553\nb9 6563\n6a 3e64\n9a cbcc\n80 ca8a\nc0 5028\nb3 4f87\n68 9c42\n11 8309\nca 58c4\n71 9581\nb5 6593\nb1 65a3\n4e 985e\n9b 6967\n39 a7e3\n91 69ab\n2c acf8\n69 1643\nda db6c\nc0 da2a\n96 6b96\n52 9b84\nbd 6d79\n19 2163\nf3 5f27\nd4 51b0\nd3 fb8f\ned fcd1\n36 2716\ndf 515f\n3d aff9\n6b 9ee5\naf 6ef7\nca facc\n34 8d1a\n2d 2653\n73 9f25\nb7 6f37\n30 8d2a\n29 2663\n72 3786\n98 61c0\nf 2a57\nac ecf8\n6a 1ce6\ne9 5643\n3f a7d7\ncb 5a47\nc9 f8e1\n98 c34a\n12 2326\nf2 7786\nbf 67d5\n61 9681\na5 6693\nf6 5536\nf4 553a\n47 3a1d\nf9 7f69\n56 1116\n9b 6bed\n81 6aab\na1 ec03\nbb ed45\n91 4103\ned 7c79\nc6 7a96\n27 a61d\n95 4113\n1d 895b\n62 bc24\n48 bae2\nb1 450b\nb5 451b\n51 3b21\nb3 ef05\n1d 8153\na8 ee68\n1f 8157\n52 b904\n86 623c\n15 8193\ncf f2fd\n50 b9a0\n39 854b\nc5 5091\nfc 5d58\n57 1b9d\nc8 f268\n3f 8557\n29 aec3\n43 b005\ned 5e71\nf3 ff05\n5d 9153\ne8 fe68\n5f 9157\n5b 9167\n10 2988\n7f 9557\n8 2240\n92 6b24\n12 3a6\nf6 751e\n0 2280\n9b 4345\n2 8a6\n81 4203\nd 8a5b\na3 4607\n89 e041\na5 6c13\n47 9abf\nbf 6d55\n61 9c01\n55 31b3\n5c b952\na7 4617\nb6 4d3e\n6b 1665\n8a e8cc\nc7 5217\n9c c952\naa eccc\n90 eb8a\ne7 5617\n1f 29df\n98 c962\na4 ee30\ne 807e\n4d 9a7b\n38 a74a\n6c 3458\n83 68ad\n2a a4c6\nee 5474\nd4 5332\nd 8253\n54 331a\n6e 345c\n85 68b1\n99 e9eb\nd6 5336\nf 8257\n70 1fa0\n40 baa0\ne2 5c06\n29 864b\n2f 8657\n70 b582\n87 481f\n4d 9253\n4f 9257\nc aad2\n26 ac14\n4b 9267\n63 1e8d\n19 b43\n0 2a88\n6f 9657\nb8 c542\n84 cab0\n6d 965b\n6b 9667\n64 b49a\n7e b5dc\n7b bfcf\n95 c111\n9e eb54\n84 ea12\n45 3891\n66 96b4\naa 66c6\n69 966b\n8a 6064\nba edcc\nf7 5717\na0 ec8a\nb0 c582\nc7 581f\n39 f4b\ndd 5971\n17 8315\nc3 582f\nb1 4503\n13 a3ad\nb5 4513\nf9 7fe9\n56 1196\n2d ae71\na3 6e25\nd 73\n93 4907\n31 8783\n97 4917\nfb 5def\n35 8793\n12 b86\n39 d69\n95 491b\n82 6a84\n1e 29fe\n59 9349\n9d 635b\n13 387\n20 428\n1 a209\nf1 5523\n12 8b0e\n2c 8c50\n9a 696e\nbb 6747\n77 9735\n38 a7ea\n9e 6bfc\n84 6aba\n40 9aa8\n73 1fa5\nd3 f305\n3d 8553\n58 bbc2\n72 bd04\na6 663c\n35 8593\n1f 8957\ne 22d4\n15 899b\n7d 9553\na aac4\n5f 9957\n5b 9967\nf2 5504\nd8 53c2\nd3 f32d\n3d 857b\n59 996b\n26 8c9e\n2c 2678\nd2 d304\n53 99a7\n22 ca6\nbb 4745\na1 4603\n2b e65\n57 b3b7\nc5 5299\nc4 d812\nde d954\nd 8859\n8a 6a6e\n6b 1e65\nc7 5a17\n99 41cb\n18 ab4a\n2d 8653\n89 c8e1\n58 934a\nf6 fd1e\n12 8ba6\nab c645\n83 4807\n8d c8f1\n5c 935a\n87 4817\ncc d052\n34 8f38\n27 8e97\n1 aa01\nb 8a47\nf 8a57\n14 8b38\n7 8a97\nac cc52\n88 e2e2\n91 49a9\na2 e424\n6d 9653\n69 9663\n7c b5d8\na 826c\nc3 5827\n4f 9a57\n98 c942\na4 ee10\ne 805e\ncc 727a\n4d 9a5b\n4b 9a67\n44 b89a\n5e b9dc\n34 518\n49 9a6b\n19 23e3\n33 2525\nb9 c549\na8 ee4a\ndb f967\n4 8890\n0 8\n9b e367\n8a 48ec\nd1 5103\n1d a97b\n68 3640\n8a e046\n81 e2a9\ncb 704d\nb1 6f0b\n6d 9ef9\n62 9c06\n7b 9547\n65 9e11\nfe 757e\nfc dff8\n15 a193\n35 71b\n11 a1a3\n31 72b\n5 aa31\n9a 4944\n80 4802\n81 4089\n13 a1a7\nd0 dba8\n33 72f\n14 99a\n3f a557\n31 a5ab\n88 c8ea\n9b 6365\n81 6223\n71 15a9\nd aa7b\naa ccee\na3 6627\n49 9243\n80 caa0\n69 964b\nbf 67df\n7b 97cd\n61 968b\ndd 5951\nc3 580f\ncb d8ef\n2e 476\n4e b274\nb4 6592\n70 9580\nf a257\nb6 cd9c\naf 66d5\n1 81\n1e bdc\n4 a9a\n2f a657\n90 43a0\ne6 feb4\n50 9102\ne6 7c16\n2d a65b\ne0 5402\nfa 5544\nf9 ff63\n22 8e8c\n29 449\n9b 416d\n81 402b\na1 ee29\n3f a7dd\n25 a69b\nf8 7560\n21 489\n3b a7ed\n21 a6ab\n9d 6971\n83 682f\nfb fd67\n24 8c90\n3a a76c\nf3 7d27\n20 a62a\n20 408\nbb e767\n90 4baa\naa 4cec\nb1 6523\n88 e042\n7a 9d44\nbe 6d56\n60 9c02\nd1 71a9\n1c a9d0\n4c 127a\n2 a88e\n93 6927\n31 a7a3\ne3 5e07\n9 2043\n91 692b\n79 9543\n32 786\n35 a593\naf 64d7\nbc 6578\n51 9383\n6b 94c5\n52 190c\n31 a5a3\n1f a957\n89 4849\n1b a967\na4 ce10\n92 e304\n81 4889\n13 a9a7\n4d 1071\n33 f2f\n90 e308\n4e 12f6\n11 a9ab\na4 ecb8\n62 1ca6\nfb 5745\ne1 5603\nfd 7df3\n37 a797\nc2 580e\ndc 5950\na8 ccea\nbb 6765\na1 6623\n29 2e69\n49 18eb\nc8 5248\n5a b366\n1e 356\nd5 d911\n9b 6b6d\n81 6a2b\n69 9643\nbf 67d7\n7b 97c5\n61 9683\n34 a71a\n49 9a4b\n9b 4165\n81 4023\nb 806f\na1 ee21\neb 7cef\n3f a7d5\n25 a693\n43 9805\n87 6817\n48 3268\n42 988e\n5c 99d0\n58 1148\na2 46a6\n21 481\n83 6827\nde 51fc\nc4 50ba\nf1 d529\nfa ff6c\ne0 fe2a\nac ec52\n74 953a\n1f abdd\n5 aa9b\nbd 4d53\n1b abed\n1 aaab\n2 8c\nd9 7163\n26 8c94\n5d 995b\n2e a67e\nc8 72c0\n32 50e\nbc 4df2\n98 43c0\nc6 fabe\ne0 fc00\n1d 2979\n95 eb39\n53 1b27\n3d 8d59\n59 13eb\n73 152d\na7 e49f\n72 152e\na6 449c\n95 4311\n16 9b4\nd5 79b3\n1c a3f8\nbe 455e\n2d 8cd3\nc3 fa85\n40 3a22\n5a 3b64\nb6 459e\ne6 549c\n9d 4379\ne2 54ac\nf5 df3b\ne a0d6\nfa d7e4\ne0 d6a2\nb8 4f42\na7 e49d\n58 13ea\n72 152c\n8f 6a77\n4b 9a65\n74 3f92\n8e 40d4\nb6 459c\n46 3296\n6d 3479\n28 244a\ned d679\nb2 6d2e\nc5 7ab3\ndf 7bf5\nae 465e\n8e ea7e\nf2 55ac\na1 ec81\neb 566d\nd5 d311\n56 99b4\n9a 69c6\n7a 95c4\n60 9482\nbe 65d6\n2 80c\n6 81c\n5 803b\n1f 817d\ndd d35b\n57 3337\n5e 99fe\nec dcf8\ne5 7631\nc 825a\ndf 5957\n7d 97d3\na a246\n13 90d\n78 3742\n12 90e\n16 91e\n1a 94e\n1e 95e\nd0 d9a2\n46 90be\n46 181c\n42 182c\n28 a4ca\n4e 185c\n35 253b\nf7 7d1d\ndd 7bdb\n13 ba5\nb8 4d60\n77 159f\n4a 186c\n56 191e\n38 ad42\n4f 1275\n9a 494e\n38 87ca\n9e 495e\n3c 87da\n8a e2c6\n93 498d\nc9 d2e3\ne3 d425\n71 9529\nb5 653b\n10 2380\n4 8210\nca 586c\nac 66fa\n68 96e8\nd3 f327\nc2 58ac\n38 fc8\n26 8494\nd2 59ae\n48 10ca\n81 4283\n9b 43c5\nf0 dda8\ne9 76e1\n12 90c\n3 a0d\n52 3904\n7 a1d\n2b a66f\n27 c95\n44 181a\n5e 195c\ned 7cdb\n23 ca5\n24 43a\n3e 57c\n62 be26\n8f e8dd\n40 182a\n5a 196c\na7 e417\n96 499c\n4d 3879\n5 2839\na4 4c18\n36 ad36\n8b 4a4d\n8 284a\n83 e2a5\nb3 6f07\n6f 9ef5\n4a b0ec\n30 afaa\n8e 4a5e\n99 63cb\n55 93b9\nb3 650d\n83 4a8d\nd9 d3e3\nf3 d525\n25 8e91\ne2 fe26\n14 8310\nc0 582a\nda 596c\nbc 67fa\n78 97e8\ne3 f427\nd2 59ac\n48 10c8\ndc 5bf0\nc2 5aae\n60 b6a8\n38 2f48\nc2 782c\n2c 8ef0\n8a 6044\na ace\n24 c10\nb acf\n25 c11\n19 2969\n10 b02\n2a c44\nb2 6504\n98 63c2\n54 93b0\n72 150e\n36 8796\nfc 5df2\n13 b0f\n2d c51\n99 63c3\nb3 6505\n55 93b1\n4d 3259\n6d 1cdb\n7e b756\n3a 8fec\n20 8eaa\n98 6140\n16 2934\n9c c958\n65 bc91\n61 3409\nd6 d914\n15 21b1\n5 8819\n79 3feb\n93 412d\nc6 5a1e\n1a bce\n34 d10\n1b bcf\n35 d11\nba cf44\na0 ce02\n30 8fa0\n8e 60f4\n1d 23db\n37 251d\ncd d2f1\n92 69a6\ne2 5e86\n8 20c2\n14 8b10\na6 4e14\n78 35c8\n8 2ac2\n22 2c04\na 2ace\n24 2c10\n62 362c\n76 9d94\naa 46cc\n13 8b2f\n2d 8c71\n58 13ca\n72 150c\n13 2b0f\n2d 2c51\n35 5b3\n1c b58\n77 17b5\n65 3e19\n8 26a\n1a 2bce\n34 2d10\n56 9bbc\n9a 6bce\nb4 6d10\n4b 184d\n2a 8c64\n10 8b22\n2d cfb\n3e a776\nac 4658\nc3 7aad\n73 1d8d\n6a b6c6\n27 261d\nc4 50b8\ndd fbd9\n26 261e\nca 7044\n6c 9ef0\nb0 6f02\n12 8b26\n33 507\nd0 d980\n46 909c\n8e 4876\n2c 86f2\nfa f7e4\ne0 f6a2\n22 2c0e\n3c 2d50\n64 b4b0\n8f e27f\n10 a922\n46 9abe\n60 9c00\na4 6c12\nbe 6d54\na0 642a\nba 656c\n15 23b1\nb2 65ac\n1e 89d4\na 226c\n4 8892\nd3 51ad\ndc 7bf0\nc2 7aae\na5 cc93\nbf cdd5\nab 666d\nf4 7790\n99 c341\n1a 89e4\n0 88a2\nbe cdd6\naa 666e\n45 3839\na3 66ad\nf6 d594\nbd e759\n3e adfc\n7b 1747\n24 acba\ndd d1db\n57 31b7\n60 b600\nca 50e6\n42 180c\nc 8ad2\n26 8c14\n1a 21c6\n76 3d9e\n2b 6c5\n4c 9ad2\n66 9c14\n5a 31c6\n20 2620\n83 e00f\n9d e151\n7 281d\n17 291d\n2 2084\ne9 5ee9\n16 291e\n1b 21c7\ne8 5eea\na 20c4\nf1 5f29\nc 8252\n82 68ac\nca 52ce\ne4 5410\n97 699d\n9e 49d6\nce 5a7c\nc0 52a8\n13 ab0f\n2d ac51\n15 ab13\n2f ac55\n37 85b7\naf e65d\n16 abbe\n30 ad00\n17 abbf\n31 ad01\nc1 5a2b\ndb 5b6d\n16 291c\n1 2083\n1b 21c5\ne8 5ee8\ndb 5967\n8 826a\nd8 dbe0\n3b 767\n1a abce\n34 ad10\nc2 5826\n9 826b\n1b abcf\n35 ad11\nc5 5a3b\ndf 5b7d\n1c abd2\n36 ad14\n1c 8352\n92 69ac\n93 41ad\n88 42ca\na2 440c\nfd f573\n26 849c\nb0 cd80\ne4 76b8\nd0 f300\n51 b9a3\n3a 854e\nf8 776a\nbf ed77\n49 1ae1\n32 858e\nf0 77aa\n66 949c\nf0 dd80\nd6 5934\n1d 8379\na6 64be\n62 94ac\n20 c0a\n3a d4c\nb7 65bf\n73 95ad\n72 95ae\nd8 7bc2\n39 a749\nf2 7d04\nb5 4593\n52 332e\n6c 3470\n98 43ca\nb2 450c\ndc 53da\nf6 551c\n2d 8c59\n4f 3a57\n8 80e0\n7e bdd4\n64 bc92\n1b ab6f\n8b c8c7\n98 c968\n30 870a\n61 bca1\n6f b4d7\n7c b578\nf9 f741\n7a bde4\n60 bca2\nb6 65be\n72 95ac\n30 2d22\naf 667f\n6b 966d\n7c bfd2\n96 c114\n46 3894\n6a 966e\n66 969e\n15 a1b1\n35 739\nf 885d\ncd 7a79\n2d 2cfb\nac 6658\nb6 4fbe\nd0 5100\n91 41a1\ndd f973\n6 889c\nc4 7ab8\n3e 2dfc\n24 2cba\nbd 6759\naa 6ce4\n90 6ba2\n20 2400\n6 22be\n46 989c\n0 a200\n5a 996e\n97 69bf\n53 99ad\nd1 d30b\n4b 32e7\neb d44d\n52 99ae\n1 8283\n1b 83c5\n92 490c\n30 8788\n96 491c\n34 8798\n83 4a0d\n39 5e1\nc8 d048\n42 3024\n28 2ee2\n19 a349\nd2 7904\n70 b780\n95 4193\n87 4a1d\n86 4a1e\n91 638b\nab 64cd\nd6 591c\n74 9798\n55 11b1\ndc 5b70\nc2 5a2e\n32 ad04\n18 abc2\nf 22d5\n16 899c\nb 8a4d\ne 8a5e\n19 a3cb\nd2 7986\n33 a50d\n93 e98d\ndd 5379\n8f 6a7f\n4b 9a6d\n7a 1d44\n60 1c02\n80 ca00\n60 1488\na4 ec18\n62 1c06\n82 ca04\n54 b1b8\n2e 8efe\n48 9040\n8c 6052\n7e 1d54\n64 1c12\n84 ca10\n64 1498\n73 bd27\ne1 5c09\n37 ad9d\n66 1c16\n86 ca14\n58 b1c8\n68 1c42\n88 ca40\n94 e190\n35 dbb\nb4 4718\n7b bd67\ne9 5c49\n25 ac9b\n3f addd\nc aa5a\n70 1588\n32 d06\n52 bb04\n54 bb10\n3e 8ffe\n58 9140\n9c 6152\n74 1d12\n44 90b0\n88 60c2\n74 1598\nd9 7b69\n36 d16\n56 bb14\n5a 9144\n40 9002\n9e 6156\n95 63b9\n76 1d16\n46 90b4\n8a 60c6\n78 1d42\n48 90e0\n8c 60f2\nc7 5817\ne 825c\nbc ed58\n7a 1d46\n4a 90e4\n30 8fa2\n8e 60f6\n71 1fa9\n91 c9a3\n7a 954e\nb7 659f\n73 958d\n72 958e\n29 846b\n9e 4374\n84 4232\n81 e203\n9b e345\n2 a8a6\n9f ebff\nb9 ed41\n26 a49c\nfa 576c\ne0 562a\nb0 ed80\na6 4e16\n78 35ca\n76 b7b6\ne4 5698\n3a 2d44\n20 2c02\n60 3488\n22 2c06\n3e 2d54\n24 2c12\n64 3498\na1 6c09\nd2 f106\n26 2c16\n28 2c42\n2e 2c56\n25 2eb9\n70 3588\n6d 9651\n32 2d06\n6f 965d\n34 2d12\nb1 6d09\nd 20f3\ne7 5eb7\n36 2d16\n3c 2d52\naf 665f\n30 2d02\n6b 964d\ne0 7ca8\n81 caa3\n9b cbe5\n6a 964e\n19 abc9\n7c 97d0\n62 968e\nde 5954\nc4 5812\n2e a65e\n8c e052\n7e 9d54\n64 9c12\nb1 65a1\n8e e056\n66 9c16\n89 c0c9\n3 20a5\nc2 722c\nd6 d994\n2c 47a\n4c b278\nc6 7816\nd a25b\nc6 fab4\naf c65f\n30 8d02\n7e 377c\n64 363a\n26 ebc\n9c e152\na3 4e07\n75 35bb\nce faf4\n38 8d42\n6c 367a\nd2 fb04\n3c 8d52\n6d 3cfb\nec 7658\n86 689e\n42 988c\n20 a620\n97 699f\n53 998d\nf a85d\ne1 de29\ndb 7b67\n4 a90\nc9 fa49\n6 a89c\n76 9fbc\nba 6fce\nd4 7110\na8 466a\n74 9d32\n7 a89d\ne2 dc06\n81 e209\n2 a8ac\nd0 7120\nc1 d801\na 246\n3 a8ad\n6e 9474\n54 9332\n1e a95e\n4a 92ce\n64 9410\n98 63c8\n1a a96e\n23 a6a5\nb8 476a\n4e 9274\n26 ac16\ndb 73ef\nf5 7531\n28 ac42\n45 1091\n42 9a0c\n86 6a1e\n7d 3fdb\n97 411d\n2c ac52\ne1 742b\nfb 756d\n2e ac56\ne3 742f\nfd 7571\n1f 1fd\n5 bb\n34 ad12\nde 5b7e\n8f 6a5f\n4b 9a4d\n87 6a9f\n43 9a8d\n62 3c24\n48 3ae2\ne8 dc48\ndc 71fa\n4f 12f7\n91 e309\n12 a9ac\nf aa5d\n72 158c\ne aa5e\n20 c08\n37 8f95\n88 6048\n60 1c08\n24 c18\ne0 5c02\nfa 5d44\n2c c58\na7 6695\n30 d08\n47 9095\n98 6148\n91 e9ab\nce 52f6\nb0 4d08\nf0 5d08\n34 d18\nb4 4d18\n2c a652\n35 d19\n9d 6159\nba cf4c\na0 ce0a\n96 e9bc\nd3 5307\n83 eaa7\n6c b652\n75 1d19\n89 60c9\n2 a2a4\nf0 f582\n1f a3f5\n5 a2b3\nc2 7026\n5 91\n85 4091\nbc 4d58\nee 7c74\nd4 7b32\n17 b9d\n6 a2b4\nf4 f592\nb4 4592\n20 2c08\n24 2c18\n30 2d08\na8 eec8\n66 1eb6\nb0 6d08\n31 2d09\na9 eec9\n67 1eb7\n34 2d18\nac eed8\n6a 1ec6\nb4 6d18\n10 2102\nea 5ec6\n30 25a0\n47 1037\n89 e049\n71 35a1\n49 18e1\n34 25b0\n39 25e1\n2e c74\n14 b32\n29 8c49\n4 2098\n29 2641\n30 8d08\n52 3b06\n57 1137\n99 e149\n1e a156\nb7 451f\n15 a3b9\n23 e07\n93 4b07\n59 19e1\n2d 2651\n34 8d18\n56 3b16\n38 8d48\n82 c2a6\n5a 3b46\n39 8d49\n9b 4b47\nc2 f026\n5 8091\n3c 8d58\n5e 3b56\n45 9091\nb4 c592\n51 1301\n9c 49da\n21 ac09\n6 209c\n99 c161\n88 ea62\n6d 1653\n30 ad08\n66 9eb6\n6e 1654\n31 ad09\n34 ad18\n6a 9ec6\n35 ad19\n39 ad49\n6f 9ef7\n3d ad59\naf e67d\n30 ad20\n80 ca08\nb3 4f05\n48 9048\n8c 605a\n84 ca18\nb7 4f15\n94 e912\n38 ad60\n86 68be\n42 98ac\nc1 d209\n71 97a9\nb5 67bb\n68 1c4a\n2 2a24\n88 ca48\ne3 d6a5\na1 4e03\nbb 4f45\nb0 cf02\nca d044\n7a 95e4\n60 94a2\nbe 65f6\n58 9148\n9c 615a\n9b 69c5\n81 6883\n42 3804\ned 76db\n23 6a5\n72 3d8e\n27 6b5\ndc d1da\n56 31b6\n30 25a2\n22 8eae\n80 6002\n9a 6144\n3c 8ff0\n9 aa41\nf5 f51b\n2b 84e5\n11 83a3\nd2 592c\nc 82d0\n49 ba41\naf 64f7\n6b 94e5\n51 93a3\ndc d152\n44 9038\n88 604a\n37 8f97\n11 ab01\n94 4912\n19 ab41\n3b 85e5\n21 84a3\n4 818\n23 84a7\n95 eb93\naf ecd5\n60 1c22\n7a 1d64\n6b b44f\n82 e8a4\na2 4e2c\n52 b30e\n6c b450\n83 e8a5\na3 4e2d\n79 b54b\n90 e9a0\ne8 fc48\n90 e1aa\n75 3733\n7c 9dfa\nba e5ec\na0 e4aa\nb0 e5aa\n9a c9e4\n80 c8a2\n82 c8a6\nc2 d8a6\n96 6116\n52 9104\n38 8fc2\n90 c9a2\n6 80be\n92 c9a6\n8 80c2\n1 2a9\nd2 d9a6\n48 90c2\n82 e8a6\nbc 4f70\na2 4e2e\n90 e9a2\n11 183\n31 af81\n13 187\nbe effe\nd8 f140\nc8 504a\n33 af85\n15 193\n35 af91\n31 58b\n35 59b\n18 1c2\nab c44f\nc2 f8a4\n8 2068\ne2 5e2c\n38 afc0\n38 5c2\ne2 fca4\n58 11c2\n78 15c2\n88 40c2\na8 44c2\nc8 50c2\ne8 54c2\na c6\n2a aec4\n1a 1c6\n20 ae82\n3a afc4\n2a 4c6\n4a b2c4\n3a 5c6\n8c e0d8\n4a 10c6\n9c e1d8\n5a 11c6\nac e4d8\n6a 14c6\nbc e5d8\n7a 15c6\n8a 40c6\naa 44c6\nca 50c6\nea 54c6\nc 2078\ne6 5e3c\n22 ae8e\n3c afd0\nc2 7284\n2c 4d2\nb6 4db6\n4c b2d0\nd2 7384\n3c 5d2\nf2 7f84\n5c 11d2\n6c 14d2\n3d 87fb\nf6 5db6\n7c 15d2\n8c 40d2\nac 44d2\ncc 50d2\nec 54d2\n2e aed4\nd1 7329\n2e 4d6\n4e b2d4\ne1 7429\n3e 5d6\nf1 7f29\n4e 10d6\n5e 11d6\n6e 14d6\n7e 15d6\n8e 40d6\nae 44d6\nce 50d6\nee 54d6\n99 41c3\nf5 7d39\n2f a6dd\n9b 41c7\n9d 41d3\n9f 41d7\na8 44ca\nac 44da\n48 3ae0\nbb 45c7\nb9 45cb\nd 2053\ne7 5e17\nbf 45d7\n87 423f\n8 8e2\nbd 45db\n28 6c2\nb2 4fa6\n68 16c2\nbc 6df8\n18 21e2\nf2 5fa6\nc8 50e2\nc1 d881\na 2c6\ne1 dc81\n2a 6c6\ndd 51d3\ndf 51d7\nd9 51e3\n6f b6fd\ndb 51e7\nbc edda\n71 b701\nc3 d88d\n26 414\nc 2d2\n96 4bb6\ne3 dc8d\n2c 6d2\nb6 4fb6\n66 1414\n4c 12d2\n37 873d\nd6 5bb6\n6c 16d2\n71 3f09\n3 a027\n1c 21f2\nf6 5fb6\nea 54e6\ne8 54ea\nc5 d891\ne 2d6\ne5 dc91\n2e 6d6\nff 55d7\nc7 523f\n97 e995\n48 18e2\n31 ad8b\n6e 16d6\nfd 55db\n68 164a\n99 4be1\nfb 55e7\nf9 55eb\na6 443e\n30 728\n23 687\n69 bce3\ne8 f640\n3b 7cd\nf8 7762\n21 68b\n34 738\n27 697\n6d bcf3\nec f650\n3f 7dd\nfc 7772\n25 69b\n97 431f\n18 9c2\nd7 531f\n58 19c2\n88 48c2\n98 49c2\n2e aedc\n29 8449\nc8 58c2\n39 8549\nd8 59c2\na 8c6\n1a 9c6\n8c e8d8\n4a 18c6\n9c e9d8\n5a 19c6\n8a 48c6\n11 830b\n2b 844d\nca 58c6\n3c a5f2\nc 8d2\n1c 9d2\n4c 18d2\n5c 19d2\n8c 48d2\n9c 49d2\n2d 8459\ncc 58d2\n3d 8559\ndc 59d2\ne 8d6\nc1 7829\n1e 9d6\n4e 18d6\n5e 19d6\n8e 48d6\n15 831b\n2f 845d\nce 58d6\n8a c246\na3 4405\n89 42c3\n3a 7ee\nf1 dda9\na7 4415\n8d 42d3\na9 46cb\n12 8b2e\n2c 8c70\nbc 4778\naf 46d7\nad 46db\n22 c04\n8 ac2\n62 1c04\n48 1ac2\na2 4c04\n88 4ac2\n29 8649\ne2 5c04\nc8 5ac2\na ac6\n90 e988\ne7 5415\ncd 52d3\n92 e98c\ndc 5378\ncf 52d7\ne3 5425\nc9 52e3\nb2 ed8c\nfc 5778\nef 56d7\ned 56db\ne9 56eb\n17 211f\n31 583\n51 b381\n35 593\n55 b391\n13 987\nd8 f940\n22 a406\n11 98b\n65 341b\n7f 355d\n17 997\nc2 f80e\ndc f950\n26 a416\n15 99b\n5b 99e5\n41 98a3\n9f 69f7\nc0 d200\n70 97a0\nb4 67b2\ncc f8da\n2 88a4\n81 c201\n7b 1545\n61 1403\n86 68b6\n42 98a4\nc1 d201\n71 97a1\nb5 67b3\n54 91b0\n98 61c2\n8a cace\na4 cc10\n64 94b0\na8 64c2\nce f8de\n83 c205\na5 e419\n63 1407\nc3 d205\n73 97a5\nb7 67b7\nce 705e\n70 9f0a\n74 97b0\nb8 67c2\na9 4c49\n3b ad67\n6 88b4\n85 c211\n7f 1555\n65 1413\nf1 5f2b\na 20c6\n16 8b14\n2a 24c6\n36 8f14\n56 91b4\n9a 61c6\n8c cad2\na6 cc14\n66 94b4\naa 64c6\n87 c215\n10 a98a\n67 1417\n4e 905c\n34 8f1a\n99 696b\nc7 d215\n74 9f1a\n49 98e3\nc8 d240\n78 97e0\nbc 67f2\n57 39bd\n4a 98e4\n8e 68f6\nc9 d241\n79 97e1\nbd 67f3\n4b 98e7\nca d244\n59 39c1\n48 90c0\n8c 60d2\n68 94c0\nac 64d2\nd6 f91e\n8b c245\nad e459\n14 a9ba\n6b 1447\n38 8f4a\ncb d245\n7b 97e5\n61 96a3\nbf 67f7\n78 9f4a\n4d 98f3\ncc d250\n6d 1453\n3e 877c\n24 863a\nf7 5d37\n4f 98f7\nce d254\n43 388f\n5d 39d1\n2e 24d6\n3e 25d6\n59 9363\n6a 94c4\n50 9382\nae 64d6\n79 9763\n8f c255\n18 a9ca\n6f 1457\n3c 8f5a\ncf d255\n7c 9f5a\n62 b426\nd0 5308\n51 19ab\na5 443b\nbf 457d\nb9 45c3\nbd 45d3\n88 48ca\n41 1201\n8c 48da\n9b 49c7\naa e446\n99 49cb\n9f 49d7\nae e456\n9d 49db\nec fcda\n22 8ca4\na1 c601\n44 92b0\na2 6404\n88 62c2\nf0 fda8\nb a867\n74 1f12\n64 96b0\na8 66c2\ne3 d605\ne8 54e2\n65 9cb3\n7f 9df5\ne4 d610\nc9 5049\n5b b167\nfd 55d3\n5a 39ce\nf 2f5\nc1 f881\na 22c6\ne1 fc81\n2a 26c6\n46 92b4\n8a 62c6\n76 1f16\n68 b4c8\nf9 55e3\n29 8ce3\na8 c640\n37 2dbd\n69 9ce3\ne8 d640\nfa dd6c\ne0 dc2a\n2b 8ce7\naa c644\n39 2dc1\n6b 9ce7\nea d644\nc3 f88d\n26 2414\nc 22d2\ne3 fc8d\n2c 26d2\nc2 da04\n72 9fa4\nb6 6fb6\ncc 58da\n52 9ba6\neb d645\n6d 9cf3\nec d650\n6f 9cf7\nee d654\n34 8730\nc5 f891\ne 22d6\n1b bcd\nd8 7b62\n1 a8b\n6f 365d\n1f bdd\ndc 7b72\n5 a9b\nc3 d20d\n44 98b0\n88 68c2\n6 a8b4\n85 e211\nb9 6dc9\n45 9211\na 28c6\n55 9311\n1a 29c6\n46 98b4\nc5 d211\n8a 68c6\n87 e215\n4e b05c\n34 af1a\na a8e4\n89 e241\nda f96c\nc0 f82a\nc7 d21d\n48 98c0\n8c 68d2\n8b e245\n38 af4a\n1e 29d6\n66 16be\nf0 5fa2\n4a 98c4\n8e 68d6\n59 9b63\n8f e255\n3c af5a\na9 46c3\nad 46d3\nad 6cdb\n69 9cc9\n1 88b\nd8 7962\n12 a306\n1b 9cd\n22 aca4\na1 e601\na3 e605\n25 acb3\n3f adf5\na4 e610\ndc 7972\n5 89b\n16 a316\n1f 9dd\n27 acb7\n95 4b99\na6 e614\na7 e615\n29 ace3\na8 e640\nfa fd6c\ne0 fc2a\n80 4a88\n12 aba6\nab e645\n2d acf3\nac e650\n84 4a98\n16 abb6\naf e655\ne3 5c2d\nc9 5aeb\nfc 5ff8\n15 2193\na 80c6\ne2 7424\nc8 72e2\n4a 90c6\n2a a644\n19 bc9\n11 21a3\n3 8aaf\n1d 8bf1\n99 c1cb\n13 21a7\n5 8ab3\n1f 8bf5\na2 ee84\nc 80d2\ne2 fe84\n4c 90d2\nb1 ef29\ne 80d6\nd1 f329\n2e 84d6\n87 48bd\nec 76f2\nf1 ff29\n4e 90d6\nda 7b6e\n2e a654\n1d bd9\n31 25ab\nd9 51c3\n6f b6dd\ndb 51c7\ne8 54ca\nfb 55c7\nf9 55cb\nb 2ed\n88 60e2\nc3 5807\na 824c\n74 1d32\n99 61e3\n68 94c8\nac 64da\nb9 65eb\n70 1728\na5 e699\n63 1687\n2d 24d9\nb7 6dbd\n7b 17cd\n61 168b\n87 c21f\n8 88c2\n97 c31f\n18 89c2\n66 343c\n4c 32fa\nc7 d21f\n48 98c2\nd7 d31f\n58 99c2\na6 443c\n8c 42fa\n4a 98c6\ne 88d6\nc3 50af\ndd 51f1\ne6 7c34\n2d a679\ncc 7af2\n4e 98d6\na9 c6cb\n23 26a7\ne3 5405\nc9 52c3\n22 8c04\n8 8ac2\na6 6c16\n62 9c04\n48 9ac2\n94 c99a\n49 92c1\na7 6415\n8d 62d3\n96 c99e\n4b 92c5\n9c 6378\n8f 62d7\n6d b4d9\n69 96c9\nad 66db\n38 85e2\nb2 cdae\nab 66e7\n7e 9f7c\n64 9e3a\na9 66eb\n35 2593\n31 25a3\n17 2997\n81 c8a9\n7a 9f6c\n60 9e2a\nbe 6f7e\na 2ee\n24 430\nc1 d8a9\nc8 58ca\n30 8720\n22 a404\n11 989\n8 a2c2\na8 64e2\ne3 5c07\n41 baa1\n2a 864c\n79 95c1\nbd 65d3\nb9 65e3\n91 c9a9\n1a 3ee\n34 530\nd1 d9a9\n48 98c8\n41 3201\n8c 68da\nb5 6db9\na a8c6\n2a e4e\nd9 71e1\ne a8d6\nfa dfe4\ne0 dea2\n2e e5e\nc3 70af\ndd 71f1\ne9 56c3\n81 e8a9\n1f b55\n5 a13\n69 96c1\nb4 cd9a\nad 66d3\nb0 cdaa\na9 66e3\n2f c55\n15 b13\n97 c33f\n18 89e2\n8b 6ae7\na3 6c2d\n89 6aeb\n19 81c3\n1b 81c7\nb3 ef85\n1d 81d3\na8 eee8\n1f 81d7\nf a07d\ne9 de41\n28 84ca\nc2 f28c\n2c 84da\n3b 85c7\n39 85cb\nc8 f2e8\n3f 85d7\nd3 f38d\n3d 85db\n48 90e2\n4a 90e6\n2a a664\n19 be9\nf3 ff85\n5d 91d3\ne8 fee8\n5f 91d7\n59 91e3\n5b 91e7\n6a 94e6\n39 fe9\n68 94ea\n7f 95d7\n7d 95db\n7b 95e7\n79 95eb\n23 8405\n9 82c3\nd3 592f\n27 8415\nd 82d3\ne2 5c86\n29 86cb\n8b 484f\nf5 5d33\n3c 8778\n2f 86d7\ne6 5c96\n2d 86db\n8f 485f\n67 9415\n4d 92d3\n5c 9378\n4f 92d7\na7 6437\n63 9425\n49 92e3\n7c 9778\n6f 96d7\n6d 96db\ncf 585f\n69 96eb\n82 c206\n11 2983\n9e c354\n1f 89f7\n84 c212\n13 298f\n86 c216\n15 2993\n88 c242\n96 631c\n17 29bf\nc8 d242\nd6 731c\n57 39bf\nca d246\n8c c252\ncc d252\n8e c256\nce d256\n39 85c3\nd3 f385\n3d 85d3\n1b 2345\n1 2203\n8 88ca\n1f 2355\n5 2213\nc 88da\n1b 89c7\na2 c606\n68 94e2\nbe c754\n3f 8df7\na4 c612\n10 2b2a\n2a 2c6c\n7d 95d3\na6 c616\n91 6b89\ne6 d616\n79 95e3\na8 c642\n87 681f\n43 980d\nb6 671c\n37 2dbf\ne8 d642\nc6 7894\nd a2d9\n93 6187\n95 6bb9\nea d646\nc9 d243\n4a 98e6\nc3 faa7\nac c652\n47 981d\nec d652\n4b 326f\n5f 99d7\nae c656\n99 6bc9\nee d656\nda d344\nc0 d202\n5b 99e7\n91 4909\n88 e242\n93 490d\n8a e246\n7b 37ed\n61 36ab\n95 4919\n8c e252\n97 491d\n8e e256\n7f 37fd\n65 36bb\n29 86c3\n8b 4847\n8f 4857\n18 8b68\nb 8ac7\n23 8c0d\n9 8acb\n1c 8b78\nf 8ad7\n27 8c1d\nd 8adb\n6d 96d3\ncf 5857\n69 96e3\ncb 5867\nb1 4d09\na8 e642\nb5 4d19\nac e652\n5c 9b78\n4f 9ad7\n67 9c1d\n4d 9adb\na7 6c3f\n63 9c2d\n49 9aeb\n59 91c3\n5b 91c7\n68 94ca\n7b 95c7\n79 95cb\nec defa\n1f a1d7\ne9 fe41\nc9 5043\ndc dbd8\n56 3bb4\n3f 75f\nf3 dfa7\n19 a1e3\ne3 dc85\n39 76b\na4 c690\nd8 dbe8\n3b 76f\n2c a4da\n98 43c8\n2a a4e6\n3f a5d7\ne9 5443\na9 44c9\n3b a5e7\n39 a5eb\n69 96cb\ncb 584f\nd5 7933\n1c a378\nf a2d7\n23 a425\n9 a2e3\nb a2e7\nc8 70c8\nf5 7d33\n3c a778\n2f a6d7\ne6 7c96\n2d a6db\n4b 984d\n8f 685f\n2b a6e7\ne8 74c8\n79 95c3\n9a c9ec\n93 6325\n80 c8aa\nb3 4da7\n94 e99a\n49 b2c1\nda d9ec\n3d 573\nd3 7325\nc0 d8aa\n3a 87ec\n20 86aa\nf3 5da7\n41 3203\n5b 3345\n48 98ca\n5b 99c7\n28 a4e2\n39 a5e3\na3 6425\n89 62e3\n90 c9aa\nb2 c70e\ne3 fca5\nc a8da\n89 e243\na a8e6\n2a e6e\n9a e344\n80 e202\n89 48c9\n1b a9e7\na4 ce90\n3b f6f\n69 96c3\ncb 5847\nb3 6da7\n35 8f11\na7 6c1f\n63 9c0d\n49 9acb\nf3 7d2f\n2d a6d3\n4b 9845\n8f 6857\n29 a6e3\n8b 6867\n27 ac1d\nd aadb\nb aae7\nc8 78c8\n23 ac2d\n9 aaeb\n2 88ac\n81 c209\n7b 154d\n61 140b\n80 48a0\n69 144b\n2d 86d3\nf3 5d2f\n79 97e9\nbd 67fb\n29 8ceb\n22 2624\na8 c648\n69 9ceb\n62 3624\ne8 d648\n63 3625\n6a 9cec\nae 6cfe\n50 9baa\ne9 d649\n29 aceb\n66 1636\na8 e648\n8 e2\nca 5046\n28 aee0\n18 1e2\nda 5146\n38 afe0\n28 4e2\nea 5446\n48 b2e0\n38 5e2\n97 e195\n48 10e2\n58 11e2\nb7 e595\n68 14e2\n78 15e2\n88 40e2\n98 41e2\ne7 7cb7\n2e a6fc\na8 44e2\na e6\n2a aee4\n1a 1e6\n20 aea2\n3a afe4\n2a 4e6\n4a b2e4\n3a 5e6\n8c e0f8\n4a 10e6\n9c e1f8\n5a 11e6\nac e4f8\n6a 14e6\nbc e5f8\n7a 15e6\n9a 41e6\n30 a700\naa 44e6\nba 45e6\nec 745a\n22 424\n8 2e2\n97 e395\n62 1424\n48 12e2\nb7 e795\n68 16e2\na2 4424\n88 42e2\na8 46e2\n43 18ad\nee 745e\nc1 d8a1\na 2e6\n97 433f\n18 9e2\nd7 533f\n58 19e2\n88 48e2\n98 49e2\n2e aefc\n29 8469\n2 8286\nc8 58e2\n39 8569\n12 8386\nd8 59e2\n89 4243\na 8e6\n8c e8f8\nc9 5243\n4a 18e6\nec 7c5a\n22 c24\n8 ae2\nbf 47d7\n97 eb95\n62 1c24\n48 1ae2\nff 57d7\na2 4c24\n88 4ae2\n29 8669\ne2 5c24\nc8 5ae2\nee 7c5e\na ae6\ne2 5ea6\n8 20e2\n14 8b30\n28 24e2\n34 8f30\n90 c10a\na 20e6\n16 8b34\nb0 c50a\n2a 24e6\n36 8f34\nc0 da00\n81 ca01\nc1 da01\n83 ca05\n55 b1b9\nc3 da05\n73 9fa5\nb7 6fb7\nc4 da10\n74 9fb0\nd2 7104\nb8 6fc2\n85 ca11\nc5 da11\nc6 da14\n76 9fb4\nba 6fc6\n87 ca15\n59 b1c9\nc7 da15\nc8 da40\n89 ca41\nc9 da41\na8 c448\n22 2424\n8 22e2\n28 26e2\n1a 23ce\n34 2510\nd1 f989\n8b ca45\n5d b1f9\ncb da45\n61 9ea3\n7b 9fe5\nbf 6ff7\n8d ca51\n23 8eaf\n9b 6145\n3d 8ff1\n81 6003\ncd da51\nd4 fb98\n37 271f\n90 c30a\naa c44c\nc1 f8a1\na 22e6\n8f ca55\n25 8eb3\n3f 8ff5\n83 6007\ncf da55\n97 633f\n53 932d\n18 29e2\nc3 d22d\n88 68e2\na 8a4c\nd3 d32d\n98 69e2\n81 ea01\n83 ea05\n85 ea11\n87 ea15\n89 ea41\n8b ea45\n8d ea51\n8f ea55\n8 80e2\n18 81e2\n28 84e2\n1a 81e6\n2a 84e6\n3a 85e6\n9 e9\n30 2d82\n6b 96cd\naf 66df\nec f45a\n22 8424\n8 82e2\nee f45e\na 82e6\n2a 86e6\n87 c23f\n8 88e2\nc7 d23f\n48 98e2\nd7 d33f\n58 99e2\ne7 7c1d\n3 aa5\ncd 7adb\n89 c243\na 88e6\n99 c343\n1a 89e6\n62 9c24\na6 6c36\n48 9ae2\nee fc5e\na 8ae6\nb3 4707\n2 2226\n88 c24a\nbb 4747\n42 3226\nc8 d24a\nbe edfc\na4 ecba\nfb 5747\nd9 fbe1\n22 2626\na8 c64a\n62 3626\ne8 d64a\n88 e24a\nba e74c\n3b adef\na0 e60a\na8 e64a\n9a cb44\n80 ca02\n82 ca06\n54 b1ba\n9e cb54\n84 ca12\nde db54\nc4 da12\n86 ca16\n58 b1ca\nc6 da16\n88 ca42\nc8 da42\n8a ca46\n7b 1fed\n61 1eab\n5c b1fa\nca da46\ncc da52\nd7 f3bf\nf1 f501\nce da56\nd9 f3c3\nf3 f505\n47 329d\nc0 d220\ncc f8fa\n81 c221\n96 e394\n7b 1565\n61 1423\nc1 d221\nce f8fe\n83 c225\na5 e439\n63 1427\nc3 d225\nce 707e\n70 9f2a\n4f 32dd\nc8 d260\nd4 f93a\n89 c261\nc9 d261\nca d264\n28 44a\n59 39e1\nd6 f93e\n8b c265\n38 8f6a\ncb d265\n78 9f6a\nec fcfa\na1 c621\ne3 d625\n6f 36dd\ne8 d660\nf4 fd3a\na9 c661\ne9 d661\naa c664\n39 2de1\nea d664\neb d665\n81 e221\n6d 9e71\n83 e225\n6f 9e75\n4a b06c\n30 af2a\n8b e265\n38 af6a\na0 e620\n80 4208\n12 a326\n1 8ab\n1b 9ed\na1 e621\n91 4ba9\na2 e624\n82 420c\n3 8af\n1d 9f1\na3 e625\na8 e660\na9 e661\n80 4aa8\nab e665\nc0 da08\n19 2141\nf3 5f05\nce 70fc\n70 9fa8\nb4 6fba\n81 ca09\nc1 da09\ncf 70fd\n71 9fa9\nb5 6fbb\nc4 da18\n1d 2151\n3 200f\nf7 5f15\nd2 710c\n74 9fb8\nb8 6fca\n85 ca19\nc5 da19\n42 3a24\nc8 da48\ne1 5e03\n7 203f\nfb 5f45\n3 2a25\n89 ca49\n43 3a25\nc9 da49\n7 2a35\n8d ca59\n81 ea09\n43 1a07\n85 ea19\n47 1a37\n89 ea49\n4b 1a47\n8d ea59\n9a c364\n7 229f\n80 c222\n82 c226\n8a c266\na2 4e8c\nca d266\n8 20c8\ne2 5e8c\na2 c626\ne2 d626\n7 a2bd\n6f 36df\ne8 d662\nea d666\n9a e364\n89 48e9\n80 e222\n6c 9e72\n8b 48ed\n82 e226\n6e 9e76\n93 492d\n8a e266\n45 b831\n9a cb4c\n80 ca0a\nb3 4f07\n9e cb5c\n84 ca1a\nb7 4f17\nde db5c\nc4 da1a\n1d 2153\nf7 5f17\n2 2a26\n88 ca4a\nbb 4f47\n42 3a26\nc8 da4a\nfb 5f47\n46 3a36\ncc da5a\nf1 f509\nff 5f57\n81 c229\n96 e39c\n7b 156d\n61 142b\nc1 d229\n89 c269\n9e e3dc\n84 e29a\n69 146b\nc9 d269\na0 c628\na1 c629\na8 c668\ne8 d668\na9 c669\ne9 d669\n81 e229\n6d 9e79\na0 e628\na1 e629\na8 e668\na9 e669\n47 3a9d\nc0 da20\n81 ca21\nc1 da21\nc2 da24\n1e 23de\nd5 f999\ne3 fcad\n2c 26f2\n83 ca25\nc3 da25\n4f 3add\nc8 da60\ne9 fce9\n32 272e\n89 ca61\nc9 da61\nd0 fba8\n33 272f\nca da64\neb fced\nd1 fbab\n34 2732\n8b ca65\ncb da65\n81 ea21\n83 ea25\n8b ea65\n9a c36c\n80 c22a\nb3 4727\n88 c26a\nbb 4767\nc8 d26a\nfb 5767\nba c76c\na0 c62a\na8 c66a\ne8 d66a\n9a e36c\n80 e22a\nba cdee\nb3 6727\n6c 9e7a\n84 4890\n88 e26a\nbb 6767\nba e76c\na0 e62a\na4 4c90\na8 e66a\n9a cb64\n7 2a9f\n80 ca22\nda db64\n47 3a9f\nc0 da22\na3 6c2f\nbd 6d71\n82 ca26\nc2 da26\na5 6c33\n61 9c21\nbf 6d75\nf 2adf\n88 ca62\n4f 3adf\nc8 da62\n8a ca66\nca da66\nc0 d280\n81 c281\n7b 15c5\n61 1483\n18 360\nc1 d281\n8d e2d1\n58 1360\nc2 d284\n8e e2d4\n59 1363\n83 c285\na5 e499\n70 1528\n63 1487\n0 222\n1a 364\nc3 d285\n8f e2d5\n40 1222\n5a 1364\nce 70de\n70 9f8a\nc4 d290\n85 c291\n7f 15d5\n65 1493\nc5 d291\n42 122e\n12 a984\n5c 1370\n98 69ea\nc6 d294\n87 c295\n74 1538\n67 1497\n4 232\n1e 374\n99 69eb\nc7 d295\n44 1232\n5e 1374\n74 9f9a\na0 c680\ne0 d680\na1 c681\n38 760\ne1 d681\nad e6d1\n78 1760\na2 c684\n39 763\ne2 d684\nae e6d4\n79 1763\ne3 d685\naf e6d5\n60 1622\n7a 1764\ne4 d690\n31 ad83\n7b 176f\na5 c691\nd9 dbe9\n22 62e\n3c 770\ne5 d691\n62 162e\n32 ad84\n7c 1770\na6 c694\nb8 6dea\ne6 d694\n33 ad87\n7d 1773\n64 1632\n7e 1774\n81 e281\n12 8986\n18 2360\n6d 9ed1\n83 e285\n0 2222\n1a 2364\n6f 9ed5\n4a b0cc\n30 af8a\n4 2232\n1e 2374\n4e b0dc\n34 af9a\na0 e680\na1 e681\n32 8d86\n38 2760\na2 e684\n39 2763\na3 e685\n20 2622\n3a 2764\na4 e690\nd8 fbe8\n3b 276f\nd9 fbe9\n36 8d96\n22 262e\n3c 2770\nda fbec\nc0 faaa\n3d 2773\ndb fbed\nc1 faab\n24 2632\n3e 2774\n81 ca29\nc1 da29\nc8 da68\ne1 5e23\nfb 5f65\n89 ca69\nc9 da69\n81 ea29\n89 ea69\n67 16b5\nb2 4d8e\n9a c3c4\n80 c282\na9 c469\n82 c286\n9e c3d4\n84 c292\nde d3d4\nc4 d292\n89 6249\na 28ec\nad c479\n86 c296\ned d479\nc6 d296\n8b 624d\nc 28f0\nba c7c4\na0 c682\nfa d7c4\ne0 d682\na2 c686\ne2 d686\na7 663d\n28 2ce0\nbe c7d4\na4 c692\nfe d7d4\ne4 d692\na6 c696\ne6 d696\n9a e3c4\n80 e282\n6c 9ed2\na9 e469\n82 e286\n6e 9ed6\n9e e3d4\n69 1463\n84 e292\nad e479\n6b 1467\n86 e296\nba e7c4\na0 e682\na2 e686\nbe e7d4\na4 e692\n88 ca6a\nbb 4f67\nc8 da6a\nfb 5f67\n81 c289\nb 2c7\n18 368\nc1 d289\n4b 12c7\n8d e2d9\n58 1368\nb7 e73d\n38 ade0\na0 c688\ne0 d688\ne1 d689\n6b 16c7\nad e6d9\n78 1768\n58 b1e0\n81 e289\nb 22c7\n12 898e\n18 2368\n6d 9ed9\na0 e688\na1 e689\n23 607\nc0 da80\nc1 da81\n8d ead1\n58 1b60\nc2 da84\n8e ead4\n59 1b63\nc3 da85\n8f ead5\n40 1a22\n5a 1b64\n27 617\nc4 da90\n5b 1b6f\n85 ca91\n2 a2e\n1c b70\nc5 da91\n42 1a2e\n5c 1b70\nc6 da94\n5d 1b73\n87 ca95\n4 a32\n1e b74\nc7 da95\n44 1a32\n5e 1b74\n83 ea85\n0 2a22\n1a 2b64\n2 2a2e\n1c 2b70\n4 2a32\n1e 2b74\nba c7cc\na0 c68a\nfa d7cc\ne0 d68a\n9a e3cc\n80 e28a\n6c 9eda\nba e7cc\na0 e68a\n9a cbc4\n80 ca82\nda dbc4\nc0 da82\na3 6c8f\nbd 6dd1\na5 6c93\n61 9c81\nbf 6dd5\n9e cbd4\n84 ca92\nde dbd4\nc4 da92\nad cc79\n86 ca96\ned dc79\nc6 da96\nc0 d2a0\ne 25c\n98 4b40\n81 c2a1\n7b 15e5\n61 14a3\n59 3b41\nc2 d2a4\n80 4a02\n9a 4b44\na0 c6a0\na1 c6a1\na2 c6a4\ne2 d6a4\na0 4e02\nba 4f44\n81 e2a1\nb1 6f03\ncb 7045\n6d 9ef1\na0 e6a0\na1 e6a1\na2 e6a4\n23 60f\n3d 751\nc0 da88\n19 21c1\nf3 5f85\n81 ca89\nb ac7\n18 b68\n24 610\nc1 da89\n4b 1ac7\n8d ead9\n58 1b68\n27 61f\nc4 da98\n85 ca99\nf ad7\n1c b78\nc5 da99\n4f 1ad7\n5c 1b78\n81 ea89\nb 2ac7\n18 2b68\nf 2ad7\n1c 2b78\n9a c3e4\n80 c2a2\n58 3b42\nba c7e4\na0 c6a2\ne2 d6a6\nba 4f46\n9a e3e4\n80 e2a2\nca 7046\n6c 9ef2\nba e7e4\na0 e6a2\n9e cbdc\n84 ca9a\nc4 5038\nb7 4f97\nde dbdc\nc4 da9a\nae 4e74\n1d 21d3\nf7 5f97\na0 c6a8\ne0 d6a8\n4a b066\nb8 4f48\na0 e6a8\n23 627\nc0 daa0\ne a5c\nc2 daa4\nc3 daa5\n6a b64c\n81 eaa1\n7c 357a\n6c b650\n83 eaa5\n7e 357e\nfa d7ec\ne0 d6aa\n23 ae85\nb8 4f4a\n9a e3ec\n80 e2aa\nb3 67a7\nca 704e\n6c 9efa\nba e7ec\na0 e6aa\n28 4c8\nb2 4dac\n58 11c8\n68 14c8\nf2 5dac\n78 15c8\n1a a1e6\n88 40c8\nef f45f\nb 82e7\nd8 51c8\nff f55f\n1b 83e7\n29 4c9\naa e6e6\nb3 4dad\n59 11c9\n62 3c0c\n48 3aca\n69 14c9\nea f6e6\nf3 5dad\n79 15c9\n1b a1e7\n89 40c9\n5b b1e7\nc9 50c9\nd9 51c9\ne2 7c0c\nc8 7aca\n9a cbe4\n80 caa2\n62 14ac\n82 caa6\nc d8\na7 e437\n96 49bc\n1c 1d8\n2c 4d8\n3c 5d8\n5c 11d8\n6c 14d8\nf6 5dbc\n7c 15d8\n1e a1f6\n8c 40d8\n9c 41d8\ndc 51d8\nda 716e\n1d 1d9\nfa 756e\n3d 5d9\n46 301c\n2c 2eda\n5d 11d9\n66 3c1c\n4c 3ada\n6d 14d9\nee f6f6\nf7 5dbd\n7d 15d9\n1f a1f7\n8d 40d9\ndd 51d9\ne6 7c1c\n2 aa4\ncc 7ada\n9c 41da\nd5 7393\nef 74d5\n90 438a\naa 44cc\n91 438b\nab 44cd\n94 439a\nae 44dc\n95 439b\naf 44dd\nba 45ce\na5 449b\nbf 45dd\n19 a363\n8 8e8\nbe 45de\nc8 50e8\nc9 50e9\n8 2c8\n28 6c8\nbe e55e\n48 12c8\n68 16c8\n7a b7e6\ne8 56c8\naf 6c7f\n51 9b2b\n6b 9c6d\nda d9c6\n9 2c9\n7c 1758\n93 4bad\n3a 87c6\nfa ddc6\n29 6c9\nb3 4fad\nbf e55f\n49 12c9\nd3 5bad\n7a 97c6\n69 16c9\ndc 51da\n5b b3e7\nc9 52c9\nd9 51e9\ne2 7c2c\nc8 7aea\nd8 51ea\nd4 539a\nee 54dc\nd5 539b\nef 54dd\nd0 53aa\nea 54ec\nd1 53ab\neb 54ed\n36 8d14\n1c 8bd2\nc 2d8\n2c 6d8\n4c 12d8\n6c 16d8\n3 a02d\n1e a3f6\n8c 42d8\nf 887d\n3e a7f6\nac 46d8\n7e b7f6\nec 56d8\n55 9b3b\n6f 9c7d\nca 726e\nde d9d6\nd 2d9\nfe ddd6\nea 766e\n2d 6d9\n4d 12d9\nd7 5bbd\n7e 97d6\n6d 16d9\n1d 21f9\nf7 5fbd\ne5 549b\nff 55dd\n59 b363\n48 18e8\n1f a3f7\n8d 42d9\nfe 55de\n10 300\n80 4aa2\n9a 4be4\n5f b3f7\ncd 52d9\nfa 55ee\n4d 3ad1\n3c 7d0\n22 68e\nfe 7774\ne4 7632\n27 69d\nb1 4f81\n26 69e\nca 50c4\nb0 4f82\n19 a343\n8 8c8\n59 b343\n48 18c8\n69 b443\n58 19c8\n3a 8dc6\n2a a444\n10 a302\n19 9c9\n40 b202\n5a b344\n49 18c9\nae 46fe\n7a 9dc6\n50 b302\n6a b444\n59 19c9\ne aa7e\n72 15ac\n5d b353\n4c 18d8\n6d b453\n5c 19d8\n54 b312\n6e b454\n5d 19d9\n67 169d\n17 21bd\nf1 5f81\n66 169e\n16 21be\nf0 5f82\na6 441c\n8c 42da\na0 448a\nba 45cc\na4 449a\nbe 45dc\n30 d80\naf 46dd\nae 46de\nd8 51e8\n8 ac8\nbe ed5e\n48 1ac8\n5a bbe6\nc8 5ac8\n9 ac9\n3a 8fc6\nbf ed5f\n49 1ac9\n7a 9fc6\ne6 541c\ncc 52da\nc9 52e9\ne2 542c\nc8 52ea\ne4 549a\nfe 55dc\ne0 54aa\nfa 55ec\nc ad8\n4c 1ad8\n1e abf6\n8c 4ad8\n5e bbf6\ncc 5ad8\nca 7a6e\nd ad9\ne1 fe29\n3e 8fd6\n4d 1ad9\n7e 9fd6\n70 1d80\nef 56dd\nee 56de\neb 56ed\nea 56ee\n13 a307\nd9 7963\n2 88c\n17 a317\ndd 7973\n6 89c\n6c 36d2\nd0 7922\na a2c6\n13 98d\n12 98e\n16 99e\n28 24c8\nb2 6dac\na8 64c8\n57 b317\n46 189c\n2e 8c76\n53 b327\nc1 5209\n42 18ac\nbc 47d2\n56 199e\nb8 47e2\n1 283\n1b 3c5\n9b e347\n8a 48cc\n82 e206\n8b 48cd\nf0 7702\n9f e357\n8e 48dc\n86 e216\n8f 48dd\nf4 7712\n4f 12f5\n9a 49ce\n53 1305\n9e 49de\n8 22c8\n28 26c8\nfa fdc6\n29 26c9\n1d 1fb\nb3 6fad\ndf f357\nce 58dc\nc6 f216\ncf 58dd\nc 22d8\n2c 26d8\nac 66d8\n15 ab3b\n2f ac7d\nfe fdd6\n2d 26d9\nb7 6fbd\nde 59de\n23 a407\n12 98c\n6 a9e\n8 28c8\n18 29c8\n88 68c8\n5e 9356\n9 28c9\n3a adc6\nde d356\n89 68c9\n63 b427\nd1 5309\n52 19ac\n46 1a9e\nab e447\n80 488a\n9a 49cc\naf e457\n84 489a\n9e 49dc\n8b 4acd\na4 4c10\n8a 4ace\n8f 4add\n8e 4ade\nb3 658d\n8 2ac8\n88 6ac8\n9 2ac9\n3a afc6\n89 6ac9\nef f457\nc4 589a\nde 59dc\nc 2ad8\n8c 6ad8\ncf 5add\nce 5ade\n9 80c9\n8d 60db\n49 90c9\n9d 61db\n59 91c9\n62 bc0c\n48 baca\n22 24ac\n23 24ad\n1c 81d8\n3e 2fd6\n5c 91d8\nb0 c520\n37 259d\nca f06e\nd 80d9\n36 259e\n4d 90d9\n5d 91d9\n66 bc1c\n4c bada\n33 25ad\n28 c40\ne afe\n32 25ae\nd8 51ca\nd0 538a\nea 54cc\nd1 538b\neb 54cd\nfa 55ce\nc 2f0\n88 60e8\n67 1c97\n74 1d38\n89 60e9\n6c b672\n75 1d39\ndb 59c5\nc1 5883\n8 82c8\n21 683\n3b 7c5\nf1 570b\n72 1dae\n61 1683\n7b 17c5\nc2 5884\n9 82c9\nf2 570c\n73 1daf\n58 91c8\n9c 61da\na7 641d\n8d 62db\n49 92c9\n99 61e9\nc7 7a35\n95 639b\n51 9389\naf 64dd\n90 63aa\naa 64ec\n86 4a3e\n25 693\n3f 7d5\n4c 92d8\n65 1693\n7f 17d5\n8 28e8\nca f26e\nc6 5894\nd 82d9\nf6 571c\n77 1dbf\n63 168d\n7c 17d0\n62 168e\n32 25ac\na0 c620\n27 269d\n1b 1cf\nb1 6f81\nb9 c763\n26 269e\n23 26ad\ne2 540c\nc8 52ca\n4b 986f\ne0 548a\nfa 55cc\neb 56cd\nea 56ce\n98 61e8\nc6 7a34\n8 8ac8\n21 e83\n3b fc5\n61 1e83\n7b 1fc5\na6 641c\n48 92c8\n8c 62da\n89 62e9\n75 1f39\n3e 855c\n24 841a\n55 b9b1\na0 64aa\nba 65ec\n6a 96cc\nae 66de\n8 e8\nab 66ed\naa 66ee\n45 38b9\nb8 47c2\n3b 8d67\n52 198e\n9 86b\n28 a4c8\n36 f16\ne3 de8d\n9 a0c9\n17 b17\n99 c961\n6 289c\n81 6209\n2 28ac\n3 28ad\n6e 1474\n54 1332\n16 299e\ndb f347\nca 58cc\nda 59ce\nc2 7884\n9 a2c9\n8f 68dd\n25 2693\n3f 27d5\n5f 1355\n45 1213\n8 a8c8\n21 2c83\n3b 2dc5\n46 1214\n9 a8c9\n91 6309\n12 29ac\neb f447\nc0 588a\nda 59cc\ncb 5acd\ne4 5c10\nca 5ace\n8 aac8\n21 2e83\n3b 2fc5\n9 aac9\nc aad8\n25 2e93\n3f 2fd5\n85 409b\n9f 41dd\nb2 ef8c\n1c 81da\n55 b393\n6f b4d5\n10 838a\n2a 84cc\ne8 76e8\ne9 76e9\n14 839a\n2e 84dc\nec 76f8\ned 76f9\nd0 f380\n3a 85ce\na9 4e61\n2e aede\nd4 f390\n3e 85de\n8d 60fb\n49 90e9\nf2 ff8c\n5c 91da\n62 bc2c\n48 baea\n58 91ea\n54 939a\n6e 94dc\n55 939b\n6f 94dd\naf 64ff\n51 93ab\n6b 94ed\n65 949b\n7f 95dd\n7e 95de\n7a 95ee\n22 840c\ndb 59c7\n8 82ca\n20 848a\n3a 85cc\n24 849a\n3e 85dc\nf4 5d3a\n2e 86de\n9c 61fa\n58 91e8\n64 949a\n7e 95dc\nbe 65fe\n60 94aa\n7a 95ec\n34 2d92\n6f 96dd\n6e 96de\n3 2205\na 88cc\n3f dd7\nc8 7ae8\n26 c96\nc9 7ae9\n7 2215\ne 88dc\ncc 7af8\ncc f872\nf 88dd\ncd 7af9\n47 3215\n4e 98dc\n4f 98dd\n8e 68fe\n43 3225\nc9 d249\n4a 98ec\n8f 68ff\n4b 98ed\n13 2305\n0 888a\n1a 89cc\n17 2315\n4 889a\n1e 89dc\nc8 fa62\nb 8acd\n24 8c10\na 8ace\nd9 53e9\ncc fa72\nf 8add\n1d 21d1\nf7 5f95\n3 208f\ne 8ade\n33 a58d\ndd 53f9\n57 3315\n44 989a\n5e 99dc\nd9 d349\n40 98aa\n9e 69fe\n53 3325\n5a 99ec\n4f 9add\n4e 9ade\na9 6c41\n8f 6aff\n4b 9aed\na8 6c42\n64 9c30\n4a 9aee\n58 91ca\naf 64df\n51 938b\n6b 94cd\n7a 95ce\ne3 dead\n9 a0e9\nf6 df9e\n1c a1da\n3b fcd\nf8 7f62\n21 e8b\nf3 dfad\n19 a1e9\n47 ba35\n14 a39a\n2e a4dc\n59 19eb\n6a b466\nd8 5348\n10 a3aa\n2a a4ec\n6 8a3e\n3a a5ee\nbe 65de\n60 948a\n7a 95cc\nf2 dfac\n18 a1e8\n26 c36\n46 ba34\nc2 78a4\nab 444f\n9 a2e9\n20 a4aa\n3a a5ec\nf4 7d3a\na9 4661\n2e a6de\n8f 68df\n4b 98cd\ne a8dc\n13 a185\ne0 dea8\nf a8dd\nea dc46\ne1 dea9\n47 1237\n89 e249\na a8ec\nb a8ed\n5c 9372\n6 80b6\n74 1f98\n53 3305\n40 988a\n9e 69de\n5a 99cc\n8f 6adf\n4b 9acd\n64 9c10\n4a 9ace\nf aadd\nea de46\n89 4a61\ne aade\nad 4c53\nb aaed\n9a cbec\n80 caaa\nb3 4fa7\nda dbec\n3d 773\nc0 daaa\nbd 6df9\n19 21e3\nf3 5fa7\n28 4e8\n38 5e8\n66 1e34\n48 10e8\nee 76de\n58 11e8\n68 14e8\n78 15e8\n88 40e8\na8 44e8\nb8 45e8\nc 2070\ne6 5e34\n29 4e9\n39 5e9\n67 1e35\n42 302c\n28 2eea\n59 11e9\n62 3c2c\n48 3aea\n69 14e9\n79 15e9\n89 40e9\na9 44e9\nb9 45e9\nd 2071\ne7 5e35\nc2 702c\na8 6eea\n8 2e8\n28 6e8\nbe e57e\n48 12e8\n68 16e8\na8 46e8\nf3 d70d\nb8 6dc2\n74 9db0\nc8 52e8\ne8 56e8\n16 211c\nda d9e6\n9 2e9\n3a 87e6\nfa dde6\n29 6e9\nbf e57f\n49 12e9\n7a 97e6\n69 16e9\na9 46e9\nb9 6dc3\n75 9db1\n29 a463\n18 9e8\n7a 9fcc\n60 9e8a\nbe 6fde\n69 b463\n58 19e8\n99 e363\n88 48e8\na9 e463\n98 49e8\nb9 c743\n3a 8de6\n2a a464\n10 a322\n19 9e9\n40 b222\n5a b364\n49 18e9\nf9 d743\n7a 9de6\n50 b322\n6a b464\n59 19e9\n90 e322\naa e464\n99 49e9\n8 ae8\na5 469b\nbf 47dd\nbe ed7e\n48 1ae8\ne5 569b\nff 57dd\n88 4ae8\nc8 5ae8\n9 ae9\n3a 8fe6\nbf ed7f\n49 1ae9\n7a 9fe6\nf2 5fac\n18 21e8\ncc da58\n46 3a34\n28 24e8\n38 25e8\nf3 5fad\n19 21e9\ncd da59\n47 3a35\n39 25e9\n2e c7c\n14 b3a\nc6 dabe\ne0 dc00\nd4 71b2\nc7 dabf\n2a 646\ne1 dc01\nd5 71b3\nc8 dac2\ne2 dc04\nd6 71b6\nc9 dac3\ne3 dc05\nd7 71b7\nca dace\ne4 dc10\nd8 71c2\n8b cacf\na5 cc11\n55 91b1\n99 61c3\ncb dacf\n2e 656\ne5 dc11\ncc dad2\ne6 dc14\nda 71c6\ncd dad3\ne7 dc15\n8f caff\na9 cc41\n59 91e1\n9d 61f3\ncf daff\ne9 dc41\ndd 71f3\n90 cb02\naa cc44\n40 90a2\n5a 91e4\n9e 61f6\nd0 db02\nea dc44\nde 71f6\nd1 db03\neb dc45\ndf 71f7\n93 cb0f\nad cc51\nd3 db0f\ned dc51\n94 cb12\nae cc54\nd4 db12\nee dc54\nd5 db13\nef dc55\n96 cbbe\nb0 cd00\ndf 59d7\nc 82da\n26 841c\ne4 7638\nd6 dbbe\nf0 dd00\n4c 92da\n66 941c\n97 cbbf\nb1 cd01\ne5 7639\nd7 dbbf\n3a 746\nf1 dd01\n98 cbc2\nb2 cd04\nd8 dbc2\nf2 dd04\n99 cbc3\nb3 cd05\n68 3ce0\ne7 763d\nd9 dbc3\nf3 dd05\nda dbce\nf4 dd10\ndb dbcf\n3e 756\nf5 dd11\n9c cbd2\nb6 cd14\ndc dbd2\nf6 dd14\n9d cbd3\nb7 cd15\ndd dbd3\nf7 dd15\n8 22e8\n28 26e8\n88 62e8\nd3 f30d\n3d 855b\n54 b9b0\na8 66e8\na3 cc0f\nbd cd51\nda f9e6\n9 22e9\na8 46c8\n3a a7e6\nfa fde6\n29 26e9\na5 cc13\nbf cd55\n9f 4bfd\n85 4abb\n18 29e8\n8f e2df\n10 a982\n5a 136e\n86 eabe\na0 ec00\n87 eabf\na1 ec01\n88 eac2\na2 ec04\n8a eace\na4 ec10\n8b eacf\na5 ec11\n8c ead2\na6 ec14\n8f eaff\na9 ec41\n93 eb0f\nad ec51\n96 ebbe\nb0 ed00\n97 ebbf\nb1 ed01\n98 ebc2\nb2 ed04\n99 ebc3\nb3 ed05\n9a ebce\nb4 ed10\n9b ebcf\nb5 ed11\n9c ebd2\nb6 ed14\n9d ebd3\n68 1c62\nb7 ed15\n8 2ae8\n61 9689\na5 669b\nbf 67dd\n88 6ae8\na3 ec0f\nbd ed51\n9 2ae9\n28 84e8\nd0 d30a\nea d44c\n4a 32e6\n38 85e8\naa 6e46\n66 9e34\n9 80e9\n29 84e9\n39 85e9\nab 6e47\n67 9e35\n42 b02c\n28 aeea\ndb 59e5\nc1 58a3\n8 82e8\na6 643c\n8c 62fa\n48 92e8\n61 16a3\n7b 17e5\nc2 58a4\n9 82e9\n1 2221\n8 88e8\n21 ca3\na0 4600\n3b de5\n11 2321\n18 89e8\n9 88e9\n19 89e9\n8 8ae8\n21 ea3\n3b fe5\n8c 6afa\na6 6c3c\n48 9ae8\n61 1ea3\n7b 1fe5\n9 8ae9\nc1 78a3\ndb 79e5\naa 444e\n8 a2e8\n21 26a3\n3b 27e5\n21 2ea3\nc1 d009\n3b 2fe5\nba cd44\na0 cc02\na2 cc06\nbe cd54\na4 cc12\nfe dd54\ne4 dc12\na6 cc16\ne6 dc16\naa cc46\na1 cea9\nae cc56\nee dc56\nb0 cd02\nfe 777c\ne4 763a\nbc cd52\n24 8c38\n17 8b97\nba ed44\na0 ec02\na2 ec06\nbe ed54\na4 ec12\na6 ec16\na8 ec42\n10 ab28\n3 aa87\nae ec56\n9f 41fd\n85 40bb\nb0 ed02\n26 a41e\nb4 ed12\ndb 5b45\nc1 5a03\n5a 3be4\n40 3aa2\ne0 dc08\nd4 71ba\n41 3aa3\n5b 3be5\n2a 64e\ne1 dc09\nd5 71bb\n5e 3bf4\n44 3ab2\ne4 dc18\nd8 71ca\nc7 7a17\n55 91b9\n99 61cb\n45 3ab3\n5f 3bf5\n2e 65e\ne5 dc19\n9 2ae3\n23 2c25\na9 cc49\n59 91e9\n9d 61fb\n49 3ae3\n63 3c25\ne9 dc49\ndd 71fb\ncf 7a57\n4d 3af3\n67 3c35\ned dc59\nd2 7b06\n6a 3ce4\n50 3ba2\ne9 7641\nf0 dd08\n51 3ba3\n6b 3ce5\n3a 74e\nf1 dd09\nd6 7b16\n6e 3cf4\n54 3bb2\ned 7651\nf4 dd18\n55 3bb3\n6f 3cf5\n3e 75e\nf5 dd19\nda 7b46\n58 3be2\n72 3d24\nf8 dd48\n19 2be3\n33 2d25\nb9 cd49\n59 3be3\n73 3d25\nf9 dd49\nee fc74\nd4 fb32\n17 8b9d\nde 7b56\n76 3d34\n5c 3bf2\nfc dd58\n2 202c\n57 9b9d\n1d 2bf3\n37 2d35\nbd cd59\n5d 3bf3\n77 3d35\nfd dd59\n3 202d\n5f 1bf7\na1 ec09\n63 1c07\na5 ec19\n67 1c37\na9 ec49\n6b 1c47\nad ec59\n6e 1cf6\ned 5653\nb0 ed08\n6f 1cf7\nee 5654\nb1 ed09\n72 1d06\nb4 ed18\n73 1d07\nb5 ed19\n77 1d37\nb9 ed49\n7f 9755\n65 9613\n7b 1d47\nbd ed59\nfa dd4c\n5a 3be6\ne0 dc0a\nfe dd5c\n5e 3bf6\ne4 dc1a\ne9 7643\n6a 3ce6\nf0 dd0a\n2 8a2c\n6 8a3c\n6d 9671\n32 2d26\nb8 cd4a\n72 3d26\nf8 dd4a\na 8a6c\n6d 9cd1\n53 9b8f\nee fc76\n17 8b9f\n76 3d36\nfc dd5a\n57 9b9f\n1c 2170\nf6 5f34\n2 202e\ne 8a7c\nba ed4c\na0 ec0a\nbe ed5c\na4 ec1a\n79 9fc3\nd7 7117\na8 ec4a\n1d abd1\n3 aa8f\n69 b641\nb4 ed1a\n67 3c9d\ne0 dc20\na1 cc21\n2a 666\ne1 dc21\n88 cae2\na2 cc24\n18 8340\nc8 dae2\ne2 dc24\n9c 6352\n58 9340\nf2 dd84\n56 3b9c\ne9 dc61\n90 cb22\n17 2b9f\naa cc64\nd0 db22\n57 3b9f\nea dc64\nd1 db23\neb dc65\n37 2d9d\nb0 cd20\n77 3d9d\nf0 dd20\nb1 cd21\nd 82fb\nc6 58b6\n27 843d\n3a 766\nf1 dd21\n26 2c9c\nb9 cd61\n15 833b\nce 58f6\n2f 847d\n67 3c9f\ne0 dc22\nfa dd64\na1 cc23\nbb cd65\ne1 dc23\nfb dd65\na0 ec20\na1 ec21\n88 eae2\na2 ec24\n18 a340\na9 ec61\nf2 fd84\n90 eb22\naa ec64\nb0 ed20\nb1 ed21\na1 ec23\nbb ed65\n27 2c9f\nba cd64\na0 cc22\nea dc66\nf a8fd\n26 843e\nba ed64\na0 ec22\naa ec66\nb0 ed22\n26 a43e\n95 4133\nca 7ac6\n2b a64d\n9d 4173\n9f 4177\nd3 db87\ne0 dc28\na1 cc29\nc3 7a27\n2a 66e\ne1 dc29\n82 ca86\na9 cc69\ncb 7a67\nc2 da86\ne9 dc69\nb1 cd29\nab ccc7\nb8 cd68\n13 8bad\neb dcc7\nf8 dd68\n92 cb86\nb9 cd69\nd2 db86\nf9 dd69\na1 ec29\n82 ea86\na9 ec69\na3 ec87\ned 5673\nb0 ed28\nee 5674\nb1 ed29\n9f 4375\n85 4233\n92 eb86\nb9 ed69\na9 6663\nb0 cd2a\n79 b741\nb8 cd6a\n2d 8cf1\n13 8baf\nf8 dd6a\nb1 6d03\n6d 9cf1\n53 9baf\nba ed6c\na0 ec2a\nd3 7127\nb5 4533\nb0 ed2a\nb8 ed6a\n2d acf1\n13 abaf\nb7 4537\nbd 4573\nbf 4577\na0 cc80\na2 cc84\n59 bb61\n38 76a\ne2 dc84\nf 827d\na4 cc90\nd1 7b21\n3b d6f\ne4 dc90\n96 eb9e\n7b 1d6f\na5 cc91\na6 cc94\nb3 cd85\ne7 76bd\nf3 dd85\na0 6628\nb4 cd90\ne8 76c8\ne0 7628\nf4 dd90\na1 6629\nb5 cd91\ne9 76c9\ne2 762c\nf6 dd94\na3 662d\nb7 cd95\ne3 762d\nf7 dd95\nec 5670\na2 ec84\nbf 4775\na5 4633\n71 1d23\na6 ec94\n47 18bf\nc6 521c\nfd 5771\ne3 562f\nb3 ed85\nfe 577c\ne4 563a\nb4 ed90\ne6 563e\nb6 ed94\na7 4637\naf 4677\nba cdc4\na0 cc82\nfa ddc4\ne0 dc82\na8 6660\na2 cc86\ne8 7660\ne2 dc86\nfe ddd4\nea 766c\ne4 dc92\nac 6670\na6 cc96\nec 7670\ne6 dc96\nba 676c\na0 662a\nb4 cd92\ne8 76ca\nec 5672\na2 ec86\na6 ec96\nc6 521e\ne0 dc88\ne4 dc98\na5 cc99\n2f cd7\n3c d78\nd4 7b38\nc7 7a97\na9 66c1\nb0 cd88\n89 6a63\n33 a70d\nf9 7d69\nd2 7b86\ne9 76c1\nf0 dd88\nb 8847\nad 66d1\nb4 cd98\n49 9a61\n8d 6a73\n37 a71d\nfd 7d79\nd6 7b96\nf 8857\n3e 7de\nf5 dd99\nce 7a74\n63 1c87\n70 1d28\na5 ec99\n2f 2cd7\n3c 2d78\nb4 ef38\na7 ee97\n72 1f26\ned 56d3\nb0 ed88\nee 56d4\nb1 ed89\n72 1d86\nb4 ed98\n73 1d87\nb5 ed99\na2 ce24\na0 cca0\ne0 dca0\n14 b1a\n2e c5c\n70 970a\na1 cca1\n2a 6e6\ne1 dca1\na2 cca4\n18 83c0\ne2 dca4\n9c 63d2\nb6 6514\n58 93c0\ne3 dca5\nb7 6515\n9d 63d3\n59 93c1\nb0 cda0\n26 84bc\nb1 cda1\n27 84bd\n17 39f\nee 7476\na2 eca4\nc2 522c\n18 a3c0\n11 18b\n31 af89\n15 19b\n35 af99\n3c 5d0\n22 48e\n33 58f\nf8 f548\n28 4ca\nb2 4dae\n48 b2c8\n68 14ca\na1 4683\nbb 47c5\nf2 5dae\n78 15ca\n88 40ca\nc8 50ca\na2 6e8c\nc da\n4b 12e5\n96 49be\n2c aed8\nb2 6f8c\n1c 1da\n3c afd8\n55 3393\n6f 34d5\nc2 728c\n2c 4da\n4c b2d8\nd2 738c\n3c 5da\n7c 15da\nfc 7d58\n18 be0\n8c 40da\ncc 50da\n73 15af\n9d 41db\naa 44ce\nae 44de\nd0 db08\n4a 3ae4\nbb 45cf\nbf 45df\n8 8ea\nc8 50ea\n22 40c\n8 2ca\nac 4cf0\n92 4bae\n28 6ca\n62 140c\n48 12ca\n26 8694\nec 5cf0\nd2 5bae\n68 16ca\n18 21ea\nf2 5fae\ndd 51db\nd9 51eb\nea 54ee\n26 41c\nc 2da\n2c 6da\n66 141c\n4c 12da\nf0 5d00\nd6 5bbe\n6c 16da\n1c 21fa\nf6 5fbe\nf7 df35\n1d a171\n3 a02f\nfb 55ef\n1b 3cd\nd8 7362\n1 28b\n7a 3de4\n60 3ca2\nf9 7741\n1f 3dd\ndc 7372\n5 29b\ne3 760f\n7e 3df4\n64 3cb2\nfd 7751\nfa 7766\n3d 7d1\n23 68f\n69 bceb\ne8 f648\n8 8ca\n18 9ca\n48 18ca\n81 4a83\n9b 4bc5\n58 19ca\n91 4b83\nab 4cc5\n5f 13dd\n45 129b\nc 8da\n1c 9da\n4c 18da\n5c 19da\na7 441d\n8d 42db\nab 46cf\n14 8b32\n2e 8c74\n30 d82\naf 46df\ne7 541d\ncd 52db\ne3 542d\nc9 52eb\n70 1d82\nef 56df\neb 56ef\nba cde4\na0 cca2\ne aa5c\nfa dde4\ne0 dca2\nc4 7a10\n2e c5e\na2 cca6\n32 8504\n18 83c2\ne2 dca6\nb6 6516\n72 9504\n58 93c2\n13 98f\nd8 f948\n17 99f\ndc f958\nb0 cda2\n26 84be\n46 189e\n2c 24da\n3c 25da\n57 199f\ne 87c\n48 90c8\n8c 60da\nd2 530c\n53 19af\n8a 48ce\n43 1205\n8e 48de\n9b 49cf\n9f 49df\nce 58de\n26 241c\nc 22da\n52 9bac\nb0 6d00\n96 6bbe\n2c 26da\n72 9fac\nd0 7100\nb6 6fbe\ndf 59df\ned 5cf3\n27 8697\n34 8738\na2 eca6\n92 e984\ndc 5370\nc2 522e\n32 a504\n18 a3c2\nc 28da\n1c 29da\na5 4c11\n8b 4acf\n8f 4adf\ncf 5adf\n7c 15d0\n62 148e\nc3 d885\n19 36b\n8 80ca\n48 90ca\n11 21ab\n1d 8bf9\na2 ee8c\nc 80da\nb0 c522\n37 259f\ne2 fe8c\n4c 90da\n33 25af\nd9 51cb\nea 54ce\nfb 55cf\n99 61eb\nc7 7a37\n50 938a\n6a 94cc\nae 64de\n79 976b\n1f 23dd\n98 c360\n5 229b\nba c764\na0 c622\n27 269f\na3 642d\n89 62eb\n75 1f3b\nab 66ef\n5c 19d0\n42 188e\n99 c963\n6 289e\n90 c922\n17 299f\ne6 de9e\nc a0da\n92 630c\n13 29af\nca 58ce\ndb 59cf\ne9 5ce3\n23 8687\n30 8728\n43 3205\n4a 98cc\n8e 68de\n59 9b6b\n41 988b\n5b 99cd\n9f 69df\ne5 5c11\ncb 5acf\n1b 234d\n1 220b\n19 81cb\n47 9a17\nb3 ef8d\n1d 81db\nc0 f280\n2a 84ce\ne8 76ea\nc4 f290\n2e 84de\nec 76fa\nd1 f381\n3b 85cf\nd5 f391\n3f 85df\n48 90ea\nf3 ff8d\n5d 91db\n59 91eb\n6a 94ee\n7f 95df\n7b 95ef\n23 840d\nc2 5886\n9 82cb\nc6 5896\n27 841d\nd 82db\n8d 4853\n67 941d\n4d 92db\n6f 96df\n6b 96ef\ne0 dca8\na1 cca9\nac 4652\nc3 7aa7\na9 66e1\nb0 cda8\n62 3404\n48 32c2\n33 a72d\nd2 7ba6\nb1 cda9\nee 747e\n7 2217\ne 88de\ndd 51f9\ne6 7c3c\ncc 7afa\n1b 89cf\n1f 89df\n47 3217\n4e 98de\n5f 99df\nda d34c\nc0 d20a\n5b 99ef\na1 eca9\n4e 1054\n34 f12\n25 8c11\nb 8acf\nf 8adf\n4f 9adf\n59 91cb\n6a 94ce\n7b 95cf\nf3 dfaf\n19 a1eb\n2a a4ee\n6b 96cf\ncd 5853\nc6 7896\n27 a41d\nd a2db\nf5 7d3b\n2f a6df\n43 3207\n4a 98ce\n5b 99cf\n89 4861\ne a8de\ndd 71f9\n80 4820\n1f a9df\nc9 584b\n65 9c11\n4b 9acf\nf aadf\nfa ddec\nf3 7725\ne0 dcaa\n8 ea\nca 504e\n28 aee8\n28 4ea\nea 544e\n48 b2e8\n38 5ea\na8 ee48\n66 1e36\n97 e19d\n48 10ea\n81 42a3\n9b 43e5\nb7 e59d\n68 14ea\na1 46a3\nbb 47e5\n78 15ea\n88 40ea\na8 44ea\nb8 45ea\nc 2072\ne6 5e36\nb 2acd\n22 42c\n8 2ea\n28 6ea\n97 e39d\n62 142c\n48 12ea\nb7 e79d\n68 16ea\na8 46ea\nf3 d70f\n74 9db2\n18 9ea\n88 48ea\n58 99c8\n9c 69da\n51 3301\nbf 47df\nff 57df\na0 ce00\na1 ce01\naf 64f5\n51 93a1\n95 63b3\na2 ce04\n52 93a4\n96 63b6\n74 b5b8\n8 a040\ne2 de04\n9 a041\ne3 de05\nc9 5849\n5b b967\na5 ce11\na6 ce14\n56 93b4\n9a 63c6\n78 b5c8\nc a050\ne6 de14\nd a051\ne7 de15\na8 ce40\na9 ce41\naa ce44\n7c b5f8\nea de44\neb de45\nad ce51\ned de51\nef de55\n9b 4145\n81 4003\n83 4007\na9 ee41\n89 4043\nab ee45\n8b 4047\n8 80ea\n28 84ea\n38 85ea\n66 9e36\n1b 2365\n1 2223\n8 88ea\n2b 2465\n11 2323\n18 89ea\n22 8c2c\n8 8aea\nbe cf54\na4 ce12\na6 ce16\n78 b5ca\nc a052\ne6 de16\na8 ce42\naa ce46\n7c b5fa\nac ce52\nec de52\nae ce56\nee de56\ne0 de08\n16 819e\ne4 de18\n5b b96f\ne5 de19\n42 b82e\n5c b970\n62 3e24\ne8 de48\n23 2e25\na9 ce49\n63 3e25\ne9 de49\n67 3e35\ned de59\na1 ee09\n9b 414d\n81 400b\n89 404b\nbe cf5c\na4 ce1a\n9a e9cc\n80 e88a\nd7 5317\nfe df5c\ne4 de1a\n22 2e26\na8 ce4a\n9e e9fc\n84 e8ba\ndb 5347\n62 3e26\ne8 de4a\n26 2e36\nac ce5a\n88 e8ca\ndf 5357\n66 3e36\nec de5a\n27 2e9d\na0 ce20\na1 ce21\n8 a060\ne2 de24\n3e 27de\nf5 fd99\n9 a061\ne3 de25\n6f 3edd\ne8 de60\n1e 81f6\na9 ce61\ne9 de61\naa ce64\nea de64\neb de65\n6f 3edf\ne8 de62\naf 4c5f\nd aaf9\naa ce66\nc2 5a8c\nea de66\nf aafd\n95 413b\na1 ce29\na9 ce69\n2 a004\ne9 de69\n9f 437d\n85 423b\n51 9903\n8d 427b\n59 9943\nd6 539e\na8 ce6a\ndb 5367\nb5 453b\n10 380\na0 ce80\n6 a0bc\ne0 de80\na1 ce81\n38 f60\n7 a0bd\ne1 de81\na2 ce84\n8 a0c0\ne2 de84\n9 a0c1\ne3 de85\naf eed5\n60 1e22\n7a 1f64\na a0cc\ne4 de90\n7b 1f6f\na5 ce91\n22 e2e\n3c f70\nb a0cd\ne5 de91\n90 6320\n62 1e2e\n7c 1f70\na6 ce94\nc a0d0\ne6 de94\n98 c9ea\nab 6465\n91 6323\nd a0d1\ne7 de95\n99 c9eb\n92 6324\nbf 477d\na5 463b\nb 80cf\na1 ee81\n9b 41c5\n81 4083\n38 2f60\nd 80d3\na3 ee85\n90 4128\n83 4087\n20 2e22\n3a 2f64\n9f 41d5\n85 4093\n22 2e2e\n3c 2f70\nad 467b\nf6 579e\nfa dfc4\n6 a0be\ne0 de82\nc3 708f\ndd 71d1\n8 a0c2\ne2 de86\nc5 7093\ndf 71d5\nbe cfd4\na4 ce92\nfe dfd4\na a0ce\ne4 de92\ncd d079\na6 ce96\nc a0d2\ne6 de96\ne0 de88\ne1 de89\n6b 1ec7\nad eed9\n78 1f68\nd7 f33d\n58 b9e0\ne4 de98\na5 ce99\n9b e34d\n81 e20b\n2 a8ae\n1c a9f0\n2f ed7\n3c f78\ne5 de99\n83 6287\n90 6328\n6f 1ed7\n7c 1f78\ndb f34d\nc1 f20b\n42 b8ae\n5c b9f0\na1 ee89\n2b 2ec7\n38 2f68\na0 cea0\na1 cea1\na2 cea4\n8 a0e0\ne2 dea4\n9 a0e1\ne3 dea5\nb 80ef\na1 eea1\n9b 41e5\n81 40a3\nb2 4fae\ncc 50f0\ndc 51f2\nde 51f6\n74 b710\nd4 53b2\nee 54f4\nec 54f8\ned 54f9\n1e 8bde\nfe 55f6\nfd 55f9\nc4 fa90\n27 2617\n2e 8cde\nfc 55fa\nc2 50ae\ndc 51f0\nc4 50b2\nde 51f4\ne4 54b2\nfe 55f4\nfc 55f8\ned 56f9\nec 56fa\ne3 54af\nfd 55f1\nd1 fb29\n2e 8cd6\nfc 55f2\n4f 3ad5\ndd f373\n6 829c\ncc 58f8\nd4 f332\nee f474\n17 839d\ndd 59f9\na6 eebe\nc0 f000\na7 eebf\nc1 f001\na8 eec2\nc2 f004\naa eece\nc4 f010\nab eecf\nc5 f011\nac eed2\nc6 f014\naf eeff\nc9 f041\nb3 ef0f\ncd f051\nb6 efbe\nd0 f100\nb7 efbf\nd1 f101\nb8 efc2\nd2 f104\nb9 efc3\nd3 f105\nba efce\nd4 f110\nbb efcf\nd5 f111\nbc efd2\nd6 f114\nbd efd3\nd7 f115\nbf efff\nd9 f141\nc1 f003\ndb f145\nbb 6fed\na1 6eab\nc3 f00f\ndd f151\nc5 f013\ndf f155\n61 9ea9\nbf 6ffd\na5 6ebb\nc6 f2be\ne0 f400\nc7 f2bf\ne1 f401\nc8 f2c2\nd1 5989\ne2 f404\nba cfe4\na0 cea2\n83 60af\n9d 61f1\nca f2ce\ne4 f410\ncb f2cf\ne5 f411\n8 a0e2\ne2 dea6\nc5 70b3\ndf 71f5\ncc f2d2\nd5 5999\ne6 f414\ncf f2ff\n50 b9a2\ne9 f441\nd0 f302\nd9 59c9\nea f444\nd3 f30f\n54 b9b2\ned f451\nd6 f3be\nf0 f500\nd8 f3c2\nf2 f504\ne2 54ae\nfc 55f0\nda f3ce\nf4 f510\ndb f3cf\nf5 f511\ndc f3d2\nf6 f514\ned 56f1\ndf f3ff\nf9 f541\ne1 f403\nd0 5988\nfb f545\n1d 81d1\nda f166\n3 808f\ndb 73ed\nc1 72ab\ned f473\n16 839c\ndc 59f8\ne3 f40f\nfd f551\ndc f172\n1f 81dd\n5 809b\n50 93a0\n94 63b2\nae 64f4\n82 60ae\n9c 61f0\n84 60b2\n40 90a0\n9e 61f4\n94 c9b8\n8d 62f1\n79 1f41\na6 6434\n48 92e0\n8c 62f2\n78 1f42\n4a 92e4\n8e 62f6\nbc ef58\n7a 1f46\n6c b4f8\n60 94a0\na4 64b2\nbe 65f4\n6a 96e4\nae 66f6\n49 38c1\nad 66f9\na2 64ae\nbc 65f0\nb4 cdb8\nad 66f1\n68 96e0\nac 66f2\n47 38bd\n32 8fae\n90 6102\n4c 90f0\n92 6106\n34 8fb2\n4e 90f4\n43 90af\n5d 91f1\n66 bc34\n4c baf2\nf2 ffa4\n5c 91f2\n45 90b3\n5f 91f5\n4e baf6\n5e 91f6\n54 93b2\nb2 6506\n6e 94f4\nb0 650a\n6c 94f8\n8e 42f6\n65 94b3\n7f 95f5\n7e 95f6\n4d 10f9\n7d 95f9\n7c 95fa\n42 90ae\n5c 91f0\n44 90b2\n5e 91f4\n98 c9ca\n91 6303\nab 6445\n12 29a6\n4d 92f1\naa 6446\n66 9434\n4c 92f2\n64 94b2\n7e 95f4\n7c 95f8\n9e 43f6\n52 93ae\nb0 6502\n6c 94f0\n7c 95f2\n92 6906\ncd d251\n4e 98f4\n82 422c\n45 98b3\nc4 d210\n5f 99f5\ndd d353\n5e 99f6\n5d 99f9\nda f144\nc0 f002\nde f154\nc4 f012\nc8 f042\n3e a75e\ncc f052\n34 af38\n27 ae97\nd0 f102\nd4 f112\nfa f544\ne0 f402\nd1 598b\ne2 f406\nb1 6f09\nfe f554\ne4 f412\nd5 599b\ne6 f416\nb5 6f19\ne8 f442\nd9 59cb\nea f446\ne1 f6a9\nb9 6f49\nec f452\n54 b338\n47 b297\ndd 59db\nee f456\nbd 6f59\n62 94ae\n7c 95f0\nc3 d20f\ndd d351\n44 98b2\n5e 99f4\n55 3331\n5c 99f8\nab 6c4d\n91 6b0b\n4d 9af9\ne a0f4\n77 179f\n57 b11f\nb8 4f60\n2e 67c\nf a0f5\n2f 67d\n3e afde\nb9 4f61\n3d 779\nf6 dfb6\n1c a1f2\ne6 dc94\n3c 77a\n77 b51f\nd8 5360\naf 4457\nd a2f1\n15 9b9\n26 a434\nc a2f2\nf a2f5\n2f a6f5\n37 dbd\n2e a6f6\n12 a3ae\n2c a4f0\n8d e251\ne a8f4\n77 1f9f\nf a8f5\nf aaf5\ne aaf6\n5 80b3\n1f 81f5\nd1 f109\ndf 5b57\nd8 f148\n13 18f\ne1 f409\nef 5e57\ne8 f448\n3d 5d1\nfa 7566\n23 48f\nf0 f508\nfe 5f56\nc f0\n2 ae\n1c 1f0\n12 3ae\n2c 4f0\n22 4ae\n3c 5f0\n42 10ae\n5c 11f0\n62 14ae\n7c 15f0\n72 3fae\n8c 40f0\n82 40ae\n9c 41f0\n92 43ae\nac 44f0\na2 44ae\nbc 45f0\nd f1\n3 af\n1d 1f1\n13 3af\n2d 4f1\n33 faf\n4d 10f1\n66 3c34\nec dc58\n4c 3af2\n53 13af\n6d 14f1\n73 3faf\n8d 40f1\n83 40af\n9d 41f1\n93 43af\nad 44f1\na3 44af\nbd 45f1\ne f4\n4 b2\n1e 1f4\n14 3b2\n2e 4f4\n24 4b2\n3e 5f4\n44 10b2\n5e 11f4\n54 13b2\n6e 14f4\n64 14b2\n7e 15f4\n74 3fb2\n8e 40f4\n84 40b2\n9e 41f4\n94 43b2\nae 44f4\na4 44b2\nbe 45f4\nf f5\nae cc5c\n94 cb1a\ne 2af6\n15 3b3\n2f 4f5\n25 4b3\n3f 5f5\nc 80f8\nce d05c\nb4 cf1a\n2e 2ef6\n25 2631\n2c 8cf8\nee dc5c\nd4 db1a\n4e 3af6\n90 610a\n4c 90f8\n75 3fb3\n8f 40f5\nd2 7bac\n3c dfa\n95 43b3\naf 44f5\nf2 7fac\n5c 11fa\n2c 6f0\n4c 12f0\n6c 16f0\n8c 42f0\nac 46f0\nec 56f0\n78 3dca\n2d 6f1\n98 49ca\n4d 12f1\nf8 7dca\nad 46f1\n2e 6f4\n11 a9a9\n4e 12f4\n31 ada9\n6e 16f4\n8e 42f4\nae 46f4\n91 e9a9\nce 52f4\nb1 eda9\nee 56f4\n7a 3dce\n2f 6f5\nda 79ce\n8f 42f5\nfa 7dce\naf 46f5\n76 153c\n5c 13fa\n8b 424d\nc 8f0\n9b 434d\n81 420b\n2 8ae\n1c 9f0\ndb 534d\nc1 520b\n42 18ae\n5c 19f0\n8c 48f0\n2 c\n82 48ae\n9c 49f0\n12 10c\n6 8294\ncc 58f0\n38 fca\n52 110c\nd 8f1\nf8 d74a\n72 3726\nd1 5ba9\ne2 f624\n5c 135a\n8d 48f1\n3 d\n35 791\nf2 7726\n13 10d\n1c 2b50\n2 2a0e\n7 8295\ncd 58f1\n29 ecb\n43 100d\n39 fcb\n53 110d\n8d 4251\ne 8f4\ncd 5251\n4e 18f4\n44 18b2\nc3 520f\ndd 5351\n5e 19f4\nf 8f5\n5 8b3\n84 4210\n1f 9f5\n45 18b3\nc4 5210\n5f 19f5\nc af0\nfa 5dce\n4c 1af0\n8c 4af0\n2 20c\nd af1\nfb 5dcf\n4d 1af1\n8d 4af1\n3 20d\ne af4\nfc 5dd2\n4e 1af4\nf af5\n22 24ae\n3c 25f0\n94 c118\ne 20f4\nb4 c518\n14 23b2\n2e 24f4\n24 24b2\n3e 25f4\nda f14c\nc0 f00a\n95 c119\nf 20f5\nc8 f04a\nd0 f10a\nd8 f14a\nc 22f0\n2c 26f0\n8c 62f0\n78 1f40\nac 66f0\n14 89b8\nd 22f1\nfc 75d2\nfa f54c\ne0 f40a\nb4 c718\n35 8dbb\n2e 26f4\n95 c9bb\n8e 62f4\n60 1e02\n7a 1f44\nb5 cdbb\nae 66f4\nfd 75d3\n36 8dbc\nb5 c719\n2f 26f5\ne8 f44a\nf0 f50a\n2 a22c\nf8 f54a\nc3 7827\na a26c\n8c 68f0\n2 200c\n82 68ae\n9c 69f0\nec 5ed0\n12 210c\nd 28f1\n5e 1376\n5c 335a\n8d 68f1\n3 200d\nde 5376\n94 c918\n8d 6251\ne 28f4\na7 4e9d\n95 c919\nf 28f5\n5 28b3\n84 6210\n1f 29f5\nc0 f800\na 2246\nc1 f801\nc2 f804\nc3 f805\nc4 f810\ne 2256\nc5 f811\nc6 f814\nc7 f815\nc9 f841\n25 ae1b\n3f af5d\ncb f845\ncc f850\n14 938\n7 897\ncd f851\nce f854\nb6 e734\na5 4cb9\ncf f855\nd0 f900\n1a 2346\nd1 f901\nd2 f904\nd3 f905\nd4 f910\n1e 2356\nd5 f911\nd6 f914\nd7 f915\nc 2af0\n8c 6af0\n2 220c\nd9 f941\nd 2af1\n8d 6af1\n3 220d\nc1 f803\ndb f945\n94 cb18\ne 2af4\nc3 f80f\ndd f951\nac e6f2\nb5 4db9\n95 cb19\nf 2af5\nc5 f813\ndf f955\nc 80f0\n12 83ae\n2c 84f0\n22 84ae\n3c 85f0\nd 80f1\n13 83af\n2d 84f1\ne 80f4\n4 80b2\n1e 81f4\n14 83b2\n2e 84f4\n24 84b2\n3e 85f4\nf 80f5\n15 83b3\n2f 84f5\n25 84b3\n3f 85f5\n2e aef6\nc 82f0\naa 6444\n90 6302\n4c 92f0\ne 82f4\n2e 86f4\n92 6306\n4e 92f4\nb2 6706\n6e 96f4\n5a b9ce\nf 82f5\n7a bdce\n2f 86f5\n9b c34d\n81 c20b\n2 88ae\n1c 89f0\ndb d34d\nc1 d20b\n42 98ae\n5c 99f0\n83 4287\n90 4328\n8d c251\ne 88f4\n83 c20f\n9d c351\n4 88b2\n1e 89f4\nf 88f5\n5 88b3\n84 c210\n1f 89f5\nd 8af1\n1b 21e5\n1 20a3\n91 6b03\nab 6c45\n4d 9af1\nf 8af5\n89 c0cb\n3 20a7\nae 4456\nc a2f0\ne a2f4\n2e a6f4\n8b e24d\nc a8f0\n2c e78\n75 1f9b\nd a8f1\n9 28e9\n5e 9376\n2d e79\nae 4c56\nc aaf0\naf 4c57\nd aaf1\ne aaf4\nc0 f020\nc1 f021\na8 eee2\nc2 f024\n38 a740\nc9 f061\nde 7bf6\n25 a63b\n3f a77d\nb0 ef22\nca f064\nd1 f121\nb8 efe2\nd2 f124\nb9 efe3\nd3 f125\nd9 f161\nc1 f023\ndb f165\ne1 f421\nc8 f2e2\nd1 59a9\ne2 f424\ne9 f461\nd0 f322\n13 838d\nd9 59e9\nea f464\nf1 f521\nd8 f3e2\nf2 f524\nd9 f3e3\nf3 f525\nf9 f561\n1b 81ed\n1 80ab\n1c 81f0\n2 80ae\ne1 f423\nd0 59a8\nfb f565\n1d 81f1\n3 80af\nda f944\nc0 f802\nc2 f806\nde f954\nc4 f812\nc6 f816\nc8 f842\n3e af5e\nca f846\n24 2630\nc1 faa9\ncc f852\nce f856\nd0 f902\n1 2209\nd2 f906\nd4 f912\n5 2219\nd6 f916\nd8 f942\n9 2249\nda f946\nb1 4dab\ndc f952\nd 2259\nde f956\n3 808d\nda f164\nc0 f022\n13 818d\nd0 f122\n3 2807\n23 848d\nfa f564\ne0 f422\n25 8491\nd1 59ab\ne2 f426\ne d6\nb1 6f29\n43 b2a7\n13 838f\n2d 84d1\nd9 59eb\nea f466\n16 116\nb9 6f69\n33 858d\nf0 f522\ne 225e\nc5 f819\ncd f859\nd0 f908\nd4 f918\ndd f959\nc3 f087\nd0 f128\n3 280d\nde 5b76\nd1 f129\ndf 5b77\ncb f0c7\nd8 f168\nb 284d\n13 1af\nd5 5113\n33 afad\nd3 f387\ne0 f428\nee 5e76\n3e 85d6\ne1 f429\nef 5e77\ndb f3c7\ne8 f468\n3d 5f1\n23 4af\ne3 f487\nf0 f528\nfe 5f76\nf1 f529\nff 5f77\neb f4c7\nf8 f568\n33 5af\nda f94c\nc0 f80a\nde f95c\nc4 f81a\nc8 f84a\n2 8824\ncc f85a\n85 c231\nd0 f90a\na 88e4\n89 c241\nd4 f91a\n8d c271\nd8 f94a\n12 8924\ndc f95a\nda f16c\nc0 f02a\nf3 7527\nc8 f06a\n24 490\nfb 7567\nd0 f12a\n1d 2951\n3 280f\nd8 f16a\nb 284f\n4d b0f1\n33 afaf\nfa f56c\ne0 f42a\ne8 f46a\nf0 f52a\nf8 f56a\nc0 f820\na 2266\nc1 f821\nc8 f860\n82 4204\n3 8a7\nc9 f861\n25 ae3b\n3f af7d\nca f864\ncb f865\nd0 f920\n1a 2366\nd1 f921\n1 8889\nd8 f960\nd9 f961\nc1 f823\ndb f965\nc2 f084\n63 1caf\n7d 1df1\ne2 560c\n59 3163\nc6 f094\n67 1cbf\ne6 561c\n5d 3173\nd0 f180\n71 1dab\nf0 5708\nd3 f185\nd4 f190\n75 1dbb\nf4 5718\nd6 f194\ne0 f480\ne2 f484\n79 3563\ne4 f490\n7b 356f\ne5 f491\ne6 f494\n7d 3573\nf0 f580\nf1 f581\nf3 f585\nf4 f590\nf5 f591\nf6 f594\n3 888d\nda f964\nc0 f822\n5 8891\nc2 f826\n38 af42\nb 88cd\nc8 f862\n3e af7e\nd 88d1\nca f866\n13 898d\nd0 f922\nfa f5c4\ne0 f482\ne2 f486\nb1 6f89\nfe f5d4\ne4 f492\ne6 f496\nb5 6f99\ncb f8c7\nd8 f968\n92 430c\n13 9af\nd0 f188\nd1 f189\ne0 f488\n14 2112\nee 5ed6\nf0 f588\nb a047\nfe 5fd6\nf1 f589\nff 5fd7\nc8 f86a\nd0 f92a\nd8 f96a\nfa f5cc\ne0 f48a\nf0 f58a\na4 4412\nbe 4554\n2 a2ac\n18 236a\nc2 f884\n8 2048\ne2 5e0c\nc 2058\ne6 5e1c\n1d 237b\nc7 f895\nd 2059\ne7 5e1d\n33 2507\nd0 f980\n82 c026\nf0 5f08\n1a 23c6\nd1 f981\n83 c027\nf1 5f09\n19 2149\nf3 5f0d\n37 2517\nd4 f990\n86 c036\nf4 5f18\n87 c037\nf5 5f19\n2c 247a\nd6 f994\n2d 247b\nd7 f995\n1d 2159\nf7 5f1d\nd2 f1a4\nf2 f5a4\nc2 f886\nc6 f896\nc 205a\ne6 5e1e\nd0 f982\nf0 5f0a\n1 2289\nd2 f986\n18 214a\nf2 5f0e\nd4 f992\nf4 5f1a\n5 2299\nd6 f996\n1c 215a\nf6 5f1e\ne 22de\nc5 f899\n4f 38d7\n5c 3978\n33 250f\nd0 f988\n37 251f\nd4 f998\nd1 f1a9\n6f 1455\n18 a9c8\n55 1313\nf1 f5a9\n38 adc8\n75 1713\nff 5ff7\nde f9dc\nc4 f89a\n89 c2c1\nd4 f99a\nd0 f1aa\n1d 29d1\n3 288f\nf0 f5aa\n92 c30e\nac c450\nc3 f8a5\n9 2069\ne3 5e2d\n39 afc1\n99 41e3\n2f a6fd\nbb 45e7\nb9 45eb\nd 2073\ne7 5e37\ncc 50f2\ndd 51f3\ndf 51f7\n75 b711\nee 54f6\n13 218d\nec 54fa\nff 55f7\nfd 55fb\n23 6a7\ne8 f660\na3 4425\n89 42e3\nab 46e7\na9 46eb\n90 e9a8\ne7 5435\ncd 52f3\n30 a528\n23 a487\ned 56fb\n22 a426\n90 4308\n11 9ab\n65 343b\n7f 357d\n99 e9c9\nd6 5314\n57 19b7\nb9 45e3\naa e466\n99 49eb\nfd 55f3\n17 839f\nee f476\ndd 59fb\n0 2a\n1a 16c\n3b 7e5\n21 6a3\nbb 4545\na1 4403\n1 a2a1\na3 4407\nb9 6de9\na9 4443\ncb f245\n9 a2e1\nab 4447\na9 46e3\na3 4c2d\n89 4aeb\n61 bca3\n7b bde5\ne0 f600\n52 b306\n41 188b\n5b 19cd\n62 bca4\ne1 f601\ne3 f605\n65 bcb3\n7f bdf5\ne4 f610\n66 bcb4\ne5 f611\n67 bcb7\nd5 5b99\ne6 f614\nb0 eda8\ned 56f3\n6a bce4\n50 bba2\ne9 f641\n6e bcf4\n54 bbb2\ned f651\ne7 5c3d\ncd 5afb\n50 93a2\n6a 94e4\nae 64f6\nad c6db\n27 26b7\n94 c9ba\na7 6435\n49 92e1\n8d 62f3\n69 96e9\nad 66fb\n68 94e0\nac 64f2\n59 99e9\n9d 69fb\n12 1ac\na7 cc1d\n8d cadb\n7 2ab7\n68 9c60\nac 6c72\nc2 f8a6\nfc 5f70\n8 206a\ne2 5e2e\n52 b104\n38 afc2\nb4 cdba\n69 96e1\nad 66f3\n49 9ae9\na7 6c3d\n8d 6afb\n19 81e3\n8e 62dc\n1b 81e7\n3b 85e7\n39 85eb\ne2 fea4\n4c 90f2\n4e 90f6\n2e a674\n1d bf9\nf3 ffa5\n5d 91f3\n5f 91f7\n6e 94f6\n3d ff9\n6c 94fa\n7f 95f7\n7d 95fb\ned f45b\n23 8425\n9 82e3\n6d 96fb\n39 85e3\nae 66dc\n9a c344\n80 c202\n1b 89e7\n98 c348\n12 2324\n19 89eb\n6c 94f2\n7d 95f3\ncd d253\n4e 98f6\nde d354\nc4 d212\n5f 99f7\ndc d358\n56 3334\n5d 99fb\nda f344\nc9 58c9\n5b b9e7\nc0 f202\ncb 58cd\nc2 f206\nd1 5909\nc8 f242\nd5 5919\ncc f252\n29 86e3\n8b 4867\n23 8c2d\n9 8aeb\n6d 96f3\nf1 5d09\ne8 f642\nf5 5d19\nec f652\nab 6c4f\n67 9c3d\n4d 9afb\n7c 3fd8\ne a0f6\ne5 dc39\n2e 67e\nb8 4f62\n3d a5fb\nb8 e542\n42 12ac\ndd 7373\n6 29c\n90 4b80\n27 a435\nd a2f3\nf a2f7\ncc 70d8\n2f a6f7\nec 74d8\ne6 7cb6\n2d a6fb\n2c a4f2\n8d e253\ne a8f6\n9e e354\n8d 48d9\n84 e212\n1f a9f7\n9c e358\n5a 1346\n1d a9fb\na 22ee\n24 2430\nc1 f8a9\n5f 1b55\n45 1a13\n2d a6f3\nf aaf7\ncc 78d8\n27 ac3d\nd aafb\n1a 23ee\n34 2530\nd1 f9a9\n6f 1c55\n55 1b13\nbb 454d\na1 440b\n61 bcab\n7b bded\ne0 f608\n62 bcac\ne1 f609\n32 5ac\nda f9ec\n3d 2573\nc0 f8aa\n75 9f11\nd0 f9aa\nda f34c\n5b b9ef\nc0 f20a\nc8 f24a\nfa f74c\n7b bdef\ne0 f60a\ne8 f64a\nfd 7773\n26 69c\nb0 4f80\na2 6ea4\nc f2\nce 5056\n2c aef0\nb2 6fa4\n1c 1f2\n22 aeae\nde 5156\n3c aff0\nc2 72a4\n2c 4f2\nee 5456\n4c b2f0\ne2 7ea4\n4c 10f2\nf2 7fa4\n5c 11f2\n6c 14f2\n7c 15f2\n8c 40f2\ndb 7bc7\ne8 7c68\n22 a60c\n9c 41f2\nac 44f2\ne f6\n2e aef4\n2e 4f6\n4e b2f4\n3e 5f6\n90 e108\n4e 10f6\n5e 11f6\nb0 e508\n6e 14f6\n7e 15f6\n9e 41f6\nfa 7d6c\ne0 7c2a\n34 a710\nae 44f6\nbe 45f6\nc3 d8ad\n26 434\nc 2f2\n66 1434\n4c 12f2\n6c 16f2\na6 4434\n8c 42f2\nac 46f2\n47 18bd\nc5 d8b1\ne 2f6\ne5 dcb1\n2e 6f6\n9b 434f\n1c 9f2\ndb 534f\n5c 19f2\n8c 48f2\n1c 150\n2 e\n9c 49f2\n2d 8479\n6 8296\ncc 58f2\n5c 1150\n42 100e\n3d 8579\n16 8396\ndc 59f2\ne8 7ec0\n52 110e\n8d 4253\ne 8f6\n90 e908\ncd 5253\n4e 18f6\n22 6ac\ne6 5eb6\nc 20f2\n18 8b40\n94 c11a\ne 20f6\n0 8a02\n1a 8b44\nb4 c51a\n2e 24f6\n20 8e02\n3a 8f44\n23 6ad\nc3 f8ad\nac c458\n26 2434\nc 22f2\nc5 f8b1\n94 c31a\nae c45c\ne 22f6\ne5 fcb1\nb4 c71a\n2e 26f6\n9b 634f\n57 933d\n1c 29f2\nc7 d23d\n48 98e0\n8c 68f2\ne 8a5c\nd7 d33d\n58 99e0\n9c 69f2\n8d 6253\n94 c91a\n49 9241\ne 28f6\na7 4e9f\n2b 8c4f\nc1 fa01\n2d 8c53\nc3 fa05\n2f 8c5f\nc5 fa11\nc7 fa15\nc9 fa41\ncb fa45\ncd fa51\ncf fa55\na2 eea4\nc 80f2\nb2 efa4\n1c 81f2\nc2 f2a4\n2c 84f2\nd2 f3a4\n3c 85f2\n2e 84f6\n3e 85f6\nd f9\n26 8434\nc 82f2\ne 82f6\n2e 86f6\n8d c253\ne 88f6\n9d c353\n1e 89f6\n8f e0dd\n40 102a\n5a 116c\ne0 f620\ne1 f621\ne3 f625\ne9 f661\nc0 5aa8\neb f665\n8b e24f\nc a8f2\n2c e7a\n4e 125c\nd8 5b40\n14 ab92\n2e acd4\n0 a880\n4a 126c\n10 aba2\na9 e641\n2a ace4\nda f364\n3 828d\nc9 58e9\nc0 f222\ncb 58ed\n5 8291\nc2 f226\nd 82d1\nd3 592d\nca f266\n1 a881\n4b 126d\n11 aba3\n2b ace5\nf1 5d29\n2b 86cd\ne8 f662\nc1 fa09\nc5 fa19\ncd fa59\ne0 f628\n3e 87d6\ne1 f629\ne8 f668\n85 4ab1\n6e 165c\n4 203a\n1e 217c\nf8 5f40\n20 ac80\n6a 166c\n30 afa2\n4a b0e4\nda f36c\nc0 f22a\nc4 5890\nc8 f26a\nfa f76c\ne0 f62a\ne4 5c90\ne8 f66a\n66 169c\n2b 8c6f\nc1 fa21\n2d 8c73\nc3 fa25\n19 343\ncb fa65\n2b 84cf\nc1 f281\nbb 45c5\na1 4483\n52 9986\n58 3360\n2d 84d3\nc3 f285\nb0 4528\na3 4487\n40 3222\n5a 3364\n2f 84df\nc5 f291\nbf 45d5\na5 4493\ne0 f680\ne1 f681\n72 9d86\n78 3760\ne2 f684\ne3 f685\n60 3622\n7a 3764\ne4 f690\ne5 f691\ne6 f694\nda f3c4\nc0 f282\ne9 f469\nc2 f286\nde f3d4\nc4 f292\nfe f7d4\ne4 f692\n16 93c\n87 429d\n5e 1bfc\n44 1aba\n6f b677\n86 429e\n9 8843\n83 42ad\n1e 8bd6\nc1 fa29\nc1 f289\nbb 45cd\na1 448b\ne0 f688\ne1 f689\nfa f7cc\ne0 f68a\n2f 8cdf\nc5 fa91\nc7 fa95\ne0 f6a0\n2e 265c\nb8 6f40\ne2 f6a4\na0 6e02\nba 6f44\n50 1900\ncf 525d\n11 8301\n5c b9da\nce 525e\nc7 529d\nc6 529e\n49 9843\nc3 52ad\ndc 53f0\nc2 52ae\n24 2610\nc1 fa89\nc5 fa99\ne0 f6a8\n67 9e95\nb8 6f48\nfa f7ec\ne0 f6aa\n22 4ac\n77 15bd\n76 15be\n90 43aa\naa 44ec\n91 43ab\nab 44ed\ncc 50f8\ncd 50f9\ndc 51fa\nd4 53ba\nee 54fc\nd5 53bb\nef 54fd\nfe 55fe\n10 320\n76 15bc\na0 44aa\nba 45ec\nab 46ed\nbb 6dc7\n77 9db5\ndc 51f8\ncd 52f9\n23 a48d\ne6 543c\ncc 52fa\n3c a5d0\n22 a48e\ne4 54ba\nfe 55fc\n70 1da0\nef 56fd\nee 56fe\n13 a327\n81 4209\n2 8ac\n3 8ad\n68 36e2\na a2e6\n13 9ad\n57 b337\nc5 5219\n46 18bc\nbc 47f2\n5 293\n1f 3d5\n67 b437\nd5 5319\n56 19bc\nbd ed53\n47 1abd\n26 861c\ndf 5bd7\nec 5c78\n60 1c00\n46 1abe\n2d 8cf3\nac c650\nc3 faa5\nbe 457e\n37 25bd\n2c c50\n12 b0e\n36 25be\n50 93a8\n94 63ba\nae 64fc\na5 64bb\n61 94a9\nbf 65fd\n9a 4b4e\n36 25bc\n27 26bd\n1b 1ef\nb1 6fa1\n26 26be\n8d 62f9\nb 8067\n79 1f49\n59 b9c1\n60 94a8\na4 64ba\nbe 65fc\n30 2da0\naf 66fd\n6a 96ec\nae 66fe\n49 38c9\n85 6219\n6 28bc\n95 6319\n16 29bc\n20 2c00\n6 2abe\n17 1bd\n10 83aa\n2a 84ec\n11 83ab\n2b 84ed\n21 84ab\n3b 85ed\nd0 f3a0\n3a 85ee\n5d 91f9\n66 bc3c\n4c bafa\nf2 ffac\n5c 91fa\n65 94bb\n7f 95fd\n7e 95fe\n20 84aa\n3a 85ec\n5c 91f8\naa 644e\n66 943c\n4c 92fa\n64 94ba\n7e 95fc\nb3 670f\n34 2db2\n6f 96fd\n6e 96fe\n3 2225\n89 c249\na 88ec\nb 88ed\n45 98bb\nc4 d218\n5f 99fd\n99 c349\n13 2325\n0 88aa\n1a 89ec\nb 8aed\n24 8c30\na 8aee\ndd d359\n57 3335\n44 98ba\n5e 99fc\n93 6b0f\nad 6c51\n4f 9afd\n68 9c40\nac 6c52\n4e 9afe\n73 b5ad\n14 a3ba\n2e a4fc\ncb 52c7\nd8 5368\n2e a6fe\n4b 1247\n8d e259\ne a8fc\n28 ac40\ne aafe\nc f8\n2c 4f8\n4c 10f8\n5c 11f8\n6c 14f8\n7c 15f8\n8c 40f8\nac 44f8\n2d 4f9\n5d 11f9\n66 3c3c\n4c 3afa\n6d 14f9\n7d 15f9\n8d 40f9\nad 44f9\nc 2f8\n2c 6f8\n4c 12f8\n6c 16f8\nac 46f8\ncc 52f8\n22 a48c\nec 56f8\nde d9f6\nd 2f9\n3e 87f6\nfe ddf6\n2d 6f9\n4d 12f9\n7e 97f6\n6d 16f9\nad 46f9\nbd 6dd3\n79 9dc1\n1d a373\nc 8f8\n2d a473\n1c 9f8\n5d b373\n4c 18f8\n6d b473\n5c 19f8\n9d e373\n8c 48f8\nad e473\n9c 49f8\nbd c753\n3e 8df6\n54 b332\n6e b474\n5d 19f9\n94 e332\nae e474\n9d 49f9\nc af8\n4c 1af8\n8c 4af8\ncc 5af8\nd af9\n3e 8ff6\n4d 1af9\n7e 9ff6\nc 22f8\n2c 26f8\n8c 62f8\na 8066\n78 1f48\nd7 f31d\n58 b9c0\nac 66f8\nfe fdf6\n2d 26f9\nc8 fac2\ne2 fc04\nca face\ne4 fc10\ncb facf\n2e 2656\ne5 fc11\ncc fad2\ne6 fc14\nd3 fb0f\ned fc51\nd6 fbbe\nf0 fd00\nd7 fbbf\n3a 2746\nf1 fd01\nd8 fbc2\nf2 fd04\nd9 fbc3\nf3 fd05\nda fbce\nf4 fd10\ndb fbcf\n3e 2756\nf5 fd11\ndc fbd2\nf6 fd14\ndd fbd3\nf7 fd15\ndf fbff\nf9 fd41\n1b 89cd\nd8 f962\n1 888b\n55 b31b\n6f b45d\ne1 fc03\nfb fd45\n1d 89d1\n9 2269\nda f966\n3 888f\ndb 7bed\nc1 7aab\nc 2af8\n8c 6af8\ne3 fc0f\nfd fd51\ndc f972\n1f 89dd\n5 889b\ne5 fc13\nff fd55\nde f976\nd 2279\n7 889f\ndf 7bfd\nc5 7abb\n2c 84f8\nd4 d31a\nee d45c\n4e 32f6\nd 80f9\n2d 84f9\nc5 58b3\ndf 59f5\nc 82f8\n90 630a\naa 644c\n4c 92f8\n65 16b3\n7f 17f5\nc6 58b4\nd 82f9\n8f 487d\n5 2231\nc 88f8\n25 cb3\na4 4610\n3f df5\nd 88f9\n1d 89f9\nd 8af9\nc5 78b3\nae 445e\ndf 79f5\nc a2f8\n25 26b3\n3f 27f5\n25 2eb3\nc5 d019\n3f 2ff5\nfa fd44\ne0 fc02\ne2 fc06\nfe fd54\ne4 fc12\ne6 fc16\ne8 fc42\nec fc52\n54 bb38\n47 ba97\nee fc56\ndf 51fd\nc5 50bb\ne0 fc08\n0 8aa0\ne4 fc18\n1 8aa1\n2e 265e\ne5 fc19\n8 8ae0\nec fc58\n47 ba9d\n9 8ae1\ned fc59\nf0 fd08\n3a 274e\nf1 fd09\n10 8ba0\nf4 fd18\n3e 275e\n11 8ba1\nf5 fd19\nf8 fd48\n33 d8f\n18 8be0\nfc fd58\n37 d9f\n19 8be1\nfd fd59\nfa fd4c\ne0 fc0a\nfe fd5c\n0 8aa2\n1a 8be4\ne4 fc1a\ne8 fc4a\n22 8c24\n8 8ae2\nec fc5a\n47 ba9f\na5 c631\nf0 fd0a\n2 aa2c\n6 aa3c\nad c671\nf8 fd4a\na aa6c\ne aa7c\ne0 fc20\ncc 72f0\n36 53e\n2a 2666\ne1 fc21\ne9 fc61\nd0 fb22\n13 8b8d\nea fc64\nf0 fd20\n3a 2766\nf1 fd21\nf9 fd61\n80 c208\n1b 89ed\n1 88ab\n55 b33b\n6f b47d\ne1 fc23\nfb fd65\n82 c20c\n1d 89f1\n3 88af\n23 8c8d\nfa fd64\ne0 fc22\n25 8c91\ne2 fc26\n2c 8652\n43 baa7\n2d 8cd1\n13 8b8f\nea fc66\n33 8d8d\nf0 fd22\n6e b47e\nd3 fb87\ne0 fc28\n2a 266e\n3e 8dd6\ne1 fc29\ndb fbc7\ne8 fc68\ne5 5c13\nff 5d55\n2c 8658\n43 baad\nc2 fa86\ne9 fc69\na3 460d\n24 cb0\n90 c90a\n45 9231\na 28e6\n89 6243\neb fcc7\nf8 fd68\nb2 470c\n33 daf\nd2 fb86\nf9 fd69\nb3 470d\n34 db0\ne8 fc6a\n5d bbf1\nff 5d57\n2c 865a\n43 baaf\nf0 fd2a\nf8 fd6a\n38 276a\ne2 fc84\nf a27d\ne4 fc90\n2e 26d6\ne5 fc91\nf0 fd80\nd6 7934\n1d a379\n66 b49c\n3a 27c6\nf1 fd81\n67 b49d\nf3 fd85\nf4 fd90\nf6 fd94\nf7 fd95\ne2 fc86\nfe fdd4\ne4 fc92\ne6 fc96\ne0 fc88\ne4 fc98\n2e 26de\ne5 fc99\nf0 fd88\nb a847\n3a 27ce\nf1 fd89\nf4 fd98\nf a857\nfe fddc\ne4 fc9a\na9 c6c1\nf4 fd9a\n6 aabc\n77 15bf\naa 44ee\nbb 45ef\ncc 50fa\ndd 51fb\n67 16bf\na3 442d\n89 42eb\nd4 d310\n55 99b3\nab 46ef\nf6 d714\n77 9db7\ne7 543d\ncd 52fb\n3d a5d1\n23 a48f\n70 1da2\nef 56ff\nd6 531c\n57 19bf\n61 1c01\n47 1abf\n37 25bf\n50 93aa\n6a 94ec\nae 64fe\n27 26bf\na7 643d\n49 92e9\n8d 62fb\n79 1f4b\n30 2da2\n6b 96ed\naf 66ff\n21 2c01\n7 2abf\ne0 fca0\n36 5be\n14 2b1a\n2e 2c5c\nb0 c70a\n2a 26e6\ne1 fca1\nf0 fda0\n24 2c1a\n3e 2d5c\n66 b4bc\n3a 27e6\nf1 fda1\n5c 9b50\n42 9a0e\n25 2c1b\n3f 2d5d\n67 b4bd\nf2 fda4\n68 b4c0\n19 81eb\n47 9a37\nc0 f2a0\n2a 84ee\nd1 f3a1\n3b 85ef\ne2 feac\n4c 90fa\nf3 ffad\n5d 91fb\n6e 94fe\n7f 95ff\nc1 d829\na 26e\nab 644f\n67 943d\n4d 92fb\n6f 96ff\n89 c24b\n3 2227\na 88ee\n9a c34c\n80 c20a\n1b 89ef\nde d35c\nc4 d21a\n5f 99ff\nad 6c53\n69 9c41\n4f 9aff\nf7 dfbf\n1d a1fb\n98 e142\n22 eac\ne0 7622\n23 68d\nfa 7764\n2f a6ff\n8d e25b\ne a8fe\n9e e35c\n84 e21a\n1f a9ff\n29 ac41\nf aaff\nfa fde4\ne0 fca2\n2e 2c5e\ne2 fca6\nf0 fda2\n3e 2d5e\n66 b4be\n2e 2e7e\n37 5bd\n40 3000\n26 2ebe\n2a 26ee\ne1 fca9\n90 c98a\na3 6405\n45 92b1\n89 62c3\n27 6bd\nb1 4fa1\n26 6be\nca 50e4\nb0 4fa2\nfa fdec\ne0 fcaa\nf0 fdaa\n4e 3a7e\nf4 7f30\n5e 117e\n85 e8b9\nc2 5204\n43 18a7\nce 505e\n2c aef8\nc2 72ac\n2c 4fa\nee 545e\n4c b2f8\ne2 7eac\n4c 10fa\n6c 14fa\n7c 15fa\n8c 40fa\nac 44fa\n57 11bd\n60 3c00\n46 3abe\n26 43c\nc 2fa\n2c 6fa\n66 143c\n4c 12fa\n6c 16fa\nac 46fa\nf7 d71f\n78 9dc2\n56 11be\n4e 385c\nc 8fa\n1c 9fa\n41 1221\n8c 48fa\n5c 99d8\n55 3311\n1a a9c4\n0 a882\n4a 126e\n21 aea1\nc3 5007\neb fe45\na2 eeac\nc 80fa\nc2 f2ac\n2c 84fa\ndf 59f7\n26 843c\nc 82fa\nff 5df7\n2c 86fa\n8e 487e\ne1 fe09\ndb 514d\nc1 500b\n78 3fe0\n17 a19f\n9e 417e\n6c 1452\n83 48a7\n9f 417f\nbf 457f\n0 282\n1a 3c4\nb6 45be\nb7 45bf\n30 d22\naf 467f\n7b 9d47\nde 517e\ndf 517f\n20 e82\n3a fc4\nd6 51be\nce 785c\n6c b6d8\nd7 51bf\ncf 785d\n6d b6d9\n81 e883\n9b e9c5\ncb 526f\n3b a545\n10 988\n21 a403\n85 e893\n9f e9d5\n50 1922\ncf 527f\n14 998\n3f a555\n25 a413\n4b 90cf\ne1 fe81\ndb 51c5\nc1 5083\n37 79f\n4d 90d3\ne3 fe85\nd0 5128\nc3 5087\n4f 90df\ne5 fe91\ndf 51d5\nc5 5093\ndd 53f1\nc3 52af\ne0 5400\nc6 52be\n49 9863\ne1 fe89\n30 700\na0 4ea2\nba 4fe4\n50 1300\n21 8629\nc0 5aa2\nda 5be4\n70 1700\ne0 5ea2\nfa 5fe4\n78 1740\n19 9e3\n98 4340\n31 da3\nb0 4700\n39 de3\nb8 4740\n71 1da3\nf0 5700\n79 1de3\nf8 5740\n12 304\n0 202\n1a 344\n32 704\nd7 dbbd\n20 602\n3a 744\n15 a9b9\n52 1304\n1d a9f9\n40 1202\n5a 1344\n13 9a7\n92 4304\n1b 9e7\n80 4202\n9a 4344\n33 da7\nb2 4704\n3b de7\na0 4602\nba 4744\n95 e9b9\n53 19a7\nd2 5304\n5b 19e7\n9d e9f9\nc0 5202\nda 5344\nb5 edb9\n73 1da7\nf2 5704\n7b 1de7\nbd edf9\ne0 5602\nfa 5744\n14 310\n84 4ab2\n9e 4bf4\n2 20e\n1c 350\n8c 4af2\na6 4c34\nd1 db89\n34 710\n54 1310\nc4 5ab2\n25 8639\nde 5bf4\n42 120e\n5c 1350\ncc 5af2\n2d 8679\ne6 5c34\n62 160e\n93 4ba5\n7c 1750\n15 9b3\n94 4310\n1d 9f3\n82 420e\n9c 4350\n35 db3\nb4 4710\n3d df3\na2 460e\nd3 7ba5\nbc 4750\n5d 19f3\nc2 520e\ndc 5350\n7d 1df3\ne2 560e\nfc 5750\n16 314\n4 212\n1e 354\nd3 db8d\n36 714\ndb dbcd\nc1 da8b\n24 612\n3e 754\n19 a9c9\n56 1314\n44 1212\n5e 1354\n64 1612\n7e 1754\n17 9b7\n96 4314\n1f 9f7\n84 4212\n9e 4354\n37 db7\nb6 4714\n5f 19f7\nc4 5212\nde 5354\nb9 edc9\n77 1db7\nf6 5714\n3a ad6e\n7f 1df7\ne4 5612\nfe 5754\n30 780\n70 1780\n12 384\n32 784\n34 790\nb4 4790\n1d 8bf3\n37 8d35\n65 9c93\n7f 9dd5\n6b 366d\n36 794\n96 4394\nb6 4794\n67 9c97\n74 9d38\nb8 6d4a\n6d 3671\n10 b00\nfe 5dde\n50 1b00\n58 1b40\n90 4b00\n6 21c\n12 b04\n52 1b04\n40 1a02\n5a 1b44\n92 4b04\nd2 5b04\nc0 5a02\nda 5b44\n14 b10\n54 1b10\n42 1a0e\n5c 1b50\n94 4b10\n82 4a0e\n9c 4b50\nd4 5b10\nc2 5a0e\ndc 5b50\n16 b14\n56 1b14\n44 1a12\n5e 1b54\n96 4b14\n84 4a12\n9e 4b54\nd6 5b14\nc4 5a12\nde 5b54\n10 b80\n50 1b80\n2f 86df\nf5 5d3b\neb 7c67\n14 b90\nb9 4d4b\n94 4b90\n4b 3a6d\n16 b94\n19 abe9\nbb 4d4f\n96 4b94\n4d 3a71\n10 2300\n80 6aa2\n9a 6be4\nfe 75de\n30 2700\na0 6ea2\nba 6fe4\n11 29a3\n90 6300\n62 1e0e\n7c 1f50\n19 29e3\n98 6340\n31 2da3\nb0 6700\n39 2de3\nb8 6740\n19 89cb\n12 2304\n0 2202\n1a 2344\nd7 fbbd\n20 2602\n3a 2744\n99 c9cb\n13 29a7\n92 6304\n64 1e12\n7e 1f54\n1b 29e7\n80 6202\n9a 6344\nb9 cdcb\n33 2da7\nb2 6704\n3b 2de7\na0 6602\nba 6744\n14 2310\n84 6ab2\n40 9aa0\n9e 6bf4\n2 220e\n1c 2350\n48 9ae0\n8c 6af2\na6 6c34\nd1 fb89\n34 2710\na4 6eb2\n60 9ea0\nbe 6ff4\nd9 fbc9\n22 260e\n3c 2750\n35 2db3\nb4 6710\n1d 89db\n16 2314\n4 2212\n1e 2354\ndb fbcd\nc1 fa8b\n24 2612\n3e 2754\n9d c9db\n17 29b7\n96 6314\n1f 29f7\n40 9200\n84 6212\n9e 6354\nbd cddb\n37 2db7\nb6 6714\n3f 2df7\n60 9600\na4 6612\nbe 6754\n6a 3cc4\n50 3b82\n30 2780\n32 2784\n92 6384\n64 1e92\n7e 1fd4\nb2 6784\n14 2390\n75 9539\nb9 654b\n34 2790\nb4 6790\n16 2394\n36 2794\nb6 6794\n10 2b00\n90 6b00\n6 221c\n12 2b04\n92 6b04\n14 2b10\n94 6b10\n16 2b14\n96 6b14\n10 2b80\n71 9d29\nb5 6d3b\n90 6b80\n99 c361\n6 229c\n12 2b84\n2d 245b\n92 6b84\n69 9449\nad 645b\n14 2b90\n94 6b90\n16 2b94\n41 b2a1\ne3 5407\n10 8300\n30 8700\nae 6454\n94 6312\n50 9300\nb4 6712\n70 9700\nbc 6752\n78 9740\nd8 5960\n12 8304\n0 8202\n1a 8344\nf8 5d60\n32 8704\n20 8602\n3a 8744\n96 6316\n52 9304\n9e 6356\n40 9202\n5a 9344\nb6 6716\n72 9704\nbe 6756\n60 9602\n7a 9744\n42 920e\n5c 9350\nda 71e6\n93 cba5\n62 960e\n7c 9750\nfa 75e6\ndc 5970\nc2 582e\n16 8314\n4 8212\nca 586e\n1e 8354\n56 9314\n44 9212\n5e 9354\n76 9714\n64 9612\n7e 9754\n10 8380\n94 6392\nae 64d4\n50 9380\nd8 59e0\n12 8384\neb f467\nc0 58aa\nda 59ec\n14 8390\n54 9390\nb 826d\nc2 58ae\ndc 59f0\n16 8394\ne2 5cae\nfc 5df0\n36 8794\nae 6c54\n94 6b12\n50 9b00\n5e 31f4\n44 30b2\n9c 6b52\n58 9b40\n4c 30f2\n96 6b16\n52 9b04\ncc d0da\n46 30b6\n9e 6b56\n40 9a02\n5a 9b44\nd4 d11a\n4e 30f6\n56 9b14\n4a 30c6\n10 8b80\n12 8b84\neb fc67\n14 8b90\n16 8b94\n10 a300\nd8 7960\n1 889\n12 a304\n0 a202\n9 8c9\n1a a344\nf8 7d60\n21 c89\n32 a704\n29 cc9\n20 a602\n3a a744\nfc 7d70\ne2 7c2e\n25 c99\n36 a714\nea 7c6e\n2d cd9\n24 a612\n3e a754\n10 a380\nf8 7de0\n32 a784\nda 79ec\nc0 78aa\n14 a390\n10 ab00\n12 ab04\n14 ab10\n16 ab14\n10 ab80\n12 ab84\n2d a45b\n14 ab90\n4b 90ef\ne1 fea1\ndb 51e5\nc1 50a3\n2f 2e5d\nfb 55c5\ne1 5483\n37 a51f\n98 4360\nf0 5528\ne3 5487\n80 4222\n9a 4364\nff 55d5\ne5 5493\n82 422e\n9c 4370\n30 720\n85 e291\n50 1320\na5 e691\n70 1720\n2f a4df\n90 4320\nb0 4720\nb8 4760\nf8 5760\ndc 735a\n12 324\n87 e295\n52 1324\na7 e695\n72 1724\n92 4324\nb2 4724\na0 4622\nba 4764\nd2 5324\nc0 5222\nda 5364\nf2 5724\ne0 5622\nfa 5764\nf4 7518\n10 3a0\n30 7a0\n50 13a0\n70 17a0\nb0 47a0\nd0 53a0\nfc 77da\n32 7a4\n52 13a4\n72 17a4\n92 43a4\nb2 47a4\nd2 53a4\nf2 57a4\n16 11c\n3c fda\n56 111c\n68 3eca\n82 400c\n6c 3eda\n86 401c\n70 3f0a\n8a 404c\n74 3f1a\n8e 405c\n78 3fca\n92 410c\n7c 3fda\n96 411c\n80 400a\n9a 414c\nb4 4f1a\nce 505c\nc0 500a\nda 514c\nf2 7f26\n35 f91\nfb 55cd\ne1 548b\n8b 42c7\n98 4368\n3d fdb\n57 111d\n46 3a1e\n69 3ecb\n83 400d\n6d 3edb\n87 401d\n79 3fcb\n93 410d\n9c 6b50\n82 6a0e\nc6 7a1e\n85 ea91\n50 1b20\n6f bcdf\nd0 5b20\na5 e611\n26 acb4\n46 123c\n77 bd1f\nd8 5b60\n4 a890\n4e 127c\n87 ea95\n52 1b24\nc0 5a22\nda 5b64\n50 1ba0\nd0 5ba0\nbc e552\n46 12bc\n52 1ba4\n6d 147b\n30 2720\n92 c986\n98 6360\nb0 6720\nb2 cd86\nb8 6760\n80 6222\n9a 6364\nb9 cdeb\nb2 6724\na0 6622\nba 6764\n10 23a0\n30 27a0\n90 63a0\nb0 67a0\n98 c3c8\n12 23a4\nb8 c7c8\n32 27a4\n92 63a4\nb2 67a4\n2 2c\na 6c\n53 118f\n12 12c\n30 f2a\n4a 106c\n87 e09d\n38 fea\n52 112c\n68 3eea\n82 402c\n70 3f2a\n8a 406c\nd3 518f\n78 3fea\n92 412c\n80 402a\n9a 416c\nb0 4f2a\nca 506c\n9b e147\n25 eb1\nc0 502a\nda 516c\n35 fb1\n3 2d\n29 eeb\n43 102d\n39 feb\n53 112d\n5c 3b70\n42 3a2e\n69 3eeb\n83 402d\n98 cb48\n12 2b24\n76 351e\n80 6a22\n9a 6b64\n98 cbc8\n12 2ba4\n76 359e\n10 8320\n18 8360\n7b 95c5\nbf 65d7\n61 9483\n38 8760\n9f 61d5\n41 9081\n85 6093\nbf 65d5\n61 9481\na5 6493\ndc f35a\n12 8324\n0 8222\n1a 8364\n70 9528\nb4 653a\n63 9487\nfc f75a\n32 8724\n20 8622\n3a 8764\n94 6138\n43 9085\n87 6097\n40 9222\n9e 6376\n5a 9364\nb6 6736\n72 9724\nb4 6538\n63 9485\na7 6497\nbe 6776\n60 9622\n7a 9764\nf4 f518\n10 83a0\nf6 f51c\ndc f3da\n12 83a4\n86 409c\n3d 2f79\n5d 19fb\ndc 5358\n6e b476\n96 419c\nec 5458\n7e b576\n26 2e96\n4d 3079\nc6 509c\n9f 69d5\n41 9881\n85 6893\ndc fb5a\n12 8b24\n76 951e\n96 6b36\n52 9b24\n94 6938\n43 9885\n87 6897\n96 6bb6\n52 9ba4\n10 a320\n18 a360\n30 a720\n38 a760\n1 8a9\n12 a324\n0 a222\n9 8e9\n1a a364\n21 ca9\n32 a724\n29 ce9\n20 a622\n3a a764\nb2 4506\n10 a3a0\n12 a3a4\n2 ac\n42 10ac\n82 40ac\n92 41ac\nc2 50ac\n38 7c8\nd2 51ac\n83 40ad\nc3 50ad\n39 7c9\n12 ab24\n0 aa22\n1a ab64\n95 e9b3\n7e b55e\n12 aba4\nf1 5521\n76 b59e\nfb 55e5\ne1 54a3\nd9 7b41\na 24c\n94 4b30\n4a 124c\nd4 5b30\n10 ab82\n2a acc4\n81 4aa1\n6a 164c\n7 8bf\n86 421c\nf 8ff\n8e 425c\n4b 18ef\nca 524c\n4f 18ff\nce 525c\n6b 1cef\nea 564c\n6f 1cff\nee 565c\nb 24d\n95 4b31\n4b 124d\nd5 5b31\n11 ab83\n2b acc5\n4c 18f0\ncb 524d\nd 82f1\n58 b9ca\naa 466c\nf3 578f\na0 ec80\nea 566c\nd9 7363\n2 28c\nf9 7763\n22 68c\n62 168c\na2 468c\nb 8aef\n25 8c31\ne2 568c\na9 6c43\n4b 9aef\n65 9c31\n83 428d\nc3 528d\nb9 6741\n20 2ca2\n3a 2de4\n82 42ac\na2 46ac\nc2 52ac\n2 a0c\n6 a1c\na a4c\n42 1a0c\n3d ad5b\n46 1a1c\n4a 1a4c\n4e 1a5c\n82 4a0c\n86 4a1c\n8a 4a4c\n8e 4a5c\nc2 5a0c\nc6 5a1c\nca 5a4c\nce 5a5c\n42 1a2c\n3d ad7b\n4a 1a6c\n82 4a2c\n8a 4a6c\nc2 5a2c\n18 abc0\nd9 7b63\n2 a8c\ndd 7b73\n6 a9c\n46 1a9c\n2e 8e76\nc6 5a9c\nb8 ed42\n42 1aac\nbc e758\n7a 1746\n3d adfb\n82 4aac\nc2 5aac\nf4 5f10\n0 200a\n1a 214c\n8 22ca\n22 240c\n92 6bae\nac 6cf0\n10 230a\n2a 244c\n9a 6bee\nb4 6d30\n18 23ca\n32 250c\na2 6cae\nbc 6df0\n20 240a\n3a 254c\n24 8eb8\n82 600c\n54 1b1a\n6e 1c5c\n2c 8ef8\n8a 604c\n30 8f08\n8e 605c\n34 8fb8\n92 610c\n64 1c1a\n7e 1d5c\n38 8fc8\n96 611c\n80 600a\n3c 8ff8\n9a 614c\n88 62ca\n44 92b8\na2 640c\n98 63ca\n54 93b8\nb2 650c\n9 22cb\n23 240d\nfe 5776\n11 230b\n2b 244d\n21 240b\n3b 254d\n25 8eb9\n83 600d\n35 8fb9\n93 610d\n65 1c1b\n7f 1d5d\n3d 8ff9\n81 600b\n9b 614d\n89 62cb\n45 92b9\na3 640d\n91 630b\n12 29ae\n4d 92f9\nab 644d\ne4 5e30\na 206c\n45 9a9b\n5f 9bdd\nec 5ef0\n12 212c\n67 9c9d\n0 202a\nf4 5f30\n1a 216c\n55 9b9b\n6f 9cdd\n82 602c\n8a 606c\n92 612c\n64 1c3a\n7e 1d7c\n80 602a\n9a 616c\ned 5ef1\n13 212d\nf5 5f31\n1 202b\n1b 216d\n83 602d\n93 612d\n65 1c3b\n7f 1d7d\n2 208c\n12 218c\n22 248c\n32 258c\n82 608c\n92 618c\na2 648c\nb2 658c\n3 208d\nde 53f6\n23 248d\nfe 57f6\n33 258d\n83 608d\n87 609d\n93 618d\n97 619d\na3 648d\n2 20ac\n12 21ac\n82 60ac\n92 61ac\n3 20ad\n13 21ad\n83 60ad\n93 61ad\na 224c\n94 6b30\n22 260c\n16 13e\nac 6ef0\n26 261c\n2a 264c\n1e 17e\nb4 6f30\n6e 1e5c\n4e b8d4\n7 28bf\n86 621c\n11 8123\n23 2caf\n3d 2df1\na2 660c\n27 2cbf\na6 661c\n31 8523\nb 224d\n95 6b31\n23 260d\n17 13f\nad 6ef1\n2b 264d\n1f 17f\nb5 6f31\n4 28b0\n83 620d\n6f 1e5d\n4f b8d5\n36 8d94\n22 262c\n96 c994\n82 622c\n6e 1e7c\ncd f251\n4e b8f4\n9e c9d4\n84 c892\n8a 626c\n56 b934\nb6 cd94\na2 662c\nbe cdd4\na4 cc92\naa 666c\n17 8995\n3 222d\n54 3390\n97 c995\n83 622d\n6f 1e7d\n1e 835e\n4f b8f5\n85 c893\n9f c9d5\n8b 626d\n57 b935\n2 228c\n22 268c\nb9 c761\n26 269c\n1a 1ce\nb0 6f80\na2 668c\nb aaef\n25 ac31\n23 268d\n83 628d\na3 668d\n22 26ac\n82 62ac\na2 66ac\n83 62ad\n2 280c\n12 290c\n0 280a\n1a 294c\n92 690c\n30 a788\n13 290d\nee 5c76\n2 288c\n12 298c\n82 688c\n92 698c\n2 2a0c\n6 2a1c\na 2a4c\n82 6a0c\n86 6a1c\n11 8923\n8a 6a4c\nf0 d58a\n8e 6a5c\n19 8963\n83 6a0d\n8b 6a4d\n82 6a2c\n8a 6a6c\n2 2a8c\n99 cb61\n6 2a9c\n86 6a9c\n90 c300\n11 89a3\n65 b433\n54 19b8\n7f b575\n83 6a8d\n82 6aac\n12 810c\n16 811c\n0 800a\n1a 814c\n8e 605e\n30 8f0a\n4a 904c\na6 641e\n48 92ca\n62 940c\n13 810d\n1c ab50\n2 aa0e\n17 811d\n6 aa1e\na aa4e\nf1 7729\n4e ba5e\n2 802c\na 806c\n53 918f\n12 812c\n0 802a\n1a 816c\ndb 59e7\n8 82ea\n22 842c\n8e 607e\n30 8f2a\n4a 906c\n40 902a\n9e 617e\n5a 916c\na6 643e\n48 92ea\n62 942c\n3 802d\n13 812d\nc2 58a6\n9 82eb\n23 842d\n31 8f2b\n8f 607f\n4b 906d\n4a ba6e\na7 643f\n49 92eb\n63 942d\n12 818c\n16 819c\n97 619f\n53 918d\n57 919d\nc1 5a21\n46 ba9e\n2 80ac\n12 81ac\n97 61bf\n53 91ad\n5c bbf0\nfe 5d56\n42 baae\n46 921c\nf5 fdb9\n3e 27fe\nc8 70e2\n4e 925c\ncc 70f2\n66 961c\n81 caa1\nae 665e\n6a 964c\ne8 74e2\n85 cab1\n6e 965c\nec 74f2\nc9 5869\n3 820d\n9b 61e5\n81 60a3\ncd 5879\n7 821d\nb 824d\nf 825d\nc2 5a86\ne9 5c69\n23 860d\nbb 65e5\na1 64a3\n87 621f\n8 28c2\n43 920d\n37 27bf\nc 28d2\n47 921d\n8f 625f\n10 2902\n4b 924d\n14 2912\n4f 925d\ne3 5c27\n2a 866c\n1c 8358\nd5 5913\n73 978f\n86 623e\n42 922c\n8e 627e\n4a 926c\nae 667e\n6a 966c\n3 822d\n87 623f\n8 28e2\n43 922d\nd9 f363\nc8 58e8\n2 828c\n46 929c\n66 969c\na0 6400\n86 62be\n42 92ac\n6 881c\nc4 7a38\nb 2245\n12 890c\nf 2255\n16 891c\n86 681e\n42 980c\n46 981c\n4b 3245\n96 691e\n52 990c\n4f 3255\n56 991c\n13 890d\n17 891d\n93 eba7\n7c b752\n97 691f\n53 990d\n57 991d\n2b 445\n11 303\n9b 4be7\nbd 6dfb\n79 9de9\n13 307\n61 9cab\nbf 6dff\ne0 d608\n7b 9ded\n17 317\n65 9cbb\ne4 d618\n7f 9dfd\n1b 347\n1d 353\n1f 357\n31 70b\nf8 dfe8\n11 a183\nd5 5131\nbb 4fef\n50 3ba0\n39 74b\ndd 5171\nc3 502f\nf3 df87\n19 a1c3\n86 e294\n6b 1465\n51 1323\n5d 1353\na6 e69c\n71 172b\n51 b1a3\nae e6dc\n79 176b\n59 b1e3\n2 882c\nb 2265\n12 892c\n1a 9e6\n99 4343\n86 683e\n42 982c\n4b 3265\n96 693e\n52 992c\n1e 9f6\n9d 4353\naf 44d5\n95 4393\n46 9896\n4c 3270\n3a dee\nd0 7ba0\nb9 474b\n5e 19f6\ndd 5353\nd9 5363\nf9 576b\n13 892d\n22 a4a6\n90 4388\n78 b762\n97 693f\n53 992d\n62 b4a6\nd0 5388\n31 703\nbb 4fe7\nda dbcc\nc0 da8a\n3d 753\nc7 5037\n2b c4d\n11 b0b\n2f c5d\n15 b1b\na6 e694\n71 1723\n7d 1753\n57 1b17\n49 b0c9\n6f 1c5d\n55 1b1b\n5d 1b5b\n9d eb79\n5b 1b67\n8e eadc\n59 1b6b\nb5 4793\n66 9c96\n6c 3670\n99 4b4b\n9d 4b5b\n4c 3a78\n7e 1df6\nfd 5753\nf9 5763\ndd 5b5b\ndb 5b67\n2d 8651\nd9 5b6b\neb 5ced\n25 8691\ne2 f626\nd1 5bab\n1b 945\n1 803\n1f 955\n5 813\n7 817\n9 843\nd 853\n11 903\n15 913\n19 943\n5b 91e5\n9f 61f7\n41 90a3\n1d 953\n5b 1945\n41 1803\n85 e819\n43 1807\n72 1704\n35 adb9\n5f 1955\n45 1813\n47 1817\n76 1714\n39 adc9\n49 1843\n8d e859\n4b 1847\n60 1602\n7a 1744\n3d adf9\n73 9fa7\n4d 1853\n4f 1857\nc8 706a\n77 9fb7\n51 1903\n55 1913\n59 1943\n5d 1953\n9b 4945\n81 4803\n89 4843\n27 86bf\n91 4903\n7 1f\n95 4913\n99 4943\n2f ae5d\na5 6e11\nf 5f\n9d 4953\n3b 87cf\n47 101f\nd9 5943\n77 97bf\ne5 7e11\n4f 105f\ndd 5953\n7b 97cf\n14 a9b8\n6b 1445\n51 1303\n22 862c\ndb 5be7\n1c a9f8\n59 1343\n90 4ba0\n79 174b\n1c 89da\n2f 2455\n15 2313\n1e 89de\n17 2317\n99 c34b\n1a 89ee\n13 2327\n1d 2353\n1f 2357\n1b 2367\n35 271b\n31 272b\ne3 fc85\n39 276b\n9c e9f8\n5a 19e6\nd9 5343\n7a 1dee\nf9 574b\n59 9341\n1e 29f6\n9d 6353\nab 64e5\n91 63a3\nb9 676b\nb1 67ab\n34 adb8\n71 1703\nfb 5fe7\n3c adf8\n79 1743\n9d eb59\n5b 1b47\n4d b0f9\n59 1b4b\nda fbcc\nc0 fa8a\n3d 2753\n17 2b17\n2f 2c5d\n15 2b1b\n99 cb4b\n13 2b27\n2b 2c6d\n11 2b2b\n1f 2b57\n47 b2b7\n1d 2b5b\n5f b3fd\n45 b2bb\n1b 2b67\n19 2b6b\nbc edf8\n7a 1de6\nf9 5743\ndb 5b47\nd9 5b4b\n46 9a1c\n25 a439\nde 79f4\nc4 78b2\n8e 6a5e\n4a 9a4c\n29 a469\n2 a286\nc8 78e2\n4e 9a5c\n2d a479\n6 a296\ncc 78f2\n79 9741\n3e 2df6\nbd 6753\nb9 6763\n71 9781\nb5 6793\nb1 67a3\n59 9b49\n9d 6b5b\n99 6b6b\nab 6ced\n91 6bab\n7 8a1d\n87 6a1f\n43 9a0d\n51 3101\n37 2fbf\ndc 717a\n47 9a1d\nab 4445\n12 9a6\n91 4303\naf 4455\n16 9b6\n95 4313\n36 dbe\nb5 471b\n98 e9c8\n56 19b6\nef 5455\nd5 5313\neb 5465\nd1 5323\n76 1dbe\nf5 571b\nf1 572b\n1f 215f\n19 8343\n1b 8347\n1d 8353\n1f 8357\n2b 84c5\n11 8383\ndb 59ef\n2f 84d5\n15 8393\n86 6a3e\n42 9a2c\n8e 6a7e\n4a 9a6c\n3b 8747\n50 bba0\nf2 5d06\n39 874b\n3f 8757\nf9 5de3\n33 8787\nfd 5df3\n37 8797\n5d 9353\n5f 9357\n5b 9367\n73 1f8d\n7b 9767\n3 8a2d\n87 6a3f\n43 9a2d\n32 da6\nb1 4703\nb8 edc8\n76 1db6\nf5 5713\nd7 5b17\n39 8743\n3d 8753\n1b 8b47\n86 6a9e\n42 9a8c\n46 9a9c\n19 8b4b\n1f 8b57\n1d 8b5b\n7d 9753\n5f 9b57\n5d 9b5b\n5b 9b67\naf 6cff\n6b 9ced\n51 9bab\n1b 94d\n1 80b\n1f 95d\n5 81b\n9 84b\nd 85b\n5b 194d\n41 180b\n5f 195d\n45 181b\n49 184b\ncf 70ff\n71 9fab\n4d 185b\nd3 710f\n75 9fbb\n9b 494d\n81 480b\n9f 495d\n85 481b\n89 484b\n8d 485b\ndf 595d\nc5 581b\ncd 585b\n94 e9b8\neb 5445\n52 19a6\nd1 5303\n34 a538\n27 a497\n9c c9da\naf 6455\n16 29b6\n51 9301\n95 6313\n71 9709\n36 2dbe\nb5 671b\nb1 672b\n59 9343\n5b 9347\n7b 9747\n90 cba0\n79 974b\n86 6abe\na0 6c00\n42 9aac\nc 8d8\n1d a353\n2f a4d5\ndb 79ef\n15 a393\n2b a4e5\n11 a3a3\nf2 7d26\n35 d91\n39 a76b\nb4 edb8\n72 1da6\nf1 5703\nbc cdda\n71 9701\n36 2db6\nb5 6713\nb8 cdea\nb1 6723\n51 9b09\naf 6c5d\n95 6b1b\nab 6c6d\n91 6b2b\n79 9743\n5b 9b47\n59 9b4b\n2 a00c\nac 4e78\nf5 5f9b\ne4 de10\na a04c\nfd 5fdb\nec ded0\n12 a10c\naf 4ed7\nbc 4f78\n16 a11c\nf4 df10\n0 a00a\n1a a14c\n10 a30a\n2a a44c\n1d ab5b\n19 ab6b\n2f acdd\n15 ab9b\n3 a00d\nad 4e79\ne5 de11\nb a04d\ned ded1\n13 a10d\nbd 4f79\n17 a11d\n2 a02c\nf5 5fbb\ne4 de30\na a06c\n16 2196\nfd 5ffb\nec def0\n12 a12c\nf4 df30\n0 a02a\n1a a16c\n8 a2ea\ndb 79e7\n22 a42c\ned def1\n13 a12d\n7c 17d8\nc2 78a6\n9 a2eb\n23 a42d\ne 8a7e\n2 a08c\nac 4ef8\n12 a18c\nbc 4ff8\n32 a58c\ndc 53f8\n1b 965\n1 823\n3 827\n11 923\n3 a08d\nad 4ef9\n13 a18d\nbd 4ff9\n5b 1965\n41 1823\n85 e839\n43 1827\n49 1863\n8d e879\n4b 1867\n86 e894\n51 1923\n9b 4965\n81 4823\n91 4923\n99 4963\n2f ae7d\n13 8307\nd9 5963\n2 a0ac\n12 a1ac\n3 a0ad\n13 a1ad\nc8 7868\n2 a20c\nc3 7807\na a24c\nc9 7869\n3 a20d\nb a24d\nf a25d\nc2 7a86\ne9 7c69\n23 a60d\ndb 7be7\n22 a62c\ne3 7c27\n2a a66c\n3 a22d\nc2 7aa6\n23 a62d\n15 a319\nce 78d4\n1b 96d\n1 82b\n5b 196d\n41 182b\n49 186b\n9b 496d\n81 482b\n2 a80c\n6 a81c\na a84c\n12 81ae\n4f 1257\n12 a90c\n16 a91c\n0 a80a\n1a a94c\n3 a80d\n7 a81d\nb a84d\n13 81af\n13 a90d\n17 a91d\n2 a82c\n4f 1277\n12 a92c\n13 a92d\n90 6388\ne 80f6\n7c 1fd8\n4c 1278\n2 a88c\n4f 12d7\n5c 1378\n12 a98c\n1f 9d5\n5 893\n15 993\n5f 19d5\n45 1893\n54 1938\n47 1897\n9f 49d5\n85 4893\n94 4938\n87 4897\n95 4993\n4c 3870\ndf 59d5\nc 82d8\nc5 5893\n2 aa0c\n6 aa1c\na aa4c\n7 aa1d\n3 aa2d\n56 b316\n5f 19dd\n45 189b\n92 e306\n9b 49cd\n81 488b\n96 e316\n9f 49dd\n85 489b\nd2 f306\ndb 59cd\nc1 588b\nd6 f316\ndf 59dd\nc5 589b\n90 4300\n11 9a3\n65 3433\n7f 3575\n7a 154e\n91 49a3\n27 aebd\ne5 7433\nff 7575\n52 b326\nc0 5208\n5b 19ed\n41 18ab\n95 433b\naf 447d\n92 e326\n9b 49ed\n81 48ab\nd5 733b\nef 747d\nd2 f326\ndb 59ed\n15 8391\nc1 58ab\n1b b45\n1 a03\n9 a43\nd a53\n49 1a43\n4d 1a53\n9b 4b45\n81 4a03\n9f 4b55\n85 4a13\n89 4a43\n8d 4a53\ndf 5b55\nc5 5a13\nc9 5a43\n2c ac78\n1f abd7\ncd 5a53\n49 1a63\n5c 39d8\nc9 5a63\n8d 4ad9\n1f abf7\ndc 79d8\n8 aae8\naa 4c4e\n5f 1bd5\n45 1a93\n9f 4bd5\n85 4a93\ndf 5bd5\nc5 5a93\nc6 d894\n1c 37a\n76 9d3c\nba 6d4e\n5c 9bfa\n6f 3675\n9b 4be5\n6a 164e\n81 4aa3\n7 a2b5\n9c 437a\nf6 dd3c\ndc dbfa\nef 7675\ndb 5be5\nc1 5aa3\n92 e98e\n47 b2b5\ndc 537a\n32 a50e\n10 308\n18 348\n88 4aea\na2 4c2c\n58 1348\nc8 5aea\ne2 5c2c\nb7 e71d\n68 166a\n38 adc0\n78 1748\n58 b1c0\n19 9eb\n2a a466\n98 4348\n39 deb\nb8 4748\n79 1deb\nf8 5748\n14 318\n15 9bb\n26 a436\n94 4318\n10 388\n30 788\n70 1788\nb0 4788\neb 746f\n14 398\n34 798\n54 1398\nc1 5aa9\n3c 8772\n74 1798\n26 a4b6\n94 4398\n17 893d\nb4 4798\n66 b4b6\nd4 5398\n65 161b\n7f 175d\n45 b093\n5f b1d5\n95 4bb3\naf 4cf5\n7e 175e\na5 ee99\n70 1f28\n63 1e87\n2d 2cd9\n5e b1d6\n31 ad81\n61 162b\n7b 176d\n41 b0a3\n5b b1e5\naf e6df\n30 ad82\n7a 176e\n7e 9776\n29 2ce9\nc8 50c8\n5a b1e6\n10 b08\n50 1b08\n58 1b48\n22 ac26\n90 4b08\n62 bc26\nd0 5b08\n26 ac9c\n6a bc66\nd8 5b48\n14 ab9a\n2e acdc\n14 b18\n54 1b18\n5c 1b58\nf7 57b5\n66 bc36\nd4 5b18\n6e bc76\ndc 5b58\nd5 7bb3\nef 7cf5\nbe 475e\nb0 4f28\na3 4e87\nb3 478d\nb2 478e\n7d 9dd3\n69 366b\nb6 479e\n6d 367b\n61 b603\n7b b745\n50 1b88\n38 8f62\na1 e603\nbb e745\n22 aca6\n90 4b88\ne1 f603\nfb f745\n62 bca6\nd0 5b88\n2e 867e\n65 b613\n7f b755\n54 1b98\n3c 8f72\na5 e613\nbf e755\n26 acb6\n94 4b98\nfe 575e\n9 20c3\nf0 5f28\ne3 5e87\nad 6cd9\ne5 f613\nff f755\n66 bcb6\nd4 5b98\nb0 ed82\nfa 576e\nfe d776\na9 6ce9\nf2 57ae\n10 2308\n18 2348\n88 6aea\na2 6c2c\n30 2708\n38 2748\n11 29ab\n90 6308\ne 8076\n7c 1f58\n42 b88e\n5c b9d0\n19 29eb\n47 9295\n98 6348\n39 2deb\n67 9695\nb8 6748\n14 2318\n34 2718\n15 29bb\n94 6318\n35 2dbb\nb4 6718\n10 2388\n30 2788\nb0 6788\n14 2398\n34 2798\n94 6398\n17 a93d\nb4 6798\n17 b1d\n16 b1e\n1a b4e\n5 a1b\n1f b5d\n8b 62cf\na5 6411\n47 92bd\n1e b5e\na4 6412\n60 9400\nbe 6554\n46 92be\n57 1b1d\n56 1b1e\n53 1b2d\n87 ea9f\n6c 1c70\n52 1b2e\n45 1a1b\n5f 1b5d\n5e 1b5e\n41 1a2b\n5b 1b6d\n8f eadf\n5a 1b6e\n10 2b08\n90 6b08\n14 2b18\n81 4a0b\n9b 4b4d\n94 6b18\n85 4a1b\n9f 4b5d\n9e 4b5e\n93 4b8d\n49 3a6b\n97 4b9d\n96 4b9e\n4d 3a7b\n7f 9757\n10 2b88\nff d757\n90 6b88\n14 2b98\nc5 5a1b\ndf 5b5d\nde 5b5e\n94 6b98\nd7 5b9d\nd6 5b9e\n8d 4a7b\nee 5e7c\n7 2017\nf3 5f07\n19 2143\ne8 5e6a\n1b 2147\nc2 f88c\n3f 2555\n25 2413\nc4 f890\n27 2417\nc8 f8c0\n2b 2447\nca f8cc\n2d 2453\ncc f8d0\n2f 2457\n31 2503\nbb 6de7\n23 8e0f\n3d 8f51\n27 8ebf\n41 9001\n9f 6155\n85 6013\n29 8ec3\n43 9005\n87 6017\n2d 8ef3\n47 9035\n8b 6047\n2f 8eff\n49 9041\n8d 6053\n31 8f03\n4b 9045\n8f 6057\n33 8faf\n4d 90f1\n91 6103\n3d 8ff3\n57 9135\n9b 6147\n47 92bf\nbf 6555\n61 9401\na5 6413\n63 9405\n49 92c3\na7 6417\n4b 92ef\n65 9431\na9 6443\n67 9435\n4d 92f3\nab 6447\n4f 92ff\n69 9441\nad 6453\n51 9303\n6b 9445\naf 6457\n53 93af\n6d 94f1\nb1 6503\n5b 93ef\n75 9531\nb9 6543\nc9 58c3\n10 8308\nd1 5903\n18 8348\ne9 5cc3\n30 8708\nf1 5d03\n38 8748\nae 645c\n94 631a\n50 9308\n9c 635a\n58 9348\nb4 671a\n70 9708\nbc 675a\n78 9748\ncd 58d3\n14 8318\n92 61ae\n54 9318\n74 9718\nf2 75ae\n10 8388\n94 639a\nae 64dc\n50 9388\neb f46f\n14 8398\n54 9398\n61 160b\n7b 174d\n41 b083\n5b b1c5\n91 4ba3\nab 4ce5\n7a 174e\n7e 9756\n29 2cc9\n5a b1c6\n33 272d\n35 8d93\n21 262b\n3b 276d\nf1 fd29\n3a 276e\n94 6b1a\nae 6c5c\n50 9b08\n97 63b5\n5e 31fc\n44 30ba\n9c 6b5a\n58 9b48\n85 62b3\n41 92a1\n9f 63f5\n4c 30fa\ne1 560b\n7c 1df0\n62 1cae\nfb 574d\nfa 574e\nfe d756\na9 6cc9\n54 9b18\n26 a494\nd2 79ae\n48 30ca\nf3 578d\n54 9b98\nb5 cd93\na1 662b\nbb 676d\nba 676e\n55 3939\nb3 67ad\nb2 67ae\nc9 78c3\n10 a308\nd1 7903\n18 a348\ne9 7cc3\n30 a708\nf1 7d03\n38 a748\ncd 78d3\n14 a318\ned 7cd3\n34 a718\n10 a388\n14 a398\n53 1b0d\n6c 1c50\n52 1b0e\n77 35bd\n41 1a0b\n5b 1b4d\n5a 1b4e\n17 2b1d\n16 2b1e\n13 2b2d\n2c 2c70\n12 2b2e\n5 2a1b\n1f 2b5d\n1e 2b5e\n1a 2b6e\nc1 5a0b\ndb 5b4d\nda 5b4e\ne5 74bb\nff 75fd\nd3 5b8d\n41 9a09\n85 6a1b\n9f 6b5d\n5a 9b4c\n40 9a0a\n9e 6b5e\n9a 6b6e\n52 9b8c\n96 6b9e\n51 bba3\n6b bce5\n3a 874e\nfd 75f3\n95 cbb3\naf ccf5\n7e 975e\n2d acd9\n7a 976e\n66 1634\n29 ace9\n77 979d\n76 979e\ne6 5c36\n2d 867b\n93 4b0d\n97 4b1d\n96 4b1e\nd7 5b1d\nd6 5b1e\n99 69e3\n59 99e1\n9d 69f3\ned 5ed3\n13 210f\n5e 9b5e\n5a 9b6e\n56 9b9e\nd 8a7b\neb d64d\n6c 9cf0\nb0 6d02\n52 9bae\n1f 215d\n5 201b\nf3 5f0f\n19 214b\n3f 255d\n25 241b\n29 244b\nb3 6d2f\n31 250b\nbb 6def\n3d 8f59\n39 254b\n41 9009\n9f 615d\n85 601b\n45 9039\n89 604b\n49 9049\n8d 605b\n4d 90f9\n91 610b\n7d 1d5b\n55 9139\n99 614b\nbf 655d\n61 9409\na5 641b\n6d 94f9\nb1 650b\nb3 672d\nb2 672e\n4d 38f9\nf9 75e3\n91 cba3\nab cce5\n7a 974e\n66 1614\n29 acc9\n36 d94\n3a a76e\nd3 5b0d\nec 5c50\nd2 5b0e\nf7 75bd\n97 6b1d\n52 9b0c\n96 6b1e\n5a 9b4e\n65 b4bb\n7f b5fd\n1e ab5e\nc6 7296\ned 7479\n1a ab6e\nab e64d\n2c acf0\n12 abae\n3b 2565\n21 2423\n2d 8e71\n76 9f94\na9 c44b\nc0 f8a0\n23 2427\n2f 8e75\nc8 f8e0\n2b 2467\n9b 6165\n81 6023\n83 6027\n8b 6067\nbb 6565\na1 6423\na3 6427\n5c 9b7a\nab 6467\n3b 256d\n21 242b\n2d 8e79\nd3 f985\n29 246b\n9b 616d\n81 602b\n89 606b\nbb 656d\na1 642b\n14 2138\nee 5efc\n7 2097\nda fb66\n3 8a8f\n1d 8bd1\nfa 5fec\ne0 5eaa\n13 2187\n3b 25c5\n21 2483\nea fe66\n2d 8ed1\n30 2528\n23 2487\n2f 8ed5\n3f 25d5\n25 2493\n34 2538\n27 2497\n31 2583\nfa ff66\n23 8e8f\n3d 8fd1\n9b 61c5\n81 6083\n90 6128\n83 6087\n91 6183\nbb 65c5\na1 6483\nb0 6528\na3 6487\n76 9d1c\n6f 3655\n5c 9bda\nb1 6583\n98 c160\n1f 21dd\n5 209b\n11 218b\nda fb6e\n1d 8bd9\n3b 25cd\n21 248b\nea fe6e\n2d 8ed9\n31 258b\nfa ff6e\n3d 8fd9\n91 618b\nbb 65cd\na1 648b\n61 9489\nbf 65dd\na5 649b\nb1 658b\n3 287\n10 328\nab 46c7\nb8 4768\n10 3a8\n30 7a8\n50 13a8\n70 17a8\nb0 47a8\nd0 53a8\n86 eab4\n6f b65f\n3 a87\n10 b28\n43 1a87\n85 ea99\n50 1b28\n83 4a87\n90 4b28\n8b 4ac7\n98 4b68\nc3 5a87\nd0 5b28\na5 e619\n63 1607\n26 acbc\ncb 5ac7\nd8 5b68\nad e659\n14 abba\n6b 1647\n2e acfc\n61 b623\n7b b765\n50 1ba8\na1 e623\nbb e765\n90 4ba8\n2f ae5f\ne1 f623\nfb f765\nd0 5ba8\n92 c98e\n47 92b5\n8b 62c7\n98 6368\nb2 cd8e\nab 66c7\n67 96b5\nb8 6768\n10 23a8\n30 27a8\n90 63a8\n7c 1ff8\nb0 67a8\n3 2a87\n10 2b28\n83 6a87\n90 6b28\n3b 25e5\n21 24a3\n8b 6045\n2d 8ef1\na9 c4cb\n23 24a7\n2f 8ef5\nff d777\n90 6ba8\nc9 58e3\n3 8287\n10 8328\nb 82c7\nd1 5923\n18 8368\nbf 65df\n7b 95cd\n61 948b\nf1 5d23\n2b 86c7\n38 8768\n10 83a8\n41 9889\n9f 69dd\n85 689b\nae 6cfc\n94 6bba\n50 9ba8\nc9 78e3\n3 a287\n10 a328\nb2 450e\n10 a3a8\n8b 604d\n2d 8ef9\nbb 65ed\na1 64ab\na 88ce\n3 2207\nab 6ee7\n2a 8cce\nc0 fa80\n23 2607\nc8 fac0\n32 8d0e\n2b 2647\n88 c8ca\n9b 6345\n2 28a6\n81 6203\na8 ccca\n22 2ca6\nbb 6745\na1 6603\naa ccce\na3 6607\nb2 cd0e\n67 9635\nab 6647\n9 224b\nad 6c71\n93 6b2f\n3b 274d\n21 260b\n29 264b\n9b 634d\n1c 29f0\n2 28ae\n81 620b\n6d 1e5b\na 28ee\n45 9239\n89 624b\n2a 2cee\n65 9639\na9 664b\n3b 27c5\n21 2683\n30 2728\n23 2687\n9b 63c5\n81 6283\nb0 6728\na3 6687\n1b 23cd\n1 228b\n3b 27cd\n21 268b\nbb 67cd\na1 668b\na aaee\n24 ac30\n2b 44d\n11 30b\nb5 4d31\n9b 4bef\n2f 45d\n15 31b\n19 34b\nd0 db88\n33 70f\nfa dfec\ne0 deaa\n13 a187\nd8 dbc8\n52 3ba4\n3b 74f\ne8 deea\n1b a1c7\ndf 5175\nc5 5033\n6f 145d\n55 131b\nf9 5d41\ndf 5bff\n86 e29c\n6b 146d\n51 132b\n8e e2dc\n59 136b\n73 172f\nc1 5089\n53 b1a7\n96 4bb4\n7f 175f\n5f b1d7\nb3 478f\nd9 536b\nb1 ed83\nfb 576f\nf3 57af\n17 b1f\n1f b5f\n57 1b1f\n6d 1c71\n53 1b2f\n5f 1b5f\n9b 4b4f\n9f 4b5f\n97 4b9f\ndf 5b5f\n2f 8655\ndb 5b6f\nd7 5b9f\n8e 4a7c\ned 5cf1\n27 8695\nd3 5baf\n1b 2945\n1 2803\n1f 2955\n5 2813\n7 2817\n9 2843\nd 2853\n11 2903\n13 2907\n15 2913\n9b 6945\n81 6803\n83 6807\n3b a7c5\n21 a683\n9f 6955\n41 9801\n85 6813\ne9 7ceb\n3d a7d1\n23 a68f\n47 9835\n8b 6847\n29 a6c3\n49 9841\n8d 6853\nf1 7d2b\n2b a6cf\n4d 98f1\n91 6903\n7 201f\n4f 98f5\n93 6907\n51 9901\n95 6913\n6b 144d\n51 130b\n2f 86d5\nf5 5d31\ndb 5bef\n59 134b\n92 4ba4\n7b 174f\n2f 245d\n15 231b\n2b 246d\n11 232b\n66 9e9c\nc3 f885\n19 236b\n6e 9edc\ndc fbd8\n3f 275f\n5a 19ee\nd9 534b\n7c 1df2\nfb 574f\n99 636b\nab 64ed\n91 63ab\nbb 676f\nb3 67af\n6d 1c51\n53 1b0f\n5b 1b4f\n17 2b1f\n2d 2c71\n13 2b2f\n1f 2b5f\ndb 5b4f\n53 9b8d\n97 6b9f\nab 444d\n12 9ae\n91 430b\n6b 366f\n7f 9dd7\nef 545d\n56 19be\nd5 531b\neb 546d\nd1 532b\nf3 572f\nd2 5906\n19 834b\n2b 84cd\ne8 f462\n11 838b\nec f472\n2f 84dd\n15 839b\n52 bba4\n3b 874f\n56 bbb4\n3f 875f\n59 936b\n96 cbb4\n7f 975f\n7b 976f\n77 979f\ne7 5c37\n2e 867c\n73 97af\n1c 8378\nf 82d7\nd5 5933\ne1 7e01\n4b 104f\nad 4c51\n93 4b0f\n97 4b1f\nd7 5b1f\n1b 8b4f\n1f 8b5f\n5f 9b5f\n5b 9b6f\n1b 294d\n1 280b\n1f 295d\n5 281b\n9 284b\n4b b0ed\n31 afab\nd 285b\n4f b0fd\n35 afbb\n11 290b\n19 294b\n9b 694d\n81 680b\n9f 695d\n41 9809\n85 681b\n45 9839\n89 684b\n49 9849\n8d 685b\n4d 98f9\n91 690b\n55 9939\n99 694b\neb 544d\n52 19ae\nd1 530b\n88 42e0\n27 a49f\naf 645d\n51 9309\n16 29be\n95 631b\nab 646d\n91 632b\n7d 1f7b\nb3 672f\n59 934b\n92 cba4\n7b 974f\nd2 7926\n15 991\n19 a36b\n2f a4dd\n15 a39b\n2b a4ed\n11 a3ab\n37 d95\n3b a76f\n33 a7af\n51 9921\n95 6933\ne5 5e13\nff 5f55\nb 204f\ned 5c51\nd3 5b0f\n53 9b0d\n97 6b1f\n5b 9b4f\n1b 2965\n1 2823\n89 c84b\n3 2827\n11 2923\n9b 6965\n81 6823\n91 6923\n99 6963\n1b 296d\n1 282b\n9 286b\n9b 696d\n81 682b\n89 686b\n1f 29d5\n5 2893\n14 2938\n7 2897\nf4 573a\n13 2987\n90 6928\n83 6887\n40 900a\n5a 914c\n9e 615e\n91 6983\n80 c022\n9a c164\n7 209f\n93 6987\n1b 29cd\n1 288b\n98 c960\n1f 29dd\n5 289b\n11 298b\n9b 69cd\n81 688b\n91 698b\n89 c8cb\n82 6204\n3 28a7\nf8 5f42\n1e 217e\n7a 354e\n91 69a3\n1b 29ed\n80 6208\n1 28ab\n9b 69ed\n81 68ab\n1b 2b45\n1 2a03\n3 2a07\n1f 2b55\n5 2a13\n9 2a43\nb 2a47\nd 2a53\n9b 6b45\n81 6a03\n9f 6b55\n41 9a01\n85 6a13\n45 9a31\n89 6a43\n49 9a41\n8d 6a53\n1b 2b4d\n1 2a0b\n9 2a4b\n9b 6b4d\n81 6a0b\n45 9a39\n89 6a4b\n1b 2b65\n1 2a23\n9b 6b65\n81 6a23\n1b 2bcd\n1 2a8b\nc6 f894\n1c 237a\n6a 364e\n9b 6be5\n81 6aa3\n4b 92c7\n58 9368\n9c 637a\n55 1331\n0 a8a8\n57 1335\n32 252c\n18 23ea\n24 8e38\n43 122f\n13 a985\n5d 1371\n12 a986\n5c 1372\nb 28ed\nb8 e748\n39 adeb\n76 1736\n25 2cb1\n75 1739\n55 b1b1\n74 173a\n54 b1b2\n7e 1776\n13 2baf\n2d 2cf1\n33 ad8d\n7d 1779\n43 b0af\n5d b1f1\n32 ad8e\n7c 177a\n5c b1f2\nd7 53b5\na8 ece8\ne5 5633\nff 5775\nb3 ed8d\nfd 5779\n2e 8e5e\nb2 ed8e\n67 b6b5\nfc 577a\n75 1731\n74 1732\n23 2cad\n33 ad85\n63 162f\n7d 1771\n32 ad86\n7c 1772\n57 1b35\n98 eb48\n56 1b36\n61 34a3\n7b 35e5\n48 b0e8\n55 1b39\n6e 1c7c\n54 1b3a\na7 4e35\n79 35e9\n5c 1b7a\naf 4e75\ndd 5b79\nff f777\nee 5cfc\nd4 5bba\n1b 8145\n1 8003\n3 8007\nc8 d8ea\ndb 7365\nc1 7223\n1f 8155\n5 8013\n7 8017\ncc d8fa\ndf 7375\nc5 7233\n9 8043\na3 ee05\nd 8053\na7 eeb5\n11 8103\n13 8107\nab eec5\n15 8113\na0 ee28\n17 8117\n3b 8545\n21 8403\n23 8407\ne8 dcea\nfb 7765\ne1 7623\n3f 8555\n25 8413\nf7 dd97\nfd 7771\ne3 762f\n27 8417\nec dcfa\nff 7775\ne5 7633\n29 8443\n2b 8447\nf0 dd2a\ne9 7663\nc3 f205\n2d 8453\nff ddd7\neb 766f\n2f 8457\nf4 dd3a\ned 7673\nc7 f2b5\n31 8503\n33 8507\ncb f2c5\n35 8513\nc0 f228\n37 8517\n5b 9145\n9f 6157\n41 9003\n43 9007\n5f 9155\n45 9013\n47 9017\n49 9043\n4b 9047\ne3 fe05\n4d 9053\n4f 9057\ne7 feb5\n51 9103\n53 9107\neb fec5\n55 9113\ne0 fe28\n57 9117\n7b 9545\nbf 6557\n61 9403\n63 9407\n7f 9555\n65 9413\n67 9417\n69 9443\n6b 9447\n6d 9453\n0 2888\n6f 9457\n71 9503\n73 9507\n75 9513\n77 9517\n1c 89f8\n15 2331\ncb f8ed\n2e 2474\n14 2332\n9d c359\n1e 89fc\n4 88ba\n17 2335\ncd f8f1\n9c c35a\n16 2336\nd3 f92d\n1c 2372\n5 2233\nc 88fa\n1f 2375\n4f b0dd\n35 af9b\nd5 f931\n1e 2376\ned fcf1\nd3 fbaf\nbc c75a\n36 2736\n35 2739\n34 273a\ne6 fc94\n3c 277a\n58 9360\n9c 6372\n8c c8fa\n85 6233\n41 9221\n9f 6375\n4c 307a\n95 63b1\nac ccfa\na5 6633\n61 9621\nbf 6775\n6c 347a\nb7 cd9f\nbd 6779\n6b 96c7\n78 9768\nbc 677a\nb7 67b5\n7e 35fc\n64 34ba\nf3 fd2d\nd9 fbeb\n3c 2772\n9d cb59\n17 2b35\n9c cb5a\n16 2b36\n15 2b39\nb4 4f18\n46 b036\n1e 2b76\n1d 2b79\n1c 2b7a\nb7 cd97\na3 662f\nbd 6771\n78 9760\nbc 6772\n57 393d\nb5 67b1\n4c 387a\n40 9a22\n5a 9b64\n9e 6b76\n9d 6b79\n4b 9ac7\n58 9b68\n9c 6b7a\n80 e8a8\nd7 5335\nb2 652c\n98 63ea\na0 eca8\nf7 5735\nb8 67ea\nb9 edeb\nf6 5736\nf5 5739\n65 9633\n7f 9775\n7d 9779\n7c 977a\nf4 5732\n19 23c9\na3 6cad\nee 5c7c\nd4 5b3a\nf9 75e9\n7c 9772\n11 abab\n2b aced\n5e 9b76\n5d 9b79\n51 312b\n9b 6bc7\na8 6c68\n57 9bb5\nef d655\n56 9bb6\n1b 814d\n1 800b\n1f 815d\n5 801b\n9 804b\na3 ee0d\nd 805b\na7 eebd\n11 810b\nab eecd\n15 811b\nda 59c6\n3b 854d\n21 840b\n3f 855d\nde 59d6\n25 841b\n40 b8a0\n29 844b\nc7 f2bd\n31 850b\ncb f2cd\n35 851b\n5f 915d\n45 901b\n49 904b\ne3 fe0d\n4d 905b\ne7 febd\n51 910b\neb fecd\n55 911b\nbf 655f\n7b 954d\n61 940b\n80 c8a0\n69 944b\n71 950b\n9c c9f8\n95 6331\nae 6474\n50 9320\n94 6332\n9e c9fc\n84 c8ba\n97 6335\n5e 317c\n44 303a\n52 9324\n96 6336\n67 b497\n74 b538\nbe cdfc\na4 ccba\nb7 6735\n7e 357c\n64 343a\nb5 6739\n63 9687\n70 9728\nb4 673a\n1c a372\n34 2f98\n5 a233\n1f a375\nb7 4517\n15 a3b1\n2e a4f4\n14 a3b2\n3c a77a\nbc cdf8\nb5 6731\n70 9720\nb4 6732\n43 9a87\nae 6c7c\n50 9b28\n94 6b3a\nc1 7aa9\n3c a772\n5 aa33\n1f ab75\n8c 4a58\n1e ab76\n1d ab79\n1c ab7a\n1b 8165\n1 8023\n71 1f09\n3 8027\n13 8127\n3b 8565\n21 8423\n23 8427\n33 8527\n9f 6177\n5b 9165\n41 9023\n43 9027\n4b 9067\n63 1c8d\n51 9123\n53 9127\n63 9427\n6b 9467\n71 9523\n73 9527\n1b 816d\n1 802b\n9 806b\n52 918e\n11 812b\nda 59e6\n3b 856d\n21 842b\n31 852b\n9f 617f\n5b 916d\n41 902b\n49 906b\n51 912b\nbf 657f\n7b 956d\n61 942b\n52 918c\n96 619e\n69 946b\n71 952b\n1b 81c5\n1 8083\n10 8128\n3 8087\ndb 73e5\nc1 72a3\n1f 81d5\n5 8093\n14 8138\n7 8097\ndf 73f5\nc5 72b3\n3b 85c5\n21 8483\n30 8528\n23 8487\nfb 77e5\ne1 76a3\n3f 85d5\n25 8493\nfd 77f1\ne3 76af\n34 8538\n27 8497\nff 77f5\ne5 76b3\n5b 91c5\n9f 61d7\n41 9083\n94 613a\n50 9128\n43 9087\n5f 91d5\n45 9093\n54 9138\n98 614a\n47 9097\n7f 95d5\n65 9493\n74 9538\nb8 654a\n67 9497\n4 8232\n1e 8374\n3b 85cd\nf8 f562\n21 848b\nfc f572\n3f 85dd\n25 849b\n1b 81e5\n1 80a3\n71 1f89\n3 80a7\n7b 95e5\nbf 65f7\n61 94a3\n59 bb41\n14 330\nd1 dba9\n34 730\n54 1330\n74 1730\n94 4330\nb4 4730\na2 462e\nbc 4770\nf4 5730\ne2 562e\nb2 ed84\nfc 5770\n15 331\n35 731\nc0 daa8\n23 62f\n3d 771\n95 4331\nb5 4731\na3 462f\nbd 4771\n16 334\nd3 dbad\n36 734\n19 a9e9\n56 1334\n96 4334\nb6 4734\n99 e9e9\nd6 5334\nc4 5232\nde 5374\nb9 ede9\nf6 5734\ne4 5632\nfe 5774\n17 335\n34 7b0\n74 17b0\n94 43b0\nb4 47b0\nd4 53b0\nf4 57b0\n35 7b1\n75 17b1\n95 43b1\nb5 47b1\n36 7b4\n76 17b4\n96 43b4\nb6 47b4\nd6 53b4\nf6 57b4\n14 b30\n54 1b30\n82 4a2e\n9c 4b70\n15 b31\n55 1b31\n43 1a2f\n5d 1b71\n16 b34\n56 1b34\n84 4a32\n9e 4b74\nc4 5a32\nde 5b74\n17 b35\n5 a33\n1f b75\n14 bb0\nb9 4d6b\n54 1bb0\n33 870f\nf9 5d6b\n7d 175b\n94 4bb0\na 2cc\nd4 5bb0\n4a 12cc\n15 bb1\na0 4c2a\nba 4d6c\n55 1bb1\ne0 5c2a\n34 8710\nfa 5d6c\n64 161a\n7e 175c\n95 4bb1\nc8 7262\nb 2cd\nd5 5bb1\n4b 12cd\n16 bb4\nbb 4d6f\n56 1bb4\n35 8713\nfb 5d6f\n17 bb5\na2 4c2e\nbc 4d70\n57 1bb5\ne2 5c2e\n36 8714\nfc 5d70\n14 2330\nd1 fba9\n34 2730\n94 6330\nb4 6730\nb6 cd96\na2 662e\nbc 6770\n9c c358\n1d 89fb\n16 2334\nd3 fbad\nbc c758\n3d 8dfb\n36 2734\n9d c9fb\n96 6334\n84 6232\n40 9220\n9e 6374\nbd cdfb\nb6 6734\na4 6632\n60 9620\nbe 6774\n34 27b0\n94 63b0\nb4 67b0\n35 27b1\nbc c7d8\n36 27b4\n96 63b4\nb6 67b4\n14 2b30\n82 6a2e\n9c 6b70\n15 2b31\n9c cb58\n16 2b34\n84 6a32\n40 9a20\n9e 6b74\n14 2bb0\ne7 d615\nb9 6d6b\n7d 375b\n94 6bb0\na 22cc\n9c cbd8\n16 2bb4\nbb 6d6f\n9d cbd9\n17 2bb5\na2 6c2e\nbc 6d70\n14 8330\n98 6342\n54 9330\n42 922e\n5c 9370\nb8 6742\n74 9730\n62 962e\n7c 9770\n15 8331\n35 8731\n23 862f\n3d 8771\n99 6343\n1a 29e6\n55 9331\n16 8334\n36 8734\n24 8632\n3e 8774\n9a 6346\n56 9334\n44 9232\n5e 9374\nba 6746\n76 9734\n64 9632\n7e 9774\n17 8335\n5 8233\n1f 8375\n37 8735\n9b 6347\n57 9335\n32 a52c\n18 a3ea\n14 83b0\n15 83b1\n16 83b4\n17 83b5\n98 6b42\n54 9b30\n48 30e2\n15 8b31\n99 6b43\n55 9b31\n51 3123\n9a 6b46\n56 9b34\nd0 d10a\n4a 30e6\n17 8b35\n9b 6b47\n57 9b35\n32 ad2c\n6f 1677\n18 abea\n14 8bb0\n15 8bb1\n99 6bc3\nb3 6d05\n55 9bb1\n16 8bb4\n17 8bb5\n14 a330\n34 a730\nc a2da\n26 a41c\ndf 79d7\n15 a331\n35 a731\n5 8b9\n16 a334\n4 a232\nd 8f9\n1e a374\n25 cb9\n36 a734\n24 a632\n2d cf9\n3e a774\n17 a335\n37 a735\nb6 4516\n14 a3b0\n7b 95ed\nbf 65ff\n61 94ab\ncf 7afd\n39 d4b\n14 ab30\n2 aa2e\n1c ab70\n15 ab31\n16 ab34\n4 aa32\n1e ab74\n17 ab35\nb6 4d16\n14 abb0\n1b 8345\n1 8203\nc9 5863\n3 8207\n1f 8355\ncb 586f\n5 8213\ncd 5873\n7 8217\n3b 8745\n21 8603\ne9 5c63\n23 8607\neb 5c6f\n3f 8755\n25 8613\ned 5c73\n27 8617\n9f 6357\n5b 9345\n41 9203\n47 9217\n7b 9745\nbf 6757\n61 9603\n67 9617\n1b 834d\n1 820b\n1f 835d\n5 821b\n3f 875d\nde 5bd6\n25 861b\n9f 635f\n5b 934d\n41 920b\n5f 935d\n45 921b\nbf 675f\n7b 974d\n61 960b\n7f 975d\n65 961b\n1b 836d\n1 822b\n19 a943\n52 938c\n96 639e\n2b 465\n11 323\n13 327\n1b 367\n18 a9e8\n6f 1475\n55 1333\n99 e349\n0 a8aa\n1a a9ec\n57 1337\n3e 8f7c\n24 8e3a\n8 a8ea\n5f 1377\n2c 8e7a\n75 173b\n55 b1b3\nb9 476b\n93 e987\ndd 5373\n33 a507\n88 e8ea\ndf 5377\na8 ecea\nff 5777\n31 723\n13 b27\n2b c6d\n11 b2b\n1b b67\n19 b6b\n7c 1dd0\n62 1c8e\n38 ade8\n75 1733\n99 eb49\n57 1b37\n49 b0e9\n6f 1c7d\n55 1b3b\n5d 1b7b\nb9 4763\n99 4b6b\nab 4ced\na2 e626\n91 4bab\nb3 ed87\nfd 5773\ndd 5b7b\nd7 5bb7\ne6 f636\nef 5cfd\nd5 5bbb\n1b 8945\n1 8803\n3 8807\n1f 8955\n5 8813\ndd 7b71\nc3 7a2f\n7 8817\nd 8853\ncb 7a6f\n11 8903\n5f 337d\n45 323b\n13 8907\n15 8913\n17 8917\n19 8943\n4d 327b\n1d 8953\n9f 6957\n5b 9945\n41 9803\n43 9807\n5f 9955\n45 9813\n47 9817\n4b 9847\n4d 9853\n4f 9857\n53 9907\n57 9917\nc 8f2\n8b 424f\n5d 9953\n10 38a\n2a 4cc\nb4 4db0\n2f 2475\n1c 89fa\n15 2333\n9d c35b\n1e 89fe\n17 2337\n1f 2377\n35 273b\nb5 4db1\n41 9223\n5b 9365\n9f 6377\n56 b396\n7d b579\n61 9623\n7b 9765\nbf 6777\n14 39a\n2e 4dc\nb8 4dc0\n9d cb5b\n17 2b37\n1f 2b77\n1d 2b7b\nb9 4dc1\n79 9761\nbd 6773\n41 9a23\n5b 9b65\n9f 6b77\n59 9b69\n9d 6b7b\n53 9ba5\n97 6bb7\naf 6cfd\n51 9ba9\n95 6bbb\nab 4465\n91 4323\nb1 472b\n98 e9e8\nef 5475\nd5 5333\n9a e9ec\n80 e8aa\nd7 5337\nba edec\na0 ecaa\nf7 5737\nf5 573b\nf2 5d26\n39 876b\n5d 9373\n7 80b7\n75 1f99\n5f 9377\n77 1f9d\n10 2ba8\n7f 9777\nb1 4723\nab 4c6d\n91 4b2b\nb8 ede8\nf5 5733\n19 8b6b\n68 3668\n7c 9dd0\n62 9c8e\n7d 9773\n5f 9b77\n5d 9b7b\na8 6c6a\n57 9bb7\nb3 6d0f\n6f 9cfd\n55 9bbb\n1b 894d\n1 880b\n1f 895d\n5 881b\n9 884b\nd 885b\na 2244\n11 890b\ne 2254\n15 891b\n9f 695f\n5b 994d\n41 980b\n5f 995d\n45 981b\n49 984b\n4d 985b\n4a 3244\n51 990b\n4e 3254\n55 991b\n20 48a\n3a 5cc\n9c c9fa\naf 6475\n51 9321\n95 6333\n9e c9fe\n53 9325\n97 6337\n75 b539\nbe cdfe\n73 9725\nb7 6737\n71 9729\nb5 673b\n21 48b\nf8 7562\n3b 5cd\n2f a4f5\n15 a3b3\n24 49a\n3e 5dc\nbc cdfa\n71 9721\nb5 6733\nfc 7572\n25 49b\n3f 5dd\n1d ab7b\n2f acfd\n15 abbb\n1b 8965\n1 8823\n9f 6977\n5b 9965\n41 9823\n51 9923\n59 9963\nf0 5500\nd6 53be\n1b a347\na 8cc\nc8 7862\n2 a206\nb 8cd\n70 3702\n1f a357\ne 8dc\ncc 7872\n6 a216\nf 8dd\n74 3712\n1b 896d\n1 882b\n9f 697f\n5b 996d\n41 982b\n49 986b\n4a 3264\n51 992b\n4 89a\n2f a457\n1e 9dc\n9 2261\n10 8928\n3 8887\nb4 ef10\n1e 815e\n1f 89d5\nb 226d\n5 8893\ndd 7bf1\nac 465a\nc3 7aaf\nd 2271\n14 8938\n7 8897\n1b 236d\n1 222b\n15 8993\n49 3261\n94 693a\n50 9928\n43 9887\nf4 ff10\n5e 915e\n4b 326d\n5f 99d5\n45 9893\n54 9938\n98 694a\n4d 3271\n47 9897\n50 138a\n6a 14cc\nf4 5db0\n51 138b\n6b 14cd\nf5 5db1\n54 139a\n6e 14dc\nf8 5dc0\n55 139b\n6f 14dd\nf9 5dc1\n5f 99dd\n45 989b\n15 b33\n2f c75\n60 148a\n7a 15cc\n61 148b\n7b 15cd\n64 149a\n7e 15dc\n65 149b\n7f 15dd\n82 c204\n3 88a7\nb4 ef30\n1e 817e\nc2 d204\n43 98a7\nf4 ff30\n5e 917e\nd0 d300\n51 99a3\n5b b347\n4a 18cc\n5f b357\n4e 18dc\ncb d8ed\n2e 474\n14 332\nd3 d92d\n1c 372\neb dced\nd1 dbab\n34 732\nf3 dd2d\nd9 dbeb\n3c 772\nae 4474\n94 4332\n9c 4372\nb4 4732\ncd d8f1\n16 336\nd5 d931\n1e 376\ned dcf1\nd3 dbaf\n36 736\nf5 dd31\ndb dbef\n3e 776\n1c b72\n6e 1c74\n54 1b32\n79 35e1\n5c 1b72\n9c 4b72\n4a 124e\n2a acc6\nf9 75e1\n6e 1cf4\ned 5651\n54 1bb2\nae 4cf4\n94 4bb2\naf 4655\n16 bb6\n98 ebc8\nef 5655\n56 1bb6\n9f 69ff\nc0 d208\n5b 99ed\n41 98ab\n2e 2c74\n14 2b32\n1c 2b72\nae 6c74\n50 9b20\n94 6b32\nc1 f809\na 224e\n58 9b60\n9c 6b72\n6b b447\n40 188a\n5a 19cc\nae 6cf4\n50 9ba0\n94 6bb2\nb6 cd1c\n9c cbda\naf 6655\n16 2bb6\n2e 8474\n14 8332\n1c 8372\n34 f98\n34 8732\n74 9732\n23 acad\n16 8336\n1c 8b72\n6e 9c74\n54 9b32\n79 b5e1\n5c 9b72\n6f b457\n44 189a\n5e 19dc\n1e 8b76\n56 9b36\n61 b4a3\n7b b5e5\n6e 9cf4\nb2 6d06\ned d651\n54 9bb2\naf c655\n16 8bb6\n2e a474\n1d 9f9\n14 a332\n1c ab72\n84 4a18\n16 ab36\nad e651\n2e acf4\n14 abb2\n1b 8b45\n1 8a03\n3 8a07\n1f 8b55\n5 8a13\n7 8a17\n9 8a43\nd 8a53\n9f 6b57\n5b 9b45\n41 9a03\n5f 9b55\n45 9a13\n49 9a43\n4d 9a53\n1b 8b4d\n1 8a0b\n1f 8b5d\n5 8a1b\n5b 9b4d\n9f 6b5f\n41 9a0b\n5f 9b5d\n45 9a1b\n49 9a63\n5c b9d8\n1b 8b6d\n1 8a2b\n1f 8bd5\n5 8a93\n5f 9bd5\n45 9a93\ne5 fc1b\nff fd5d\n1b 8be5\n1 8aa3\n1c 837a\nba ed4e\n44 1ab8\n6f b675\n5c 937a\n55 1339\n35 adb1\n13 a98d\n5d 1379\na2 e60c\n23 acaf\n3d adf1\n24 ac90\n6e 167c\n97 c11f\nf8 5f60\n34 afb2\n4e b0f4\n77 173d\n57 b1b5\n76 173e\n7a 9746\n25 2cb9\nc4 5098\n56 b1b6\n35 ad91\n65 163b\n7f 177d\n45 b0b3\n5f b1f5\n34 ad92\n7e 177e\na5 eeb9\n63 1ea7\n2d 2cf9\ncc 50d8\n5e b1f6\na1 462b\nbb 476d\nb3 47ad\nb2 47ae\na4 ec90\nee 567c\ne5 563b\nb5 ed91\nff 577d\nb4 ed92\nfe 577e\n9 20e3\ne3 5ea7\nad 6cf9\nf7 57bd\nf6 57be\n13 b2d\n1a b6e\n46 1a3c\n4e 1a7c\n57 1b3d\n56 1b3e\n45 1a3b\n5f 1b7d\n5e 1b7e\n9a 4b6e\nc6 5abc\n1b a145\ne8 de68\n1 a003\nab 4e6f\n1a 21ce\nf4 5f92\nea de6c\n3 a007\n1c 21d2\nf6 5f96\nec de78\n1f a155\n5 a013\nee de7c\n7 a017\ne3 de07\n9 a043\nfc 5fd2\ne7 de17\nd a053\nf8 df68\neb dec7\n11 a103\nbb 4f6f\nfa df6c\ne0 de2a\n13 a107\nfc df78\nef ded7\n15 a113\nfe df7c\ne4 de3a\n17 a117\nf3 df07\n19 a143\ne8 de6a\n1b a147\n16 99c\n27 a417\n18 9c8\n29 a443\n0 88a\n1a 9cc\n2b a447\n1c 9d8\n2d a453\n31 a503\n91 e983\ndb 536f\n35 a513\n95 e993\ndf 537f\n37 a517\n39 a543\n10 98a\n3b a547\n15 2339\n2e 247c\n14 233a\n26 263c\n1a 16e\nb0 6f20\n2e 267c\nb8 6f60\n97 c99f\n9d 6379\ne6 749c\na5 663b\n61 9629\nbf 677d\nb7 67bd\n17 2b3d\n16 2b3e\n5 2a3b\n1f 2b7d\n1e 2b7e\nf0 d5aa\n8e 6a7c\n86 6abc\n85 6a3b\n41 9a29\n9f 6b7d\n5a 9b6c\n40 9a2a\n9e 6b7e\nd5 5339\n6 8a1e\n11 a38b\n2b a4cd\nee 547c\nd4 533a\n2a a4ce\nf6 573e\nfa d746\na5 6cb9\nda 5be6\n21 862b\n3b 876d\n3a 876e\n5d 9379\n6e 967c\n7e 977e\n63 9ea7\n6a 1644\n2d acf9\nc6 5a3c\n2 aa8e\n1c abd0\n1a 8b6e\nab c64d\n2c 8cf0\n12 8bae\n4e 9a7c\n5e 9b7e\n9b 6bcf\nb5 6d11\n57 9bbd\nef d65d\n70 9d00\nb4 6d12\n56 9bbe\nf5 df11\n1b a14d\n1 a00b\nf4 5f9a\n1f a15d\n5 a01b\ne3 de0f\nfd df51\n9 a04b\nfc 5fda\ne7 de1f\nd a05b\neb decf\n11 a10b\nef dedf\n15 a11b\nf3 df0f\n19 a14b\n29 a44b\n95 6339\nae 647c\n43 9287\n50 9328\n94 633a\n38 2de0\nb7 673d\n1c a37a\n86 6a3c\n5 aa3b\n1f ab7d\n1e ab7e\n1b a165\n1 a023\n1a 21ee\nf4 5fb2\neb dee7\n11 a123\n81 4009\n13 a127\n7c 17d2\n3b a565\n10 9a8\n21 a423\n91 4309\n12 9ac\n23 a427\n31 a523\na1 4409\n33 a527\nf5 df31\n1b a16d\n1 a02b\nf4 5fba\ne3 de2f\nfd df71\n9 a06b\nfc 5ffa\neb deef\n11 a12b\nda 79e6\n3b a56d\n21 a42b\n29 a46b\n31 a52b\n3b a5c5\n21 a483\n3f a5d5\n25 a493\n31 a583\n33 a587\nf7 5535\ndd 53f3\nf5 df91\n1b a1cd\n1 a08b\n1f a1dd\n5 a09b\n11 a18b\n3b a5cd\n21 a48b\n3f a5dd\n25 a49b\n31 a58b\n3b a5e5\n21 a4a3\n91 4389\n23 a4a7\nf5 dfb1\n1b a1ed\n1 a0ab\n3b a5ed\n21 a4ab\n7 297\n14 338\n47 1297\n54 1338\nb3 e70d\n34 adb0\n67 1697\n74 1738\n54 b1b0\n87 4297\n94 4338\na7 4697\nb4 4738\nc7 5297\nd4 5338\n10 a38a\n2a a4cc\n15 339\n95 4339\nb5 4739\nbd 4779\n14 3b8\n34 7b8\n54 13b8\n74 17b8\n94 43b8\nb4 47b8\nd4 53b8\nf4 57b8\nf 277\n7 a97\n14 b38\n47 1a97\n54 1b38\n87 4a97\n94 4b38\n8f 4ad7\n9c 4b78\nc7 5a97\nd4 5b38\ncf 5ad7\ndc 5b78\n9d 4b79\nf7 f717\ne6 5c9c\n25 a633\n3f a775\n14 bb8\n65 b633\n7f b775\n54 1bb8\na5 e633\nbf e775\n94 4bb8\ne5 f633\nff f775\nd4 5bb8\n7 2297\n14 2338\n27 2697\n34 2738\n43 9285\n87 6297\n94 6338\n63 9685\na7 6697\nb4 6738\n14 23b8\n34 27b8\n94 63b8\nb4 67b8\n7 2a97\n14 2b38\n87 6a97\n43 9a85\n94 6b38\n4b 9ac5\n8f 6ad7\n9c 6b78\n14 2bb8\n94 6bb8\ncd 58f3\n7 8297\n14 8338\n47 9297\n98 634a\n54 9338\nb8 674a\n67 9697\n74 9738\nce 58f4\n15 8339\nee 5cf4\nd4 5bb2\n35 8739\ndc 5bf2\nf6 5d34\n3d 8779\n3a 2dee\nb9 674b\n75 9739\n14 83b8\n98 6b4a\n47 9a97\n54 9b38\n81 62a3\n9b 63e5\n48 30ea\n15 8b39\n99 6b4b\n55 9b39\nb2 6d0c\n98 6bca\n54 9bb8\ncd 78f3\n7 a297\n14 a338\ned 7cf3\n27 a697\n34 a738\ndf 79df\nce 78f4\n15 a339\nee 7cf4\nd4 7bb2\n35 a739\n1b a345\n1 a203\nc9 7863\n3 a207\ncb 786f\n1f a355\n5 a213\ncd 7873\n7 a217\n9 a243\nb a247\n3b a745\n10 b88\n21 a603\ne9 7c63\n12 b8c\n23 a607\neb 7c6f\n3f a755\n14 b98\n25 a613\ned 7c73\n16 b9c\n27 a617\n1a bcc\n0 a8a\n2b a647\nbc 4578\naf 44d7\n7 aa97\n14 ab38\n2d 2ef3\ncd d059\n47 3035\nf aad7\n1c ab78\n35 2f33\n4f 3075\n15 ab39\ncd d0d9\n47 30b5\nad 44db\n49 3ae1\nab 44e7\na9 44eb\n1b a34d\n1 a20b\n1f a35d\n5 a21b\nc2 7806\n9 a24b\nda 7bc6\n3b a74d\n21 a60b\nde 7bd6\n3f a75d\n25 a61b\ne2 7c06\n29 a64b\n1b a365\n1 a223\n3b a765\n10 ba8\n21 a623\n9c 4978\n8f 48d7\n9e e356\n8d 48db\n9a e366\n89 48eb\n1b a36d\n1 a22b\nda 7be6\n3b a76d\n21 a62b\n2d a459\ncc 78d2\n1b a3c5\n1 a283\ne9 7ce3\n30 a728\n23 a687\nfc 5578\nef 54d7\neb 54e7\ne9 54eb\n1b a3cd\n1 a28b\n3b a7cd\n21 a68b\n9d 6951\n83 680f\nde f356\ncd 58db\nda f366\n1d 83d1\n3 828f\nc9 58eb\n2b 46d\n11 32b\n6f 147d\n55 133b\n77 173f\nc5 5099\n57 b1b7\n35 ad93\n7f 177f\ncd 50d9\n5f b1f7\nbb 476f\nb3 47af\nb5 ed93\nff 577f\n2d c71\n13 b2f\n57 1b3f\n5f 1b7f\n7c 175a\nad 4cf1\n93 4baf\ndf 5b7f\nf1 5d01\nd7 5bbf\n1b a945\n1 a803\n3 a807\n1f a955\n5 a813\n7 a817\n9 a843\n42 928c\n86 629e\nd a853\n11 a903\n13 a907\n15 a913\n17 a917\n1b a947\n7d 9771\n63 962f\n1d a953\n7f 977d\n65 963b\n2f 247d\n15 233b\nd4 fbb8\n37 273f\ndc fbf8\n3f 277f\nc4 7290\n2e 4de\nb8 4dc2\n7b 976d\n61 962b\nbf 677f\n11 3ab\n2b 4ed\n17 2b3f\n1f 2b7f\nc0 72a0\n2a 4ee\n5b 9b6d\n41 9a2b\n9f 6b7f\nb1 6d01\n53 9bad\n97 6bbf\nab 446d\n91 432b\nc2 d20e\ndc d350\n5d 99f3\nb3 472f\nfe d754\ne4 d612\n7f 9df7\nef 547d\nd5 533b\n3b 876f\n33 87af\na1 6e01\nb 4f\n7f 977f\n5f 9b7f\nb5 6d13\n71 9d01\n57 9bbf\n1b a94d\n1 a80b\n1f a95d\n5 a81b\n9 a84b\nd a85b\n4e 1256\n11 a90b\n15 a91b\n19 a94b\naf 647d\n51 9329\n95 633b\n73 972d\n38 2de2\nb7 673f\nd4 7390\n3e 5de\n2f a4fd\n15 a3bb\n21 4ab\n3b 5ed\nd0 73a0\n3a 5ee\n1f ab7f\n1b a965\n1 a823\n11 a923\n19 a963\n52 93ac\nb0 6500\n96 63be\ne 8de\n2 a226\nb 8ed\n70 3722\n89 424b\na 8ee\n1b a96d\n1 a82b\n1e 9de\n99 434b\n1a 9ee\n4d 1273\n10 a928\n3 a887\n1e a15e\n3d f51\n23 e0f\n4f 127f\n1f a9d5\n5 a893\n3f f5d\n25 e1b\n14 a938\n7 a897\n27 e1f\n5b 136f\n11 a983\n4b 104d\n31 f0b\n5d 1373\n13 a987\n4d 1051\n33 f0f\n5f 137f\n15 a993\n4f 105d\n35 f1b\n51 13ab\n6b 14ed\n6a 14ee\n1b a9cd\n1 a88b\n1f a9dd\n5 a89b\n4e 12d6\n11 a98b\n7e 15de\nfe 7d5c\ne4 7c1a\n0 aa2\n1a be4\n61 14ab\n7b 15ed\n7a 15ee\n82 e204\n3 a8a7\nf8 df42\n1e a17e\n90 e300\n11 a9a3\n4b 106d\n31 f2b\n4e 18de\n1b a9ed\n80 e208\n1 a8ab\n5e 19de\n2e 47c\n14 33a\nb2 6d0e\ned d659\n6e 9cfc\n54 9bba\n67 3635\nae 447c\n94 433a\nee dcfc\nd4 dbba\ne7 7635\nb4 473a\n27 a6b5\nbc 477a\n1c b7a\n7 aab5\n9c 4b7a\n2e cfc\n3f a777\nad 4659\n14 bba\n6e 1cfc\n7f b777\ned 5659\n54 1bba\na7 4eb5\nae 4cfc\nbf e777\n94 4bba\n2e 847c\n14 833a\nb2 ed0e\n67 b635\n34 873a\n3c 877a\n6e 947c\n54 933a\n74 973a\n1b ab45\n1 aa03\n3 aa07\n1f ab55\n5 aa13\n7 aa17\n9 aa43\n71 1581\nd aa53\n1c 8b7a\n4f 1077\n9b 63e7\n79 b5e9\n2e 8cfc\nad c659\n27 2635\n14 8bba\n89 e0c9\n47 10b7\naf 44df\nd1 db09\n4b 3ae5\n2e a47c\n14 a33a\n34 a73a\nab 44ef\n1b ab4d\n1 aa0b\n1f ab5d\n5 aa1b\n9 aa4b\n87 489f\n6c 145a\n9d 49f1\n83 48af\n1b ab65\n1 aa23\n9 aa63\n8f 48df\n1b ab6d\n1 aa2b\n1f abd5\n5 aa93\neb 54ef\n1b abcd\n1 aa8b\nc7 589f\n17 8395\ndd 59f1\nc3 58af\n78 17c0\n79 17c1\nd9 53c1\nf9 57c1\n20 682\n3a 7c4\n60 1682\n7a 17c4\n80 4282\n9a 43c4\na0 4682\nba 47c4\nc0 5282\nda 53c4\ne0 5682\nfa 57c4\n41 1283\n5b 13c5\n86 601c\n28 8ec8\nc1 5283\ndb 53c5\ne1 5683\nfb 57c5\na6 4cb4\n2d 86f9\ne6 5cb4\na2 468e\nbc 47d0\n25 8c33\n3f 8d75\n43 128f\n5d 13d1\ne7 5cb5\n63 168f\n7d 17d1\na3 468f\nbd 47d1\nf4 5dba\nc3 528f\ndd 53d1\ne3 568f\nfd 57d1\n4 292\n1e 3d4\n24 692\n3e 7d4\n44 1292\n5e 13d4\n64 1692\n7e 17d4\n84 4292\n9e 43d4\na4 4692\nbe 47d4\nc4 5292\nde 53d4\ne4 5692\nfe 57d4\nb6 4514\n9c 43d2\n1f 8977\nb9 47c9\nb8 47ca\nd1 fb21\n3b 8d6f\nbe 47d6\nbd 47d9\nbc 47da\n18 bc0\n58 1bc0\n37 871f\nfd 5d7b\n98 4bc0\ne 2dc\nd8 5bc0\n4e 12dc\n19 bc1\n59 1bc1\ne4 5c3a\nfe 5d7c\n99 4bc1\ncc 7272\nf 2dd\nd9 5bc1\n4f 12dd\n0 a82\n1a bc4\n40 1a82\n5a 1bc4\nff 5d7f\n80 4a82\n9a 4bc4\nc0 5a82\n21 8609\nda 5bc4\n1 a83\n1b bc5\n41 1a83\n5b 1bc5\nde 53d6\nc1 5a83\ndb 5bc5\nf2 5524\nd8 53e2\nc1 52a3\ndb 53e5\nda 53e6\n2 a8e\n1c bd0\n42 1a8e\n5c 1bd0\n82 4a8e\n9c 4bd0\nc2 5a8e\ndc 5bd0\n3 a8f\nda 7b66\n1d bd1\n43 1a8f\n5d 1bd1\n83 4a8f\n9d 4bd1\nc3 5a8f\ndd 5bd1\n4 a92\n1e bd4\n44 1a92\n5e 1bd4\n84 4a92\n9e 4bd4\n25 8619\nc4 5a92\nde 5bd4\n5 a93\n1f bd5\nfe 57d6\nfd 57d9\nfc 57da\n7f 9d7f\ne1 56a3\nfb 57e5\nfa 57e6\nf9 57e9\n44 9010\n2a 8ece\nf8 57ea\n20 2682\n3a 27c4\n80 6282\n9a 63c4\na0 6682\nba 67c4\n1 2283\n1b 23c5\nd1 730b\neb 744d\n52 39ae\na1 6683\nbb 67c5\n74 9f18\n68 34ca\na6 6cb4\nc6 70b4\n25 ac33\n3f ad75\n3 228f\n1d 23d1\n76 371e\na7 6cb5\ned 7459\n9 2e1\n54 39ba\nc7 70b5\n83 628f\n9d 63d1\n89 42e1\nd4 79ba\n4 2292\n1e 23d4\n24 2692\n3e 27d4\na4 6692\n60 9680\nbe 67d4\n5 2293\n1f 23d5\nd5 731b\nb 2e5\nef 745d\n56 39be\n41 9281\n85 6293\n9f 63d5\n8b 42e5\nd6 79be\n4c 30da\n9a 4bc6\n2b ace7\naa e644\n99 4bc9\n2f acf7\nae e654\n9d 4bd9\n18 2bc0\n79 9d69\n52 9b86\nbd 6d7b\n98 6bc0\ne 22dc\n0 2a82\n1a 2bc4\n80 6a82\n9a 6bc4\nfc 57d2\n7f 9d77\n1 2a83\n1b 2bc5\n81 6a83\n9b 6bc5\n48 38ca\nf9 57e1\n2a 8ec6\nf8 57e2\n2 2a8e\n1c 2bd0\n82 6a8e\n9c 6bd0\n4 2a92\n1e 2bd4\n5 2a93\n1f 2bd5\n6f bcf7\nee f654\ndd 5bd9\n41 9a81\n85 6a93\n9f 6bd5\n1 201\n4c 38da\nea f664\nd9 5be9\n0 8282\n1a 83c4\n20 8682\n3a 87c4\n40 9282\n9e 63d6\n5a 93c4\n60 9682\nbe 67d6\n7a 97c4\n21 8683\n3b 87c5\nf1 d70b\n6b 36e7\n72 9dae\nac 6478\n41 9283\n9f 63d7\n5b 93c5\n43 928f\n5d 93d1\nca 58ee\n4 8292\n1e 83d4\nea 5cee\n24 8692\n3e 87d4\n44 9292\n5e 93d4\n64 9692\n7e 97d4\neb 5cef\n25 8693\n3f 87d5\nf5 d71b\n6f 36f7\n76 9dbe\n65 9693\n7f 97d5\nda 53c6\nfa 57c6\nf9 57c9\nf8 57ca\n7b 9d6f\n18 8bc0\n19 8bc1\n9d 6bd3\nb7 6d15\n59 9bc1\n0 8a82\n1a 8bc4\n1 8a83\n1b 8bc5\n9f 6bd7\nac 6c78\n41 9a83\n5b 9bc5\n99 63e1\nb2 6524\n98 63e2\n9a 63e6\n78 b5e8\nbd 67d9\na1 66a3\nbb 67e5\n67 9e97\nb8 6f4a\n74 9f38\n68 34ea\nba 67e6\n24 41a\n3e 55c\n55 39b1\nb9 67e9\nc6 523c\nc9 78eb\n3 a28f\n1d a3d1\n48 18e0\nc7 523d\n9 82e1\ned f459\n54 b9ba\nf8 57c2\n7b 9d67\n3b 874d\n21 860b\nda 5bc6\n6b bce7\nea f644\nd9 5bc9\n78 97c0\nbc 67d2\nad 4c59\n3f ad77\nd0 d920\n57 399d\nb9 67e1\nb8 67e2\n3c 558\n53 39ad\n40 9a82\n5a 9bc4\n9e 6bd6\n1a 83c6\n36 8514\n1c 83d2\nc1 f229\n1e 83d6\n76 9514\n5c 93d2\n5e 93d6\nea 7e6e\n2d ed9\nb7 6535\n9d 63f3\n59 93e1\nb6 6536\n72 9524\n58 93e2\n9f 63f7\n41 92a3\n5b 93e5\n5a 93e6\n9f e17f\n29 ee9\n19 8bc9\n32 8d0c\n2b 2645\n18 8bca\n36 8d1c\n2f 2655\n1c 8bda\n5e 9bd6\n9f 6bf7\n41 9aa3\n5b 9be5\n5a 9be6\n5a 93c6\n9f e15f\n29 ec9\n36 a514\n1c a3d2\n96 e994\nc6 523e\nbb 4547\n19 a3e1\n32 a524\n18 a3e2\n1 a2a3\n1b a3e5\n88 42c8\n1a a3e6\n21 a6a3\n3b a7e5\n5a 9bc6\n36 ad1c\n1c abda\n1 aaa3\n1b abe5\ne0 5420\n7f b5df\n88 4ac8\n1a abe6\nfc 7558\n18 3e0\n38 7e0\n58 13e0\n78 17e0\n37 a59f\n98 43e0\nb8 47e0\n77 b59f\nd8 53e0\nf8 57e0\nfd 7559\n19 3e1\n39 7e1\n59 13e1\n79 17e1\n99 43e1\nb9 47e1\nfe 755c\n0 2a2\ne4 741a\n1a 3e4\n20 6a2\n3a 7e4\n40 12a2\n5a 13e4\n60 16a2\n7a 17e4\n80 42a2\n9a 43e4\na0 46a2\nba 47e4\nc0 52a2\nda 53e4\ne0 56a2\nfa 57e4\ne5 741b\nff 755d\n1 2a3\n1b 3e5\n41 12a3\n5b 13e5\nb0 6f00\n1a 14e\nec 7ed0\n56 111e\nf0 7f00\n5a 114e\n9c 4150\n82 400e\n86 401e\n92 410e\n96 411e\n20 aea8\ndc 5150\nc2 500e\n24 aeb8\nc6 501e\n30 afa8\nd2 510e\n34 afb8\nd6 511e\n58 1be0\n77 bd9f\nd8 5be0\nfd 7d59\n19 be1\n59 1be1\nd9 5be1\n40 1aa2\n5a 1be4\ne5 7c1b\nff 7d5d\n1 aa3\n1b be5\n41 1aa3\n5b 1be5\n18 23e0\n38 27e0\n98 63e0\nb8 67e0\n19 23e1\n39 27e1\na0 c408\n0 22a2\n1a 23e4\n20 26a2\n3a 27e4\n80 62a2\n9a 63e4\na0 66a2\nba 67e4\na1 c409\n1 22a3\n1b 23e5\n1c 170\n2 2e\n5c 1170\n42 102e\n87 e09f\ne8 7ee0\n52 112e\n9c 4170\n82 402e\n18 2be0\n7c 35da\n98 6be0\n32 5a4\nfc 75da\na0 cc08\n0 2aa2\n1a 2be4\na1 cc09\n1 2aa3\n1b 2be5\nfc f558\n18 83e0\nb6 6534\n9c 63f2\n58 93e0\nfd f559\n19 83e1\nfe f55c\ne4 f41a\n0 82a2\n1a 83e4\n20 86a2\n3a 87e4\n40 92a2\n9e 63f6\n5a 93e4\n60 96a2\nbe 67f6\n7a 97e4\ne5 f41b\nff f55d\n1 82a3\n1b 83e5\n21 86a3\n3b 87e5\n12 18e\n5c 11d0\n42 108e\n40 9aa2\n9e 6bf6\n5a 9be4\nba 4546\n18 a3e0\n0 a2a2\n1a a3e4\n20 a6a2\n3a a7e4\nba 4d46\n18 abe0\n7c b5da\n0 aaa2\n1a abe4\nf9 5561\n7e b5de\n86 421e\nca 524e\n1c 370\n2 22e\n1c 3d0\n2 28e\n7b 3de7\ne0 7602\nfa 7744\n5c 13d0\n42 128e\n9c 43d0\n82 428e\ndc 53d0\nc2 528e\nd9 f9c9\n3c 2550\n22 240e\n2e 8e5c\n2a 244e\nef d67d\n70 9d20\nb4 6d32\n3a 254e\n3e 8ffc\n24 8eba\n9c 6150\n82 600e\n6e 1c5e\n4e 90fc\n34 8fba\n92 610e\n7e 1d5e\n5e 93fc\n44 92ba\nbc 6550\na2 640e\n54 93ba\n6e 94fc\nb2 650e\n2f 24d5\n15 2393\n5f 9bdf\nec 5ef2\n12 212e\nf4 5f32\n1a 216e\n9c 6170\n82 602e\n6e 1c7e\n8a 606e\n92 612e\n7e 1d7e\n1c 21d0\nf6 5f94\n2 208e\ne 8adc\n12 218e\n3c 25d0\n22 248e\n2e 8edc\n32 258e\n9c 61d0\n82 608e\n42 908c\n86 609e\n92 618e\nbc 65d0\na2 648e\nb2 658e\nf6 5fb4\n1c 21f0\n2 20ae\n12 21ae\ne1 fc09\n2a 264e\n1d 29f3\n9c 6350\n82 620e\n6e 1e5e\n4e b8d6\n42 920c\n86 621e\n4a 924c\n8e 625e\n31 d8b\n3d 2df3\nbc 6750\na2 660e\n16 8996\n1c 2370\n2 222e\n1e 89d6\nc1 f829\na 226e\n96 c996\n9c 6370\n82 622e\n6e 1e7e\ncd f253\n4e b8f6\n9e c9d6\n8a 626e\nc4 5818\n56 b936\n1c 23d0\n2 228e\n3c 27d0\n22 268e\n9c 63d0\n82 628e\nbc 67d0\na2 668e\n1c 2950\n2 280e\na 284e\n4c b0f0\n32 afae\n4d 9259\n12 290e\n1a 294e\n1c 29d0\n2 288e\n4d 92d9\n12 298e\n9c 69d0\n82 688e\n78 17c2\nd1 d981\n1a 3c6\nf1 dd81\n3a 7c6\n9c e3d8\n5a 13c6\nbc e7d8\n7a 17c6\nd3 d98d\n36 514\n1c 3d2\na6 4cb6\n76 1514\n5c 13d2\n2d 86fb\ne6 5cb6\nc1 7229\nd5 d991\n1e 3d6\ne1 7629\nf5 dd91\n3e 7d6\n5e 13d6\n7e 17d6\n2b 4c5\n11 383\nb6 453e\n31 78b\n35 79b\na8 eec0\n12 810e\nac eed0\n16 811e\nb0 ef00\n1a 814e\nc8 f2c0\n32 850e\nf0 772a\ncc f2d0\n36 851e\n5c 9150\n42 900e\ne0 fe00\n4a 904e\ne8 fec0\n52 910e\nec fed0\n56 911e\nf0 ff00\n5a 914e\n72 950e\nb3 4505\n99 43c3\nb7 4515\n9d 43d3\nbb 47c7\nb9 47cb\n22 8c2e\n3c 8d70\nbd 47db\n32 d04\n18 bc2\n72 1d04\n58 1bc2\nb2 4d04\n98 4bc2\n39 8749\nf2 5d04\nd8 5bc2\n1a bc6\nf7 5515\ndd 53d3\n9c ebd8\n5a 1bc6\nec 5478\ndf 53d7\nf3 5525\nd9 53e3\ndb 53e7\n36 d14\n1c bd2\n76 1d14\n5c 1bd2\nb6 4d14\n9c 4bd2\nf6 5d14\n3d 8759\ndc 5bd2\nfd 57db\nfb 57e7\nf9 57eb\nd3 f98d\n36 2514\n1c 23d2\n62 9ca4\ne1 d601\na6 6cb6\nf3 fd8d\n3c 27d2\nc6 70b6\nd5 f991\n1e 23d6\nf5 fd91\n3e 27d6\n31 783\n35 793\n2b ccd\ne8 7c62\n22 a606\n11 b8b\n2f cdd\nec 7c72\n26 a616\n15 b9b\n1c 8170\n2 802e\na0 ee20\na 806e\na8 eee0\n12 812e\nb0 ef20\n1a 816e\n3c 8570\n22 842e\nc8 f2e0\n32 852e\n5c 9170\n42 902e\ne0 fe20\n4a 906e\ne8 fee0\n52 912e\nf0 ff20\n5a 916e\n72 952e\n6b 1ced\n62 b626\n51 1bab\nb9 47c3\nbd 47d3\na8 4c68\n9b 4bc7\nbd 6ddb\n79 9dc9\n32 2d04\n18 2bc2\nb2 6d04\n54 9bb0\n98 6bc2\nfd 57d3\n1a 2bc6\n56 9bb4\n9a 6bc6\nf9 57e3\n36 2d14\n1c 2bd2\nb6 6d14\n58 9bc0\n9c 6bd2\n1e 2bd6\n2d 86d1\nf3 5d2d\nea f666\nd9 5beb\n2b 24e5\nb1 c509\n11 23a3\n12 818e\n5c 91d0\n42 908e\n56 919e\n35 279b\n31 27ab\nf3 5505\nd9 53c3\ne8 5468\ndb 53c7\nfb 57c7\n32 8d04\n18 8bc2\n72 9d04\nb6 6d16\n58 9bc2\nb3 6525\n99 63e3\n76 9d14\n5c 9bd2\nbb 67e7\n74 9f3a\nb9 67eb\n35 2793\n31 27a3\n52 91ae\n2f 2cdd\na8 cc60\n15 2b9b\n2b 2ced\n11 2bab\nf9 57c3\ne8 5c68\n22 860c\ndb 5bc7\n79 97c1\nbd 67d3\nb9 67e3\n9b 6be7\n2b 2445\n18 89ca\n11 2303\nb3 6d2d\n99 6beb\ncc 587a\n6 821e\na 824e\ne 825e\n3c 8750\ne8 5c6a\n53 bba5\n22 860e\n57 bbb5\nec 5c7a\n26 861e\n46 921e\n4a 924e\n4e 925e\n33 8505\n19 83c3\n28 8468\n1b 83c7\n37 8515\n1d 83d3\n2c 8478\n1f 83d7\n3b 87c7\n3f 87d7\n77 9515\n5d 93d3\n6c 9478\n5f 93d7\nb7 6537\n73 9525\n59 93e3\n5b 93e7\n7f 97d7\n7b 97e7\n1c 8370\n2 822e\na 826e\n3c 8770\n22 862e\n4a 926e\n28 8c68\n1b 8bc7\n33 8d0d\n19 8bcb\n2c 8c78\n1f 8bd7\n37 8d1d\n1d 8bdb\n5b 9be7\n73 9d2d\nb7 6d3f\n59 9beb\nc8 58ea\n1c 83d0\n2 828e\ncc 58fa\n6 829e\n5c 93d0\n42 928e\nb8 c560\n25 249b\n3f 25dd\n46 929e\n73 9505\nb7 6517\n59 93c3\n68 9468\nac 647a\n5b 93c7\n7b 97c7\n37 a515\n1d a3d3\n33 a525\n19 a3e3\n89 42c9\n1b a3e7\nd8 71c8\na9 46c9\n3b a7e7\nf8 75c8\n68 9c68\nac 6c7a\n5b 9bc7\n37 ad1d\n1d abdb\n89 4ac9\n1b abe7\nd8 79c8\n33 ad2d\n19 abeb\nb 2247\n12 890e\nea 7c6c\n24 a610\nd0 7b2a\nf 2257\n16 891e\n5c 9950\n42 980e\n46 981e\n4b 3247\n52 990e\n4f 3257\n56 991e\n1c 8970\n2 882e\n46 9a1e\n1c 8b70\n2 8a2e\n5c 9b70\n42 9a2e\n25 2c3b\n3f 2d7d\nf6 df14\n1c a150\n2 a00e\nac 4e7a\nfe df54\ne4 de12\na a04e\nec ded2\n12 a10e\n16 a11e\nf4 df12\n1a a14e\n2a a44e\n36 a51e\ndf 5bf5\nc5 5ab3\n3a a54e\ne3 5c25\nc9 5ae3\nf6 df34\n1c a170\n2 a02e\nfe df74\ne4 de32\na a06e\nec def2\n12 a12e\nf4 df32\n1a a16e\n3c a570\n22 a42e\n32 a52e\nf6 df94\n1c a1d0\n2 a08e\nc6 503c\nac 4efa\n12 a18e\n32 a58e\nf6 553c\ndc 53fa\nf6 dfb4\n1c a1f0\n2 a0ae\n12 a1ae\nc8 786a\n1c a350\n2 a20e\na a24e\ne a25e\ne8 7c6a\n3c a750\n22 a60e\nec 7c7a\n26 a61e\n2a a64e\n1c a370\n2 a22e\n6 894\na a26e\n3c a770\n22 a62e\nc8 78ea\n1c a3d0\n2 a28e\n81 4221\ncc 78fa\n6 a29e\n6 a81e\nd5 7139\n12 a90e\n16 a91e\n1a a94e\n1c a970\n2 a82e\nfc 755a\n32 524\n18 3e2\n38 7e2\na7 e495\n72 1524\n58 13e2\n78 17e2\nb2 4524\n98 43e2\nd1 d9a1\nfe 755e\n1a 3e6\nf1 dda1\n3a 7e6\n9c e3f8\n5a 13e6\nbc e7f8\n7a 17e6\nfc 7d5a\n32 d24\n18 be2\na7 ec95\n72 1d24\n58 1be2\nb2 4d24\n98 4be2\nf2 5d24\n39 8769\nd8 5be2\nfe 7d5e\n1a be6\n9c ebf8\n5a 1be6\n32 2524\nb8 c548\n18 23e2\n24 8e30\nba c54c\na0 c40a\nd1 f9a1\n1a 23e6\n26 8e34\n32 2d24\nb8 cd48\n18 2be2\nb2 6d24\n98 6be2\nba cd4c\na0 cc0a\n1a 2be6\nfc f55a\n32 8524\n18 83e2\nfe f55e\n1a 83e6\n32 8d24\nfc fd5a\n18 8be2\n72 9d24\nb6 6d36\n58 9be2\nfe fd5e\n1a 8be6\n32 ad24\n18 abe2\n18 3c8\nb3 e727\na2 4cac\n58 13c8\nf3 f727\ne2 5cac\n78 17c8\nb8 47c8\n21 8c2b\n3b 8d6d\nf8 57c8\n61 9c2b\nbf 6d7f\n7b 9d6d\n19 3c9\na3 4cad\n59 13c9\ne3 5cad\n79 17c9\n2b a4e7\n99 43c9\n6b b4e7\nd9 53c9\n1c 3d8\n3c 7d8\nc6 50bc\n5c 13d8\nf7 f737\ne6 5cbc\n2e a4f6\n9c 43d8\n5 883b\n1f 897d\nbc 47d8\nfc 57d8\n65 9c3b\n7f 9d7d\nda 736e\n1d 3d9\nfa 776e\n3d 7d9\nc7 50bd\n5d 13d9\ne7 5cbd\n7d 17d9\n2f a4f7\n9d 43d9\n6f b4f7\ndd 53d9\nf0 7722\n33 78d\n32 78e\n77 179d\n76 179e\nd7 db95\n2d 67b\n73 17ad\n72 17ae\nb6 451c\n9c 43da\nf6 dd9c\nef 76d5\n1f 897f\nae 46dc\na1 468b\nbb 47cd\nbe 47de\n29 a643\n18 bc8\n69 b643\n58 1bc8\na9 e643\n2a ace6\n98 4bc8\ne9 f643\n6a bce6\nd8 5bc8\n6a b644\n59 1bc9\nf2 552c\nd8 53ea\nee 56dc\nea 56ec\n2d a653\n1c bd8\nad e653\n2e acf6\n9c 4bd8\ned f653\n6e bcf6\ndc 5bd8\nfe 57de\ne1 56ab\nfb 57ed\n46 9014\n2c 8ed2\nfa 57ee\n18 23c8\na2 6cac\n38 27c8\nb8 67c8\n21 ac2b\n3b ad6d\n39 27c9\n99 63c9\nb9 67c9\n1c 23d8\na6 6cbc\n3c 27d8\nbc 67d8\n25 ac3b\n3f ad7d\n3d 27d9\n9d 63d9\n16 b9e\n56 1b9e\n6c 1cf0\neb 564d\n52 1bae\n8a 4acc\n8e 4adc\n81 4a8b\n9b 4bcd\nb4 4d10\n9a 4bce\n85 4a9b\n9f 4bdd\n9e 4bde\n18 2bc8\n98 6bc8\nce 5adc\n1c 2bd8\n9c 6bd8\nc5 5a9b\ndf 5bdd\nde 5bde\nd1 5983\n18 83c8\n9c 63da\nb6 651c\n58 93c8\nd2 5984\n19 83c9\nb7 651d\n9d 63db\n59 93c9\nd5 5993\n1c 83d8\n5c 93d8\nda f36e\nd6 5994\n1d 83d9\n5d 93d9\n73 178d\n33 27ad\n32 27ae\ne1 568b\nfb 57cd\nfa 57ce\n99 63e9\naa 66ec\na1 66ab\nbb 67ed\nba 67ee\n55 39b9\nd2 7984\n19 a3c9\n27 e17\nd5 7993\n1c a3d8\nd6 7994\n1d a3d9\na9 cc63\n16 2b9e\nab 664d\n2c 2cf0\n12 2bae\nca 5acc\nc1 5a8b\ndb 5bcd\nf4 5d10\nda 5bce\n6f 1655\n18 abc8\n31 2f83\n4b 30c5\n8e 6adc\n98 c340\n19 89e3\n1c abd8\n35 2f93\n4f 30d5\n41 9a89\n85 6a9b\n9f 6bdd\n5a 9bcc\n40 9a8a\n9e 6bde\n69 9e6b\n32 850c\n18 83ca\nb6 ed9e\n6b b6c5\n36 851c\n1c 83da\n6f b6d5\nfc f772\n25 869b\n3f 87dd\n3e 87de\n76 951c\n5c 93da\nb7 653d\n9d 63fb\n59 93e9\nb6 653e\n72 952c\n58 93ea\n6e 96dc\n65 969b\n7f 97dd\n7e 97de\na 8acc\nd8 fb62\n1 8a8b\n1b 8bcd\ndc fb72\n5 8a9b\n1f 8bdd\n13 218f\n4e 9adc\na8 6c40\n8e 6afe\n4a 9aec\n5e 9bde\nb9 6d41\n41 9aab\n9f 6bff\n5b 9bed\nb6 651e\n72 950c\n58 93ca\n36 a51c\n1c a3da\nbb 454f\nd2 79a4\n19 a3e9\n8e 6ade\n4a 9acc\n74 9d10\n5a 9bce\ne aadc\nac 4c52\na aaec\n18 3e8\n38 7e8\n58 13e8\n78 17e8\n98 43e8\nb8 47e8\nd8 53e8\nf8 57e8\n19 3e9\n39 7e9\n59 13e9\n79 17e9\n99 43e9\nb9 47e9\n29 a663\n18 be8\n69 b663\n58 1be8\na9 e663\n98 4be8\ne9 f663\nd8 5be8\n6a b664\n59 1be9\naa e664\n99 4be9\n18 23e8\n38 27e8\n98 63e8\nb8 67e8\n19 23e9\n39 27e9\n18 2be8\n98 6be8\nd1 59a3\n18 83e8\nb6 653c\n9c 63fa\n58 93e8\nd2 59a4\n19 83e9\nb6 6d3c\n9c 6bfa\n58 9be8\n19 8be9\nd1 79a3\nba 454e\n18 a3e8\n26 e36\n31 2fa3\nd1 d109\n4b 30e5\nbf 47f5\na5 46b3\n9d 4973\n3b 87ef\n9f 4977\n7e 155e\n95 49b3\n97 49b7\n2d aed1\n9f 4bf5\n6e 165e\n85 4ab3\n87 4ab7\na9 6ccb\n65 9cb9\n17 8317\ndd 5973\n7b 97ef\ncd 5a73\n32 50c\n18 3ca\nbc 4df0\na2 4cae\n78 17ca\n36 51c\n1c 3da\n3c 7da\nc6 50be\n7c 17da\ned def3\n13 a12f\ne8 7462\n2b 4cd\n11 38b\nec 7472\n2f 4dd\n15 39b\n33 78f\n73 17af\nb7 451d\n9d 43db\nbb 47cf\nab 4ec5\nf7 551d\ndd 53db\nf3 552d\nd9 53eb\naf 4ed5\nfb 57ef\n36 251c\n1c 23da\n3c 27da\nb5 4d11\n9b 4bcf\n9f 4bdf\ndf 5bdf\nb0 c722\n37 279f\n33 27af\nb3 652d\n99 63eb\nbb 67ef\nf5 5d11\ndb 5bcf\n41 9a8b\n5b 9bcd\n9f 6bdf\nae 6e7e\n6a 9e6c\nb5 6d31\n9b 6bef\nd2 5986\n33 850d\n19 83cb\nd6 5996\n37 851d\n1d 83db\n3f 87df\n77 951d\n5d 93db\nb7 653f\n73 952d\n59 93eb\n7f 97df\n35 8d11\n1b 8bcf\n1f 8bdf\nb9 6d43\n75 9d31\n5b 9bef\nb7 651f\n73 950d\n59 93cb\nd6 7996\n37 a51d\n1d a3db\nd2 79a6\n33 a52d\n19 a3eb\n3b a7ef\n59 9961\n9d 6973\n75 9d11\n5b 9bcf\n32 52c\n18 3ea\nf1 d709\n72 9dac\nb6 6dbe\n6b 36e5\n38 7ea\n78 17ea\nb2 452c\n98 43ea\nf2 ddac\neb 76e5\nb8 47ea\n32 852c\n18 83ea\nb6 edbe\n6b b6e5\n32 8d2c\n2b 2665\n18 8bea\n9d 497b\na6 e436\n95 49bb\ne5 56b3\nff 57f5\nfd 57f9\n2e 8ede\nfc 57fa\ne3 56af\nfd 57f1\n2e 8ed6\nee f674\ndd 5bf9\na5 66b3\n61 96a1\nbf 67f5\n6c 34fa\nbd 67f9\na3 66af\nbd 67f1\n43 92af\nbb 6545\na1 6403\n5d 93f1\n76 9534\nba 6546\n5c 93f2\n5e 93f6\n2d ef9\n5e 9bf6\na1 6c0b\nbb 6d4d\n5d 9bf9\n51 31ab\nbf 4557\n3 a2af\n1d a3f1\n36 a534\n1c a3f2\n2 2ae\n1c 3f0\n22 6ae\n3c 7f0\n42 12ae\n5c 13f0\n62 16ae\n7c 17f0\n82 42ae\n9c 43f0\na2 46ae\nbc 47f0\ne2 56ae\nfc 57f0\n3 2af\n1d 3f1\n23 6af\n3d 7f1\n43 12af\n5d 13f1\n63 16af\n7d 17f1\n83 42af\n9d 43f1\na3 46af\nbd 47f1\n4 2b2\n1e 3f4\n24 6b2\n3e 7f4\n44 12b2\n5e 13f4\n64 16b2\n7e 17f4\n84 42b2\n9e 43f4\na4 46b2\nbe 47f4\nc4 52b2\nde 53f4\ne4 56b2\nfe 57f4\n5 2b3\n1f 3f5\n2 aae\n1c bf0\n42 1aae\n5c 1bf0\n82 4aae\n9c 4bf0\n3 aaf\n1d bf1\n43 1aaf\n5d 1bf1\n83 4aaf\n6c 165a\n9d 4bf1\n4 ab2\n1e bf4\n44 1ab2\n5e 1bf4\n5 ab3\n1f bf5\n2 22ae\n1c 23f0\n22 26ae\n3c 27f0\n82 62ae\n9c 63f0\na2 66ae\nbc 67f0\n3 22af\n1d 23f1\n23 26af\n3d 27f1\na4 c418\n4 22b2\n1e 23f4\n24 26b2\n3e 27f4\n84 62b2\n40 92a0\n9e 63f4\n60 96a0\na4 66b2\nbe 67f4\na5 c419\n5 22b3\n1f 23f5\n2 2aae\n1c 2bf0\n82 6aae\n9c 6bf0\na4 cc18\n4 2ab2\n1e 2bf4\n2 82ae\n1c 83f0\na0 6402\nba 6544\n42 92ae\n5c 93f0\n3 82af\n1d 83f1\n4 82b2\n1e 83f4\n24 86b2\n3e 87f4\n44 92b2\na2 6406\n5e 93f4\n64 96b2\n7e 97f4\n5 82b3\n1f 83f5\n25 86b3\n3f 87f5\na1 6c03\nbb 6d45\n43 9aaf\n5d 9bf1\n51 31a3\nbe 4556\n2 a2ae\n1c a3f0\n2 aaae\nbe 4d56\n1c abf0\nb3 4525\n99 43e3\nbb 47e7\nb9 47eb\ndf 53f7\nff 57f7\nfd 57fb\n2b ced\n22 a626\n11 bab\n99 ebc9\n57 1bb7\n36 8716\nfc 5d72\n6f 1cfd\n66 b636\n55 1bbb\nb9 47e3\nb3 4d2d\naa e666\n99 4beb\nfd 57f3\nee f676\nf7 5d3d\ndd 5bfb\n20 4aa\n3a 5ec\nb7 cd1d\n9d cbdb\n17 2bb7\n78 9d60\nbc 6d72\n2f 2cfd\n15 2bbb\nb7 6d3d\n59 9be9\n9d 6bfb\nfd f55b\n33 8525\n19 83e3\n77 9535\nbb 6547\n5d 93f3\n5f 93f7\n7f 97f7\n33 8d2d\n19 8beb\n5f 9bf7\n77 9d3d\nbb 6d4f\n5d 9bfb\n2e 6dc\nb8 4fc0\nec 7672\n2f 6dd\nb9 4fc1\n37 a535\n1d a3f3\n2a 6ec\n2b 6ed\n37 ad3d\n1d abfb\n0 8aa\n99 4349\n2b a467\n1a 9ec\ne adc\ncc 7a72\nf add\na aec\nf8 5dca\nb aed\nf9 5dcb\n60 14aa\n7a 15ec\n6f 16dd\n1f 21fd\n5 20bb\nf9 5fc1\n6a 16ec\n6b 16ed\n6b b467\n40 18aa\nd9 5349\n5a 19ec\n4e 1adc\n4f 1add\n4a 1aec\n4b 1aed\nd3 d9ad\n36 534\n1c 3f2\nf3 ddad\n3c 7f2\n76 1534\n5c 13f2\n7c 17f2\nb6 4534\n9c 43f2\nd5 d9b1\n1e 3f6\nf5 ddb1\n3e 7f6\na0 e408\n5e 13f6\n7e 17f6\n76 1d34\n5c 1bf2\nb6 4d34\n9c 4bf2\nb6 6d34\n58 9be0\n9c 6bf2\nbe cd5c\na4 cc1a\n1e 2bf6\n36 8534\n1c 83f2\n1e 83f6\n36 8d34\n1c 8bf2\nba 6d46\n76 9d34\n5c 9bf2\n1e 8bf6\n33 7ad\n32 7ae\n77 17bd\n76 17be\naa 46ec\nf5 d711\nba 6dc6\n76 9db4\na1 46ab\nbb 47ed\nee 56fc\ne5 56bb\nff 57fd\nfe 57fe\nbc ed52\n46 1abc\n57 1bbd\nef 5cd7\n36 871c\nfc 5d78\n70 1d00\nef 565d\n56 1bbe\n26 26bc\n1a 1ee\nb0 6fa0\n9d 63f9\nae 66fc\n61 96a9\na5 66bb\nbf 67fd\n7a 97ec\n60 96aa\nbe 67fe\n59 39c9\naf 665d\n30 2d00\n16 2bbe\na1 640b\nbb 654d\n5d 93f9\nb2 670e\n6e 96fc\n65 96bb\n7f 97fd\n7e 97fe\na 8aec\n1 8aab\n1b 8bed\n34 8d30\n1a 8bee\n92 6b0e\nac 6c50\n4e 9afc\na3 6c0f\nbd 6d51\n45 9abb\n5f 9bfd\n2b e47\ne aafc\nbf 45f7\nbd 45fb\neb 5e47\n1c 3f8\n3c 7f8\n5c 13f8\n7c 17f8\n9c 43f8\nbc 47f8\nfc 57f8\n1d 3f9\n3d 7f9\n5d 13f9\n7d 17f9\n9d 43f9\nbd 47f9\n2d a673\n1c bf8\n6d b673\n5c 1bf8\nad e673\n9c 4bf8\ned f673\ndc 5bf8\nae e674\n9d 4bf9\n1c 23f8\n3c 27f8\n9c 63f8\nbc 67f8\n3d 27f9\n1c 2bf8\n9c 6bf8\nd5 59b3\n1c 83f8\nf5 5db3\n3c 87f8\n84 483a\n9e 497c\na0 640a\nba 654c\n5c 93f8\nd6 59b4\n1d 83f9\nf6 5db4\n3d 87f9\n85 483b\n9f 497d\naf 46f7\n35 2fb3\nd5 d119\n4f 30f5\nad 46fb\nae e476\n9d 49fb\na7 4c3d\n8d 4afb\n33 7af\n77 17bf\nb3 452d\n99 43eb\nbb 47ef\nf7 553d\ndd 53fb\n33 a58f\nff 57ff\n71 1d01\n57 1bbf\n36 871e\n67 bcb5\nfc 5d7a\n7b 97ed\n61 96ab\nbf 67ff\n25 4bb\n3f 5fd\n31 2d01\n17 2bbf\n6b 9cc7\n78 9d68\nbc 6d7a\nd4 73b0\n3e 5fe\nd2 59a6\n33 852d\n19 83eb\n7f 97ff\n35 8d31\n1b 8bef\nbd 6d53\n79 9d41\n5f 9bff\ne1 dca9\n2a 6ee\n2f 6fd\nb9 4fe1\ne5 dcb9\n2e 6fe\nd2 5124\nb8 4fe2\ne a2f6\n17 9bd\naf 445d\n95 431b\n16 9be\n5 8bb\n84 4218\n16 a336\n1f 9fd\n9d 435b\n1e 9fe\n7 abd\nf5 5d9b\n9f 4bd7\nac 4c78\n20 c00\n6 abe\nf afd\nfd 5ddb\n65 14bb\n7f 15fd\n7e 15fe\n6a 16ee\n6f 16fd\nf9 5fe1\n6e 16fe\nf8 5fe2\n56 b336\n45 18bb\nc4 5218\n5f 19fd\n4f 1afd\n68 1c40\n4e 1afe\n73 35ad\nbe 45fe\nbf 45ff\nf5 d719\nba 6dce\n76 9dbc\n6f 36f5\n3c 7fa\n7c 17fa\nb6 453c\n9c 43fa\nf6 ddbc\nef 76f5\nbc 47fa\n36 d3c\n1c bfa\nba edce\n6f b6f5\n3c 87fa\n9e 497e\n36 8d3c\n2f 2675\n1c 8bfa\n30 da2\naf 46ff\n7b 9dc7\n97 49bf\n2d aed9\na0 4c00\n86 4abe\na1 4c01\n87 4abf\ncb 7acd\n35 d1b\nd3 7b0d\n3d d5b\n5d bb59\n75 1d1b\na6 ec9c\n71 1d2b\nae ecdc\n94 eb9a\n79 1d6b\nbd 4d5b\nb5 4d9b\nfd 5d5b\nf a7d\nf1 5dab\n29 e4b\n2d e5b\n67 1e17\na5 ee39\n63 1e27\n6 2a96\n2d 2c79\ncc 5058\n5e b176\n5c b17a\nad ee79\n6b 1e67\n69 1e6b\nab 4e47\na9 4e4b\nad 4e5b\ned 5e5b\neb 5e67\n2 2006\ne9 5e6b\n59 b141\n3f afff\ndb 734f\n5c 39f2\nfb 5fed\ne1 5eab\n71 1d0b\n79 1d4b\n35 2d1b\n31 2d2b\n3d 2d5b\n67 9615\n39 2d6b\n79 9d49\nbd 6d5b\nb1 6dab\nad ee59\n6b 1e47\n69 1e4b\n3f 2f5d\n25 2e1b\n3b 2f6d\n21 2e2b\n2d 2e5b\n29 2e6b\ne9 5e4b\na0 4e20\n3f afdf\nd7 733f\n58 39e2\na9 6e6b\nb1 4d0b\nb5 4d1b\nf5 5d1b\n7 a3d\n2b 86cf\nf1 5d2b\n3b 8d47\n6f 367f\ncf fafd\n39 8d4b\nc8 fa68\n3f 8d57\nd3 fb0d\n3d 8d5b\n39 2761\n33 8d87\n67 36bf\nc0 faa8\n23 262f\n3d 2771\n37 8d97\n7f 9d57\n7d 9d5b\n79 9d6b\nf2 d704\n73 9da7\na7 4e17\n5c 3972\n9 2063\ne3 5e27\n86 6a96\n42 9a84\nad 6c79\nfb 5f6d\ne1 5e2b\nf 285f\n51 b101\n37 afbf\n2b 8e47\n29 8e4b\n2f 8e57\n2d 8e5b\n6f 9e57\n6d 9e5b\n6b 9e67\n71 9d09\nb5 6d1b\nb1 6d2b\n79 9d4b\n3f ad57\n16 1bc\n37 ad97\nb2 e704\na1 4c89\n33 ada7\nb0 e708\n6e 16f6\n31 adab\n6b 9e47\n69 9e4b\n2d ae5b\n29 ae6b\n22 48c\nf9 7563\n8 aca\n22 c0c\nc ada\n26 c1c\nd adb\n27 c1d\n10 b0a\n2a c4c\n19 bcb\n2a a646\n33 d0d\n1d bdb\n2e a656\n37 d1d\n4c 1ada\n66 1c1c\n4d 1adb\n67 1c1d\n97 eb9d\n48 1aea\n62 1c2c\n49 1aeb\n63 1c2d\n9f ebdd\n85 ea9b\n50 1b2a\n6a 1c6c\n86 ea9c\n51 1b2b\n6b 1c6d\n5d 1bdb\n6e b656\n77 1d1d\n8b 60cd\n76 1d1e\n59 1beb\n6a b666\n73 1d2d\n96 eb9c\n61 1c2b\n7b 1d6d\naf ecdf\n7a 1d6e\n91 4b0b\nab 4c4d\n94 4b1a\nae 4c5c\n95 4b1b\naf 4c5d\na3 4c8d\nb7 e717\na6 4c9c\na7 4c9d\na1 4c0b\nbb 4d4d\n6f 1675\n18 abe8\nba 4d4e\na5 4c1b\nbf 4d5d\n1c abf8\nbe 4d5e\naa e6c6\nb3 4d8d\nae e6d6\nb7 4d9d\n6b 16c5\nb6 4d9e\nd4 5b1a\nee 5c5c\nd5 5b1b\nef 5c5d\n25 8611\nd1 5b2b\neb 5c6d\ne7 5c9d\ne5 5c1b\nff 5d5d\n5c bbf8\nfe 5d5e\ne1 5c2b\n35 8711\nfb 5d6d\n34 8712\nfa 5d6e\n6f 1cf5\n55 1bb3\nee f6d6\nf7 5d9d\n18 bca\n32 d0c\n1c bda\n36 d1c\n5c 1bda\n76 1d1c\n8a 60cc\na7 ec9d\n58 1bea\n72 1d2c\naf ecdd\n95 eb9b\n60 1c2a\n7a 1d6c\n6b 1e6d\n1a 834e\n4b b8e5\n6a 1e6e\nc9 f243\n4a b8e6\na4 4c1a\nbe 4d5c\nb6 4d9c\nab 4e4d\naa 4e4e\naf 4e5d\nae 4e5e\na6 4e9e\ne4 5c1a\nfe 5d5c\nf6 5d9c\n86 4a96\nad 4c79\nef 5e5d\nee 5e5e\nd 20d9\ne7 5e9d\nc 20da\ne6 5e9e\n9 20e9\ne3 5ead\nfc 5ff0\n8 20ea\ne2 5eae\n48 1aca\n62 1c0c\n49 1acb\n63 1c0d\n50 1b0a\n6a 1c4c\n51 1b0b\n6b 1c4d\n59 1bcb\n6a b646\n73 1d0d\n87 60bd\n72 1d0e\n42 90ac\n86 60be\n61 1c0b\n7b 1d4d\n7a 1d4e\nc 2ada\n26 2c1c\nd 2adb\n27 2c1d\n8 2aea\n22 2c2c\n9 2aeb\n23 2c2d\n1d 2bdb\n37 2d1d\n36 2d1e\n19 2beb\n33 2d2d\n6d 9679\n32 2d2e\n21 2c2b\n3b 2d6d\n3a 2d6e\nd1 5b0b\neb 5c4d\ne3 5c8d\ne1 5c0b\nfb 5d4d\nea f6c6\nf3 5d8d\na6 6c9c\na7 6c9d\na5 6c1b\n61 9c09\nbf 6d5d\nba 6d6e\nb7 6d9d\n72 9d8c\n6b 36c5\nb6 6d9e\n58 1bca\n72 1d0c\n86 60bc\n60 1c0a\n7a 1d4c\n6b 1e4d\n4b b8c5\n6a 1e4e\n4a b8c6\n1c 2bda\n36 2d1c\n18 2bea\n32 2d2c\n20 2c2a\n3a 2d6c\n27 2e1d\n23 2e2d\n2b 2e6d\neb 5e4d\nea 5e4e\n9 20c9\ne3 5e8d\nfc 5fd0\n8 20ca\ne2 5e8e\n60 9c08\na4 6c1a\nbe 6d5c\nb6 6d9c\na3 4e27\nab 6e6d\nd fb\na3 6ead\n88 4aca\na2 4c0c\n89 4acb\na3 4c0d\n8c 4ada\na6 4c1c\n8d 4adb\na7 4c1d\naa e646\n99 4bcb\nb3 4d0d\n10 aba8\n67 1635\nb2 4d0e\nae e656\n9d 4bdb\nb7 4d1d\n6b 1645\n14 abb8\nb6 4d1e\ncc 5ada\ne6 5c1c\ncd 5adb\ne7 5c1d\nee f656\ndd 5bdb\nf7 5d1d\n14 8b1a\n2e 8c5c\n15 8b1b\n2f 8c5d\nfd fd73\n26 8c9c\n21 8c0b\n3b 8d4d\nd0 fb00\n3a 8d4e\n25 8c1b\n3f 8d5d\nd4 fb10\n3e 8d5e\n2b 26c7\n38 2768\n32 8d8e\n2f 26d7\n3c 2778\n36 8d9e\n54 9b1a\n6e 9c5c\n55 9b1b\n6f 9c5d\ne1 d609\na6 6cbe\n62 9cac\na7 6cbf\n63 9cad\n65 9c1b\n7f 9d5d\n7e 9d5e\n97 6b9d\n7a 9d6e\n77 9d9d\n6f 36d7\n7c 3778\n76 9d9e\n98 4bca\nb2 4d0c\n9c 4bda\nb6 4d1c\ndc 5bda\nf6 5d1c\n20 8c0a\n3a 8d4c\n24 8c1a\n3e 8d5c\n2b 8e4d\n2f 8e5d\nc0 f888\n3d 2551\n23 240f\n64 9c1a\n7e 9d5c\n6f 36d5\n76 9d9c\n6 8a96\n2d 8c79\n4f 3a77\n6f 9e5d\n6e 9e5e\naf 6e7f\n6b 9e6d\n6a 9e6e\n67 9e9d\n66 9e9e\n7c 9ff0\nda 7144\nc0 7002\n62 9eae\nc8 5aca\ne2 5c0c\nc9 5acb\ne3 5c0d\nea f646\nd9 5bcb\nf3 5d0d\n8c 6ada\n48 9ac8\na6 6c1c\n49 9ac9\n8d 6adb\na7 6c1d\n9d 6bdb\n59 9bc9\nb7 6d1d\n72 9d0c\n6b 3645\n58 9bca\nb6 6d1e\naf 6c5f\n51 9b0b\n6b 9c4d\na7 6c9f\n63 9c8d\n7a 9d4e\n6b 36c7\n78 3768\n72 9d8e\n14 ab1a\n2e ac5c\n36 85be\n15 ab1b\n2f ac5d\n37 85bf\n10 ab2a\n2a ac6c\na1 e609\n22 acac\n25 ac1b\n3f ad5d\n33 adad\nd8 5bca\nf2 5d0c\n9c 6bda\n58 9bc8\nb6 6d1c\nd 7b\na3 6e2d\n60 9c0a\nbe 6d5e\n7a 9d4c\naf 6e5f\n6b 9e4d\n6a 9e4e\na7 6e9f\n63 9e8d\nfc 75fa\n7c 9fd0\n62 9e8e\n20 ac2a\n3a ad6c\n6f 16f7\nb1 e709\n32 adac\n3c d50\n22 c0e\n26 c1e\nc0 7a00\n2a c4e\n7d 95f1\n63 94af\n66 1c1e\n9f ebdf\n6a 1c6e\n77 1d1f\n7f 1d5f\n35 8fbb\n4f 90fd\n93 610f\nc aaf8\nae 4c5e\na6 4c9e\n1d abf9\nbf 4d5f\nb3 4d8f\nb7 4d9f\n24 8612\n3e 8754\nea 5c6e\n5f 1bf5\n45 1ab3\ne6 5c9e\n5d bbf9\nff 5d5f\nf3 5daf\n2b e4f\n2f e5f\n67 1e1f\n7d 1f71\n63 1e2f\n6f 1e5f\n4 28b2\n9d 6351\n1e 29f4\n83 620f\n6b 1e6f\nab 4e4f\naf 4e5f\nef 5e5f\neb 5e6f\nd 20db\ne7 5e9f\ndd 7353\n5e 39f6\n9 20eb\nfd 5ff1\ne3 5eaf\n7c 1d50\n62 1c0e\n6a 1c4e\n73 1d0f\n7b 1d4f\n26 2c1e\n3c 2d70\n22 2c2e\n2a 2c6e\n37 2d1f\n33 2d2f\n3f 2d5f\n50 9b0a\n6a 9c4c\nae 6c5e\naa 6c6e\na5 cc19\n1f 2bf5\n5 2ab3\n62 9c8c\na6 6c9e\n61 9c0b\n7b 9d4d\nbf 6d5f\n73 9d8d\nb7 6d9f\nb3 6daf\n35 8f19\n7d 1f51\n63 1e0f\n6b 1e4f\n27 2e1f\n3d 2f71\n23 2e2f\n2f 2e5f\n2b 2e6f\neb 5e4f\nd9 7343\n5a 39e6\nab 6e6f\n0 aaa8\nbc 4d50\na2 4c0e\n4 aab8\na6 4c1e\n11 aba9\nb3 4d0f\n15 abb9\nb7 4d1f\nd1 fb01\n3b 8d4f\nd5 fb11\n3f 8d5f\n39 2769\n33 8d8f\n3d 2779\n37 8d9f\n6e 9c5e\n87 6a9d\n6a 9c6e\n6c 3678\n66 9c9e\n7f 9d5f\n7d 3779\n77 9d9f\nf2 d70c\n73 9daf\nd 205b\ne7 5e1f\n5e 3976\nfd 5f71\n9 206b\ne3 5e2f\n2b 8e4f\n2f 8e5f\n6f 9e5f\n6b 9e6f\n67 9e9f\nfc 5d50\n40 baa8\ne2 5c0e\n62 9c0c\n48 9aca\na6 6c1e\n73 9d0d\n59 9bcb\nb7 6d1f\n6a 9c4e\n7b 9d4f\n10 8b2a\n2a 8c6c\n2e ac5e\nd6 7396\nfd 7579\na1 e60b\nbb e74d\n3c adf0\n22 acae\n98 4be0\n37 ad9f\nb2 e70c\n33 adaf\n6b 9e4f\n7d 9fd1\n63 9e8f\n2b ae6f\n4c 1af2\n66 1c34\n4d 1af3\n67 1c35\n28 2cea\n57 1b97\n64 1c38\n65 1c39\nd4 5b32\nee 5c74\nc6 5a96\n27 861d\ned 5c79\nf5 f733\ne4 5cb8\nf6 f734\ne5 5cb9\ne5 5c33\n2c 8678\nff 5d75\nfe 5d76\n37 871d\nd6 5b96\nfd 5d79\nec f6f2\nf5 5db9\n65 1e39\n14 831a\n2e 845c\n45 b8b1\n7e 1f7c\n64 1e3a\ne4 5c32\nfe 5d74\nf4 5db8\nef 5e75\nca 706c\nb0 6f2a\n6 2014\ned 5e79\n1f 2157\nec 5e7a\nd 20f1\ne7 5eb5\nfe 5ffc\n17 2197\ne4 5eba\nac cc58\nc 2af2\n26 2c34\nd 2af3\nad cc59\n27 2c35\n17 2b97\n24 2c38\n25 2c39\nc4 5018\n56 b136\nbc cd5a\n36 2d36\n35 2d39\n3e 2d76\n48 18c2\nc7 521f\n5c 397a\na4 6cb8\n60 9c22\n7a 9d64\nbe 6d76\nbc cd58\n1c 2bf2\n36 2d34\n27 2c97\n34 2d38\nac eef8\n6a 1ee6\nad ce59\n27 2e35\n25 2e39\n2f 2e75\n5f bbdd\n45 ba9b\n2d 2e79\naf 6e75\nad 6e79\n11 103\na7 6eb5\nd7 5b97\ne4 5c38\ne5 5c39\nec f672\n2f 86dd\nf5 5d39\nab 6cc7\nb8 6d68\n67 9cb5\nb9 6dcb\n75 9db9\ne7 5c97\n2e 86dc\nf4 5d38\ne5 5e39\nfe 5f7c\n17 2117\ne4 5e3a\n97 6b97\n53 9b85\na4 6c38\na5 6c39\n63 9c87\n69 3661\n70 9d28\nb4 6d3a\n14 ab32\n2e ac74\n67 96bd\nab 66cf\n6 aa96\n2d ac79\n61 1603\n7b 1745\n24 acb8\ndd d1d9\n57 31b5\n62 1604\n25 acb9\n16 ab96\n3d ad79\na7 6e35\na5 6e39\nbc 4f7a\n27 aeb5\n7 b7\na aee\n24 c30\n12 b2e\n2c c70\n1a bee\n34 d30\n4a 1aee\n64 1c30\n8a 4aee\na4 4c30\nca 5aee\ne4 5c30\n20 ac82\n6a 166e\n3a adc4\nb aef\n25 c31\n1b bef\n35 d31\n23 c2f\n3d d71\n4b 1aef\n65 1c31\n8b 4aef\na5 4c31\na3 4c2f\nbd 4d71\ncb 5aef\ne5 5c31\n21 ac83\n6b 166f\n3b adc5\n27 8615\nd3 5b2f\ned 5c71\ne3 5c2f\n37 8715\nfd 5d71\nc af2\n26 c34\n1c bf2\n36 d34\nd af3\n27 c35\na5 4c33\nbf 4d75\ne3 560d\n64 1cb0\nf3 570d\n74 1db0\na4 4cb0\ne4 5cb0\n25 cb1\n35 db1\n65 1cb1\n75 1db1\n74 171a\na5 4cb1\ne5 5cb1\na5 4611\n26 cb4\nb5 4711\n36 db4\ne5 5611\n66 1cb4\nf5 5711\n76 1db4\n27 cb5\n67 1cb5\n24 e30\n2c e70\n64 1e30\na4 4e30\n25 e31\n2d e71\n76 1f94\n65 1e31\na5 4e31\nb 206d\ne5 5e31\n21 ae83\n3b afc5\n26 e34\na6 4e34\n78 35e8\nee 5e74\n27 e35\nda f146\n64 1eb0\na4 4eb0\ndb f147\n65 1eb1\na5 4eb1\n66 1eb4\na6 4eb4\nc 20f0\ne6 5eb4\n67 1eb5\n8a 6aee\na4 6c30\nb 2aef\n25 2c31\n1b 2bef\n35 2d31\n8b 6aef\na5 6c31\na3 660d\n24 2cb0\nb3 670d\n34 2db0\na4 6cb0\nb4 6db0\n10 238a\n2a 24cc\n35 2db1\nac ccd8\na5 6611\n26 2cb4\nbc cdd8\nb5 6711\n36 2db4\nad ccd9\n27 2cb5\n24 2e30\n2c 2e70\ne 7e\na4 6e30\nac 6e70\n25 2e31\n2d 2e71\nf 7f\na5 6e31\nad 6e71\nac ce58\n26 2e34\na6 6e34\n24 2eb0\ne fe\na4 6eb0\n25 2eb1\nf ff\na5 6eb1\nac ced8\n26 2eb4\n10 102\na6 6eb4\nad ced9\n27 2eb5\nb2 6f2e\ncc 7070\n23 8c2f\n3d 8d71\na3 c60d\n24 8cb0\nb3 c70d\n34 8db0\n68 36e8\n25 8cb1\n35 8db1\n69 36e9\na9 6cc3\n65 9cb1\na5 c611\n26 8cb4\nb5 c711\n36 8db4\n27 8cb5\n37 8db5\na8 6e42\n64 9e30\n25 8e31\na9 6e43\n65 9e31\n27 8e35\n82 6004\n24 8eb0\na8 6ec2\nc2 7004\n64 9eb0\n83 6005\n25 8eb1\na9 6ec3\nc3 7005\n65 9eb1\n26 8eb4\naa 6ec6\n66 9eb4\n27 8eb5\n12 ab2e\n2c ac70\n65 96b9\na9 66cb\n1a abee\n34 ad30\n1b abef\n35 ad31\n23 ac2f\n3d ad71\nc aaf2\n26 ac34\nbd 67d1\na3 668f\n1c abf2\n36 ad34\n25 ae31\n1f 175\n5 33\n27 ae35\n7 37\nc7 5017\n25 aeb1\n1f 1f5\n5 b3\n31 d2b\n39 d6b\n59 bb69\n55 1933\na8 ec48\n66 1c36\n58 b1e8\n75 1d3b\n2c 867a\nff 5d77\n3e 87fc\n24 86ba\nf7 5db7\nf5 5dbb\n2b e67\n29 e6b\n72 1f8e\n5f 1b75\n45 1a33\n4d 1a73\na9 ee49\n67 1e37\nab 4e67\n60 9c88\na4 6c9a\nbe 6ddc\na9 4e6b\n18 21ca\nf2 5f8e\n6 2016\ned 5e7b\nff 5ffd\ne5 5ebb\n15 2933\nac cc5a\n26 2c36\n2e 2c76\n2c 2c7a\n35 2d3b\n3d 2d7b\n51 99a1\n7e 355e\n95 69b3\n6a 9c64\n50 9b22\nae 6c76\n61 9c23\n7b 9d65\nbf 6d77\n1f 2b75\n5 2a33\n3f 2f7d\n25 2e3b\n2d 2e7b\nb1 4d2b\n39 8d6b\nb2 c704\n33 8da7\nb0 c708\n2a 26e4\n31 8dab\n5d 9973\ne5 d613\nff d755\n66 9cb6\n7d 9d7b\nbb 4f6d\na1 4e2b\ndf 5b75\nc5 5a33\nff 5f7d\ne5 5e3b\n29 8e6b\n72 9f8e\n4d 9a73\na3 6c07\n5f 9bf5\n45 9ab3\n6f 9e77\n6d 9e7b\n60 9c28\n53 9b87\nbe 6d7c\na4 6c3a\n1d a973\n94 e310\n15 a9b3\n9c 4b58\n2e ac76\nd9 d961\n46 389c\n2c ac7a\n5f 3177\nb6 e714\na5 4c99\n37 adb7\nb4 e718\n72 1706\n35 adbb\n41 9a21\n9f 6b75\n85 6a33\n63 9e25\na7 6e37\n71 15a1\nd aa73\n7d bd79\n56 bb96\n1f abf5\n5 aab3\n2d ae7b\n3f affd\n25 aebb\n26 49c\nfd 7573\n3e d74\n24 c32\n44 ba30\nc2 7a24\n2c c72\n4c ba70\nca 7ae4\n34 d32\nd2 7b24\n3c d72\n7e 1d74\n64 1c32\nbe 4d74\na4 4c32\nac 4c72\nbc 4d72\n2e c76\n4e ba74\n36 d36\n3e d76\nae 4c76\nbe 4d76\ne6 5c16\n2d 865b\n44 bab0\nb3 470f\n34 db2\ne3 560f\nfd 5751\n7e 1df4\n64 1cb2\nf3 570f\n74 1db2\nc3 5887\nd0 5928\na 82cc\nbe 4df4\na4 4cb2\nb4 4db2\nfe 5df4\ne4 5cb2\nf4 5db2\nbf 4755\na5 4613\n26 cb6\n2f 865f\n46 bab4\nb5 4713\n36 db6\na8 ecc8\nff 5755\ne5 5613\n66 1cb6\n3e f74\n24 e32\n2c e72\n7e 1f74\n64 1e32\na6 4e36\n78 35ea\n7e 1ff4\n64 1eb2\nbe 4ff4\na4 4eb2\na 20ee\nfe 5ff4\ne4 5eb2\n26 eb6\n3e 2d74\n24 2c32\n2c 2c72\n6f 967d\n34 2d32\n60 9c20\nbe 6d74\na4 6c32\n60 9ca0\nbe 6df4\na4 6cb2\nac ccda\n61 9601\nbf 6755\na5 6613\n26 2cb6\n3e 8d74\n24 8c32\n7e 9d74\n64 9c32\n26 8c36\n66 9c36\nb3 c70f\n34 8db2\n68 36ea\ne3 d60f\nfd d751\n7e 9df4\n64 9cb2\na5 c613\nbf c755\n26 8cb6\n3e 8f74\n24 8e32\n7e 9f74\n64 9e32\n26 8e36\n3e 8ff4\n82 6006\n24 8eb2\n7e 9ff4\nc2 7006\n64 9eb2\n26 8eb6\n3e ad74\n24 ac32\n34 ad32\n94 4b18\n26 ac36\n8 aea\n22 c2c\n9 aeb\n23 c2d\n10 b2a\n2a c6c\n73 1d8f\n19 beb\n2a a666\n33 d2d\nc8 7ae0\n32 d2e\n21 c2b\n3b d6d\nd0 7b20\n3a d6e\n4c 1afa\n66 1c3c\n4d 1afb\n67 1c3d\n5d 1bfb\n6e b676\n77 1d3d\n8b 60ed\na1 4c2b\nbb 4d6d\nba 4d6e\nd5 5b3b\nef 5c7d\ne5 5c3b\nff 5d7d\nfe 5d7e\n18 bea\n32 d2c\n20 c2a\n3a d6c\n5c 1bfa\n76 1d3c\n8a 60ec\n67 1e3d\n16 831e\ndc 597a\n47 b8b5\n66 1e3e\nc5 f213\ndf f355\n46 b8b6\na3 4ead\nbc 4ff0\na2 4eae\nd 20f9\ne7 5ebd\nc 20fa\ne6 5ebe\nc 2afa\n26 2c3c\nd 2afb\n27 2c3d\n14 2b3a\n2e 2c7c\n15 2b3b\n2f 2c7d\n1d 2bfb\n37 2d3d\n36 2d3e\n3e 2d7e\n51 9b29\n95 6b3b\naf 6c7d\n60 9c2a\n7a 9d6c\nbe 6d7e\n1c 2bfa\n36 2d3c\n24 2c3a\n3e 2d7c\n27 2e3d\n26 2e3e\n1d 3fb\n37 53d\n2f 2e7d\naf 6e7d\n11 10b\na7 6ebd\nb7 45bd\na1 c609\n22 8cac\nd0 fb20\n3a 8d6e\nb1 c70b\n2b 26e7\n32 8dae\n54 9b3a\n6e 9c7c\naa 6cce\ne5 d619\n66 9cbc\nab 6ccf\n67 9cbd\n7e 9d7e\nbb 6dcf\n77 9dbd\nd 2079\ne7 5e3d\n23 ae8f\n3d afd1\nc 207a\ne6 5e3e\n56 b114\n3c afd2\n20 8c2a\n3a 8d6c\nb1 c709\n2b 26e5\n32 8dac\n2b 8e6d\n74 9f90\n2a 8e6e\n64 9c3a\n7e 9d7c\n6f 9e7d\n6e 9e7e\nab 6ecf\nc5 7011\n67 9ebd\nc4 7012\nde 7154\n66 9ebe\n14 ab3a\n2e ac7c\na7 6e3d\n62 9e2c\na6 6e3e\n9d 43fb\nb7 453d\n2e ae7e\n25 843b\nde 59f6\n3f 857d\n40 b000\n26 aebe\n37 85bd\n27 49f\nfe 7576\n17 b97\n24 c38\n1f bd7\n2c c78\n75 1d9b\n27 c97\n34 d38\n97 4b97\na4 4c38\na7 4c97\nb4 4d38\naf 4cd7\nbc 4d78\n2c a672\n35 d39\n16 b96\n3d d79\na5 4c39\nac e672\nb5 4d39\n96 4b96\nbd 4d79\n35 a733\n24 cb8\n34 db8\n75 b733\n64 1cb8\n74 1db8\nb5 e733\na4 4cb8\nb4 4db8\n64 1e38\nc3 f20d\n2d 845b\n44 b8b0\na4 4e38\n17 2115\ne4 5e38\n1f 2155\n5 2013\nec 5e78\na5 4e39\n9a e14e\n24 eb8\nda f14e\n64 1eb8\na4 4eb8\n17 2195\ne4 5eb8\n24 2cb8\n34 2db8\n35 2db9\n24 2e38\n2c 2e78\na4 6e38\nac 6e78\n24 2eb8\na4 6eb8\n27 8c97\n2d 2671\n34 8d38\n2f 8cd7\n3c 8d78\n25 8c39\ncd da5b\n47 3a37\n35 8d39\n16 8b96\n3d 8d79\na9 6c4b\n65 9c39\n2d 26f1\n34 8db8\n25 8cb9\ne7 dc1d\ncd dadb\n47 3ab7\n35 8db9\n25 8e39\na9 6e4b\n65 9e39\nc2 700c\na8 6eca\n64 9eb8\n17 ab97\n24 ac38\n3d 2ff3\ndd d159\n57 3135\n27 ac97\n34 ad38\n6a 9ee6\n25 ac39\n35 ad39\n6b 9ee7\n25 ae39\n1f 17d\n5 3b\n3c d70\n22 c2e\nc0 7a20\n2a c6e\nc9 7ae1\n33 d2f\n7f 1d7f\n93 612f\naa 4c6e\nb3 4daf\n49 b2c9\nee 5c7e\n2b e6f\n74 1f92\n67 1e3f\n6f 1e7f\n97 c997\n9d 6371\n83 622f\nbd 4ff1\na3 4eaf\nef 5e7f\nd 20fb\ne7 5ebf\n26 2c3e\n2e 2c7e\n37 2d3f\n3f 2d7f\n50 9b2a\n6a 9c6c\nae 6c7e\n73 9dad\nb7 6dbf\n27 2e3f\n2f 2e7f\nc1 7001\n63 9ead\na7 6ebf\nc0 fa20\n2a 8c6e\nb2 c70c\n33 8daf\n6e 9c7e\nf6 d71c\n77 9dbf\nbd 4f71\na3 4e2f\nd 207b\ne7 5e3f\n2b 8e6f\n74 9f92\n6f 9e7f\nc5 7013\ndf 7155\n67 9ebf\n2e ac7e\nb6 e71c\n37 adbf\n63 9e2d\na7 6e3f\n2f ae7f\n41 b001\n27 aebf\nff 7577\n50 1902\ncf 525f\n3e d7c\n24 c3a\n44 ba38\nc2 7a2c\n2c c7a\n4c ba78\n17 abb5\nac 4c7a\n69 1661\nb4 4d3a\n63 360f\n7d 3751\n27 acb5\nbc 4d7a\n3e dfc\nbd 4759\n24 cba\ne6 5c1e\n44 bab8\nbe 4dfc\na4 4cba\n74 9d98\n6d 36d1\n69 16e1\nb4 4dba\nbe 4f7c\na4 4e3a\nca faec\n2d 2673\n34 8d3a\nd2 fb2c\n3c 8d7a\n3e ad7c\n24 ac3a\ndd d15b\n57 3137\n34 ad3a\n5b 914d\n41 900b\n9f 615f\n9b 616f\n9f c9d7\n8b 626f\n9d 63f1\n83 62af\na7 461d\n28 cc0\nb7 471d\n38 dc0\ne7 561d\n68 1cc0\nf7 571d\n78 1dc0\na8 4cc0\ne8 5cc0\n29 cc1\n39 dc1\n69 1cc1\n79 1dc1\na9 4cc1\ne9 5cc1\n10 b82\n2a cc4\n20 c82\n3a dc4\n50 1b82\n6a 1cc4\n60 1c82\n7a 1dc4\n90 4b82\naa 4cc4\na0 4c82\nba 4dc4\nd0 5b82\n31 8709\nea 5cc4\ne0 5c82\nfa 5dc4\n11 b83\n2b cc5\n51 1b83\n6b 1cc5\na1 4c83\nbb 4dc5\nd1 5b83\neb 5cc5\ne1 5c83\n28 86c8\nfb 5dc5\n12 b8e\n2c cd0\n92 4b8e\nac 4cd0\na2 4c8e\nbc 4dd0\nd2 5b8e\nec 5cd0\ne2 5c8e\nfc 5dd0\nea 7c66\n13 b8f\n2d cd1\n23 c8f\nfa 7d66\n3d dd1\n93 4b8f\nad 4cd1\na3 4c8f\nbd 4dd1\nd3 5b8f\ned 5cd1\ne3 5c8f\nfd 5dd1\n14 b92\n2e cd4\n24 c92\n3e dd4\n94 4b92\nae 4cd4\na4 4c92\nbe 4dd4\n35 8719\nd4 5b92\nee 5cd4\ne4 5c92\nfe 5dd4\n15 b93\n2f cd5\na5 4c93\nbf 4dd5\ne5 5c93\n2c 86d8\nff 5dd5\n9e e156\n28 ec0\ncd 507b\nde f156\n68 1ec0\na8 4ec0\ne 20fc\ne8 5ec0\n9f e157\n29 ec1\ndf f157\n69 1ec1\na9 4ec1\nf 20fd\ne9 5ec1\n2a ec4\n6a 1ec4\naa 4ec4\n10 2100\nea 5ec4\n2b ec5\n6b 1ec5\n11 2101\neb 5ec5\n2c ed0\nac 4ed0\nea 7e66\n2d ed1\nad 4ed1\n13 210d\ned 5ed1\n2e ed4\nae 4ed4\n14 2110\nee 5ed4\n2f ed5\n15 2111\nef 5ed5\nca 70cc\nb0 6f8a\nb9 e743\n3a ade6\na8 4cc8\nba e744\na0 e602\n3b ade7\na9 4cc9\nbd e753\n3e adf6\nac 4cd8\nba 4dc6\nb0 e702\nb9 4dc9\n6d 16f1\nb8 4dca\nbe 4dd6\nb4 e712\nbd 4dd9\n71 1701\nbc 4dda\nfd f753\n7e bdf6\nec 5cd8\nfe f754\n7f bdf7\ne4 f612\ned 5cd9\nf9 f763\n22 868c\ne8 5ce8\nfa f764\n23 868d\ne0 f622\ne9 5ce9\nfe 5dd6\nf4 f712\nfd 5dd9\nfc 5dda\nf0 f722\n33 878d\nf9 5de9\n32 878e\nf8 5dea\na7 661d\n28 2cc0\nb7 671d\n38 2dc0\na8 6cc0\nb8 6dc0\n14 239a\n2e 24dc\nb9 6dc1\na8 c460\n15 239b\n2f 24dd\n10 2b82\n2a 2cc4\n20 2c82\n3a 2dc4\n90 6b82\naa 6cc4\na0 6c82\nba 6dc4\n11 2b83\n2b 2cc5\n91 6b83\nab 6cc5\nd 2f1\n58 39ca\na1 6c83\nbb 6dc5\n12 2b8e\n2c 2cd0\n22 2c8e\n3c 2dd0\n92 6b8e\nac 6cd0\na2 6c8e\nbc 6dd0\n14 2b92\n2e 2cd4\n24 2c92\n3e 2dd4\n94 6b92\n50 9b80\nae 6cd4\nbd 6f73\n79 9f61\na4 6c92\n60 9c80\nbe 6dd4\n95 6b93\n51 9b81\naf 6cd5\n11 301\n5c 39da\nb8 4dc8\nbc 4dd8\naa 4ec6\nc2 500c\na8 4eca\nc6 501c\nac 4eda\n28 2ec0\n12 10e\na8 6ec0\n29 2ec1\n2a 2ec4\n14 112\naa 6ec4\n2b 2ec5\n15 113\nab 6ec5\n2c 2ed0\n16 11e\nac 6ed0\n2d 2ed1\nfc 5dd8\n17 11f\nad 6ed1\n32 878c\nf8 5de8\n2e 2ed4\nae 6ed4\n2f 2ed5\n7f bff7\ned 5ed9\nec 5eda\naf 6ed5\na7 c61d\n28 8cc0\nb7 c71d\n38 8dc0\n6c 36f8\n29 8cc1\n39 8dc1\n6d 36f9\nad 6cd3\n69 9cc1\n10 8b82\n2a 8cc4\n20 8c82\n3a 8dc4\n11 8b83\n2b 8cc5\n21 8c83\n3b 8dc5\naf 6cd7\nbc 6d78\n51 9b83\n6b 9cc5\n22 8c8e\n28 2668\n3c 8dd0\n23 8c8f\n29 2669\nfa fd66\n3d 8dd1\n14 8b92\n2e 8cd4\n24 8c92\n2a 266c\n3e 8dd4\n25 8c93\n2b 266d\n3f 8dd5\n86 6014\n28 8ec0\nc6 7014\nac 6ed2\n68 9ec0\n87 6015\n29 8ec1\nad 6ed3\nc7 7015\n69 9ec1\n2a 8ec4\nae 6ed6\n6a 9ec4\n2b 8ec5\nf9 f743\n7a bde6\ne8 5cc8\n2b 8ee7\nfa f744\n7b bde7\ne0 f602\ne9 5cc9\nfa 5dc6\nf0 f702\nf9 5dc9\na8 6ce8\n60 9c82\n7a 9dc4\nbe 6dd6\nf8 5dc8\n3b 8fe7\n7b bfe7\ne9 5ec9\ne8 5eca\n2b aec5\nbc 6dd8\n18 21c2\nf2 5f86\nad 6ed9\nb7 c73d\n38 8de0\n2f aed5\n6 96\na9 6ee9\n29 8cc9\nea fc6e\n2d 8cd9\n39 8dc9\n31 2703\n38 8dca\nfa fd6e\n3d 8dd9\nd2 fb8c\n35 2713\n3c 8dda\nae 6cf6\n50 9ba2\ne9 d641\n6a 9ce4\naf 6cf7\n51 9ba3\n6b 9ce5\n61 9ca3\nbf 6df7\ne0 d600\n7b 9de5\n71 3723\n78 9dea\n31 2701\n38 8dc8\n35 2711\n3c 8dd8\n87 601d\n29 8ec9\n42 900c\n86 601e\n28 8eca\n46 901c\n2c 8eda\nf9 d741\n60 9ca2\nbe 6df6\n7a 9de4\nc6 703e\n68 9eea\n71 3703\n78 9dca\n7f 1775\n65 1633\n28 ace8\n41 30a3\n5b 31e5\n21 aca3\na0 e600\n3b ade5\n41 122b\n11 a981\n5b 136d\n76 1734\n39 ade9\nc6 701e\n68 9eca\nb9 e741\n20 aca2\n3a ade4\n8f e2dd\n40 122a\n10 a980\n5a 136c\n2b aee5\ncb 504f\n29 aee9\na7 463d\n28 ce0\nce 72d6\nb7 473d\n38 de0\ne7 563d\n68 1ce0\nf7 573d\n78 1de0\na8 4ce0\nb8 4de0\n22 8684\ne8 5ce0\n32 8784\nf8 5de0\n29 ce1\nd6 d99e\ndc 7378\ncf 72d7\n39 de1\n69 1ce1\n79 1de1\n78 174a\na9 4ce1\nb9 4de1\n23 8685\ne9 5ce1\n33 8785\nf9 5de1\na9 4641\nf4 7d1a\n10 ba2\n2a ce4\n20 ca2\nb9 4741\n3a de4\ne9 5641\n50 1ba2\n6a 1ce4\n60 1ca2\nf9 5741\n7a 1de4\nf5 7d1b\n11 ba3\n2b ce5\n51 1ba3\n6b 1ce5\n9e e176\n28 ee0\nde f176\n7 809f\n68 1ee0\n47 b09f\na8 4ee0\n9f e177\n29 ee1\ndf f177\n69 1ee1\na9 4ee1\nf4 7f1a\n2a ee4\n6a 1ee4\nf5 7f1b\n2b ee5\n6b 1ee5\na8 6ce0\nb8 6de0\na9 6641\n10 2ba2\nb0 cd08\n2a 2ce4\n11 2ba3\nb1 cd09\n2b 2ce5\n28 2ee0\n12 12e\na8 6ee0\n29 2ee1\nb0 cf08\n2a 2ee4\nb1 cf09\n2b 2ee5\na7 c63d\n28 8ce0\ne7 d63d\nac 6cf2\n68 9ce0\nf7 d73d\nbc 6df2\n78 9de0\n29 8ce1\n39 8de1\nad 6cf3\n69 9ce1\nbd 6df3\n79 9de1\n10 8ba2\na9 c641\nf4 fd1a\n2a 8ce4\nb9 c741\n20 8ca2\n3a 8de4\naf 6edd\nf5 fd1b\n11 8ba3\n2b 8ce5\n21 8ca3\na0 c600\n3b 8de5\n86 6034\n28 8ee0\nac 6ef2\nc6 7034\n68 9ee0\n87 6035\n29 8ee1\nad 6ef3\nc7 7035\n69 9ee1\nf4 ff1a\n2a 8ee4\nae 6ef6\n6a 9ee4\nf5 ff1b\n2b 8ee5\ncb 5047\n29 aee1\na7 461f\n28 cc2\n48 bac0\nb7 471f\n38 dc2\ne7 561f\n68 1cc2\nf7 571f\n78 1dc2\nd4 5938\nc7 5897\ne 82dc\na8 4cc2\ne8 5cc2\n2a cc6\n4a bac4\n3a dc6\nac ecd8\n6a 1cc6\nbc edd8\n7a 1dc6\naa 4cc6\n31 870b\nea 5cc6\nc2 7a84\n2c cd2\n4c bad0\nd2 7b84\n3c dd2\nac 4cd2\nbc 4dd2\nec 5cd2\nd1 7b29\n2e cd6\n4e bad4\ne1 7c29\n3e dd6\nae 4cd6\n35 871b\nee 5cd6\n42 1004\n28 ec2\n68 1ec2\nc2 5004\na8 4ec2\ne 20fe\ne8 5ec2\n2a ec6\n46 1014\n2c ed2\nc6 5014\nac 4ed2\n12 210e\nec 5ed2\n35 d9b\n55 bb99\n99 49c3\n2f aedd\n9d 49d3\na8 4cca\n61 1601\nac 4cda\nbb 4dc7\nb9 4dcb\nbf 4dd7\nbd 4ddb\n24 8418\ndd 59d3\n20 8428\n13 8387\nd9 59e3\nec 5cda\n2c 86da\nff 5dd7\n33 878f\nf9 5deb\n3f fdd\nfc 7f72\n25 e9b\na7 661f\n63 960d\n28 2cc2\ne3 d60d\n64 9cb0\na8 6cc2\n65 9611\n2a 2cc6\ne5 d611\n66 9cb4\naa 6cc6\n67 961d\n2c 2cd2\n77 971d\n3c 2dd2\ne7 d61d\n68 9cc0\nac 6cd2\nf7 d71d\n78 9dc0\nbc 6dd2\n74 1f38\n67 1e97\n2e 2cd6\n3e 2dd6\n7f 1fdd\n65 1e9b\n50 9b82\n6a 9cc4\nae 6cd6\na3 4c05\n89 4ac3\na7 4c15\n8d 4ad3\nc3 500d\na9 4ecb\nc7 501d\nad 4edb\ne7 5c15\ncd 5ad3\ned 5edb\na7 c61f\n28 8cc2\nb7 c71f\n38 8dc2\n6c 36fa\ne7 d61f\n68 9cc2\n6a 9cc6\nd2 fb84\n28 266a\n3c 8dd2\n42 9004\n86 6016\n28 8ec2\nc6 7016\n68 9ec2\nb0 6708\n31 2dab\n20 8408\nd9 59c3\ne8 5cca\n28 86ca\nfb 5dc7\n61 3601\n68 9cc8\nac 6cda\n61 9c83\n7b 9dc5\nbf 6dd7\ne7 d695\nb9 6deb\n7b 1fcd\n61 1e8b\n5c b1da\n3f 2fdd\nb8 cf60\n25 2e9b\ne3 5c05\nc9 5ac3\na7 6c15\n49 9ac1\n8d 6ad3\na3 6c25\n89 6ae3\nc7 701d\n69 9ec9\nad 6edb\nb7 c73f\n38 8de2\nc3 702d\na9 6eeb\n19 89c3\n9 226b\n1d 89d3\n3b 2745\n21 2603\n28 8cca\nc2 fa8c\n3f 2755\n25 2613\n2c 8cda\n3b 8dc7\n32 2704\n39 8dcb\nc8 fae8\n2b 266f\n3f 8dd7\nd3 fb8d\n36 2714\n3d 8ddb\nd8 d340\n59 99e3\ne9 d643\n6a 9ce6\nfa d744\ne0 d602\n7b 9de7\n23 8c05\n9 8ac3\n27 8c15\nd 8ad3\n38 8f68\n2b 8ec7\n3c 8f78\n2f 8ed7\n47 901d\n2d 8edb\n67 9c15\n4d 9ad3\na7 6c37\n63 9c25\n49 9ae3\nc7 703f\n69 9eeb\n59 99c3\n61 3603\n7b 3745\n68 9cca\n98 e340\n19 a9e3\n39 f6b\n2c acda\n3f add7\na7 6c17\n63 9c05\n49 9ac3\nc7 701f\n69 9ecb\n27 ac15\nd aad3\n23 ac25\n9 aae3\n3c af78\n2f aed7\n47 b01d\n2d aedb\n2b aee7\n43 b02d\n29 aeeb\na7 463f\n28 ce2\nea 5c46\n48 bae0\nb7 473f\n38 de2\ne7 563f\nb7 ed95\n68 1ce2\nf7 573f\n78 1de2\na8 4ce2\nb8 4de2\n22 8686\ne8 5ce2\n32 8786\nf8 5de2\na9 4643\n2a ce6\n4a bae4\nb9 4743\n3a de6\n42 1024\n28 ee2\nb7 ef95\n68 1ee2\nc2 5024\na8 4ee2\n2a ee6\na7 663f\n63 962d\n28 2ce2\ne3 d62d\na8 6ce2\n2a 8e4c\nf3 d72d\nb8 6de2\n65 9631\nb0 cd0a\na9 6643\n2a 2ce6\na7 c63f\n28 8ce2\ne7 d63f\n68 9ce2\nf7 d73f\n78 9de2\na9 c643\n2a 8ce6\n86 6036\n42 9024\n28 8ee2\nc6 7036\n68 9ee2\n2a 8ee6\n39 a743\n28 cc8\n38 dc8\n79 b743\n68 1cc8\n78 1dc8\n30 a702\n39 dc9\n70 b702\n79 1dc9\n3d a753\n2c cd8\n3c dd8\nfa 7d6e\n34 a712\n3d dd9\n9e e15e\n28 ec8\nde f15e\n68 1ec8\n3a afe6\na8 4ec8\n7a bfe6\ne8 5ec8\n2c ed8\n3e aff6\nac 4ed8\n7e bff6\nec 5ed8\ne4 7c32\nfe 7d74\n27 c9d\nf4 7d32\n2e a6d6\n37 d9d\n67 1c9d\n73 b727\ne1 5609\n62 1cac\n63 1cad\n76 1d9e\n4d ba79\n6a b6e6\n73 1dad\nbb e747\n90 4b8a\naa 4ccc\n91 4b8b\na2 e606\nab 4ccd\nbf e757\n94 4b9a\nae 4cdc\na6 e616\n95 4b9b\naf 4cdd\na1 4c8b\nb2 e706\nbb 4dcd\n6f 16f5\nba 4dce\na5 4c9b\nb6 e716\nbf 4ddd\n73 1705\nbe 4dde\nff f757\nd4 5b9a\nee 5cdc\nd5 5b9b\ne6 f616\nef 5cdd\ne5 5c9b\nf6 f716\nff 5ddd\n3c fd0\n22 e8e\n26 e9e\n28 2cc8\n38 2dc8\na8 6cc8\n39 2dc9\nf1 5709\n72 1dac\n3c 2dd8\nb4 ef98\n72 1f86\n66 1e9e\nd9 f143\n63 1ead\n7c 1ff0\n62 1eae\na0 4c8a\nba 4dcc\na4 4c9a\nbe 4ddc\nab 4ecd\nc4 5010\naa 4ece\naf 4edd\nae 4ede\n28 2ec8\na8 6ec8\n29 2ec9\na9 6ec9\ne4 5c9a\nfe 5ddc\n2c 2ed8\nac 6ed8\n2d 2ed9\n15 2119\nef 5edd\n14 211a\nee 5ede\nc6 701c\nac 6eda\n68 9ec8\na0 cc20\n27 2c9d\na1 6609\n22 2cac\n36 2d9e\n33 2dad\nb1 670b\n6d 96f9\n32 2dae\nfb f747\nd0 5b8a\nea 5ccc\nd1 5b8b\ne2 f606\neb 5ccd\ne1 5c8b\nf2 f706\nfb 5dcd\n94 6b9a\n50 9b88\nae 6cdc\n95 6b9b\n51 9b89\naf 6cdd\na5 6c9b\n61 9c89\nbf 6ddd\n7c 1fd0\n62 1e8e\n7f 1755\n65 1613\n28 acc8\n41 3083\n5b 31c5\nb1 6709\n32 2dac\ne0 5c8a\nfa 5dcc\n11 2109\neb 5ecd\n10 210a\nea 5ece\n10 8b8a\n23 2605\n2a 8ccc\ne8 fc62\n11 8b8b\n2b 8ccd\n27 2615\n14 8b9a\n2e 8cdc\nec fc72\n15 8b9b\n2f 8cdd\n21 8c8b\nf8 fd62\n3b 8dcd\nd0 fb80\n33 2707\n3a 8dce\nfc fd72\n25 8c9b\n3f 8ddd\nd4 fb90\n37 2717\n3e 8dde\n65 9c9b\n7f 9ddd\n77 3717\n7e 9dde\nf9 d74b\n73 3727\n7a 9dee\n20 8c8a\n33 2705\n3a 8dcc\n24 8c9a\n37 2715\n3e 8ddc\ne8 fe62\n2b 8ecd\nec fe72\n2f 8edd\n3d 25d1\n23 248f\n77 3715\n64 9c9a\n7e 9ddc\n6f 9edd\n14 8992\n0 222a\n1a 236c\n6e 9ede\nc9 7041\naf 6eff\n6b 9eed\nc8 7042\n6a 9eee\naf 6cdf\n51 9b8b\n6b 9ccd\n73 3707\n7a 9dce\n67 1637\n10 abaa\na9 e649\n2a acec\n21 acab\na0 e608\n3b aded\n73 3705\n60 9c8a\nbe 6dde\n7a 9dcc\naf 6edf\n6b 9ecd\n6a 9ece\nb9 e749\n20 acaa\n77 1737\n3a adec\ncd 5053\n2b aeed\n39 a763\n28 ce8\n38 de8\n79 b763\n68 1ce8\n78 1de8\nb9 e763\na8 4ce8\nb8 4de8\n30 a722\n39 de9\n70 b722\n79 1de9\nba e764\na0 e622\na9 4ce9\nb0 e722\nb9 4de9\n9e e17e\n28 ee8\nde f17e\n68 1ee8\na8 4ee8\na9 4ee9\n28 2ce8\n38 2de8\n39 2de9\n28 2ee8\n1f 1d7\na8 6ee8\n29 2ee9\n31 2721\n38 8de8\n29 8ce9\n39 8de9\nc6 703c\nac 6efa\n68 9ee8\n28 cca\n48 bac8\n68 1cca\na1 4e83\nbb 4fc5\nc2 7a8c\n2c cda\n4c bad8\n42 100c\n28 eca\n46 101c\n2c eda\n3c dd0\n22 c8e\n26 c9e\n66 1c9e\n77 1d9f\naa 4cce\n63 1605\nae 4cde\nbb 4dcf\nbf 4ddf\n2c 2cda\n67 1e9f\n7d 1ff1\n63 1eaf\nc5 5011\nab 4ecf\naf 4edf\n15 211b\nef 5edf\nb9 cd63\n26 2c9e\nbb 674d\n3c 2df0\na1 660b\n22 2cae\nb0 cd22\n37 2d9f\nb2 670c\n33 2daf\nea 5cce\n63 3605\n50 9b8a\n6a 9ccc\nae 6cde\n79 9f6b\n61 9c8b\n7b 9dcd\nbf 6ddf\n7d 1fd1\n63 1e8f\nd1 fb81\n3b 8dcf\nd5 fb91\n3f 8ddf\n7f 9ddf\nfa d74c\ne0 d60a\n7b 9def\n45 9011\n2b 8ecf\n2f 8edf\n6f 9edf\nd1 f929\n1a 236e\nc9 7043\n6b 9eef\n63 3607\n6a 9cce\n7b 9dcf\n6b 9ecf\n2f aedf\n28 cea\nea 5c4e\n48 bae8\na8 4cea\n78 9dc8\nbc 6dda\n71 3701\nb8 4dea\n42 102c\n28 eea\nb7 ef9d\n68 1eea\nc2 502c\na8 4eea\n21 2623\n3b 2765\n28 8cea\n31 2723\n38 8dea\nfd f773\n26 869c\nec 5cf8\nfe f774\ne4 f632\n27 869d\ned 5cf9\nf4 f732\n37 879d\nfd 5df9\n36 879e\nfc 5dfa\n36 879c\nfc 5df8\n6 2094\ned 5ef9\n1f 21d7\nec 5efa\nad 6ef9\n2f aef5\ncf 505f\n2d aef9\nab 464d\n12 bae\n2c cf0\n13 baf\n2d cf1\n23 caf\na2 460c\n3d df1\nad 4651\n14 bb2\n2e cf4\na3 460f\nbd 4751\n24 cb2\n3e df4\n15 bb3\n2f cf5\n2c ef0\n2d ef1\n2e ef4\n2f ef5\nad 6651\nb4 cd18\n14 2bb2\n2e 2cf4\nb5 cd19\n15 2bb3\n2f 2cf5\n2c 2ef0\n2d 2ef1\nb4 cf18\n2e 2ef4\nb5 cf19\n2f 2ef5\na1 c60b\nbb c74d\n22 8cae\n3c 8df0\na2 c60c\n23 8caf\n3d 8df1\n71 3729\nad c651\n14 8bb2\n2e 8cf4\na3 c60f\nbd c751\n24 8cb2\n3e 8df4\n15 8bb3\n2f 8cf5\n77 9d95\n63 362d\n25 8cb3\na4 c610\n3f 8df5\ncf 5057\n2d aef1\nd4 5310\n55 19b3\n99 49e3\n2f aefd\nb9 4deb\n24 8438\n17 8397\ndd 59f3\ne9 7ec1\n53 110f\n37 879f\nfd 5dfb\na3 4c25\n89 4ae3\nc3 502d\na9 4eeb\n6 2096\ned 5efb\n94 6310\n15 29b3\n49 9ae1\na7 6c35\n8d 6af3\n1d 2351\n3 220f\nc7 703d\n69 9ee9\nad 6efb\nba c744\na0 c602\n3b 8de7\nb8 c748\n32 2724\n39 8deb\nfc d758\n76 3734\n7d 9dfb\ned fc5b\n23 8c25\n9 8ae3\n87 603f\n43 902d\n29 8eeb\nab 6c47\n67 9c35\n4d 9af3\ncb 704f\n6d 9efb\n82 e20e\n9c e350\n1d a9f3\nbe e754\na4 e612\nad 4cd9\n3f adf7\n52 13ac\n27 ac35\nd aaf3\n2f aef7\n47 b03d\n2d aefb\naf e6dd\n60 162a\n30 ad80\n7a 176c\n40 b0a2\n5a b1e4\n72 17ac\n12 23ac\n34 8d92\n20 262a\n3a 276c\n32 27ac\n46 18be\nc5 521b\ndf 535d\nd7 539d\nec 54f0\nd2 53ae\nc2 7aa4\nab 464f\n2c cf2\nee 5c56\n4c baf0\nad 4653\n2e cf6\n4e baf4\nbd 4753\n3e df6\n46 1034\n2c ef2\n69 9641\nad 6653\nb4 cd1a\n2e 2cf6\nd2 fba4\nbb c74f\n3c 8df2\n70 372a\nad c653\n2e 8cf6\n7c 3770\n76 9d96\n62 362e\n8a 6046\n46 9034\n2c 8ef2\n2e 8ef6\n85 621b\n41 9209\n6 28be\n9f 635d\n9a 636e\n7a 956e\n97 639d\n93 63ad\nac 64f0\n92 63ae\n33 a727\na1 4609\n22 cac\n23 cad\n2a a6e6\n33 dad\nb1 470b\n32 dae\n77 b737\ne5 5619\n66 1cbc\n67 1cbd\n6e b6f6\n77 1dbd\nb1 4709\n32 dac\n10 2922\n4b 926d\n8f 627f\nf5 5719\n76 1dbc\ndd f153\n67 1ebd\n66 1ebe\na5 6619\n26 2cbc\nb5 6719\n36 2dbc\n27 2ebd\n61 9e83\n7b 9fc5\nbf 6fd7\ncc 7078\n10 8baa\n23 2625\na9 c649\n2a 8cec\n11 8bab\n2b 8ced\n21 8cab\na0 c608\n3b 8ded\nd0 fba0\nb9 c74b\n33 2727\n3a 8dee\nfd d75b\n77 3737\n7e 9dfe\nb9 c749\n20 8caa\n33 2725\n3a 8dec\n89 6041\n2b 8eed\n44 9030\n88 6042\n2a 8eee\nb3 6f0f\ncd 7051\n6f 9efd\ncc 7052\n6e 9efe\n48 b040\n2e aefe\n3d a773\n2c cf8\n3c df8\nbd e773\nac 4cf8\nbc 4df8\n34 a732\n3d df9\nbe e774\na4 e632\nad 4cf9\nb4 e732\nbd 4df9\n2c ef8\n1f 21d5\n5 2093\nec 5ef8\n3c 2df8\nb4 efb8\n72 1fa6\n2c 2ef8\nac 6ef8\n2d 2ef9\n35 2731\n3c 8df8\n2d 8cf9\n3d 8df9\na1 460b\nbb 474d\n3c df0\n22 cae\n67 1ebf\n61 9609\na5 661b\nbf 675d\n26 2cbe\n41 3001\n27 2ebf\na9 c64b\nc0 faa0\n23 2627\n2a 8cee\nba c74c\nd1 fba1\na0 c60a\n3b 8def\nfe d75c\ne4 d61a\n7f 9dff\n45 9031\n89 6043\n2b 8eef\ncd 7053\n6f 9eff\n6c 14f0\n52 13ae\n49 b041\n2f aeff\n2c 24f0\n12 23ae\ned 54f1\nd3 53af\nc2 7aac\n2c cfa\nee 5c5e\n4c baf8\n61 1621\nac 4cfa\n7c 9dd8\n75 3711\n71 1721\nbc 4dfa\n46 103c\n2c efa\nc2 faac\n25 2633\n3f 2775\n2c 8cfa\nd2 fbac\n35 2733\n3c 8dfa\n46 903c\n8a 604e\n2c 8efa\n5b 936d\n41 922b\n9f 637f\nad 64f1\n93 63af\n30 f00\n31 f01\n32 f04\nb2 4f04\n18 2140\nf2 5f04\n6 203e\ne0 5e02\nfa 5f44\n34 f10\nb4 4f10\na2 4e0e\nbc 4f50\n8 204a\ne2 5e0e\nfc 5f50\nb5 4f11\na3 4e0f\nbd 4f51\n1 200b\n1b 214d\nf5 5f11\n9 204b\ne3 5e0f\nfd 5f51\n36 f14\nb6 4f14\na4 4e12\nbe 4f54\n1c 2150\n2 200e\nf6 5f14\na 204e\ne4 5e12\nfe 5f54\n70 1f80\n31 f81\nbc 4ffa\nd6 513c\n71 1f81\nb4 4f90\n75 1f91\nb5 4f91\n1 208b\n1b 21cd\nf5 5f91\n36 f94\n39 afe9\ndb 514f\nb6 4f94\nbc ef78\naf eed7\n7a 1f66\n79 1f69\n28 844a\n59 b9e1\n30 2f00\n31 2f01\n32 2f04\n1c 152\nb2 6f04\n34 2f10\n22 2e0e\n3c 2f50\nd a279\nc6 7834\n64 b6b0\n1e 15e\nb4 6f10\na2 6e0e\nbc 6f50\n35 2f11\n23 2e0f\n3d 2f51\nc7 7835\nb0 ed8a\n65 b6b1\n1f 15f\nb5 6f11\na3 6e0f\nbd 6f51\n36 2f14\n24 2e12\n3e 2f54\n66 b6b4\nb6 6f14\n60 9e00\na4 6e12\nbe 6f54\n27 ae95\nbc 4f5a\nd9 5169\nb2 4f86\ndd 5179\nb6 4f96\n30 2f80\nd5 713b\n31 2f81\n32 2f84\n1c 1d2\nb2 6f84\n34 2f90\n1e 1de\nb4 6f90\n35 2f91\n1f 1df\nb5 6f91\n36 2f94\n8f c077\nfd 5f59\n67 be95\nfc 5f5a\nb6 6f94\nfa 5f66\n5 2abb\n1f 2bfd\n12 2104\nf9 5f69\n63 bea5\nf8 5f6a\n87 c0b7\nf5 5f99\nf0 5faa\n1e 29de\n6c 34f2\n8f 6055\n31 8f01\nb5 6f13\ncf 7055\n71 9f01\nbd 6f53\n79 9f41\nb6 6f16\n72 9f04\nec d4da\n66 34b6\nbe 6f56\n60 9e02\n7a 9f44\nf4 d51a\n6e 34f6\n33 8f05\n21 8e03\n3b 8f45\n61 9e03\nbf 6f57\n7b 9f45\n56 b13c\n3c affa\n63 9e0f\n7d 9f51\n76 9f14\n6a 34c6\n37 8f15\n77 9f15\n52 b10c\n38 afca\n8e 60d4\n30 8f80\n9d 6179\n32 8f84\n34 8f90\nf2 ff26\n35 8f91\n36 8f94\nfa 5f46\n98 cb60\n5 2a9b\n1f 2bdd\n8b c067\nf9 5f49\n63 be85\nf8 5f4a\n83 c0a7\nf1 5f89\nf0 5f8a\n78 9f48\nbc 6f5a\n67 9eb7\nb8 6f6a\nb2 4f06\nb6 4f16\n1c 2152\nf6 5f16\n39 8f49\n41 9aa1\n9f 6bf5\n85 6ab3\n6e 365e\nf2 ff2e\n35 8f99\n7d 9f59\n1d 151\n3 f\na9 6ec1\n13 10f\nb1 6f01\n1b 14f\n3d 551\nc0 d888\n23 40f\nc4 d898\n27 41f\nc8 d8c8\nc1 7201\n42 38a4\n2b 44f\n5d 1151\n43 100f\ned 7ed1\n57 111f\nf1 7f01\n5b 114f\nf5 7f11\n5f 115f\n7d 1551\n63 140f\n82 48a4\n6b 144f\n9d 4151\n83 400f\n87 401f\n8b 404f\n93 410f\n97 411f\n9b 414f\nbd 4551\n1 a2a9\na3 440f\ndd 5151\n21 aea9\nc3 500f\n25 aeb9\nc7 501f\n31 afa9\nd3 510f\n35 afb9\nd7 511f\nfd 5551\n41 b2a9\ne3 540f\nce 705c\n70 9f08\nb4 6f1a\n1d 171\n3 2f\na9 6ee1\n13 12f\nc0 d8a8\n3d 571\n23 42f\n5d 1171\n43 102f\ne1 7e21\n4b 106f\ne9 7ee1\n53 112f\n7d 1571\n63 142f\n86 e29e\n6b 146f\n9d 4171\n83 402f\nbd 4571\na3 442f\nfd 5571\ne3 542f\n39 a5c3\n5d 11d1\n43 108f\n7d 15d1\n63 148f\n9d 41d1\n83 408f\n93 418f\nbd 45d1\na3 448f\ndd 51d1\nc3 508f\nfd 55d1\ne3 548f\n5d 11f1\n43 10af\n53 11af\n7d 15f1\n63 14af\n1d 351\n3 20f\n7 21f\nab 4c45\n91 4b03\nb 24f\nf 25f\n99 4b43\n47 121f\neb 5c45\nd1 5b03\n4b 124f\nef 5c75\nd5 5b33\n4f 125f\nd9 5b43\n4 8b2\n9d 4351\n1e 9f4\n83 420f\n8 8c2\n87 421f\n10 902\n8f 425f\n4c 18f2\ncb 524f\n30 f20\nf 80df\na5 ee91\n70 1f20\n31 f21\n39 f61\n71 1f21\n79 1f61\nfc 7f5a\n32 f24\na7 ee95\n72 1f24\na0 4e22\nba 4f64\ne0 5e22\nfa 5f64\n30 fa0\nb0 4fa0\n26 6bc\n4f b257\n31 fa1\n71 1fa1\nfc 7fda\n32 fa4\n6d b679\n72 1fa4\n16 13c\n1c 3fa\n36 53c\n3c ffa\n56 113c\n6c 3efa\n86 403c\n7c 3ffa\n96 413c\n3d ffb\n57 113d\n46 3a3e\n6d 3efb\n87 403d\n75 3f3b\n8f 407d\n8d 42fb\na7 443d\nb5 4f3b\ncf 507d\nbd 4ffb\nd7 513d\nc6 7a3e\nc5 503b\ndf 517d\nce 7a7e\n30 2f20\n31 2f21\nb8 cf48\n32 2f24\na0 6e22\nba 6f64\n30 2fa0\n31 2fa1\nb8 cfc8\n32 2fa4\n8e 6074\n30 8f20\n38 8f60\nce 7074\nb4 6f32\n70 9f20\n8f 6075\n31 8f21\n39 8f61\nfc ff5a\n32 8f24\n20 8e22\n3a 8f64\nb6 6f36\n72 9f24\n60 9e22\nbe 6f76\n7a 9f64\nfd ff5b\n33 8f25\n21 8e23\n3b 8f65\n36 5bc\n5f b157\n56 11bc\n86 40bc\n96 41bc\na6 44bc\nb6 45bc\nd6 51bc\n87 40bd\na7 44bd\nd7 51bd\ne0 7c00\nc6 7abe\n1d 371\n3 22f\n1b a9c5\n1 a883\n4b 126f\n9d 4371\n83 422f\n93 e985\ndd 5371\nc3 522f\nc7 52bd\n6 83c\n4 83a\n1e 97c\n7 83d\n6c 3672\nf 87d\ne a276\n17 93d\n5 83b\n1f 97d\n17 a337\n85 4219\n6 8bc\n27 a437\n95 4319\n16 9bc\n97 e337\n86 48bc\n7 8bd\n6c 36f2\n86 4a3c\n8f 4a7d\n86 4abc\nf0 5f00\n16 213c\n28 8ee8\n86 603c\n23 8e87\n30 8f28\n8e 607c\n38 8fe8\n96 613c\nf1 5f01\n17 213d\n5 203b\nf9 5f41\n1f 217d\n29 8ee9\n87 603d\n85 603b\n41 9029\n9f 617d\nf0 5f80\n16 21bc\n96 61bc\n97 61bd\n4a 1044\n30 f02\nce 5054\nb4 4f12\nbc 4f52\n1a 214e\nf4 5f12\nfc 5f52\n4a 10c4\n30 f82\n70 1f82\n59 1169\n32 f86\n84 eab0\n6d b65b\n4e 10d4\n34 f92\nce 50d4\nb4 4f92\n5d 1179\n36 f96\n76 1f96\n2b c45\n11 b03\n86 ea94\n6b 1c65\n51 1b23\n5d 1b53\na6 ee9c\n71 1f2b\n7d 1f5b\nae eedc\n79 1f6b\n4a 3044\n30 2f02\n9d 4b53\naf 4cd5\n95 4b93\n4c 3a70\n4e 3054\n34 2f12\n3c 2f52\n7e b7f4\n64 b6b2\nd a27b\nc6 7836\nce 7054\n70 9f00\nb4 6f12\n78 9f40\nbc 6f52\nb9 4f4b\nbd 4f5b\ndd 5b53\nd9 5b63\nfd 5f5b\n12 2106\nf9 5f6b\nf1 5fab\n8e 6056\n4a 9044\n30 8f02\nce 7056\n70 9f02\n78 9f42\n4e 9054\n34 8f12\n3c 8f52\n74 9f12\n7c 9f52\n4a 90c4\n8e 60d6\n30 8f82\n4e 90d4\n34 8f92\n6b 1c45\n51 1b03\n59 1b43\n2f 2c55\n15 2b13\n2b 2c65\n11 2b23\n1d 2b53\n5f b3f5\n45 b2b3\n4f 305d\n35 2f1b\n4b 306d\n31 2f2b\n3d 2f5b\n7f b7fd\n65 b6bb\n4a b044\n30 af02\n4e b054\n34 af12\n3c af52\nf9 5f4b\n4a b0c4\n30 af82\n59 9b41\n9d 6b53\n4e b0d4\n34 af92\n79 9f49\nbd 6f5b\nb9 6f6b\ncf 70dd\n71 9f89\nb5 6f9b\ncb 70ed\nb1 6fab\naf 4c55\n95 4b13\ncb 504d\nb1 4f0b\n68 3ee0\n7 a09f\ncf 505d\nb5 4f1b\nef 5c55\nd5 5b13\neb 5c65\nd1 5b23\nf5 5f1b\n19 8b43\n1d 8b53\n2f 8cd5\n15 8b93\n3b 8f47\n39 8f4b\n3f 8f57\n3d 8f5b\n5d 9b53\n7f 9f57\n7d 9f5b\n7b 9f67\n7 81f\nf 85f\n5d 1951\n43 180f\n47 181f\n4b 184f\nd1 7103\n73 9faf\n4f 185f\naf 6c55\n51 9b01\n95 6b13\nab 6c65\n91 6b23\ncf 705d\n71 9f09\nb5 6f1b\ncb 706d\nb1 6f2b\n59 9b43\n7b 9f47\n79 9f4b\n1d ab53\n2f acd5\n15 ab93\n39 af6b\n16 813c\n1c 83fa\n36 853c\nba 654e\n5c 93fa\n76 953c\n17 813d\n6 aa3e\n1d 83fb\nd6 59b6\n37 853d\n45 903b\n5f 917d\n4e ba7e\nbb 654f\n5d 93fb\n77 953d\n16 81bc\n36 85bc\n9a 61ce\n56 91bc\n9b 61cf\n57 91bd\n60 bc00\n46 babe\ndf 5bf7\n26 863c\n8a 624e\n46 923c\nf1 fda9\n3a 27ee\n4e 927c\n7 823d\nc6 5ab6\n27 863d\nce 5af6\n2f 867d\n14 2932\n4f 927d\n1d 971\n3 82f\n5d 1971\n43 182f\n4b 186f\n8a 62ce\na4 6410\n46 92bc\n7 82bd\nce 7276\n6 883c\nf 2275\n16 893c\n4 883a\n1e 897c\n8a 684e\n46 983c\n85 c219\n6 88bc\n95 c319\nf 22f5\n16 89bc\n7 88bd\nce 7876\n75 1db9\n6c b6f2\n17 89bd\n7 89f\nde 7976\n8a 6a4e\n46 9a3c\n7 8a3d\nf 8a7d\n1d 2171\nf7 5f35\n3 202f\n5d 19d1\n43 188f\n47 189f\n9d 49d1\n83 488f\ndd 59d1\nc3 588f\nf0 df00\n16 a13c\n1c a3fa\n36 a53c\nf1 df01\n17 a13d\nf9 df41\n5 a03b\n1f a17d\n7 a23d\nc2 520c\n5d 19f1\n43 18af\n6 a83c\n16 a93c\n7 aa3d\n4a 1064\n30 f22\n87 e015\n38 f62\na5 ee93\nbf efd5\n70 1f22\nad eed3\nc7 f015\n78 1f62\n32 f26\n3a f66\nba 4f66\n4a 10e4\n30 fa2\n70 1fa2\n32 fa6\n6d b67b\n4a 3064\n30 2f22\n38 2f62\n8e 6076\n4a 9064\n30 8f22\nce 7076\n70 9f22\n78 9f62\n4a b064\n30 af22\n38 af62\n8a c066\nf8 5f48\n4e b076\nbc 4f58\n8e c076\nfc 5f58\n42 b0a6\nb0 4f88\n46 b0b6\nb4 4f98\n6a 1e6c\nc9 f241\n4a b8e4\n65 1e1b\n7f 1f5d\n45 b893\n5f b9d5\n7e 1f5e\n5e b9d6\n61 1e2b\n7b 1f6d\n41 b8a3\n2a 844e\nc0 f200\n5b b9e5\naf eedf\n7a 1f6e\nd9 f343\nc8 58c8\n5a b9e6\n30 2f08\nb0 6f08\naa 4e4c\nae 4e5c\na6 4e9c\n34 2f18\n3c 2f58\nc6 783c\n64 b6b8\na1 4e0b\nbb 4f4d\nba 4f4e\nb4 6f18\nbc 6f58\na5 4e1b\nbf 4f5d\nbe 4f5e\nb3 4f8d\ncc 50d0\nb2 4f8e\nb7 4f9d\nb6 4f9e\nb0 6f88\nee 5e5c\nc 20d8\ne6 5e9c\n8 20e8\ne2 5eac\ne5 5e1b\nff 5f5d\nfe 5f5e\nb4 6f98\n1d 21d9\nf7 5f9d\n1c 21da\nf6 5f9e\nf7 df15\n1d a151\n3 a00f\n8f 605d\n31 8f09\n75 9f19\nce 70dc\nb4 6f9a\n70 9f88\n74 9f98\n6a 1e4c\n4a b8c4\n61 1e0b\n7b 1f4d\n41 b883\n5b b9c5\n7a 1f4e\n5a b9c6\n26 2e1c\n22 2e2c\n2a 2e6c\n33 2f2d\n4c 3070\n32 2f2e\n3a 2f6e\nea 5e4c\ne1 5e0b\nfb 5f4d\nfa 5f4e\n19 21c9\nf3 5f8d\naa 6e6c\na6 6e9c\nc fa\na2 6eac\n61 9e09\na5 6e1b\nbf 6f5d\na1 6e2b\nbb 6f6d\nba 6f6e\nb7 6f9d\n21 8e0b\n3b 8f4d\n3a 8f4e\n4c 90d0\n32 8f8e\n6e 9e5c\nc0 7000\na6 6ebe\n62 9eac\n65 9e1b\n7f 9f5d\n7e 9f5e\n7a 9f6e\n76 9f9e\nf5 5f13\n1b 214f\nc4 f898\n27 241f\nc8 f8c8\n2b 244f\ncc f8d8\n2f 245f\nd8 f9c8\n3b 254f\n3f 8ffd\n25 8ebb\n9d 6151\n83 600f\n43 900d\n29 8ecb\n87 601f\n2d 8efb\n47 903d\n8b 604f\n31 8f0b\n4b 904d\n8f 605f\n3d 8ffb\n57 913d\n9b 614f\n5f 93fd\n45 92bb\nbd 6551\na3 640f\n49 92cb\n63 940d\na7 641f\n51 930b\n6b 944d\naf 645f\n55 93bb\n6f 94fd\nb3 650f\na6 6e1c\nc 7a\na2 6e2c\nb7 6f1d\nae 6e5e\n6a 9e4c\na6 6e9e\n62 9e8c\n61 9e0b\nbf 6f5f\n7b 9f4d\n7a 9f4e\nb7 6f9f\n73 9f8d\n3a af6e\ned 5ef3\n13 212f\nc0 f8a8\n3d 2571\n23 242f\n9d 6171\n83 602f\nbd 6571\na3 642f\nba c564\na0 c422\n27 249f\n33 258f\nfc ff72\n25 8e9b\n3f 8fdd\n9d 61d1\n83 608f\n43 908d\n87 609f\n93 618f\nbd 65d1\na3 648f\n63 948d\na7 649f\nb3 658f\nf7 5fb5\n1d 21f1\n3 20af\n13 21af\n3d 25f1\n23 24af\nab 4ec7\nb8 4f68\n11 2103\neb 5ec7\nf8 5f68\n30 fa8\n70 1fa8\nf 825f\nb0 4fa8\n7 221f\nb 224f\n51 9b21\naf 6c75\n95 6b33\nf 225f\nc0 fa88\n3d 2751\n23 260f\nc8 fac8\n2b 264f\n23 2e87\n30 2f28\na3 6e87\nb0 6f28\nab 6ec7\n67 9eb5\nb8 6f68\nc 28f2\n47 923d\n8b 624f\nbd 6751\n3e 2df4\n24 2cb2\na3 660f\n67 963d\n2c 2cf2\nab 664f\n30 2fa8\nb0 6fa8\n8f 607d\n31 8f29\n39 8f69\n1d 2371\n17 8997\n3 222f\n1f 89d7\nb 226f\n3d 27d1\n23 268f\n73 1f2f\nd2 f304\n3c 8552\nc1 5889\n53 b9a7\n7f 1f5f\n5f b9d7\nbb 4f4f\nbf 4f5f\ncd 50d1\nb3 4f8f\nb7 4f9f\nff 5f5f\nfb 5f6f\n7b 1f4f\n37 2f1f\n4d 3071\n33 2f2f\n3f 2f5f\n67 b6bf\nfb 5f4f\nbb 6f6f\ncd 5051\nb3 4f0f\nb7 4f1f\n1d 215b\nf7 5f1f\n3b 8f4f\n3f 8f5f\n7f 9f5f\n7b 9f6f\n7 281f\n13 290f\n1b 294f\n47 983d\n8b 684f\ne2 7c86\n29 a6cb\n4f 98fd\n93 690f\n57 993d\n9b 694f\n7b 9f4f\n73 9f8f\n2a 8e6c\n1d 2971\n3 282f\n9a c964\n80 c822\n7 289f\n9d 69d1\n83 688f\n43 988d\n87 689f\n93 698f\n1d 29f1\n82 620c\n3 28af\n23 aea5\nb8 4f6a\n1d 2b51\n3 2a0f\nb 2a4f\n9d 6b51\n83 6a0f\n47 9a3d\n8b 6a4f\n9d 6bd1\n83 6a8f\n16 2114\nfd 5f79\n67 beb5\nfc 5f7a\n35 2f39\nd4 5318\n55 19bb\n66 b436\na5 6e33\n61 9e21\nbf 6f75\nbd 6f79\n6b 9ec7\n78 9f68\nbc 6f7a\ne 20d4\nf5 5f39\nf4 5f3a\n7d 9f79\n71 352b\n7c 9f7a\n1d 8151\n3 800f\ndb 736d\nd5 d993\nc1 722b\n68 1e60\n7 801f\ndf 737d\nc5 723b\na1 ee01\nb 804f\ndd d9d3\nc9 726b\na9 eec1\n13 810f\nad eed1\n78 1f60\n17 811f\nb1 ef01\n1b 814f\n3d 8551\n23 840f\nf5 dd93\nfb 776d\ne1 762b\n27 841f\nff 777d\ne5 763b\n42 b8a4\nc1 f201\n2b 844f\nfd ddd3\ne9 766b\ned 767b\nc9 f2c1\n33 850f\ncd f2d1\n37 851f\n5d 9151\n43 900f\n47 901f\ne1 fe01\n4b 904f\ne5 fe11\n4f 905f\ne9 fec1\n53 910f\ned fed1\n57 911f\nf1 ff01\n5b 914f\nf5 ff11\n5f 915f\n7d 9551\n63 940f\n82 c8a4\n6b 944f\n73 950f\nb5 6f39\nce 707c\n63 9e87\n70 9f28\nb4 6f3a\n25 ae33\n3f af75\n1f 177\n3c af7a\n1d 8171\n3 802f\na9 eee1\n13 812f\nb1 ef21\n1b 816f\n3d 8571\n23 842f\nc9 f2e1\n33 852f\n5d 9171\n43 902f\ne1 fe21\n4b 906f\ne9 fee1\n53 912f\nf1 ff21\n5b 916f\n7d 9571\n63 942f\n6b 946f\n73 952f\n13 818f\n3d 85d1\nfa f566\n23 848f\nfb 77ed\ne1 76ab\n5d 91d1\n43 908f\n7d 95d1\n63 948f\n3d 85f1\n23 84af\n53 91af\n34 f30\n35 f31\n23 e2f\n3d f71\n36 f34\na4 4e32\nbe 4f74\ne4 5e32\na 206e\nfe 5f74\n74 1fb0\nb4 4fb0\n2a 6cc\n75 1fb1\nb5 4fb1\ne8 7662\n2b 6cd\n6b 16cd\n76 1fb4\n34 2f30\na2 6e2e\nbc 6f70\n35 2f31\na3 6e2f\nbd 6f71\nbc cf58\n36 2f34\na4 6e32\n60 9e20\nbe 6f74\n34 2fb0\nd9 716b\n1e 1fe\nb4 6fb0\n2a 26cc\n35 2fb1\n1f 1ff\nb5 6fb1\n2b 26cd\nbc cfd8\n36 2fb4\nb8 6f42\n74 9f30\n68 34e2\n35 8f31\nb9 6f43\n75 9f31\n63 9e2f\n7d 9f71\n71 3523\nba 6f46\n76 9f34\nf0 d50a\n6a 34e6\n37 8f35\nbb 6f47\n77 9f35\n52 b12c\n38 afea\n35 af31\nab 6ee5\n15 133\n37 af35\n17 137\n1d 8351\nc9 586b\n3 820f\ncd 587b\n7 821f\nb 824f\ne9 5c6b\n3d 8751\n23 860f\ned 5c7b\n27 861f\n5d 9351\n43 920f\n47 921f\n4b 924f\n4f 925f\n7d 9751\n63 960f\n67 961f\n1d 8371\n3 822f\n5d 9371\n43 922f\n4b 926f\n2b c65\n11 b23\n6f 1c75\n55 1b33\nb9 4f6b\ndd 5b73\nef 5cf5\nd5 5bb3\n16 2116\nfd 5f7b\n1d 21f3\nf7 5fb7\n2f 2c75\n15 2b33\n4f 307d\n35 2f3b\n7e 375e\n51 9ba1\naf 6cf5\n95 6bb3\n61 9e23\n7b 9f65\nbf 6f77\n79 9f69\nbd 6f7b\nab 4c65\n91 4b23\ncb 506d\nb1 4f2b\ne 20d6\nf5 5f3b\n39 8f6b\n5d 9b73\nb3 6d07\n6f 9cf5\n55 9bb3\n7f 9f77\n7d 9f7b\n1d 8951\n3 880f\ndb 7b6d\nc1 7a2b\n7 881f\ndf 7b7d\nc5 7a3b\nb 884f\nc9 7a6b\nf 885f\ncd 7a7b\n13 890f\n17 891f\n5d 9951\n43 980f\n47 981f\n4b 984f\n4f 985f\n53 990f\n57 991f\ncf 707d\n71 9f29\nb5 6f3b\n2f acf5\n15 abb3\n4a 1acc\n4b 1acd\n20 248a\n3a 25cc\n49 3269\n5d 99d1\n43 988f\n4d 3279\n47 989f\n2e 26dc\nb8 6fc0\na8 c660\n2f 26dd\nb9 6fc1\n0 288a\n1a 29cc\nc2 d20c\n5d 99f1\n43 98af\na 2acc\ne 2adc\n4e 1074\n34 f32\n3c f72\n4e 10f4\n34 fb2\n74 1fb2\nce 50f4\nb4 4fb2\n36 fb6\nb8 efc8\n76 1fb6\n4e 3074\n34 2f32\n3c 2f72\n78 9f60\nbc 6f72\n4e 9074\n34 8f32\n74 9f32\n7c 9f72\n3c af72\n1d 8b51\n3 8a0f\n7 8a1f\n5d 9b51\n43 9a0f\n47 9a1f\n1d 8b71\n3 8a2f\n5d 9b71\n43 9a2f\n8b 6acd\na4 6c10\n46 9abc\n8a 6ace\n81 408b\n9b 41cd\n66 1e3c\nc5 f211\n2f 845f\n46 b8b4\n77 1f3d\n26 841e\n57 b9b5\n8b 62ed\n76 1f3e\nd5 f313\nef f455\nc4 5898\n56 b9b6\n65 1e3b\n7f 1f7d\n45 b8b3\nc4 f210\n2e 845e\n5f b9f5\n7e 1f7e\ndd f353\ncc 58d8\n5e b9f6\na2 4eac\nc 20f8\ne6 5ebc\n26 2e3c\n2e 2e7c\n10 10a\na6 6ebc\n61 9e29\na5 6e3b\nbf 6f7d\n21 8e2b\n3b 8f6d\n3a 8f6e\n6e 9e7c\n7c 3570\n62 342e\naa 6ece\nc4 7010\n66 9ebc\n65 9e3b\n7f 9f7d\n73 352f\n7e 9f7e\n68 3e60\n7 a01f\ne5 de13\nff df55\nb a04f\nfe 5fde\ned ded3\n13 a10f\n78 3f60\n17 a11f\n88 4260\n27 a41f\n2b a44f\na6 6e3c\n3d a571\n23 a42f\n33 a52f\nf7 df95\n1d a1d1\n3 a08f\n13 a18f\nf7 dfb5\n1d a1f1\n3 a0af\n13 a1af\n3d a5f1\n23 a4af\na7 4e97\nb4 4f38\nd 20d3\ne7 5e97\nf4 5f38\nef 5ed7\n15 2113\nfc 5f78\nb5 4f39\n34 fb8\n74 1fb8\nb4 4fb8\n27 2e97\n34 2f38\na7 6e97\n63 9e85\nb4 6f38\naf 6ed7\n6b 9ec5\nbc 6f78\n34 2fb8\nb4 6fb8\n35 8f39\nb9 6f4b\n75 9f39\n35 af39\nab 6eed\n15 13b\nc9 786b\n1d a351\n3 a20f\ncd 787b\n7 a21f\nb a24f\nf a25f\ne9 7c6b\n3d a751\n23 a60f\ned 7c7b\n27 a61f\n2b a64f\n1d a371\n3 a22f\n7 895\nb a26f\n3d a771\n23 a62f\n15 a31b\n2f a45d\nce 78d6\n77 1f3f\nd6 f314\nc5 5899\n57 b9b7\n7f 1f7f\nde f354\ncd 58d9\nc4 f212\n5f b9f7\ncd 50f1\nb3 4faf\nff 5f7f\n37 2f3f\n3f 2f7f\n61 9e2b\n7b 9f6d\nbf 6f7f\nd1 7101\n73 9fad\nb7 6fbf\n3b 8f6f\n7f 9f7f\n1d a951\n3 a80f\n7 a81f\nb a84f\nf a85f\n13 a90f\n17 a91f\n1b a94f\n6e 16de\n1e 21fe\nf8 5fc2\n3f af7f\n4e 1ade\n21 24ab\n3b 25ed\n1d a9d1\n4d 127b\n3 a88f\n7 a89f\n5d 137b\n13 a98f\n2b 26ed\n99 634b\n55 9339\n1a 29ee\n82 e20c\n1d a9f1\n3 a8af\ne 2ade\ndc 7172\n5 9b\n1f 1dd\nb 2aed\n24 2c30\na 2aee\n1 ab\n1b 1ed\nce 507c\nb4 4f3a\nce 50fc\nb4 4fba\n4e 907c\n34 8f3a\n10 a9aa\na9 e449\n67 1437\n3c 8f7a\n18 a9ea\n6f 1477\n4e b07c\n34 af3a\n1d ab51\n3 aa0f\n7 aa1f\nb aa4f\n1d ab71\n3 aa2f\n78 1fc0\n79 1fc1\n60 1e82\n7a 1fc4\na0 4e82\nba 4fc4\ne1 5e83\n7 20bf\nfb 5fc5\na2 4e8e\nbc 4fd0\na3 4e8f\nbd 4fd1\n9 20cb\ne3 5e8f\nfd 5fd1\n24 e92\n3e fd4\na4 4e92\nbe 4fd4\nba 4fc6\n4b b0e7\nb9 4fc9\nd2 510c\nb8 4fca\n4f b0f7\nbd 4fd9\nd6 511c\nbc 4fda\n20 2e82\n3a 2fc4\na0 6e82\nba 6fc4\na1 6e83\nbb 6fc5\n22 2e8e\n3c 2fd0\naf 445f\nd a2f9\nc6 78b4\na2 6e8e\nbc 6fd0\n23 2e8f\n3d 2fd1\n96 431e\nc7 78b5\na3 6e8f\nbd 6fd1\n24 2e92\n3e 2fd4\na4 6e92\n60 9e80\nbe 6fd4\n8f c0f7\nfd 5fd9\na5 6e93\n61 9e81\nbf 6fd5\n12 2184\nf9 5fe9\nf8 5fea\nbd 6fd3\nd7 7115\n79 9fc1\n20 8e82\n3a 8fc4\nbe 6fd6\n60 9e82\n7a 9fc4\n21 8e83\n8c 6078\n3b 8fc5\nfa 5fc6\n8b c0e7\nf9 5fc9\nf8 5fca\nbd 6fd9\n16 196\nb9 6fe9\n97 611d\n39 8fc9\n52 910c\n96 611e\n38 8fca\n56 911c\n3c 8fda\n7c 9fda\nd7 713d\nbd 6ffb\n79 9fe9\nd6 713e\n78 9fea\nd6 711e\n78 9fca\n3d afd9\n56 b11c\n3c afda\n21 aea3\n3b afe5\n38 fe0\n17 819f\n78 1fe0\n57 b19f\nb8 4fe0\n97 c19f\nf8 5fe0\n39 fe1\n79 1fe1\n20 ea2\n3a fe4\n60 1ea2\n7a 1fe4\nec 7ef0\n56 113e\n86 403e\nce 507e\n38 2fe0\nb8 6fe0\n39 2fe1\nb9 6fe1\n2f 26fd\nc0 d008\n20 2ea2\n3a 2fe4\n96 6134\n38 8fe0\n97 6135\n39 8fe1\n20 8ea2\n3a 8fe4\n60 9ea2\nbe 6ff6\n7a 9fe4\n21 8ea3\n3b 8fe5\n86 40be\na6 44be\ndb 5147\n39 afe1\n6 83e\ne 87e\n16 93e\n1e 97e\n9f 435d\n85 421b\n6 8be\n86 48be\n56 999c\n4f 32d5\n6 a3e\n11 23ab\n2b 24ed\nf0 5f02\n16 213e\n42 902c\n28 8eea\n86 603e\n52 91ac\n96 61be\nac eef0\n16 813e\ncc f2f0\n36 853e\nd4 f330\n3e 857e\nec fef0\n56 913e\n76 953e\n78 1fc2\nd2 5104\nb8 4fc2\n3a fc6\nbc efd8\n7a 1fc6\nd6 5114\nbc 4fd2\n4b 10cd\n31 f8b\n4f 10dd\n35 f9b\n71 1fab\nb3 4d05\n99 4bc3\nb7 4d15\n9d 4bd3\nc8 5068\nbb 4fc7\nd3 510d\nb9 4fcb\nd7 511d\nbd 4fdb\nf7 5d15\n24 8618\ndd 5bd3\nf3 5d25\n20 8628\nd9 5be3\n56 3114\n3c 2fd2\n27 a43d\nd a2fb\nc6 78b6\nd6 7114\n78 9fc0\nbc 6fd2\n12 2186\nf9 5feb\n56 91be\n4e b85c\nd6 7116\n78 9fc2\n56 9114\n3c 8fd2\n7c 9fd2\n2f 2cd5\n15 2b93\n4f 30dd\nc8 d060\n35 2f9b\n4b 30ed\n31 2fab\nf3 5d05\n20 8608\nd9 5bc3\nfb 5fc7\nb3 6d25\n99 6be3\nd7 711d\n79 9fc9\nbd 6fdb\nbb 6fe7\nd3 712d\nb9 6feb\n6 823e\ne 827e\n26 863e\n46 923e\n4e 927e\n33 8d05\n19 8bc3\n37 8d15\n1d 8bd3\n48 9068\n8c 607a\n3b 8fc7\n53 910d\n97 611f\n39 8fcb\n4c 9078\n3f 8fd7\n57 911d\n3d 8fdb\n73 9d25\nb7 6d37\n59 9be3\n7b 9fe7\nd7 713f\n79 9feb\n20 8400\n6 82be\nb7 6d17\n73 9d05\n59 9bc3\ncc 707a\n7b 9fc7\nd7 711f\n79 9fcb\n33 ad25\n19 abe3\n4c b078\n3f afd7\n57 b11d\n3d afdb\na9 4ec9\n3b afe7\n53 b12d\n39 afeb\n6 883e\ne 887e\n1e 897e\nf5 dd9b\nee 76d4\n9f c35d\n85 c21b\n6 88be\n95 c31b\nf 22f7\naf c45d\n16 89be\n46 9a3e\n51 b3ab\n6b b4ed\nf0 df02\n16 a13e\n36 a53e\n6 a23e\ne a27e\n26 a63e\n20 a400\n6 a2be\n6 a83e\n16 a93e\n87 e095\n52 1124\n38 fe2\nc7 f095\n78 1fe2\n3a fe6\nbc eff8\n7a 1fe6\nd2 7124\nb8 6fe2\ne5 fcb9\n2e 26fe\n96 6136\n52 9124\n38 8fe2\n52 b124\n38 afe2\na 80e6\n78 1fc8\n4a b0e6\nb8 4fc8\n4e b0f6\nbc 4fd8\n4c 10d0\n32 f8e\nd8 f142\n62 1eac\n5d b1fb\n76 1f9e\n73 1fad\n72 1fae\naa 4ecc\nae 4edc\na1 4e8b\nbb 4fcd\nd4 5110\nba 4fce\na5 4e9b\nbf 4fdd\nbe 4fde\n38 2fc8\nc2 78ac\nb8 6fc8\n39 2fc9\n3e a576\nac 4458\nc3 78ad\n6a b4c6\nb9 6fc9\n14 2118\nee 5edc\n3c 2fd8\nc6 78bc\nbc 6fd8\n3d 2fd9\nc7 78bd\n6e b4d6\ne5 5e9b\nff 5fdd\nd6 711c\nbc 6fda\n78 9fc8\n22 2eac\ne1 5e8b\nfb 5fcd\nfa 5fce\nae 6edc\n61 9e89\na5 6e9b\nbf 6fdd\n2a 8ecc\nf8 ff62\n21 8e8b\n3b 8fcd\n54 9110\n3a 8fce\n3e 8fde\nc8 7040\nae 6efe\n6a 9eec\n7e 9fde\nd9 7141\nbf 6fff\n61 9eab\n7b 9fed\nd8 7142\n7a 9fee\nae 6ede\n6a 9ecc\n61 9e8b\nbf 6fdf\n7b 9fcd\n7a 9fce\ncc 5052\n2a aeec\ndd 5153\n21 aeab\n3b afed\n38 fe8\n78 1fe8\nb8 4fe8\n11 2183\nf8 5fe8\nb9 4fe9\n38 2fe8\nb8 6fe8\n39 2fe9\nd8 53c8\n6a b4e6\nd6 713c\nbc 6ffa\n78 9fe8\n97 613d\n39 8fe9\n73 1faf\nd5 5111\nbb 4fcf\nbf 4fdf\nd5 7131\nbb 6fef\n55 9111\n3b 8fcf\n3f 8fdf\nd9 7143\n7b 9fef\n7b 9fcf\nd2 512c\nb8 4fea\n52 912c\n96 613e\n38 8fea\n16 2194\nfd 5ff9\nbd 6ff9\nda 714e\n7c 9ffa\n22 eae\n3c ff0\n23 eaf\n3d ff1\n24 eb2\n3e ff4\n22 2eae\n3c 2ff0\na2 6eae\nbc 6ff0\n23 2eaf\n3d 2ff1\na3 6eaf\nbd 6ff1\nc4 d018\n24 2eb2\n3e 2ff4\nc1 7003\ndb 7145\n63 9eaf\n7d 9ff1\n71 35a3\ndf 5157\n23 aeaf\n3d aff1\n75 1fbb\nb3 4d25\n99 4be3\nd3 512d\nb9 4feb\n24 8638\nf7 5d35\ndd 5bf3\nb7 6d35\n59 9be1\n9d 6bf3\nfd fd5b\n33 8d25\n19 8be3\n97 613f\n53 912d\n39 8feb\nbb 6d47\n77 9d35\n5d 9bf3\n7f 9ff7\ndb 714f\n7d 9ffb\n65 169b\n7f 17dd\n60 16aa\n7a 17ec\n61 16ab\n7b 17ed\n37 ad35\n1d abf3\nad 4ed9\n3f aff7\n57 b13d\n3d affb\n63 b627\n52 1bac\n44 1a9a\n6f b657\n5e 1bdc\n45 1a9b\n5f 1bdd\n40 1aaa\n6b b667\n5a 1bec\n41 1aab\n5b 1bed\n2a 26ec\n24 269a\n3e 27dc\nc8 70c0\nb8 c760\n25 269b\n3f 27dd\nc9 70c1\n20 26aa\n3a 27ec\n21 26ab\n3b 27ed\na 2aec\na9 cc61\n16 2b9c\n12 2bac\n56 1134\n3c ff2\n4 2a9a\n1e 2bdc\n0 2aaa\n1a 2bec\n56 9134\n9a 6146\n3c 8ff2\nda 7146\n7c 9ff2\n4c 10f0\n32 fae\ndc f152\n66 1ebc\n77 1fbd\n76 1fbe\n26 2ebc\n88 6040\n2a 8eec\n99 6141\n21 8eab\n3b 8fed\n54 9130\n98 6142\n3a 8fee\nb2 6f0e\ncc 7050\n6e 9efc\nc3 700f\ndd 7151\n65 9ebb\n7f 9ffd\n73 35af\ndc 7152\n7e 9ffe\n3c ff8\n3c 2ff8\nbc 6ff8\n3d 2ff9\ndc 53d8\n6e b4f6\n77 1fbf\n55 9131\n99 6143\n3b 8fef\ndd 7153\n7f 9fff\n7a 17ee\n65 16bb\n7f 17fd\n7e 17fe\n45 1abb\n5f 1bfd\n25 26bb\n3f 27fd\nc9 70e1\n28 2c40\ne 2afe\n9a 614e\n56 913c\n3c 8ffa\n34 2d30\n1a 2bee\n38 2d40\n1e 2bfe\n55 3113\nc a2fa\n26 a43c\ndf 79f7\n5d 3153\n79 356b\nd1 71a3\nf9 756b\nf1 75ab\n5f 3355\n4c 98da\n45 3213\ncf 7af7\n5b 3365\n48 98ea\n41 3223\n54 991a\n4d 3253\n6e 9cde\n67 3617\n7f 375d\n65 361b\ne9 d64b\n6a 9cee\n63 3627\n75 9d93\n7b 376d\n61 362b\n76 9d1e\n6f 3657\n84 6ab0\n6d 365b\n72 9d2e\n6b 3667\ndf 73d5\nc5 7293\nf2 dd2e\neb 7667\n75 3513\n7d 3553\n57 3917\n55 391b\nd9 d94b\n53 3927\n51 392b\n5f 3957\n5d 395b\n59 396b\nf1 75a3\nc a25a\ndf 7957\n13 925\ndd 795b\n4 890\n8 a26a\ndb 7967\n13 a30f\n2d a451\nd9 796b\n25 a491\nd1 79ab\n6c 9cda\n7f 3755\n65 3613\n68 9cea\n7b 3765\n61 3623\n74 9d1a\n6d 3653\n5f 3b5d\n45 3a1b\n5b 3b6d\n41 3a2b\n4d 3a5b\n6e 3cf6\nf4 dd1a\ned 7653\nff 77d5\ne5 7693\n77 9fbf\nd5 7113\nd1 7123\nf1 752b\n54 199a\n7f b557\n79 b56b\n71 b5ab\nea dcee\ne3 7627\n4b b267\n5f b3d5\n45 b293\n5b b3e5\n41 b2a3\n65 1c91\n69 b66b\n74 b738\n67 b697\n7f b7dd\n65 b69b\n7b b7ed\n61 b6ab\nf1 7523\nb 8e5\nd5 791b\n1a a36c\n0 a22a\nd3 7927\nb a2cf\n25 a411\nd1 792b\n7d b553\n71 b5a3\n5f b957\n5d b95b\n59 b96b\nd0 f308\n51 b9ab\nec dcda\n66 3cb6\nff 7755\ne5 7613\n5c 1bd8\n6d b653\n7f b7d5\n65 b693\n7b b7e5\n61 b6a3\n4f ba57\n4d ba5b\n4b ba67\n49 ba6b\nfd 5d53\n5b bbed\n41 baab\n48 32ea\n62 342c\n49 32eb\n63 342d\n5d 33db\n77 351d\n72 352e\n61 342b\n7b 356d\n7a 356e\nd0 732a\nea 746c\n90 698a\n45 32b1\ne7 749d\ne2 74ac\n5c 33da\n76 351c\n58 33ea\n72 352c\n60 342a\n7a 356c\n67 361d\n97 6bb5\n66 361e\ne0 742a\nfa 756c\nf2 75ac\n46 381c\n42 382c\n4f 385d\nb 245\n56 391e\n45 381b\n5f 395d\n5e 395e\n41 382b\n5b 396d\n5a 396e\n4 a210\nca 786c\n68 b6e8\n5 a211\ncb 786d\n69 b6e9\nc6 789c\nc7 789d\nc5 781b\ndf 795d\nde 795e\nc1 782b\n15 a311\ndb 796d\n14 a312\n2e a454\n1d 9d9\nda 796e\nd7 799d\n8b 42c5\nd6 799e\nbc 4558\nd3 79ad\n7a b5c6\n49 30c9\n56 391c\n52 392c\n44 381a\n5e 395c\n40 382a\n5a 396c\n43 3a2d\nc4 781a\nde 795c\nc0 782a\n14 a310\nda 796c\nd2 79ac\n48 30c8\n3 2a5\ncd 72db\ne7 741d\n35 599\nf2 752e\n50 b32a\n6a b46c\n62 b4ac\n63 b4ad\n61 b42b\n7b b56d\n7a b56e\n77 b59d\n72 b5ae\ndc 73da\n12 3a4\nf6 751c\nd8 73ea\nf2 752c\n60 b42a\n7a b56c\n72 b5ac\n85 eab3\n9f ebf5\n6e b65e\n6b b66d\ne1 5621\n66 b69e\n63 b6ad\nc5 7831\n7c b7f0\n62 b6ae\nd7 791d\n75 b799\n8b 4245\nd6 791e\n74 b79a\n4f b85d\n4a b86c\n46 b89c\n47 b89d\nc1 f209\n42 b8ac\n2c 8458\n43 b8ad\n45 b81b\n5f b95d\n5e b95e\n41 b82b\n5b b96d\n5a b96e\n57 b99d\nb 82c5\nd1 5921\n56 b99e\n3c 8558\n53 b9ad\nd6 791c\n44 b81a\n5e b95c\n40 b82a\n5a b96c\nd1 f309\n52 b9ac\n4f ba5d\n55 311b\ndf 79ff\n59 316b\n6a 346e\nd1 71ab\n2d 4d9\nea 746e\n5f 33f5\ne5 d419\n45 32b3\n24 498\nfb 756f\n5f 335d\n45 321b\ne9 7c41\ncf 7aff\n5b 336d\n55 9993\n41 322b\n4d 325b\n5d 99d3\n49 326b\n15 8933\n67 361f\n7d 3771\n77 9d97\n63 362f\n86 6ab4\n6f 365f\n46 381e\n4e 385e\n4a 386e\n57 391f\n53 392f\n5f 395f\nce 785e\n6c b6da\n4 a212\nd 8d9\n1e a354\nca 786e\n68 b6ea\nc6 789e\ndf 795f\n15 a313\n2f a455\n4 898\ndb 796f\n47 3a1f\n5d 3b71\n43 3a2f\n4f 3a5f\nb e5\nd5 711b\nd1 712b\nfc 7570\n25 499\ne2 742e\nf3 752f\n59 b16b\n51 b1ab\n6a b46e\n7b b56f\n73 b5af\n45 1891\n49 b26b\n5b b3ed\nfd 5553\n41 b2ab\n67 1c95\n6b b66f\n7d b7f1\n63 b6af\n27 a415\nd a2d3\nd3 792f\n4e b85e\n4a b86e\nc1 5821\n46 b89e\n5f b95f\nd2 f30c\n3c 855a\n53 b9af\n4f ba5f\n4b ba6f\n2a 2eee\n44 3030\n2b 2eef\n45 3031\ncc d058\n2c 2ef2\n46 3034\n3b 2fef\n55 3131\n54 3132\ndc d15a\n56 3136\n57 3397\n64 3438\n65 3439\n5d 33f3\nfd d559\n77 3535\n37 af97\n44 b038\nfc d55a\n76 3536\n75 3539\n74 353a\n7e 3576\n56 3396\n7d 3579\nb3 6f2f\ncd 7071\nb5 6f33\n71 9f21\ncf 7075\nc4 70b0\nc5 70b1\nd7 71b5\ne6 74b4\ne4 74b8\nfc 757a\nf6 75b6\nf4 75ba\n3a 2fee\n54 3130\ndc d158\n3c 2ff2\n56 3134\n90 690a\n4c 98f8\n45 3231\n5e 3374\n44 3232\n92 690e\ncd d259\n4e 98fc\n47 3235\n4c 3272\n9a 694e\n56 993c\n4f 3275\n5c 33f2\nfc d558\n76 3534\n67 3497\n74 3538\nd4 71b0\nd6 71b4\nd6 d93c\ncf 7275\n31 503\nc7 72b5\nf6 75b4\nf4 75b8\n4a 32ee\n64 3430\n4b 32ef\n65 3431\n5b 33ef\n75 3531\n74 3532\n63 342f\n7d 3571\n7c 3572\ncc d858\n46 3834\ncd d859\n47 3835\n44 3838\n4f 3875\ndd d959\n57 3935\ndc d95a\n56 3936\n45 3833\n5f 3975\n5d 3979\nd2 732e\n15 399\nec 7470\ncd d2d9\n92 698e\n47 32b5\ne4 74b0\nf4 75b2\nce 7874\n6c b6f0\ncf 7875\nb8 edca\n6d b6f1\n6 a21c\ncc 7878\n7 a21d\ncd 7879\nc4 78b8\nc5 78b9\n6c b4d2\nc5 7833\nc a278\ndf 7975\n17 a31d\ndd 7979\n16 a31e\ndc 797a\na6 441e\n4 a2b8\nd7 79b5\n33 2f8f\n4d 30d1\n1d a3fb\n37 a53d\nd6 79b6\n4c 30d2\nd5 79b9\n7c b5d2\n5a 33ee\n74 3530\n6c 9cf8\nb0 6d0a\n65 3631\n7e 3774\n64 3632\ndc d958\n56 3934\n47 3897\n54 3938\n45 3a39\n4f 3a75\n4d 3a79\nf4 75b0\n7 89d\nc4 7832\nde 7974\ncf 78d7\n16 a31c\ndc 7978\nbf 455f\n1d a3f9\nd6 79b4\n32 2f8e\n4c 30d0\nd4 79b8\ncf 7a75\n31 d03\nc7 7ab5\naa 6eee\nc4 7030\nab 6eef\nc5 7031\n17 19d\nd4 7132\nbd 6ff3\n79 9fe1\nd7 7135\n78 9fe2\nd6 7136\ncc 72f2\ne6 7434\nd7 7397\ne4 7438\ne5 7439\ndd 73f3\nf7 7535\nf6 7536\nf5 7539\nf4 753a\n32 af2e\n4c b070\n34 af32\n4e b074\n35 af33\n4f b075\n43 b02f\n5d b171\n45 b033\n5f b175\n7c b57a\ne4 5498\n76 b5b6\n45 30b9\nba 6fee\nd4 7130\nbc 6ff2\n78 9fe0\nd6 7134\nce d8fc\nc7 7235\nc6 7236\ndc 73f2\nf6 7534\ne7 7497\nf4 7538\n56 b1b4\n9a e94e\n4f b275\ncc d8f0\n2f 477\n57 193d\n4e b276\n76 b5b4\n6c b67a\nca 72ee\ne4 7430\n37 59d\nf4 7532\nc4 7838\n52 b32e\n6c b470\n74 b5b2\n4e b874\n4f b875\n36 51e\ncc 72d0\n4c b878\n4d b879\n44 b8b8\n45 b8b9\n45 b833\n5f b975\ncc 5858\n5e b976\n5d b979\n5c b97a\n55 b9b9\nda 73ee\nf4 7530\nc7 7897\ne a2dc\nd4 7938\n62 b42e\n7c b570\n74 b5b0\nb8 ed4a\n6d b671\n4f b8d7\n5c b978\nd5 f311\n3f 855f\n56 b9b4\n8a 62ec\n54 b9b8\n4f ba75\ndc 5b7a\n47 bab5\n44 30b0\n54 31b0\n64 34b0\n74 35b0\n45 30b1\n55 31b1\ncc d0d8\n46 30b4\ndc d1d8\n56 31b4\nec d4d8\n66 34b4\nfc d5d8\n76 35b4\n44 3230\n64 3630\n4d 98fb\ncc d258\n46 3234\n6d 9cfb\nec d658\n66 3634\ncd d8fb\nc6 7234\ned dcfb\ne6 7634\n44 32b0\n64 36b0\nb0 6d8a\n65 36b1\ncc d2d8\n46 32b4\nec d6d8\n66 36b4\n30 502\nc6 72b4\ne6 76b4\n44 3830\n54 3930\nc4 7830\n6 a214\ncc 7870\ne a2d4\nd4 7930\nc2 782e\n5 899\n16 a314\ndc 7970\n43 382f\n5d 3971\n7 a215\ncd 7871\n6b b6ed\nf a2d5\nd5 7931\nc3 782f\n17 a315\ndd 7971\nc3 720d\n2d 45b\n44 38b0\nd3 730d\n3d 55b\n54 39b0\nad 445b\nc4 78b0\nbd 455b\nd4 79b0\n94 431a\nae 445c\nc5 78b1\na4 441a\nbe 455c\nd5 79b1\ncc d8d8\nc5 7211\n2f 45f\n46 38b4\n16 31e\ncd d8d9\n47 38b5\ndd d9d9\n26 41e\n57 39b5\n44 3a30\n45 3a31\n2d 65b\n44 3ab0\n2e 65c\n45 3ab1\ncc dad8\n2f 65f\n46 3ab4\naf 465f\n30 d02\nc6 7ab4\ncd dad9\n47 3ab5\n2a aeee\n44 b030\n3a afee\n54 b130\n4a b2ee\n64 b430\n5a b3ee\n74 b530\n2b aeef\n45 b031\n3b afef\n55 b131\n4b b2ef\n65 b431\n5b b3ef\n75 b531\n2c aef2\n46 b034\n3c aff2\n56 b134\n5c b3f2\n76 b534\n2d aef3\n47 b035\n3d aff3\n57 b135\n5d b3f3\n77 b535\n64 b630\n6c b670\n90 e90a\n45 b231\nc2 d8ac\n3f 575\n25 433\nb0 ed0a\n65 b631\n55 1bb9\n66 b634\n92 e90e\n47 b235\nc4 d8b0\n27 437\n44 b830\n4c b870\n43 b82f\n5d b971\n46 b834\n45 ba31\ne7 5c17\n2e 865c\n45 bab1\n5e 3174\n44 3032\ncc d05a\n46 3036\n4c 3072\n55 3133\nec d45a\n66 3436\nfd d55b\n77 3537\n5e b17c\n44 b03a\n75 353b\n7f 3577\n4c b07a\ne6 74b6\nf5 75bb\n5f 3375\n4c 98fa\n45 3233\ned d65b\n6e 9cfe\n67 3637\n7f 377d\n65 363b\n76 9d3e\n6f 3677\nf6 dd3e\nef 7677\n7e 3574\n64 3432\n6c 3472\n75 3533\ncc d85a\n46 3836\n4e 3876\ndd d95b\n57 3937\n55 393b\n5f 3977\n5d 397b\nf5 75b3\n6 a21e\ncc 787a\nc a27a\ndf 7977\n17 a31f\ndd 797b\n1e a3fc\n4 a2ba\nd7 79b7\nd5 79bb\n7f 3775\n6c 9cfa\n65 3633\n5f 3b7d\n45 3a3b\ncf 7a77\nc7 7ab7\n7 9d\nde 7174\nc4 7032\nd5 7133\n79 9fe3\nd7 7137\ne6 7436\nfe 757c\ne4 743a\nf7 7537\nf5 753b\n50 3380\ncd 5059\n5f b177\n6c b47a\n75 b5bb\nce d8fe\nc7 7237\nee dcfe\ne7 7637\n4f b277\n27 49d\nfe 7574\ne4 7432\nf5 7533\n1e a37c\n4 a23a\nd7 7937\nf a2df\nd5 793b\n75 b5b3\n4e b876\n4c b87a\ncd 5859\n5f b977\n5d b97b\nd4 f318\n55 b9bb\n7f b7f5\n65 b6b3\n4f ba77\n4d ba7b\n47 bab7\n5f bbfd\n45 babb\n74 35b2\nfc d5da\n76 35b6\n5e 33f4\ne4 d418\n44 32b2\n7e 37f4\n64 36b2\n5e 3974\n44 3832\n4c 3872\n54 3932\ne a2d6\n17 99d\nd4 7932\nd3 730f\ned 7451\n54 39b2\n35 a539\nd4 79b2\ncc d8da\ndf 7355\nc5 7213\n46 38b6\ndc d9da\nd5 7313\nef 7455\n56 39b6\n5e b174\n44 b032\n54 b132\n7e b574\n64 b432\n74 b532\ne4 5418\n76 b536\n45 3039\n5e b374\n4d 18f9\n44 b232\n7e b774\n6d 1cf9\n64 b632\n4f 18fd\n46 b236\n5e b974\n44 b832\n4c b872\n5c b972\n46 b836\n37 2f97\n44 3038\n55 3139\n5d 33fb\n77 353d\n76 353e\na6 6e96\n62 9e84\ncd 7079\nc4 70b8\nc5 70b9\ne6 74bc\n47 3097\n54 3138\n5c 33fa\n76 353c\nd4 71b8\nf6 75bc\n46 383c\n4f 387d\n45 383b\n5f 397d\n5e 397e\nce 787c\n6c b6f8\ncf 787d\n6d b6f9\nc5 783b\ndf 797d\nde 797e\nd7 79bd\n7e b5d6\n4d 30d9\n56 393c\n44 383a\n5e 397c\n47 3a3d\n4f 3a7d\nc4 783a\nde 797c\nd6 79bc\n4c 30d8\ncf 7a7d\n31 d0b\nc7 7abd\n77 1d9d\n6e b6d6\nb7 6f97\n73 9f85\nc4 7038\nc5 7039\nd4 713a\ndd 73fb\nf7 753d\n36 af96\n5d b179\n7e b57e\n77 b5bd\n76 b5be\nc7 7097\nd4 7138\ndc 73fa\nf6 753c\n51 3381\n4c b27a\n76 b5bc\n4e b87c\n4f b87d\ncc 72d8\nc5 f219\n46 b8bc\n47 b8bd\n45 b83b\n5f b97d\n5e b97e\n57 b9bd\n44 b83a\n5e b97c\nd5 f319\n56 b9bc\n4f ba7d\n47 babd\n44 30b8\n64 34b8\n44 38b8\n54 39b8\n44 3a38\n44 3ab8\n47 b097\n54 b138\n45 b039\n55 b139\n64 b638\n6c b678\n45 b239\n3f 57d\n25 43b\n65 b639\n44 b838\n47 b897\n54 b938\n45 b839\n55 b939\n45 ba39\n55 313b\n77 353f\n7f 357f\nde 71fc\nc4 70ba\ne6 74be\nff 757f\n67 363f\n70 3d22\nef 767f\n4e 387e\n57 393f\n5f 397f\nce 787e\n6c b6fa\ndf 797f\nd7 79bf\n47 3a3f\n4f 3a7f\ncf 7a7f\ne1 7c01\nc7 7abf\nde 717c\n73 9f87\nc4 703a\ne6 743e\n55 b1bb\n7f b57f\n77 b5bf\n68 3ce2\ne7 763f\n6f b67f\nd7 793f\n4e b87e\n5f b97f\nd6 f31c\n57 b9bf\nc7 7a3f\n4f ba7f\n61 bc01\n47 babf\n54 b13a\n7e b57c\n64 b43a\n74 b53a\n5e b37c\n44 b23a\n7e b77c\n64 b63a\n58 31c0\n78 35c0\nd8 71c0\ne8 74c0\nf8 75c0\n59 31c1\nd9 71c1\n30 2f82\n4a 30c4\n40 3082\n5a 31c4\n60 3482\n7a 35c4\nb0 6f82\nca 70c4\nc0 7082\nda 71c4\ne0 7482\nfa 75c4\nb1 6f83\ncb 70c5\nc1 7083\ndb 71c5\n42 308e\n5c 31d0\n52 338e\n6c 34d0\n62 348e\n7c 35d0\nb2 6f8e\ncc 70d0\nc2 708e\ndc 71d0\nd2 738e\nec 74d0\ne2 748e\nfc 75d0\n53 338f\n6d 34d1\n63 348f\n7d 35d1\nb3 6f8f\ncd 70d1\nd3 738f\ned 74d1\ne3 748f\nfd 75d1\n34 2f92\n4e 30d4\n44 3092\n5e 31d4\n54 3392\n6e 34d4\n64 3492\n7e 35d4\n70 9f80\nb4 6f92\nce 70d4\nc4 7092\nde 71d4\nd4 7392\nee 74d4\ne4 7492\nfe 75d4\n65 3493\n7f 35d5\n4c b0d8\nb5 6f93\n71 9f81\ncf 70d5\ne5 7493\nff 75d5\nc7 723d\n48 38e0\n48 32c0\nbb 474f\n3c df2\nd2 7ba4\n68 36c0\nd0 d988\n33 50f\nc9 72c1\n51 998b\n4a 32c4\n71 9d8b\n6a 36c4\nd1 d98b\n34 512\nca 72c4\nd2 d98c\n35 513\ncb 72c5\n4c 32d0\n6c 36d0\nd4 d998\n37 51f\ncd 72d1\nf4 dd98\ned 76d1\n1d 897b\n55 999b\n4e 32d4\n75 9d9b\n6e 36d4\nd5 d99b\nce 72d4\nd6 d99c\ncf 72d5\nc7 721d\n48 38c0\nd7 731d\n58 39c0\nc8 78c0\nd8 79c0\nc9 78c1\nd9 79c1\n4a 38c4\n40 3882\n5a 39c4\n11 a309\nca 78c4\nc0 7882\n21 a409\nda 79c4\n4b 38c5\n41 3883\n5b 39c5\ncb 78c5\nc1 7883\n8 a2c8\ndb 79c5\n4c 38d0\n42 388e\n5c 39d0\ncd 78d1\nc3 788f\ndd 79d1\n4e 38d4\n44 3892\n5e 39d4\nc4 7892\n25 a419\nde 79d4\n4f 38d5\n45 3893\n5f 39d5\ncf 78d5\nc5 7893\nc a2d8\ndf 79d5\n48 3ac0\n32 d0e\nc8 7ac0\n49 3ac1\n33 d0f\nc9 7ac1\n4a 3ac4\n34 d12\nca 7ac4\n35 d13\ncb 7ac5\n4c 3ad0\n36 d1e\ncc 7ad0\n37 d1f\ncd 7ad1\n4e 3ad4\nce 7ad4\ncf 7ad5\nc8 70e0\nb0 6fa2\nca 70e4\nb1 6fa3\ncb 70e5\ndc 71d2\nde 71d6\nd8 71e2\ned 74d9\nd0 73a2\nea 74e4\ne8 74e8\nfe 75d6\nf8 75ea\nd8 71e0\nc0 70a2\nda 71e4\ne6 7414\ncc 72d2\nca 72e6\ne0 74a2\nfa 75e4\nf8 75e8\n22 6a4\nec 76da\n78 b5c0\n40 b082\n5a b1c4\n50 b382\n6a b4c4\n60 b482\n7a b5c4\n52 b38e\n6c b4d0\n62 b48e\n7c b5d0\n53 b38f\n6d b4d1\n63 b48f\n7d b5d1\n54 b392\n6e b4d4\n64 b492\n7e b5d4\n65 b493\n7f b5d5\ne8 74e0\n6a b6c4\n96 e99e\n4b b2c5\nf8 75e2\ncd 78d9\n4d b2d1\n2 a28c\nc8 78e8\n6e b6d4\n3f a55d\n25 a41b\nde 79d6\n91 4301\n12 9a4\ndc 79da\n4f b2d5\n13 a38d\nd9 79e9\n40 b882\n5a b9c4\n44 b892\n5e b9d4\n49 bac1\nf8 75e0\n4b bac5\n4d bad1\n12 a38c\nd8 79e8\n4f bad5\n50 b3a2\n6a b4e4\ne8 54c8\n7a b5e6\n49 30e9\n78 b5ea\n66 b414\n55 1999\n4c b2d2\n57 199d\n4e b2d6\n94 e9ba\neb 5447\n49 b2e1\n51 19a9\n62 b424\n48 b2e2\n96 e9be\n4b b2e5\n53 19ad\n4a b2e6\n60 b4a2\n7a b5e4\n68 b4e0\n78 b5e2\n4c b8d8\n4d b8d9\n48 b8e8\n49 b8e9\n59 b9e9\n78 b5e0\nd9 f341\n40 b8a2\n5a b9e4\n58 b9e8\n4d bad9\n4b bae5\neb 5c4f\n49 bae9\n48 30e0\n68 34e0\n78 35e0\n49 30e1\n59 31e1\nd0 d108\n30 2fa2\n4a 30e4\n50 33a2\nf0 d508\n6a 34e4\n60 34a2\n7a 35e4\n48 32e0\n68 36e0\n94 69ba\n50 99a8\n49 32e1\nb4 6dba\n70 9da8\n69 36e1\nd0 d308\n51 99ab\n4a 32e4\nf0 d708\n71 9dab\n6a 36e4\nd1 d9ab\n34 532\nca 72e4\nf1 ddab\nea 76e4\nd1 d309\n96 69be\n52 99ac\n4b 32e5\n2 a284\nc8 78e0\n12 a384\nd8 79e0\n18 34a\n49 38e1\n3 a285\n98 434a\nc9 78e1\na8 444a\n13 a385\nd9 79e1\nd0 d908\nc9 7241\n4a 38e4\n1a 34e\nd1 d909\n4b 38e5\n41 38a3\nc0 7200\n2a 44e\n5b 39e5\n6a b6e4\neb 5c47\n49 bae1\n78 35c2\nc8 70c2\ne8 74c2\n7a 35c6\nca 70c6\nea 74c6\nfa 75c6\n5c 31d2\n6c 34d2\n7c 35d2\ncc 70d2\nec 74d2\n4e 30d6\n5e 31d6\n6e 34d6\n7e 35d6\n70 9f82\nce 70d6\nee 74d6\ne2 7404\nc8 72c2\nca 72c6\n66 3414\n4c 32d2\n6e 36d6\nc7 721f\n48 38c2\nd7 731f\n58 39c2\n29 a449\nc8 78c2\n39 a549\nd8 79c2\n4a 38c6\n5a 39c6\n11 a30b\n2b a44d\nca 78c6\n3b a54d\n21 a40b\nda 79c6\n4c 38d2\n5c 39d2\n3d a559\ndc 79d2\n4e 38d6\n5e 39d6\ndd 71d3\ndf 71d7\nd9 71e3\ne8 74ea\nff 75d7\nc7 723f\n48 38e2\nf9 75eb\n5f 33d5\n45 3293\n5b 33e5\ne1 d409\n41 32a3\n74 3738\n67 3697\n7f 37dd\nf8 d760\n65 369b\nd4 d99a\ne7 7415\ncd 72d3\ne3 7425\nd0 d9aa\nc9 72e3\nd2 d9ae\ncb 72e7\nf2 ddae\neb 76e7\ne9 76eb\n55 99b9\n99 69cb\n62 b404\n51 1989\n48 b2c2\n53 198d\n4a b2c6\n2 8a4\n81 4201\ncc 78da\n13 a38f\n2d a4d1\nd9 79eb\n7f 37d5\n65 3693\n7b 37e5\n61 36a3\n65 96b1\nb0 cd8a\na9 66c3\n47 9ab5\n98 6b68\n8b 6ac7\na3 6c0d\n45 9ab9\n89 6acb\nf4 dd9a\ned 76d3\nf0 ddaa\ne9 76e3\ndc 7b78\ncf 7ad7\ncb 7ae7\ne3 7c2d\nc9 7aeb\n3b a5c7\nff 5575\ne5 5433\n39 a5cb\n68 b4ea\n7f b5d7\ne9 54c9\n7b b5e7\n79 b5eb\n23 a405\n9 a2c3\nd1 7923\n18 a368\nb a2c7\nf1 7d23\n38 a768\n2b a6c7\n67 b415\n4d b2d3\n5c b378\n4f b2d7\n63 b425\n49 b2e3\n4b b2e7\n7c b778\n6f b6d7\n6d b6db\n6b b6e7\n69 b6eb\n56 1316\n19 a9cb\n68 b4e2\n7d b5d3\n79 b5e3\n1 8201\n4c b8da\nd8 f348\n59 b9eb\n18 ab68\nb aac7\n23 ac0d\n9 aacb\n6d b6d3\n69 b6e3\n5c bb78\n4f bad7\n67 bc1d\n4d badb\n4b bae7\n63 bc2d\n49 baeb\n78 35e2\n7a 35e6\ne8 d448\n62 3424\n48 32e2\n39 a569\n12 a386\nd8 79e2\nc9 7243\nd0 d90a\n4a 38e6\n68 34c8\nc9 70c9\nd9 71c9\n6c 34d8\n6d 34d9\ncd 70d9\n4c 32d8\n6c 36d8\nec 76d8\n4d 32d9\n6d 36d9\n48 38c8\n58 39c8\nc9 78c9\nd9 79c9\n4c 38d8\n4d 38d9\n48 3ac8\nc8 7ac8\n49 3ac9\nc9 7ac9\n4c 3ad8\ncc 7ad8\n4d 3ad9\n62 34ac\n63 34ad\nf0 d520\n77 359d\n72 35ae\n90 638a\naa 64cc\nc8 70e8\nc9 70e9\n12 1a4\ndc 71da\nd9 71e9\na a8ce\nd8 71ea\nd4 739a\nee 74dc\nd5 739b\nef 74dd\nd0 73aa\nea 74ec\ne5 749b\nff 75dd\n48 38e8\n72 35ac\ne0 d620\n67 369d\nf9 d763\n66 369e\n63 36ad\n7c 37f0\n62 36ae\na0 648a\nba 65cc\nd8 71e8\n2 2a4\ne6 741c\ncc 72da\n26 496\nc9 72e9\nc8 f062\nb 80cd\ne2 742c\nc8 72ea\ne0 74aa\nfa 75ec\nc1 7209\n42 38ac\nb 2c5\n56 399e\n4d b0d9\n8a 68cc\nce 78dc\ncf 78dd\nc5 789b\ndf 79dd\n93 4305\nde 79de\n4d b2d9\n48 b8c8\n58 b9c8\n49 b8c9\n59 b9c9\nd1 7309\n52 39ac\n49 bac9\nce 7ade\nc5 509b\ndf 51dd\n59 b1e9\n58 b1ea\n54 b39a\n6e b4dc\n55 b39b\n6f b4dd\n50 b3aa\n6a b4ec\n65 b49b\n7f b5dd\n61 b4ab\n7b b5ed\n7a b5ee\n66 b41c\n4c b2da\neb 544f\n49 b2e9\n62 b42c\n48 b2ea\n60 b4aa\n7a b5ec\ne9 5661\n6e b6de\n47 1217\na a8cc\n4e b8dc\n4f b8dd\nc9 f249\n4a b8ec\n4b b8ed\n45 b89b\n5f b9dd\nd9 5961\n13 8305\n5e b9de\n41 b8ab\nc0 f208\n5b b9ed\nb aacd\n24 ac10\na aace\nd8 f162\n1 808b\n1b 81cd\nd9 f349\n40 b8aa\n5a b9ec\n4f badd\nc9 5a61\n4e bade\n45 909b\n5f 91dd\ned 5c53\n4b baed\n64 bc30\n4a baee\n9f 61ff\n41 90ab\n5b 91ed\n48 30e8\n68 34e8\n3f 5d7\nc8 72e8\na 80cc\n58 39e8\n49 38e9\n59 39e9\n48 3ae8\n49 3ae9\ncb f2e5\n35 8533\n37 8537\n3f 8775\n25 8633\n1d 8973\n1f 8b75\n5 8a33\n75 9533\n77 9537\n67 9637\n5f 9b75\n45 9a33\nc8 70ca\ne8 74ca\nf8 75ca\na 22ec\n2 a4\ncc 70da\n66 341c\n4c 32da\n6c 36da\nc8 78ca\n8d 42f1\nd8 79ca\n66 94bc\naa 64ce\nd9 71eb\n5f 33dd\nd8 d360\n45 329b\n7d 37f1\n63 36af\ne3 742d\nc9 72eb\neb 76ef\n48 b0ca\n68 b4ca\nd9 d963\n46 389e\n4c b0da\nd2 730c\n3c 55a\n53 39af\n57 99bd\n9b 69cf\n62 b40c\n48 b2ca\n68 b6ca\n83 4205\nce 78de\n48 b8ca\n47 9abd\na5 6c11\n8b 6acf\ne5 7c31\ncb 7aef\nf3 df8f\n19 a1cb\n48 b0ea\n59 b1eb\n6a b4ee\n7b b5ef\n23 a40d\nc2 7886\n9 a2cb\n67 b41d\n4d b2db\n63 b42d\n49 b2eb\n6f b6df\n6b b6ef\n1b a9cf\nc9 5861\n3 8205\n4e b8de\nc0 5820\n5f b9df\n25 ac11\nb aacf\n4f badf\n65 bc31\n4b baef\ncb f2ed\n35 853b\n3f 877d\nde 5bf6\n25 863b\n1f 8b7d\n5 8a3b\n75 953b\n4e 3274\n55 993b\n5f 9b7d\n45 9a3b\nb2 6fae\ncc 70f0\nb3 6faf\ncd 70f1\n70 9fa0\nb4 6fb2\nce 70f4\nb5 6fb3\n71 9fa1\ncf 70f5\nd4 73b2\nee 74f4\nec 74f8\nfe 75f6\nc2 70ae\ndc 71f0\nc4 70b2\nde 71f4\nd6 d9bc\n39 543\ncf 72f5\nce 72f6\ne4 74b2\nfe 75f4\nfc 75f8\nd2 73ae\nec 74f0\n6 a29c\ncc 78f8\n17 a39d\ndd 79f9\ne2 74ae\nfc 75f0\n16 a39c\ndc 79f8\n54 b3b2\n6e b4f4\nec 54d8\n7e b5f6\n4d 30f9\n98 e9ca\nef 5457\n4d b2f1\n55 19b9\n66 b434\n4c b2f2\n9a e9ce\n4f b2f5\n57 19bd\n4e b2f6\n64 b4b2\n7e b5f4\n52 b3ae\n6c b4f0\n7c b5f2\n4c b8f8\n4d b8f9\n5d b9f9\n62 b4ae\n7c b5f0\nc3 f20f\ndd f351\n44 b8b2\n5e b9f4\n5c b9f8\n4f baf5\nef 5c5f\n4d baf9\n32 2fae\n4c 30f0\n52 33ae\n6c 34f0\n62 34ae\n7c 35f0\n33 2faf\n4d 30f1\nd4 d118\n34 2fb2\n4e 30f4\n54 33b2\nf4 d518\n6e 34f4\n64 34b2\n7e 35f4\n4c 32f0\n6c 36f0\n54 99b8\n98 69ca\n4d 32f1\nb8 6dca\n74 9db8\n6d 36f1\nd4 d318\n55 99bb\n4e 32f4\nf4 d718\n75 9dbb\n6e 36f4\nd5 d9bb\n38 542\nce 72f4\nf5 ddbb\nee 76f4\nd5 d319\n56 99bc\n9a 69ce\n4f 32f5\ncb 724d\n4c 38f0\n6 a294\ncc 78f0\nc2 78ae\n16 a394\ndc 79f0\n43 38af\nc2 720c\n2c 45a\n5d 39f1\n7 a295\n9c 435a\ncd 78f1\nc3 78af\nac 445a\n17 a395\ndd 79f1\nd4 d918\ncd 7251\n4e 38f4\n1e 35e\nd5 d919\n4f 38f5\n45 38b3\n2e 45e\nc4 7210\n5f 39f5\n4c 3af0\n4d 3af1\nd4 db18\n4e 3af4\nd5 db19\n4f 3af5\n6e b6f4\nef 5c57\n4d baf1\n70 9fa2\nce 70f6\nee 74f6\nec 74fa\nff 75f7\nfd 75fb\nd4 d9ba\ne7 7435\ncd 72f3\nd6 d9be\ncf 72f7\nf6 ddbe\nef 76f7\ned 76fb\n75 35b3\nd4 7318\n55 39bb\n17 a39f\ndd 79fb\n7f 37f5\n65 36b3\nf4 ddba\ned 76f3\ne7 7c3d\ncd 7afb\n6c b4fa\ned 54d9\n7f b5f7\n7d b5fb\n67 b435\n4d b2f3\n4f b2f7\n6f b6f7\n6d b6fb\n6c b4f2\n7d b5f3\ndc f358\n5d b9fb\n6d b6f3\n4f baf7\n67 bc3d\n4d bafb\n7c 35f2\n7e 35f6\nec d458\n66 3434\n4c 32f2\ncb 724f\n4c 38f2\n3d a579\n16 a396\ndc 79f2\nd4 d91a\ncd 7253\n4e 38f6\n76 35be\ncc 70f8\ncd 70f9\nd4 73ba\nee 74fc\n76 35bc\ndc 71f8\ncd 72f9\ncc f072\nf 80dd\ne6 743c\ncc 72fa\ne4 74ba\nfe 75fc\n70 3da0\nef 76fd\nc5 7219\n46 38bc\nd5 7319\n56 39bc\n47 3abd\n54 b3ba\n6e b4fc\n7e b5fe\nef 545f\n4d b2f9\n66 b43c\n4c b2fa\n64 b4ba\n7e b5fc\ncd f259\n4e b8fc\n4f b8fd\n45 b8bb\nc4 f218\n5f b9fd\ndd f359\n44 b8ba\n5e b9fc\n4f bafd\n68 bc40\n4e bafe\n4c 30f8\n6c 34f8\ncc 72f8\ne 80dc\n4c 38f8\n5c 39f8\n5d 39f9\n4c 3af8\n4d 3af9\n3f 87fd\n25 86bb\n96 c314\n17 89b7\n94 c318\ne 22f4\n15 89bb\n77 35bf\ncc 70fa\ne7 743d\ncd 72fb\n70 3da2\nef 76ff\n61 3c01\n47 3abf\n4c b0fa\n6e b4fe\n7f b5ff\n67 b43d\n4d b2fb\n6f b6ff\nde f35c\nc4 f21a\n5f b9ff\n69 bc41\n4f baff\nd5 f331\n3f 857f\n96 c31c\n17 89bf\nf 8a7f\n50 3300\n70 3700\n78 3740\n51 39a3\n3a 54e\nd0 7300\n59 39e3\nd8 7340\n71 3da3\nf0 7700\nd8 d9c8\n52 39a4\n3b 54f\nd1 7301\n40 38a2\n5a 39e4\nd9 7341\nf8 ddc8\n72 3da4\nf1 7701\n59 99cb\n52 3304\n40 3202\n5a 3344\n79 9dcb\n72 3704\n60 3602\n7a 3744\nd9 d9cb\n3c 552\n53 39a7\nd2 7304\n5b 39e7\nc0 7202\nda 7344\nf9 ddcb\n73 3da7\nf2 7704\n54 3310\n42 320e\n5c 3350\n74 3710\n62 360e\n93 6ba5\n7c 3750\n55 39b3\n3e 55e\nd4 7310\n5d 39f3\nc2 720e\ndc 7350\n75 3db3\nf4 7710\ndc d9d8\n3f 55f\n56 39b4\nd5 7311\nc3 720f\n5e 39f4\n44 38b2\ndd 7351\nfc ddd8\n76 3db4\nf5 7711\n5d 99db\n56 3314\n44 3212\n5e 3354\n7d 9ddb\n76 3714\ndd d9db\n57 39b7\nd6 7314\n5f 39f7\nc4 7212\nde 7354\nfd dddb\n77 3db7\nf6 7714\n70 3780\n71 3781\n3b 5cf\nd1 7381\nf1 7781\n55 3391\n75 3791\n3f 5df\nd5 7391\nf5 7791\n50 3b00\n3a d4e\nd0 7b00\n51 3b01\n3b d4f\nd1 7b01\n52 3b04\n3c d52\nd2 7b04\n54 3b10\n3e d5e\nd4 7b10\n55 3b11\n43 3a0f\n5d 3b51\n7f 15f5\n65 14b3\n3f d5f\nd5 7b11\nc3 7a0f\ndd 7b51\nff 55f5\ne5 54b3\n56 3b14\nd6 7b14\n50 3b80\n55 3b91\n3f ddf\nd5 7b91\n6e 3454\n54 3312\n58 99e8\n9c 69fa\n51 3321\n6a 3464\n50 3322\n5c 3352\n58 3362\n76 3716\n75 3719\na5 6cb1\n74 371a\n7d 3759\nad 6cf1\n93 6baf\n7c 375a\n73 9d8f\n79 3769\n78 376a\n7e 3dfc\n64 3cba\nfd 7759\n32 724\nfc 775a\nf5 7799\nf4 779a\n78 b740\n61 1c89\n72 b704\n60 b602\n69 1cc9\n7a b744\n62 b60e\n93 eba5\n7c b750\n63 b60f\n7d b751\n65 1c99\n76 b714\nbc 6dfa\n78 9de8\n71 3721\n73 9d87\n79 3761\n51 bb01\n55 bb11\n43 ba0f\n5d bb51\n55 bb91\ndd 7b59\nff 55fd\ne5 54bb\n12 b24\ndc 7b5a\nd5 7b99\nee 7cdc\nd4 7b9a\nee 7454\nd4 7312\n76 3dbc\nf5 7719\n2a 6e4\nf4 771a\n5c b352\n58 b362\nad ecf1\n93 ebaf\n7c b75a\n79 b769\n74 1d90\n78 b76a\nd5 7b19\nee 7c5c\na ae4\nd4 7b1a\n79 b761\n5e bb56\nc8 5a48\n5a bb66\nc0 5a88\neb f645\n52 bba6\nd2 d9ac\ncb 72e5\n35 533\n5f 1175\n45 1033\ne3 7e25\n4d 1073\n7f 1575\n65 1433\n6d 1473\n9f 4175\n85 4033\n87 4037\n8f 4077\nbf 4575\na5 4433\na7 4437\naf 4477\ncd 5073\ncf 5077\n90 e9aa\ne7 5437\ned 5473\n98 e9ea\nef 5477\ncb 72ed\n35 53b\n5f 117d\n45 103b\ne3 7e2d\n4d 107b\n7f 157d\n65 143b\n9f 417d\n85 403b\n5f 11f5\n45 10b3\n9f 41f5\n85 40b3\n87 40b7\n50 3320\n70 3720\ndf 51f5\nc5 50b3\nc7 50b7\nd8 d348\n59 99eb\n52 3324\nf8 d748\n79 9deb\n72 3724\nd9 d9eb\n3c 572\nd2 7324\nc0 7222\n3 28d\nda 7364\nf9 ddeb\nf2 7724\n50 33a0\n70 37a0\n51 33a1\n71 37a1\nd8 d3c8\n52 33a4\nf8 d7c8\n72 37a4\n3c 5f2\nd2 73a4\nf2 77a4\nc0 7a22\n3 a8d\nda 7b64\n0 8a\n1a 1cc\n4 9a\n1e 1dc\n40 108a\n5a 11cc\n44 109a\n5e 11dc\n70 3f8a\n8a 40cc\n74 3f9a\n8e 40dc\n80 408a\n9a 41cc\n84 409a\n9e 41dc\nb0 4f8a\nca 50cc\nb4 4f9a\nce 50dc\nc0 508a\nda 51cc\nc4 509a\nde 51dc\nd8 7162\n1 8b\n1b 1cd\n41 108b\n5b 11cd\n45 109b\n5f 11dd\n71 3f8b\n8b 40cd\n75 3f9b\n8f 40dd\nb1 4f8b\ncb 50cd\nb5 4f9b\ncf 50dd\nc1 508b\ndb 51cd\n70 b720\n78 b760\nbc edfa\n71 b721\n61 1ca9\n72 b724\n60 b622\n69 1ce9\n7a b764\na ec\n0 aa\n1a 1ec\n10 3aa\n2a 4ec\n30 faa\n4a 10ec\n40 10aa\n5a 11ec\n50 13aa\n6a 14ec\n70 3faa\n8a 40ec\n80 40aa\n9a 41ec\nb0 4faa\nca 50ec\nc0 50aa\nda 51ec\nb ed\n31 fab\n4b 10ed\n41 10ab\n5b 11ed\n71 3fab\n8b 40ed\nb1 4fab\ncb 50ed\n51 bb21\n8e 42dc\nce 52dc\n10 980\n8f 42dd\ncb 52cd\n50 1980\ncf 52dd\n4a 12ec\n4b 12ed\n1b a367\n89 4249\na 8ec\n5b b367\nc9 5249\n4a 18ec\na acc\nc8 7a62\nb acd\n1f 375\n5 233\n7 237\nc2 daac\n3f 775\n25 633\nc4 dab0\n27 637\ncc daf0\n2f 677\nf4 5f90\n0 208a\n1a 21cc\n80 608a\n9a 61cc\n31 8f89\n8f 60dd\n81 608b\n9b 61cd\n41 9089\n85 609b\n9f 61dd\ne4 5eb0\na 20ec\nf4 5fb0\n0 20aa\n1a 21ec\n10 23aa\n2a 24ec\n20 24aa\n3a 25ec\n80 60aa\n9a 61ec\ne5 5eb1\nb 20ed\n1 20ab\nf5 5fb1\n1b 21ed\n81 60ab\n9b 61ed\n1f 37d\n5 23b\n3f 77d\n25 63b\nb 22ed\na 28cc\n0 28aa\n99 6349\n1a 29ec\n8a 6acc\n3f 7f5\n25 6b3\n27 6b7\n9f 43f5\n85 42b3\n0 808a\n1a 81cc\n4 809a\n1e 81dc\n8e 60de\n30 8f8a\n4a 90cc\n34 8f9a\n4e 90dc\n9e 61de\n40 908a\n5a 91cc\n44 909a\n5e 91dc\n8f 60df\n31 8f8b\n4b 90cd\n35 8f9b\n4f 90dd\n9f 61df\n41 908b\n5b 91cd\na 80ec\n0 80aa\n1a 81ec\n8e 60fe\n30 8faa\n4a 90ec\n9e 61fe\n40 90aa\n5a 91ec\nb 80ed\n8f 60ff\n31 8fab\n4b 90ed\n8e 62de\n4a 92cc\n4e 92dc\nc8 f262\nd1 5929\nb 82cd\ncc f272\nd5 5939\nf 82dd\n8f 62df\n10 2982\n4b 92cd\n14 2992\n4f 92dd\nc3 58a7\na 82ec\n8e 62fe\na8 6440\n4a 92ec\nb 82ed\n8f 62ff\n10 29a2\na9 6441\n4b 92ed\n6a 3444\n50 3302\nea 7444\nd0 7302\n6a 34c4\n50 3382\n70 3782\nea 74c4\nd0 7382\nf0 7782\n6a 3c44\n50 3b02\nea 7c44\nd0 7b02\n6e 3c54\n54 3b12\n5c 3b52\nee 7c54\nd4 7b12\nea 7cc4\n31 a709\nd0 7b82\n1a 89ce\n13 2307\n19 2343\n1b 2347\n" + +#define _8_SPLIT_7 "43904\nf9 2bfb28\n7b 1f69d6\nf7 2bfa09\nf7 2bf9fb\nf3 2bf9ca\nf7 2bf75b\nf5 2bf754\nf7 2bf74d\nf9 2bf87a\nf3 2bf71c\nb5 2a2e63\nb1 2a2e32\nb5 2a2bb5\nb5 2a2b53\n7b 1f6728\n79 1f6721\n71 1f65ca\n31 1d9cd9\nff 2be89e\nef 2bdf3d\nfd 2be897\ned 2bdf36\nfb 2be86d\nff 2be83c\neb 2bdf0c\nef 2bdedb\nf9 2be866\nfd 2be835\nff 2be82e\ne9 2bdf05\ned 2bded4\nef 2bdecd\nbd 2a1cf8\nad 2a1397\na9 2a1366\n6b 1f6075\n69 1f606e\n6b 1f6067\n7b 1f5714\n6b 1f4db3\n69 1f4dac\n6b 1f4da5\n29 1d94cf\n29 1d820d\ne7 2bf0a8\ne5 2bf0a1\ne7 2bedfa\ne5 2bedf3\ne3 2bedc9\ne9 2bef19\neb 2bef12\ne1 2bedc2\ne3 2bedbb\ne5 2bed91\nf7 2be747\ne7 2bdde6\nf5 2be740\nf7 2be739\ne5 2bdddf\nf3 2be716\ne3 2bddb5\nf1 2be70f\nf3 2be708\ne1 2bddae\ne3 2bdda7\ne5 2bdd7d\nff 2be5f0\nf7 2be499\nef 2bdc8f\ne7 2bdb38\nfd 2be5e9\nf5 2be492\nf7 2be48b\ned 2bdc88\ne5 2bdb31\nfb 2be5bf\nff 2be58e\nf3 2be468\neb 2bdc5e\nef 2bdc2d\ne3 2bdb07\nf9 2be5b8\nfd 2be587\nff 2be580\nf1 2be461\nf3 2be45a\ne9 2bdc57\ned 2bdc26\nef 2bdc1f\ne1 2bdb00\ne3 2bdaf9\ne5 2bdacf\na5 2a2502\na5 2a2254\na9 2a237a\na5 2a21f2\nb5 2a1ba1\na5 2a1240\nb1 2a1b70\na1 2a120f\nbd 2a1a4a\nb5 2a18f3\nad 2a10e9\na5 2a0f92\nbd 2a19e8\nb1 2a18c2\nb5 2a1891\na9 2a10b8\nad 2a1087\na1 2a0f61\na5 2a0f30\n61 1f5f17\n6b 1f5dc7\n69 1f5dc0\n6b 1f5db9\n61 1f5c69\n61 1f4c55\n7b 1f5466\n6b 1f4b05\n79 1f545f\n71 1f5308\n69 1f4afe\n6b 1f4af7\n61 1f49a7\n21 1d9378\n29 1d9221\n31 1d8769\n29 1d7f5f\n21 1d7e08\ndb 2bb988\nd9 2bb981\ndb 2bb97a\nfb 2b77d3\nda 2b3639\nda 2b362b\n99 29ede2\nb9 29ac3b\n98 296a93\n5b 1f282f\n7b 1ee688\n5b 1f2821\n7b 1ee67a\n5a 1ea4e0\n5a 1ea4d2\n19 1d5c89\n39 1d1ae2\n18 1cd93a\nd7 2bb862\nf7 2b76bb\nd5 2bb85b\nd7 2bb854\nf5 2b76b4\nf7 2b76ad\nf3 2b768a\nd3 2bb823\nf3 2b767c\nf5 2b7652\nd7 2bb5b4\nf7 2b740d\nd5 2bb5ad\nf5 2b7406\nf7 2b73ff\nf3 2b73dc\ndb 2bb6cc\nd3 2bb575\nd5 2bb54b\nf5 2b73a4\nd6 2b3513\nd4 2b350c\nd6 2b3505\nd2 2b34e2\nd2 2b34d4\nd6 2b3265\nd4 2b325e\nd6 2b3257\nda 2b338b\nd2 2b3234\nda 2b337d\nd2 2b3226\n95 29ecbc\nb5 29ab15\n91 29ec8b\n95 29ec5a\nb1 29aae4\n95 29ea0e\nb5 29a867\n99 29eb34\n95 29e9ac\nb5 29a805\n94 29696d\n90 29693c\n94 29690b\n94 2966bf\n98 2967e5\n94 29665d\n5b 1f2581\n7b 1ee3da\n59 1f257a\n79 1ee3d3\n71 1ee27c\n5a 1ea232\n5a 1ea224\n11 1d5b32\n31 1d198b\neb 2b6e72\ndf 2ba6f7\ncf 2b9d96\ndf 2ba6e9\ndb 2ba6c6\ndf 2ba695\ncb 2b9d65\ncf 2b9d34\ndb 2ba6b8\ndf 2ba687\ncb 2b9d57\ncf 2b9d26\nde 2b23a8\nce 2b1a47\nde 2b239a\nce 2b1a39\nda 2b2377\nde 2b2346\nca 2b1a16\nce 2b19e5\nda 2b2369\nde 2b2338\nca 2b1a08\nce 2b19d7\na9 29a2da\n9d 29db51\n8d 29d1f0\nbd 2999aa\nad 299049\n99 29db20\n9d 29daef\n89 29d1bf\n8d 29d18e\nb9 299979\na9 299018\n9c 295802\n8c 294ea1\n98 2957d1\n9c 2957a0\n88 294e70\n8c 294e3f\n4b 1f1ece\n6b 1edd27\n49 1f1ec7\n4b 1f1ec0\n69 1edd20\n6b 1edd19\n5b 1f156d\n4b 1f0c0c\n5b 1f155f\n4b 1f0bfe\n5a 1e921e\n4a 1e88bd\n5a 1e9210\n4a 1e88af\n9 1d5328\n29 1d1181\n9 1d4066\n39 1d0820\n29 1cfebf\n10 1cd7e3\n18 1cc678\n8 1cbd17\nc7 2baf01\ne7 2b6d5a\nc5 2baefa\ne5 2b6d53\ne7 2b6d4c\nc7 2bac53\ne7 2b6aac\nc5 2bac4c\ne5 2b6aa5\nd7 2ba5a0\nc7 2b9c3f\nf7 2b63f9\ne7 2b5a98\nd7 2ba592\nf5 2b63f2\nf7 2b63eb\ne5 2b5a91\ne7 2b5a8a\ndf 2ba449\nd7 2ba2f2\ncf 2b9ae8\nc7 2b9991\nf7 2b614b\ne7 2b57ea\nf5 2b6144\nf7 2b613d\ne5 2b57e3\nc6 2b2904\nc4 2b28fd\nc6 2b28f6\nd6 2b2251\nc6 2b18f0\nd6 2b2243\nc6 2b18e2\nde 2b20fa\nd6 2b1fa3\nce 2b1799\nc6 2b1642\nde 2b20ec\nd6 2b1f95\nce 2b178b\nc6 2b1634\n85 29e35b\na5 29a1b4\n85 29e0ad\na5 299f06\n95 29d9fa\n85 29d099\nb5 299853\na5 298ef2\n9d 29d8a3\n95 29d74c\n8d 29cf42\n85 29cdeb\nbd 2996fc\nb5 2995a5\nad 298d9b\na5 298c44\n84 295d5e\n94 2956ab\n84 294d4a\n9c 295554\n94 2953fd\n8c 294bf3\n84 294a9c\nc3 2bac22\n4b 1f1c20\nd3 2ba56f\nc3 2b9c0e\ndf 2ba3e7\nd3 2ba2c1\ncf 2b9a86\nc3 2b9960\n5b 1f12bf\n4b 1f095e\ne3 2b6d29\ne3 2b6a7b\nc2 2b28d3\n6b 1eda79\n4a 1e98d1\nf3 2b63c8\ne3 2b5a67\nd2 2b2220\nc2 2b18bf\nf3 2b611a\ne3 2b57b9\nda 2b20c9\nde 2b2098\nd2 2b1f72\nce 2b1737\nc2 2b1611\n5a 1e8f70\n4a 1e860f\nbb 2a2f9e\nb9 2a2f97\n3b 1d9e45\n7b 1bd2b4\n39 1d9e3e\n79 1bd2ad\n39 1a071c\n3b 1a0715\nb7 2a2e78\nb7 2a2e6a\nb3 2a2e47\nb7 2a2e16\nb3 2a2e39\nb7 2a2e08\nb7 2a2bca\nb5 2a2bc3\nb7 2a2bbc\nbb 2a2cf0\nb3 2a2b99\nb7 2a2b68\nb9 2a2ce9\nb3 2a2b8b\nb5 2a2b61\nb7 2a2b5a\nf5 2862d2\nf1 2862a1\nf5 286270\nb7 2696e6\nf5 286024\nf5 285fd0\nb5 26943f\nf1 285ff3\nf5 285fc2\n33 1d9cee\n33 1d9ce0\n3b 1d9b97\n33 1d9a40\n7b 1bd006\n39 1d9b90\n33 1d9a32\n71 1bd148\n33 1a05be\n31 1a05b7\n79 1bcfff\n71 1bcea8\n39 1a046e\n71 1bce9a\nbf 2a1d0d\nbd 2a1d06\naf 2a139e\nbb 2a1cdc\nbf 2a1cab\nff 28511a\nef 2847b9\nb9 2a1cd5\nab 2a136d\naf 2a133c\nfd 285167\nfd 285113\ned 2847b2\nff 28510c\nfd 285105\ne9 2847d5\nef 2847ab\ned 2847a4\nbf 26857b\naf 267c1a\n3b 1d8b83\n7b 1bbff2\n39 1d8b7c\n2b 1d8214\n69 1bc94c\n29 19fdbb\n6b 1bc945\n69 1bc93e\n29 19fdad\n79 1bbfeb\n69 1bb68a\n39 19f45a\n29 19eaf9\n6b 1bb683\n69 1bb67c\n3b 19f453\n29 19eaeb\na7 2a2517\na5 2a2510\na7 2a2509\na7 2a24b5\na7 2a24a7\na7 2a2269\na5 2a2262\na7 2a225b\na3 2a2238\na7 2a2207\na3 2a222a\na5 2a2200\na7 2a21f9\nb7 2a1bb6\na7 2a1255\nb5 2a1baf\na5 2a124e\na7 2a1247\nb3 2a1b85\nb7 2a1b54\na7 2a11f3\nb1 2a1b7e\na3 2a1216\na7 2a11e5\nb7 2a1908\na7 2a0fa7\nbf 2a1a51\nb7 2a18fa\naf 2a10f0\na5 2a0fa0\na7 2a0f99\nb3 2a18d7\nb7 2a18a6\na7 2a0f45\nef 28450b\nbf 2a19ef\nb3 2a18c9\nb7 2a1898\nab 2a10bf\naf 2a108e\na3 2a0f68\na5 2a0f3e\na7 2a0f37\ne5 28591d\ne5 28590f\ne5 28566f\na5 268ade\ne9 2857e9\ne1 285692\ne5 285661\nf5 285010\nf5 284fbc\ne5 28465b\nf1 284fdf\nf5 284fae\ne1 28467e\ne5 28464d\nb7 268424\nfd 284eb9\nf5 284d62\nfd 284e65\nf5 284d0e\ned 284504\ne5 2843ad\nbd 2682d4\nad 267973\na5 26781c\nff 284e5e\nfd 284e57\nf1 284d31\nf5 284d00\ne9 284527\nef 2844fd\ned 2844f6\ne1 2843d0\ne5 28439f\nbd 2682c6\nb7 268176\nb5 26816f\naf 26796c\nad 267965\na5 26780e\n21 1d9386\n33 1d8a2c\n33 1d877e\n7b 1bbd44\n33 1d8770\n2b 1d7f66\n61 1bc7f5\n21 19fc64\n61 1bc7e7\n21 19fc56\n69 1bc69e\n61 1bc547\n29 19fb0d\n6b 1bc697\n69 1bc690\n61 1bc539\n29 19faff\n71 1bbe94\n61 1bb533\n71 1bbe86\n61 1bb525\n33 19f2fc\n79 1bbd3d\n71 1bbbe6\n69 1bb3dc\n61 1bb285\n39 19f1ac\n29 19e84b\n21 19e6f4\n71 1bbbd8\n6b 1bb3d5\n69 1bb3ce\n61 1bb277\n31 19f047\n29 19e83d\n21 19e6e6\n9b 29edf7\nbb 29ac50\ndb 282266\n99 29edf0\n9b 29ede9\nbb 29ac42\n9a 296aa8\nda 279f17\n9a 296a9a\n99 2656ce\ndb 282258\n9b 2656c7\n99 2656c0\nd8 279f10\n98 25d37f\nda 279f09\nd8 279f02\n9a 25d378\n98 25d371\n1b 1d5c9e\n3b 1d1af7\n5b 1b910d\n19 1d5c97\n1b 1d5c90\n39 1d1af0\n3b 1d1ae9\n1a 1cd94f\n5a 1b0dbe\n1a 1cd941\n59 1b9106\n79 1b4f5f\n19 19c575\n39 1983ce\n5b 1b90ff\n59 1b90f8\n79 1b4f51\n1b 19c56e\n19 19c567\n3b 1983c7\n39 1983c0\n58 1b0db7\n18 194226\n5a 1b0db0\n58 1b0da9\n1a 19421f\n18 194218\n97 29ecd1\nb7 29ab2a\nd7 282140\n95 29ecca\n97 29ecc3\nb5 29ab23\nb7 29ab1c\n93 29eca0\n97 29ec6f\nb3 29aaf9\nb7 29aac8\n91 29ec99\n93 29ec92\n95 29ec68\n97 29ec61\nb3 29aaeb\nb7 29aaba\n97 29ea23\nb7 29a87c\nd7 281e92\n95 29ea1c\nb5 29a875\nb7 29a86e\n9b 29eb49\n93 29e9f2\n97 29e9c1\nbb 29a9a2\nb3 29a84b\nb7 29a81a\n99 29eb42\n9b 29eb3b\n93 29e9e4\n95 29e9ba\n97 29e9b3\nb5 29a813\nb7 29a80c\n96 296982\nd6 279df1\n94 29697b\n96 296974\n92 296951\n96 296920\nd2 279dc0\n92 296943\n94 296919\n96 296912\n96 2966d4\nd6 279b43\n94 2966cd\n96 2966c6\n9a 2967fa\n92 2966a3\n96 296672\nda 279c69\nd2 279b12\n9a 2967ec\n92 296695\n94 29666b\n96 296664\n95 2655a8\nd7 282132\n97 2655a1\n95 26559a\nf5 27df30\n91 265577\n95 265546\nd3 282101\nf5 27df22\n93 265570\n91 265569\n97 26553f\n95 265538\nb7 261398\n95 2652fa\nd7 281e84\nf5 27dc82\n99 265420\n95 265298\nb5 2610f1\ndb 281faa\nd9 281fa3\nd3 281e53\nd1 281e4c\n9b 265419\n99 265412\nb7 2610ea\nb5 2610e3\n94 25d259\nd6 279de3\n96 25d252\n94 25d24b\n90 25d228\n94 25d1f7\nd2 279db2\n92 25d221\n90 25d21a\n96 25d1f0\n94 25d1e9\n94 25cfab\nd6 279b35\n96 25cfa4\n94 25cf9d\nd8 279c62\nd0 279b0b\n98 25d0d1\n94 25cf49\nda 279c5b\nd2 279b04\nd0 279afd\n9a 25d0ca\n92 25cf73\n96 25cf42\n94 25cf3b\n13 1d5b47\n33 1d19a0\n11 1d5b40\n13 1d5b39\n31 1d1999\n33 1d1992\n1b 1d59f0\n13 1d5899\n3b 1d1849\n33 1d16f2\n5b 1b8e5f\n7b 1b4cb8\n19 1d59e9\n39 1d1842\n12 1cd7f8\n12 1cd7ea\n1a 1cd6a1\n12 1cd54a\n5a 1b0b10\n51 1b8faf\n71 1b4e08\n11 19c41e\n31 198277\n51 1b8fa1\n71 1b4dfa\n13 19c417\n11 19c410\n33 198270\n31 198269\n59 1b8e58\n79 1b4cb1\n71 1b4b5a\n19 19c2c7\n4a 1d00f\n38 1d8bdd\n3c 1d8bac\n1f 1d4a5f\n2c 1d824b\nf 1d40fe\nb8 10fa84\n9b 10b937\n68 1bc9ad\n61 1f5ccb\n9f 2957b4\ne8 f3854\ncb ef707\n78 1bc04c\na4 26780d\n7c 1bc01b\n5f 1b7ece\n71 1f536a\n68 1bb6eb\n6c 1bb6ba\n4f 1b756d\n61 1f4a09\n3e 19f483\n33 1d87d2\n2e 19eb22\nf8 f2ef3\ndb eeda6\ne8 f2592\ncb ee445\n49 57989\nbc 10fab5\n49 1b01a7\n9f 10b968\n38 4698d\n1b 42840\n28 4602c\nb 41edf\n1b 1cd6a0\nec f3885\ne1 12cbd4\nae d6ced\nd5 281e7d\na3 11003c\n6c 2a78e\n61 63add\n2e dbf6\n9f 1035c6\n94 25bf89\nfc f2f24\n5d 5831b\nfd 2bf8ab\nf1 12c273\n49 579eb\ndb 279c68\nbe d638c\n1f 3b783\nbf 2a2d13\nb3 10f6db\nae d5a2b\nf 3ae22\n78 29dfc\n5b 25caf\n4b 2534e\n5b 1b0b0f\n3e d295\n2e c934\n9a 1035ea\n39 19f19e\n4 19222d\n3f 3f92c\n1a 3a4f3\n9e 1035c7\n35 4b3c\nac 107e6e\na5 29a1c0\n72 1b4e70\n9c 1035c0\n9e 1035b9\n1c 3a4c9\n1e 3a4c2\n18 3a498\n1a 3a491\n70 1b4e69\na 39b30\n72 1b4e62\ne6 f34e9\n68 1bc99f\n2a 1d8275\n2e 1d8244\ne6 f2227\n78 1bc03e\n7c 1bc00d\n68 1bb6dd\n6c 1bb6ac\n9d 2957ad\n21 1d912c\n7e 1bc014\n2d 19e8de\n73 1f5363\n6e 1bb6b3\n3c 19f47c\n31 1d87cb\n90 29567a\n6c 2a780\n68 2a74f\nba 10fadf\nae 10f14d\nfc f2f16\ne8 f25e6\ndb 279c5a\nec f25b5\n3e 469b7\n3a 46986\n2e 46056\n78 29dee\nee f387e\n14 1d4662\ne3 12cbcd\nac d6ce6\na1 110035\n6e 2a787\n63 63ad6\n2c dbef\nfe f2f1d\n5f 58314\nff 2bf8a4\nf3 12c26c\nee f25bc\nbc d6385\n1d 3b77c\nbd 2a2d0c\nb1 10f6d4\nac d5a24\nd 3ae1b\nad 2a23ab\na1 10ed73\n7e 29e26\n6e 294c5\n3c d28e\n2c c92d\n18 1d4a34\n69 1bc9ac\n48 1b8804\na 19bc6c\nc8 ef6ab\n8a d2b13\n95 25d1e8\n79 1bc04b\na5 26780c\n7d 1bc01a\n58 1b7ea3\neb 2bef20\n84 263664\n3b 19f4b3\na 19a9aa\ne 19a979\n3 1d3cc8\n20 1d7e15\nf9 f2ef2\nc8 ee3e9\nbb d635a\nab d59f9\n8c 10c26d\n2d 4731e\n88 10afdc\n29 472ed\n66 1b320e\nee 123742\na9 10f184\nd7 e78e4\n98 10b93d\n3d 47c7f\n2d 4605c\na 19b9be\n29 4602b\ne9 f38b5\nc8 ef70d\n8a d2b75\n8e d2b44\n83 10be93\n1b 19c322\n3 42d9c\na 9a1c\n15 1940f1\nf9 f2f54\nfd f2f23\n48 26608\nd8 eedac\n7d 2b0ee\nc8 ee44b\n6d 2a78d\nbb d63bc\n29 3ef9f\n9a d2214\n8a d18b3\n95 25bf88\n8e d1882\n1b 19b060\n58 25c53\n48 252f2\n70 1b4e67\n2b c902\na 875a\n15 192e2f\ne7 f34e8\n8a 29d1c3\n69 1bc99e\n48 1b87f6\nfd 285111\n95 29690a\nc8 ef69d\n79 1bc03d\n7d 1bc00c\n58 1b7e95\na2 25f7a3\n48 1b7534\n9a 10b8d4\nc8 ee3db\n6b 1bc9a5\n94 1044df\n4a 1b87fd\nff 285118\n29 19fe0d\nca ef6a4\n88 d2b0c\n7b 1bc044\n6b 1bb6e3\n94 10321d\n4a 1b753b\nf7 27dc7b\nbd cf0ac\n39 19f4ac\n63 63d22\n29 19eb4b\n8 19a9a3\nda eed43\nca ee3e2\n88 d184a\n2a 1cfc77\n8e 10c266\n1b 1d5a44\nc8 ef6ff\n48 265a6\naf 10f14c\n9a 10b936\n9e 10b905\nf9 f2f46\nbe 107522\n2f 46055\n58 25c45\n48 252e4\n88 d2b6e\n8c d2b3d\n81 10be8c\n19 19c31b\nca ee444\n4a 265ad\n2d dbee\n88 d18ac\nfb f2f4d\neb f25ec\n69 5bb30\nda eeda5\nb9 d63b5\na9 d5a54\n98 d220d\n3d e54f\n8c d187b\n19 19b059\n5a 25c4c\n4a 252eb\n2d c92c\n8 8753\nc9 ef710\n26 1d815d\n2e 1d8006\n26 1d7eaf\ncc 277ffe\n66 1bc5e0\n34 19f395\n24 19ea34\n24 19e786\nbe 10f870\nb6 10f719\nae 10ef0f\na6 10edb8\n3a 46748\n62 2a66f\nc8 e5ddf\nee f237e\n4b 26612\nb 9a81\n48 1f0c66\ne7 2bed96\nb5 26817d\nef eb531\neb eb500\ncc 2b9d2c\n4b 265b0\n4c 1f0c35\nd 9a49\ndb f0076\nb 9a1f\n48 1f0c04\ne8 eb2aa\n49 26619\n64 222d9\nd1 eff0c\ne8 e9fe8\n1e 1d47be\n16 1d4667\ne 1d3e5d\n6c 22182\nd9 efdb5\n46 1b86e5\n46 1b8437\n25 19fa47\n25 19ea33\n2d 19e8dc\n1a 202f\n25 19e785\n12 1ed8\n1c 19b095\n46 5f90b\nc 19a734\n52 1f11b9\n4 19a5dd\na7 110327\nde 12147c\na7 110079\n8e 10c028\n94 25bf95\na 42f00\n15 1cd5d5\na7 10f065\n71 229c0\nde 1201ba\naf 10ef0e\n9e 10b6c7\n39 1983cc\n8e 10ad66\n29 197a6b\n86 10ac0f\n21 197914\nde 11ff0c\na 41c3e\n15 1cc313\ne 1d511f\n2 41ae7\ne7 eb3d8\nc6 ef5ee\na5 d6bfe\n1b 427e0\nd6 e78e5\n42 264c6\ndc e7d53\nef f363f\nc6 ef340\n6b 2a517\n63 2a3c0\nb5 d629d\nad 2679d5\nc 1d3df4\na5 d593c\ne0 eb153\ndc e6a91\neb 12bd1e\ne7 f2226\n2f 1d8243\nce ee1d5\nc6 ee07e\n94 d1e47\n84 d14e6\n28 19fe1c\nd9 eedaf\n6c 2117c\n9 87c6\n64 1bc5d9\n74 1bbf26\n36 19f38e\n7c 1bbdcf\n18 1dec\n41 1b00b2\n38 46741\n60 2a668\n4f 1f098d\nec f3639\ne4 f34e2\ne 97a1\n34 3f7db\n34 3f52d\n2c 3ed23\n2d 58a2\nfb ebecf\n88 c9560\n3a d018\n9a 2957e6\n2a c6b7\ndb eeda8\nd9 eeda1\n6c 2116e\ncb ee447\nc9 ee440\n14 193ea4\neb 2b5910\na2 2a2237\n4 193543\n99 d2210\na7 10edb7\n5b 25cb1\nd3 120fe3\n4b 25350\nc3 120682\n49 25349\nc1 12067b\n1b 9120\n42 1f1b1a\n83 103af1\ne7 2bdad4\n9 87b8\neb 2bdc50\n81 103aea\ne5 2bdacd\nad cd6e5\nff 2b77a2\nf3 12416a\na9 cd6b4\nbe 2a1cfe\n2d 45ee\ndd eeace\nc1 2bac1b\ndb eed46\nd9 eed3f\nc9 ee3de\nef 2b58df\na6 2a2206\n16 193e49\n14 193e42\n6 1934e8\n27 3d902\n25 3d8fb\n99 d21ae\nbb ce00e\nab cd6ad\nf6 12521e\na9 cd6a6\n4b 252ee\n49 252e7\n6b 1ec7b7\n22 1d90de\n50 1b0cc2\ndb eedb4\n3f 4f48\n2f 45e7\ndf eeac7\nc3 2bac14\n2d 45e0\ndd eeac0\n29 45af\nd9 eea8f\n64 1bc879\n8e 2637c4\nca 127b74\n6f 63eb6\nd5 2b2249\nbd cf05a\n4a 1b74e9\n8 147d\nbd cdd98\n66 1bc880\nd7 2b2250\n95 2956b8\nd7 2b1fa2\n34 19f387\n3f 469c4\n8e 10afa4\n1b 1d4782\n76 1bbc71\n12 1c8e\n72 1f6624\n2 132d\n3c 19f230\n34 19f0d9\n2c 19e8cf\n24 19e778\n60 2a65a\ne4 f34d4\n9e 1035c5\n60 2a3ac\neb 2b6bd0\n32 1d16f1\n4b 26610\nbe 10f862\na6 10edaa\na1 2a121b\n3a 4673a\n6a 63e86\n6c 1bc722\n60 290ea\n65 1edbfa\n62 2a661\n69 211a2\n20 dac9\ne6 f34db\nac d6a9a\n4f 1f0c3b\n47 1f0ae4\n62 2a3b3\nde e6a34\n28 d972\n99 103342\nf6 f2e28\ne6 f24c7\n64 5ba0b\nfe f2cd1\n68 2a511\na3 2a1222\nf6 f2b7a\nee f2370\n6c 5b8b4\nac d6aa8\nf2 12d52d\ne6 f2219\n64 5b75d\na4 d5681\n6d 22433\nc8 e60f1\n7a 29ba9\n25 19fcf5\n6a 29248\n75 1b391d\n28 d980\n67 1edc01\n38 d011\n1c 1d47b7\nca e60f8\n14 1d4660\nc 1d3e56\n44 1b8430\n8 1ccd99\n27 19fa40\n0 1ccc42\n37 19f38d\n10 1cc58f\n27 19ea2c\n0 1cbc2e\n19 1deb\n5c 1b7c26\n54 1b7acf\n4c 1b72c5\n72 2acb2\n44 1b716e\n18 1cc438\n10 1cc2e1\n2f 19e8d5\n8d 10c26e\n8 1cbad7\n27 19e77e\n85 10c117\n0 1cb980\n1e 19b08e\ne 19a72d\ndc 121475\n15 8cef\ndc 1211c7\n87 29e36e\ne7 2bdad6\nad 10ef07\n59 25caa\nd1 120fdc\na5 10edb0\n1c 19b097\ndc 11ff05\n9f 29db64\n97 29da0d\ned eb528\n8f 29d203\ne5 eb3d1\n87 29d0ac\nc4 ef5e7\n80 103df9\n61 2a667\n40 264bf\nde e7d4c\n62 5ba35\n88 103ca2\n80 103b4b\n18 193fda\nc7 2817dd\n35 3f7da\n25 3ee79\n96 d20ee\n86 d178d\n3 1d4f38\nde e6a8a\ned f2376\n16 19c4a9\ne5 f221f\ndc eeb2f\ncc ee1ce\n35 3f52c\n25 3ebcb\n9e d1f97\n96 d1e40\n58 25a07\ndf 280fd3\nd7 280e7c\n48 250a6\ncf 280672\n4c 1b8587\n40 24f4f\nc7 28051b\nd6 e68d1\n2a 19fe15\n9b 2957e5\nc6 e5f70\n8b 294e84\ne 19b9ef\n2 83b7\n7 1ccec7\n9d caeb1\n44 1b8422\n3b d263\n1e 1d47b0\n9 166\n16 1d4659\n5c 1b7c18\na6 25f526\n54 1b7ac1\n4c 1b72b7\n9d c9bef\n44 1b7160\n95 c9a98\n48 1e992a\n67 1bb5bd\n40 1e87bf\n20 5727\n3 15da\nd0 efc07\n6f 1bb466\n28 55d0\n73 1f6623\n67 1bb30f\n40 1e8511\n20 5479\n3 132c\n5e 1b7c1f\n56 1b7ac8\n0 83be\nc 19b9f6\n52 1f247b\n1c 19b087\n46 5f8fd\nc 19a726\n18 1cd9aa\nde 12146e\n61 2a659\n82 26394c\n27 19fc8e\n18 1cd6fc\n94 25bf87\na 42ef2\n15 1cd5c7\nb4 2681d0\nef 2b5b8d\na6 2a24b4\n61 2a3ab\nc1 2b2b79\na7 10f057\nb7 29a818\n3f 19f4e6\n96 10b810\n40 1b004f\n86 10aeaf\n1e 19b33e\n96 296670\n33 1d16f0\nde 1201ac\nc7 2bac45\n12 1cd548\n89 d18af\naf 10ef00\n27 19e9cc\na7 10eda9\n3f 19f238\n48 265b4\n9e 10b6b9\n16 19b185\n39 1983be\n86 10ac01\n21 197906\n1e 19b090\na 41c30\n15 1cc305\nae 2a1349\nc1 2b18b7\nff 2b6542\nb6 2a2e69\n58 259f9\n98 cb12f\ndf 280fc5\nfc 285112\n4c 1b8579\n40 24f41\n45 1e9a51\nc7 28050d\ne4 28465a\nf7 f2dd3\n21 dac8\ndc e7d45\nc0 1206dc\n58 1b0b6b\nc6 ef332\ncb 2b2cd7\nc3 2b2b80\nb9 d6353\n63 1bc540\n29 d971\n89 29613f\n87 264c3e\nc6 ee31e\n27 1d9170\nd6 279adf\n0 1921f0\n73 1b4b5f\n52 1b09b7\nc7 28026d\neb 12bd10\n6d 5b8b3\nc8 11f571\ne7 285676\nad d6aa7\n65 5b75c\ndd f0040\nde eeb28\nc6 ee070\na5 d5680\n3 1d51e6\n84 d14d8\n6a 1bc9a6\n6f 1bc728\n63 290f0\n59 26f18\nc3 2b18be\nee 2847b8\n47 1e9a58\n28 19fe0e\n99 2957de\nbf d638d\n63 1bb27e\n29 c6af\nc4 e5f69\n89 294e7d\n15 1cd821\na 4314c\n97 2642dd\nb4 26842a\nc 19b9e8\n0 83b0\n5 1ccec0\n87 26397c\na4 267ac9\nd6 11fd53\n24 1d8156\n32 1d8a8d\n22 1d812c\n2e 1d7fa4\n26 1d7e4d\n6a 1bc706\n66 1bc57e\n7 19b899\ne2 f3456\nbf ce03f\ne6 2b6a39\n62 1bb59b\n30 19f364\n20 19ea03\n17 19af38\nea f22eb\n40 1b00a5\n4b 576e2\nf5 27c9c0\n43 5758b\nb8 d60b4\n9b d1f67\n13 1cd5ab\n35 1d171c\n36 4686e\nba 10f83f\nbe 10f80e\n14 1cd5c8\nb6 10f6b7\n3a 466e6\n36 465c0\n2e 45db6\n35 1d045a\n26 45c5f\n3b 46985\n66 2a63e\nd7 279b42\nee f35de\ne2 f34b8\ne6 f3487\nc4 2b9987\n53 1b0a1a\n6a 2a4b6\n75 1b4b8b\n62 2a35f\n3 967a\n40 1f085f\n43 5789b\n11 3b664\n1 3ad03\n66 2937c\ned eb538\nfa f2cae\n5b 580a5\n11 3b3b6\n1c 2067\na 39b92\nf9 2b752c\n5f 603b4\n75 1b38c9\ne4 2b6aa4\n3c cfee\n24 1d7ea8\n38 cfbd\n1b 8e70\n34 ce97\n37 19f0df\n6 19bb48\n2c c68d\nff eabde\ne6 2b6a47\ne8 eb248\n6b 1bc9a7\n33 1d8a8c\n37 1d8a5b\n27 1d80fa\n60 222a8\n42 264b8\n2b 1d7fd4\n66 290c0\ne8 e9f86\n23 1d7e7d\n27 1d7e4c\na 1d3e2c\n68 22151\n2 1d3cd5\ne7 27d5c6\n60 21ffa\n14 1940f0\n1 1ccc35\na0 26783e\n4 19378f\n9a 10b634\n67 1bc82b\nc2 ef55b\n6b 1bc705\n21 19fa16\n25 19f9e5\n9a 10cbfa\ne9 f2593\ne3 f3455\n67 1bb569\n2c 1d7fff\nbd 2a1ca4\nda 128229\n7f 6456b\n5e 603c3\n5e 1b7bcb\n4a 1b729b\n4e 1b726a\n46 1b7113\n63 63a82\n1c 19b033\neb f359e\ne3 f3447\n8 19a703\nc 19a6d2\nf3 284d38\n52 1f1157\n0 19a5ac\n3b 47cab\n4 19a57b\n9a 10b938\n3f 47c7a\nda eeaa3\nca ee142\n98 d1f0b\n13 1d58fb\nde 12141a\na5 10f05e\n7c 29e2d\na7 110017\n8 41ee7\n8e 10bfc6\n8a 10ad35\n95 29540a\ndc 1201b3\n91 2953d9\n9c 25c08a\n27 46f20\n94 25bf33\nde 12116c\na 42e9e\n15 1cd573\n97 26402f\nb7 10f964\na7 10f003\n37 4686d\nde 120158\n14 194152\neb 2b5bbe\n3 1d4f2a\na2 2a24e5\naf 10eeac\na4 26786f\n4 1937f1\n5b 25c4f\na7 10ed55\n6b 1eca65\n22 1d938c\n20 19e747\n27 45c5e\nde 11feaa\n1a 4253d\n15 1cc2b1\ne3 f3765\ne7 f3734\n62 1b2f9d\nc2 ef5bd\nc6 ef58c\na1 d6bcd\na5 d6b9c\n20 196405\n3a 3f95c\n11 19c1d2\n67 2a63d\na8 10f123\n8b 10afd6\n42 26464\n21 da74\ne6 e9ec9\ndc e7cf1\nd0 2b322d\nd7 efc96\ne5 f24cd\nef f35dd\ne3 f34b7\ne7 f3486\n48 25356\nce ef435\nc6 ef2de\n32 19700d\n3a 3f6ae\n6f 2a4e6\n6b 2a4b5\n67 2a38f\n63 2a35e\n4a 2630d\n25 d7f7\n21 d7c6\ndc e7a43\ne7 f2472\na9 2679a4\n8 1d3dc3\nac 10ef08\n35 e406\n25 daa5\n67 2937b\nc4 2b2ba9\n28 45de0\n27 1d910e\ne0 eb0f1\neb 12bcbc\ne7 f21c4\nc2 2bac21\nce ee173\nc6 ee01c\n9c d1f3c\n35 e158\n94 d1de5\n22 19fcbe\n6b 1b3397\n88 d160c\n2d d94e\n8c d15db\n67 290cd\n5e 259dd\n42 1f1b2a\n5a 259ac\n4e 2507c\n4a 2504b\nc0 11f41a\ncc 2b2a52\n46 24f25\nc4 2b28fb\n7 19bb47\n3 19bb16\n2d c68c\n62 1f5f0f\n25 c535\n7a 1f677b\n1c 8e45\n4 1d3cff\n6b 1bc6f9\n18 8e14\n14 8cee\nc 84e4\n8 84b3\n4 838d\nd8 e6a60\nc8 280639\n3d 6211\n5d 26c99\n30 1d8a86\n38 1d892f\n3c 1d88fe\n1f 1d47b1\n1e 19b33c\n17 1d465a\n2c 1d7f9d\nf 1d3e50\n24 1d7e46\n7 1d3cf9\nb8 10f7d6\n9b 10b689\n13 19b155\n9e d21e3\n60 1bc856\n68 1bc6ff\n60 1bc5a8\n64 1bc577\n47 1b842a\n24 19ea26\n2f 46063\n9f 295506\n97 2953af\ne8 f35a6\ne0 f344f\nc3 ef302\n70 1bbef5\nbd ce038\n60 1bb594\n78 1bbd9e\n7c 1bbd6d\n5f 1b7c20\n88 10af7a\n70 1bbc47\n36 19f07e\n26 19e71d\nf8 f2c45\ndb eeaf8\n59 5803c\n51 57ee5\ne8 f22e4\n49 576db\ncb ee197\n41 57584\nb8 10f838\n13 19b1b7\nbc 10f807\n9f 10b6ba\n9e d2245\nb4 10f6b0\n97 10b563\na8 10eed7\n3 19a856\n38 466df\n1b 42592\n34 465b9\n2c 45daf\n28 45d7e\nb 41c31\n24 45c58\n6f 21178\n26 da9f\n97 10346f\ne8 f3608\nec f35d7\ne0 f34b1\n28 1d94ce\ne4 f3480\na4 d592f\nb4 2610f0\n97 25cfa3\nae d6a3f\n7a 2b127\n68 2a4af\n4b 26362\n60 2a358\n43 2620b\n2e d948\n9f 103318\n94 25bcdb\n97 1031c1\na 9770\n51 581f5\nea f35ad\n41 57894\nd3 279b11\n74 29cd6\n57 1b09e9\n36 d13e\n26 c7dd\n5c 1ebf8\n51 57f47\nbe d60de\nb6 d5f87\nae d577d\nf5 124202\n68 291ed\n4b 250a0\n6c 1bc6ce\n60 29096\n4f 1b8581\n43 24f49\n3e cfe7\n9e 2957b5\n36 ce90\n2e c686\nda e6a59\n8b d15a4\n7f 22d9b\nd8 e6a52\n1f 1d4a6f\nca 280632\n9a c9ec8\n7c 1ed3ef\n3f 620a\nc8 28062b\n98 c9ec1\n3d 6203\n4a 1d001\ne6 2b5785\nda 2b3389\n74 1bbc16\n57 1b7ac9\na 1ccd32\n12 3b5fa\nb3 cf1db\n2 1ccbdb\ndc e6a2f\na 1d40cc\n9c c9e9e\n8 193609\na1 29a181\n1f 1d4a0d\ncc e60c0\nc8 e608f\n9c c9e90\n98 c9e5f\n4e 1cfd0\n4c 1cfc9\n4a 1cf9f\n10 19b1bb\n48 1cf98\n1c d99\n60 1bc848\nd1 2b2218\n6b 63e85\n68 1bc6f1\n60 1bc59a\nd1 2b1f6a\n6b 63bd7\n32 1d8a7f\n60 1bb586\n6b 62bc3\n32 1d87d1\n2a 1d7fc7\n2e 1d7f96\n26 1d7e3f\n70 1bbc39\n6a 1f6074\n68 1bb42f\nb9 cdd67\n60 1bb2d8\nbd 261248\nb1 cdc10\n6b 62915\nba 10f7cf\nb5 299543\n62 1bc84f\nd3 2b221f\nd3 2b1f71\n72 1bbeee\n62 1bb58d\n28 19fb6e\n62 1bb2df\n3c 19f1ce\n34 19f077\n24 19e716\nfa f2c3e\nd 1459\nea f22dd\nf5 27c9b2\n90 2953cc\n35 1d170e\n68 2a4a1\n90 10c798\nd9 11fe71\n60 2a34a\nea f359f\n36 46860\n26 45eff\nba 10f831\nbe 10f800\nae 10ee9f\nf8 f2c99\n8 39b37\n5c 1ebea\nea 12cf7d\n3e 46709\n3a 466d8\n36 465b2\n2e 45da8\n35 1d044c\n78 29b40\n66 2a630\nd7 120000\n6d 21171\n24 da98\n95 103468\n78 2b120\n53 1b0a0c\n70 1b4b59\n6a 2a4a8\n26 45f0d\n75 1b4b7d\n62 2a351\n95 1031ba\n70 5c33b\n53 581ee\n60 5b9da\n43 5788d\n76 29ccf\n3b 1d8be3\n66 2936e\n2b 1d8282\n34 d137\n24 c7d6\n17 193e4a\ned eb52a\n8 41c39\nfa f2ca0\n78 5c1e4\n5b 58097\nea f233f\n68 5b883\n4b 57736\nf5 27ca14\nbc d60d7\nb4 d5f80\nac d5776\n53 1b0c66\na4 d561f\n78 29e5e\n20 45c89\n2c 1d92c1\n7a 29b47\n43 1e109\n42 26218\n25 19fc93\n80 263951\n6a 291e6\n75 1b38bb\n28 d91e\n48 1f09b6\n3c cfe0\n2c c67f\n24 c528\n8c 103c65\nc 3ab6e\n8 3ab3d\n1c 1cc3fb\n7b 63578\n14 1cc2a4\nac 10f154\n8f 10b007\n46 26495\nb6 d6297\n10 1cc273\n2 1cb919\n73 1ee281\nfe 27cb71\n18 19c574\n25 1d9107\nb5 d755f\ne9 12bcb5\na9 110136\na1 10ffdf\n88 10bf8e\n18 19c2c6\n18 19b2b2\n74 29a1a\nca ef706\n18 1d4786\nbd 268336\n1c 1d4755\neb 12ccc0\n31 19f055\n6b 22409\nca 2b2a2a\nf5 27df20\ne3 12cb69\n8 1d3e25\n0 1d3cce\ne5 27d5bf\na5 26787e\n4 1d3c9d\na9 10ee74\n7b 1b4f66\n98 10b62d\n18 19b004\n61 1bc855\nc0 ef554\n61 1bc5a7\n48 1b8556\n44 1b83ce\nda 12978b\n23 19fa0f\n28 c6b0\n27 19f9de\ne1 f344e\nc8 ef3fd\nc0 ef2a6\n75 1bbec3\n65 1bb562\n5f 1e92a2\n16 1d5bc9\n50 1b7d4c\n33 19f35c\n2e 1d7ff8\n37 19f32b\n23 19e9fb\nde 278c78\n27 19e9ca\n6 19a822\n1e 42570\n65 1bb2b4\n58 1b7bf5\n5c 1b7bc4\n50 1b7a9e\n54 1b7a6d\n48 1b7294\n4c 1b7263\n44 1b710c\nda 1284c9\n7f 6480b\na 19bc6e\n3b 19f205\n2 19bb17\n33 19f0ae\n23 19e74d\nde 2789ca\n27 19e71c\nd8 eea9c\n28 45bc\nea f360f\nc8 ee13b\nab d574b\na1 1102ef\n21 47196\ne6 1235eb\ndc 121413\n1a 1d4a2d\na9 110198\n2d 47070\n29 4703f\n66 1b2f60\n25 46f19\n18 19b066\n21 46ee8\ndc 121165\n87 29e30c\n35 47b28\n8a 10bf95\n85 10aea9\n95 29666a\n18 19b314\n25 471c7\ndc 120151\n6b 2a757\n0 42d94\n87 29e360\ned 2b5b86\na4 2a24ad\na9 10eed6\nad 10eea5\n59 25c48\na5 10ed4e\n3d 19f1dd\n3d 479d1\n35 4787a\n22 1d93e0\n6b 1ecab9\n1c 19b035\neb f35a0\nb 1d538f\n28 1d94dc\n7 1d5269\n3 1d5238\n69 1eca5e\n20 1d9385\n2d 45dae\n29 45d7d\n25 45c57\n2d 1d925e\n21 45c26\ndc 11fea3\n35 1d177e\nad d57e7\nc 41c06\n6b 2a4a9\ne1 f375e\nc0 ef5b6\nc4 ef585\n7a 5c4ed\n44 2648e\n96 10b562\n31 198267\n40 2645d\n5a 1b7e9c\n38 19715d\n62 5b9d3\n30 197006\n7a 5c23f\na5 d592e\nb5 2610ef\n88 103c40\n8 87b7\n7b 2b126\n18 193f78\n3d d29d\nc8 ee19d\na9 267c44\n8 1d4063\n33 1d8a1e\n61 2a357\nc7 28177b\n95 25bcda\ndc e6a83\n31 197fc7\n91 25bca9\n87 295fbe\neb f35ac\n75 2af97\nca ef404\nc5 ee318\n65 2a636\nae 10ef01\nb7 d6234\n21 3ee48\na7 d58d3\n86 d172b\n13 19af09\n35 1d04ae\nb5 29ab13\nde e6a28\na5 29a1b2\n69 1bc6fe\n69 1bb43e\n84 29600a\n48 2635a\n29 1d820b\ne5 f21bd\nd8 eeafe\n28 461e\n7d 2ae40\ndc eeacd\n2c 45ed\n75 2ace9\nee f3640\nd4 ee976\neb f360e\n24 4496\ncc ee16c\nc4 ee015\n7a 5af7d\n16 42419\naa 268f0c\n29 3ecf1\na7 d5625\n7b 29e64\n4b 1b87fe\n65 290c6\nff 2b64ee\nb6 2a2e15\n67 1bc88d\n58 259a5\ndf 280f71\n54 2587f\n6a 2a518\n75 1b4bed\naf 106e6e\nd7 280e1a\n4c 25075\n48 25044\ncf 280610\n44 24f1e\n4c 1b8525\n40 24eed\nc7 2804b9\n2a 19fdb3\nb5 29a865\n70 1b4bb9\n8f 294e53\n2b c654\n2f 19fb35\n23 c4fd\na5 299f04\n58 1af839\n18 192ca8\n7b 29e56\nec f25c3\n4d 579ba\ned 2bef4a\ne1 12b912\n62 1bc85d\n9c 103304\n98 1032d3\n94 1031ad\n9c 2967b4\n90 10317c\nab ce96f\n88 102972\n2b 5878\ndb efd58\n8 3987b\n70 1b4bad\n7b 5c1ea\n23 1d93df\n27 1d93ae\n82 29d06c\n4b 1e8910\n2 1d5237\n61 1bc847\n26 1d0e23\n40 1b869f\nf5 284fba\n1a 19c56d\nc0 ef546\n39 d25c\ncf 280362\n23 1d9131\n69 1bc6f0\n61 1bc599\n48 1b8548\n40 1b83f1\n37 1d8a4d\n27 1d80ec\na1 110041\n12 1d48d6\n61 1bb585\n26 1cfb61\n1a 19b2ab\n2 1d5239\n33 1d87d0\n27 1d7e3e\n1a 1d477f\n61 1bb2d7\n58 1b7be7\na2 25f4f5\n48 1b7286\n99 c9bbe\n40 1b712f\n63 1bc84e\n42 1b86a6\nf7 284fc1\n21 19fcb6\n80 263943\nc2 ef54d\n1c 4382b\n3c 469be\n6b 1bc6f7\n6d 62bed\n42 1b83f8\n65 62a96\n29 19fb5f\n21 19fa08\n25 19f9d7\n77 1bbebc\n13 1ed9\n63 1bb58c\nb3 2a1b77\ne1 f34b0\n29 1d94cd\n52 1b7d45\n42 1b73e4\nb5 cef55\n31 19f355\n7b 6458e\nbd 2a1c96\nda 12821b\n7f 6455d\n21 19e9f4\n6b 63c2d\n4e 1b8580\n42 24f48\n25 19e9c3\nad 2a1335\nca 1278ba\n6f 63bfc\n25 19fc85\n42 2620a\n1c 42569\n6b 1bb435\n63 1bb2de\n4a 1b728d\n8 19b9c5\n42 1b7136\n63 63a74\n29 19e89d\nc4 278157\n2d 19e86c\n21 19e746\n25 19e715\na9 d5744\n3b 1a0777\n22 1cfb20\n9a 264404\n33 d10e\n3f 1a0746\n1a 19c5cf\n13 1d58ed\na6 107d2c\n1a 19b30d\ne7 2843b4\nad d57e5\ne2 2bf068\nde 12140c\n2d 1d7f9c\n44 26480\n7c 29e1f\n8 41ed9\n7b 64848\ne1 f34a2\n91 2953cb\n9c 25c07c\nfb 2b77e1\n94 25bf25\n61 2a349\n3b 19f4b5\na7 10eff5\n3f 19f484\n2c 19eb1b\n21 1d7e6a\n96 10b7ae\n86 10ae4d\n13 1d462b\n1e 19b2dc\n27 45efe\nde 12014a\n69 1f4b60\nbe cddf0\nba cddbf\neb 2b5bb0\na2 2a24d7\naf 10ee9e\na7 10ed47\n7b 63586\n3f 19f1d6\n9a 10b688\n12 19b154\ne8 285a96\n18 19b304\nd8 eeaf0\n28 4610\n6b 1eca57\n22 1d937e\n68 1bc93d\n25 1d7e45\na1 d6bbf\n25 da97\n21 da66\ndc e7ce3\n14 19af3e\n71 1b4b58\n29 d90f\n25 d7e9\n85 295fb7\n21 d7b8\n61 5b9d9\n85 d14e7\nc6 ee2bc\n53 1b7a9a\na9 267996\n8 1d3db5\nab d6a0d\n11 19af02\n27 1d9100\nc7 28020b\n25 c7d5\ndc e6a21\n56 5efaa\n79 5c1e3\nea 285a9d\neb f233e\n69 5b882\n46 5e649\n1a 19b30b\n61 5b72b\nd9 f000f\n14 42412\na8 268f05\na9 d57a6\n4 41ab1\na5 d561e\n79 29e5d\n6a 1bc944\n6b 291e5\n29 d91d\n28 19fdac\n2d c67e\n8d 294e4c\n29 c64d\n25 c527\n85 294cf5\ndc e6773\n47 1b86e8\n87 10c182\na7 107fdb\nc7 ef5f1\n1a 19c2bf\n7 1d526b\n35 465ba\n18 3a1dc\n27 1d10c4\nd7 2bb5a4\n87 10c174\na5 107fd4\na7 107fcd\n18 19c572\n9e 10cc39\n1a 43b11\n9f 1048ea\n3a 3f96a\n5a 26f80\n86 d2741\nfe ebf01\n85 29e305\n7a 22dd9\na6 ce59a\n5b 1ec31\n87 ca3f2\nc4 2b15d7\n3 1d5248\n23 1d10a1\nd3 2bb581\ned 2b5be8\na4 2a250f\n5 1d4f54\n27 1d1070\nd7 2bb550\n1 1d5241\n3 1d523a\n21 1d109a\n3d 4f4d\nd0 eebf3\ndc 28222b\nd1 2bb57a\n23 1d1093\n81 d14b6\n8d 264aee\nd2 eebec\nde 282224\nd3 2bb573\n32 1d87df\nd5 2bb549\n27 1d1062\nd7 2bb542\n82 294d20\n19 9127\n40 1f1b21\nc8 e7105\nc 446\n47 1b843a\n67 1b4293\ncf ef49a\n19 9119\ne2 f34aa\n8d 10c024\nde 12975a\n8f 10c01d\n87 10bec6\nad 107e7d\nfe 1255b3\na7 107d1f\n18 19c2c4\n1f 3b4d5\nbe 108a90\nbf 108a91\n3b 3f969\ndf f00a7\n7a 22dd7\nde e7d58\nba 2a1ccd\n5a 1ec30\n86 ca3f1\n3 1d4f9a\n23 1d0df3\n47 1b83d8\n63 1b4262\nb 1d50e3\n19 90b7\na6 cd5e6\n1 1d4f93\n1d 8e46\n3 1d4f8c\n5 1d4f62\n7a 1ee679\n39 4f10\n8a 294bc9\n21 1d0dec\n3 318\nf 193950\n20 4465\n2c 197a9d\nd0 ee945\ndc 281f7d\n25 1d0dbb\n37 1d0771\n32 1d041f\na7 106d19\ndf 2ba685\ne4 124b62\n9c 10cc32\n18 43b0a\n9d 1048e3\n38 3f963\n58 26f79\n84 d273a\n1a a3e1\n6a 1b3396\n17 1d490a\n35 1d076a\n37 1d0763\nbe cf362\n9f cb1ba\nbc cf307\nb1 108656\n95 2652ec\ndc 2b239f\n78 22dd2\na4 ce593\n87 10aeb2\na7 106d0b\n18 19b2b0\n13 1d48e7\n71 22c0c\n33 1d0740\n37 1d070f\n31 1d0739\n9d 26418d\nc7 128a03\n6d 1bb6bb\n37 47b2f\na6 260a98\n32 3e543\n3e 1d1b7b\nd9 e7a11\nbe 2685dc\nc4 2b18e9\ne6 124b5b\n97 2966d3\ne4 124b00\n71 1ee2de\na6 10ed48\n7a 63587\n9e 10cc2b\ned f25c4\n1a 43b03\n69 2949c\n58 26f6b\na2 ce879\n67 1edc53\n9f 1048dc\nbc 108a29\n78 22dc4\n9c d3502\n18 a3da\n27 1d10c2\ndf e7d4b\nfc ebe98\nf1 1251e7\n9d cb1b3\n38 6233\n6d 22185\nc8 e5e43\n7 1d3d09\n65 2202e\n37 1d04c3\n2f 1cfcb9\n38 1d9e91\ne7 1235fa\n78 1bd300\n70 1bd1a9\n8 41e77\n7b 647e6\n97 10b573\n8 19bc65\nb7 1073cc\nf7 2bf9f9\ne7 2bf098\nf8 f4209\n1f 1d47b3\n17 1d465c\n5e 1f158f\nf 1d3e52\n3f 1d060c\n35 1d04bc\n37 1d04b5\n2d 1cfcb2\n7e 1ed3e8\n2f 1cfcab\n73 2af6f\n95 10b56c\n8d 10ad62\nde 128498\n87 10ac04\n20 54d9\ne0 f34a3\n2d 45db0\nd7 2bb800\n9d 10cc31\n53 1f26d8\n19 43b09\n56 1afa2a\n8a 102c89\n2f 3efcb\nfe f2c7d\n5f 58074\nf7 2b7659\nbd 108a8a\n73 1ee531\n39 3f962\ndd f00a0\n59 26f78\n85 d2739\n78 22dd0\n1b a3e0\n38 e52d\n58 1f15c5\nb8 2a1cc6\n9e cb1b9\nb0 108655\na5 ce592\n1a 2091\n13 1d4639\n7 1d3ca7\n33 1d0492\na6 106cb4\n37 1d0461\n27 1cfb00\n30 1d9cd8\nc7 12773f\n38 47ca3\n28 47342\nba 2695bf\ne7 123598\ndc eeb21\n70 1bd147\nb 1d3e21\n3 1d3cca\ne 19a97b\n27 46f12\n5 1d3ca0\n3f 1d05aa\n31 1d048b\n3c 19713c\na4 106cad\n1f 192fef\n33 1d0484\n3e 197135\na6 106ca6\n37 1d0453\n7a 1ed3b7\n2b 1cfc7a\n2f 1cfc49\nf0 f3e04\n2c 1967db\nf 19268e\ndc 280cbb\n23 1cfb23\n2e 1967d4\nde 280cb4\n27 1cfaf2\n6d 1b336b\n70 1ee2dd\n2f 45da9\n18 1cc6e8\n3a 3f95a\n11 19c1d0\nbf 108a83\n9e 1048db\ned ea274\n79 22dc3\nd7 2820d0\n9d d3501\nf7 123efb\n5b 26f71\n78 2b0be\n53 1b8fa8\n19 a3d9\nf0 1251e6\n5a 1ec22\n1f 3a4c1\n74 1bbf18\n10 1f35\n31 1d9a39\n7f 63555\n1a 1d5cff\n3a 1d1b58\n1a 8e6f\nc4 11f44d\nda f0015\n7a 1b4fc7\ne6 124b07\n73 1ee2e5\n5b 1b0e1f\n87 25c5e0\n9a 10cc08\n74 29cc8\n39 1d8bdc\n1a 43aaf\nba 108a61\n9c 10c914\n9f 104888\n3e 3f939\n3a 3f908\n1f 3b791\n14 194154\nbf 2a2d21\nb3 10f6e9\n62 5b793\nde f0046\n66 5b762\n5a 26f1e\nfa ebed0\ndc efd83\nfe ebe9f\nf3 1251ee\ndb e7d28\n17 3a0ca\n77 1bbf1e\n30 6088\nf 1d410e\n50 1e9120\n13 1f3b\n57 1d539\n15 3a0c3\n86 26397d\na9 260bb6\n8e 103cdc\nc9 ef700\n86 103b85\n84 103b7e\nc4 11f44b\n9a 2957e4\nbb 1089fe\nc5 11f44c\nb0 2613c1\n5b 1b916d\n87 26492e\n73 1ee2e3\n0 1cb974\n4 41d5f\n21 3eb8e\n5a 1b0e1e\n86 25c5df\ne2 f3448\n53 1ea13c\nf2 284d45\nbe 108a2e\n3e 3f937\nbb 108a60\n9d 10c913\nbf 108a2f\nb4 2613f2\n3f 3f938\n34 1982fb\n9a 1035f6\n3b 3f907\n30 1982ca\ndf f0045\n2c 58a1\nfe ebe9d\nf3 1251ec\n8c 295eb5\n80 10287d\n25 3ebbf\n7e 22da6\n73 5c0f5\n7a 22d75\nda e7d27\nde e7cf6\nd3 121045\n5e 1ebff\n53 57f4e\n85 10c10b\n3a 1d8b74\naf 106bc2\n45 5e8f1\n5a 1ebce\n58 1e8fc9\n1b 1de4\n38 5f31\n30 5dda\nf 1d3e60\n50 1e8e72\n13 1c8d\n74 1bbc78\n10 1c95\na 1ccd94\n18 d68\nf4 2b63f1\n4 1ccc13\n56 1afa38\n96 1034d2\n86 102b71\nd6 e6941\n93 10c794\n96 1034c4\n86 102b63\nc7 ef343\nd8 f000e\ne7 2b6cf6\ndf e7cf7\n9a d3476\n78 1b4fc0\na4 260781\n59 1b0e18\n85 25c5d9\n3a 198428\n1b 194280\n38 1983cd\n8a 294e77\nf8 ebe67\ndb e7d1a\n9b cb127\nff 28510a\nd8 f0070\ndf e7d59\n9a d34d8\n9e d34a7\n93 10c7f6\n1e a3b0\n13 436ff\nf8 ebec9\nd9 e7d21\n9b cb189\nff 28516c\n91 2652bb\ndf eedd7\nd8 2b236e\n42 1b86b4\n9f cb158\n95 26528a\ndc 2b233d\n3e 6209\n5e 1e92a1\n33 3f558\n52 1afa07\n56 1af9d6\nde 11ff0a\nb9 260255\ne3 124acb\n1a 1d5cf1\nd6 efca3\n58 1b9159\na2 260a67\n9a 10cb98\n3a 1d1b4a\n32 3e4e1\n3e 1d1b19\nf6 ebafc\n1b 1cd9a2\nba 2685ab\nd7 e7954\nbe 26857a\n78 1b4fb2\na8 110137\n8b 10bfea\n59 1b0e0a\nff 2be82c\nd9 e7cb1\n5a 1b9160\nc2 128cd1\n18 19c5c8\nda f0007\n89 d28d1\n1c 1dab\na5 10f052\n5b 1b0e11\n78 1b4f5e\n38 198421\ndb e7cb8\nb0 10f6e1\nbc 2a2d19\nfd 285103\n9e 10cbc9\nd8 f0062\ne7 2b6d4a\n1e 43ad2\n1a 43aa1\n58 26f09\n67 1edbf1\nc2 2b18af\nba 108a53\n24 197696\n9b 1048ab\nff 2be88e\n9f 10487a\nf8 ebebb\ndd e7ce2\n3e 3f92b\n15 19c1a1\n3a 3f8fa\n25 3d909\nc0 ef2fa\n1b 3b752\n7f 1f5735\n59 1ebba\nda f0069\ndd e7d52\n98 d34d1\na7 29a1b9\n9c d34a0\n24 3ebbc\n91 10c7ef\n5a 26f10\n9 97da\n1c a3a9\nfe ebe91\ndf e7ce9\nf1 125185\n40 1b86ad\n9d cb151\n73 5c0e9\n5b 1ebc1\n30 465ea\n3c 1d9c22\n3c 6202\n31 3f551\n38 61d1\n65 29374\n86 1028c3\ne 1cbd4f\n84 1028bc\n59 1b9166\n85 264927\n97 295411\n71 1ee2dc\n1b 19c5ce\n38 1a071b\nc4 e5d1b\nd8 278c4c\n58 1b0e17\n84 25c5d8\n7d 2ae32\n29 45d7f\nad 2679d3\nfc ebe96\nf1 1251e5\nce e60c7\n7c 22d9f\n71 5c0ee\n78 22d6e\n1c 192f95\n3e 6207\n33 3f556\n46 1b8429\nf9 ebec8\nfd ebe97\n9a cb188\n9e cb157\n93 1044a6\nb0 1085f3\n7d 22da0\n79 22d6f\n1e 2060\n13 3b3af\n30 3f4fc\n59 1b7e34\n16 1cc2b9\n2b 1d94d4\n51 1b7cdd\n6 1cb958\n41 1b737c\n67 5b763\nd7 efef0\n69 211a0\na5 2a11ec\nce 2bada9\nc2 127771\n67 63ab3\nc7 ef58f\nbf 1077c1\n1a 1cc433\n9f 10cbca\n1e 1cc402\n16 1cc2ab\n26 3ebc3\n4 1cb951\na9 25f646\n6 1cb94a\nf6 ebafa\nf7 ebafb\n84 c918c\n9a 2957d6\nd8 278c3e\nb 87bf\n58 1b0e09\n29 45d71\na4 107f6f\n31 1d174d\nf9 ebe58\nd6 eec1f\n9b 29db33\n5b 1b915f\n78 1bd2ac\n19 19c5c7\nda 278c45\n5a 1b0e10\n6a 1bb444\n53 1ea12e\nf2 284d37\n98 25c0ad\ned 284564\n96 d208e\n91 2652c7\n15 19c19f\n1f 90ef\n18 1cc686\n3a 3f8f8\n11 19c16e\nbb 108a52\nbf 108a21\n9a 1048aa\ne9 ea243\nb5 2995b3\n9e 104879\nf9 ebeba\nd6 eec81\ne8 f2346\n49 5773d\n3f 3f92a\n9a 1035e8\n3b 3f8f9\n68 2924f\n67 1bc57d\nc2 28023b\n58 1ebb9\na5 107d26\n9d d349f\n18 192d08\n42 5757e\n5b 26f0f\n73 5c0e7\n1d a3a8\na4 298e9b\n3c 6200\n31 3f54f\n38 61cf\n3a 1d8936\n85 10becd\n56 581cc\n1a 4259f\nea f234d\n4b 57744\n8d ca5b2\nd3 121037\nf0 125184\nbd cf2f8\n23 1d93e1\n98 cb181\na8 d57b5\n9 3abac\n91 10449f\nda e6a57\n6a 29256\na 39b3e\n5e 1ebf1\n70 5c08d\n53 57f40\naf 106bb4\n5a 1ebc0\n7d 29e2e\n3d 6201\n16 8f97\n28 c6be\n1c 2059\n11 3b3a8\n18 2028\n65 1b453a\ndc 2b23a1\nfe 125613\na3 110048\nd5 281e89\n18 19c2b8\n8 192347\n6b 294f5\n7b 1b4cb6\n44 57862\ne5 eb443\n38 198111\n47 1b86da\nec 12cd5b\n75 29a29\na5 d68ee\n16 19b183\n25 19799b\n8b 102c96\n96 d208c\n2 42da9\n4b 56482\nb 398f1\nca 2b2a28\n17 1cd88a\n17 1cd5dc\n56 1b8d9a\n76 1b4ea1\ne 39b6f\nec f362b\nca ee3f2\ne6 1248bb\n7a 1b4fb9\n15 193eb3\n97 104793\nbe 1087e4\nf9 f4208\n97 1044e5\n1b 3b514\n13 3b3bd\nb4 2a1b3e\nf6 ebdaa\nb4 cf212\n53 1eada\n1f 1cc40f\nfe ebc53\n85 29e057\nfa ebec2\n7a 22b2b\n1 1d4f2f\n72 229d4\n5b 1e983\n5c 26f48\n30 5e3c\n61 1b4509\nd1 281e58\ne7 2b6a4a\nad 107e7b\ndc 2b233f\nfe 1255b1\na3 10ffe6\nd5 281e27\n43 1b86a9\ne8 12cd2a\na1 d68bd\nc2 27818f\n8a 10af73\n95 295648\n72 1bbc40\n65 1b44ca\n82 10c0ec\ncb 11f7c5\nc0 278188\n3 19bb18\na8 110199\n23 197971\nd3 281e51\n4 1cb943\n77 1ee2b2\nd2 2b1f70\nd7 281e20\n82 25b5fe\nb1 d7272\n65 1b428c\ncd ef493\n7a 1bbda5\nc5 ef33c\ndb 2bb986\na5 d568e\nc 438\nda e6a65\n7f 22da7\n59 582ea\n47 1b842c\n27 1976f4\nd8 f0000\ne7 2b6ce8\ncc 2b9ae0\n8d d28f4\ndd e7cf0\n98 d346f\na7 29a157\nc4 2b9989\nbb 29ac40\n17 1d5bd8\n67 1bb55b\n3 1578\n36 1d1782\n37 1d1a31\n37 1d1783\n57 1b9047\n34 198307\n3a 1d8928\n85 10bebf\n76 1b4bf1\ne 398bf\n15 19c201\n34 198059\n1a 19b05f\n77 1b4ea0\n4 192531\nf 39b6e\n56 1b0cf8\n77 1b4bf2\n4 192283\nf 398c0\n56 1b0a4a\n14 193eb2\n97 10cae1\n17 3b37e\nb6 108939\nb7 10893a\n96 104792\nbf 1087e3\n55 60512\n27 1cfe10\nd7 2ba2f0\nb7 10868c\nd7 eff50\n95 d33b8\n72 22c80\ndf efdf9\n62 1edc2f\n47 1b7426\nd7 efca2\n5b 26cd1\n7a 22b29\n8e 25b786\n3d 3f683\n72 229d2\nd6 e7c01\nb2 2a1b76\n94 cb069\na 19a6fc\n67 1b327f\n22 1cfabe\nd6 e7953\nb2 2a18c8\n5d 26f47\n31 5e3b\n10 1c93\n21 1cfdc8\nb1 d7210\n41 1b8402\n30 1d87d8\n6a 5bb8c\n45 1b83d1\n34 1d87a7\n6e 5bb5b\n61 1b425b\n95 2953a8\n65 1b422a\n1 19b871\n21 1976ca\n38 6231\n72 1b4e00\n25 197699\n8 407\n7b 22d76\n43 1b83fb\n26 19ea2d\nf5 286032\n54 1f2451\ne5 f246b\n67 1b4223\nc2 277ee1\n96 10b570\n31 198275\nb 19b9c1\n49 5fd39\n43 5fbe9\n41 5fbe2\nd3 281e5f\n27 197692\n63 5ba42\n25 19768b\n8b 102986\n61 5ba3b\nf3 27dcb8\n80 25b349\n75 1b3bd9\n30 1d0418\n6a 29504\n95 d310a\nb5 cef63\nde e7a3c\n42 1b73f2\n25 1966e7\nc2 1289cf\n5a 1b8e5e\n77 1b3bd2\n75 1b3bcb\n6a 294f6\n7a 1b4cb7\n37 197041\n35 19703a\n2a c965\n62 1bc5af\nc 1cba9a\nda 2b20c7\n15 1cd883\na 431ae\nbc 268335\n80 2636a3\n74 1b4e9a\nc 39b68\n36 198302\nd9 f006f\ne4 1248b4\n9b d34d7\nd8 2ba6bc\n3c 19f23e\n95 10478c\n9d 104635\n95 1044de\n38 3f6b5\n67 1ed9b3\nb6 cf20b\n97 cb063\nb4 cf1b0\nd4 2b2248\n51 1ead3\nbe cf0b4\n4 1934e1\n9f caf0c\nbc cf059\ndc 2b20f1\n70 229cd\n59 1e97c\n51 1e825\n38 19f44b\n3a 5f8c\ndf 1297bb\n5e 26f41\n32 5e35\nd7 129664\n71 1b3ba8\n71 1b3b9a\n77 1b3b70\n11 19b1b0\n33 197010\nc7 ef2e1\n4e 2633e\n31 197009\n37 196fdf\n2a c903\n6d 1bb40d\n24 19e724\n7 19a5d7\na 39ba0\n5f 603c2\n17 1cd87c\n34 1d19c9\nb6 268485\n80 263695\n74 1b4e8c\n7f 5c4c9\nc 39b5a\nbe 26832e\n17 1cd5ce\n34 1d171b\nb6 2681d7\n56 1b903a\n1f 43adf\n56 1b8d8c\n84 10bebc\n1f 43831\n82 26369c\n76 1b4e93\ne 39b61\n57 1b0ceb\n74 1b4e38\neb 27d4ae\nfc 12536c\n15 194153\na 9a7e\ndb f0068\nf8 f41b5\nc4 2b163b\ne6 1248ad\na4 10edb1\ne4 124852\n57 1b0a3d\n74 1b4b8a\n99 d34d0\nff 2b7502\nf3 123eca\na4 107d15\n15 193ea5\na 97d0\n28 c65c\n71 1ee52a\na2 ce5cb\n50 26b66\nb6 10892d\n3e 19f237\n97 104785\nb4 1088d2\n32 3f805\nbe 1087d6\nb6 10867f\nbc 10877b\nb4 108624\na1 29a18f\n6a 1bc9b4\n32 3f557\n70 229bf\nbf 2a1a5f\nd6 eff43\nd6 efc95\nf6 ebd9c\nd7 e7bf4\nf4 ebd41\n95 cb05c\n19 1d5a3b\n53 1eacc\n70 22c19\n30 60dc\nfe ebc45\n85 29e049\n59 1f2888\na3 29a196\nf6 ebaee\n51 1f2731\ndf e7a9d\nfc ebbea\n57 1e90f7\n9d caf05\n95 cadae\n7a 22b1d\n72 229c6\n5b 1e975\n78 22ac2\n38 5f85\n5c 26f3a\na6 ce848\n30 5e2e\nbb 110d4c\nc5 12779a\nb3 110bf5\ne5 1235f3\n24 19fa48\n6d 1b3121\n65 1b2fca\n85 10ac09\nd6 12833f\n5e 1b7e6d\n35 19f394\nc6 1279de\n53 1f11bc\n56 1b7d16\nde 12824a\n79 1b4f4f\nf6 124198\na5 106a62\n2d 196590\n7e 1b3cc6\ne6 123837\n73 1ed015\n25 196439\n76 1b3b6f\n39 47c4e\nfe 1240a3\nc7 127793\ne4 12b8e0\nbb 110d3e\nb3 110be7\ne7 1235ec\n24 19fa3a\n6d 1b3113\n65 1b2fbc\n85 10abfb\na7 106a5b\n2d 196582\n25 19642b\n8 8761\ndb 2ba6b6\nbb d761e\nef 124c61\na4 d6943\ned ea01c\ne5 e9ec5\nfb 2b650f\n4e 5fa62\naf 1080d0\n6a 1b4656\n34 1d1a29\n35 1d1a2a\n35 1d177c\n74 1b4e98\n7e 22da8\n73 5c0f7\n36 198300\n74 1b4bea\nd 39b67\n37 198301\n75 1b4beb\nd 398b9\n37 198053\n95 10cada\nd7 2bb552\n9d 10c983\n15 19c44f\ne1 2856a0\n40 1f1abf\nc8 e70a3\n95 10c82c\nc8 27803f\nea eb2b1\nb5 108933\n8e 29cf46\nf7 2b73ab\nbd 1087dc\n4a 1f0c6b\n39 4f1e\n25 1cfe09\nd5 2ba2e9\nb5 d7251\nb5 108685\nde 12115e\n42 1f0b14\n73 1ee283\n39 3f6b4\n31 3f55d\n3c 620e\nd5 eff49\n13 a289\n50 1f146e\ndd efdf2\n23 1cfdcf\nb3 d7217\na1 26783f\n0 1d3c5e\nd5 efc9b\n1b a132\n38 e27f\n58 1f1317\na2 298c25\n13 9fdb\n50 1f11c0\nd4 e7bfa\nb0 2a1b6f\n96 cb062\n12 1f3a\nfd ebc4b\nca 278046\n65 1b3278\nf5 ebaf4\n98 2957cf\n27 1966e0\n90 cad2a\n55 1ea104\nd7 280bc0\nf4 284d0d\nb7 cef5c\nf4 2b6141\n79 22b23\n71 229cc\n71 643dc\n33 5e34\nc5 127738\nb5 cf211\n8e 263824\n7a 5b22b\n7e 5b1fa\n6e 5a899\n19 1d5cf7\nde 11fefe\nec ea273\n27 45c50\ne5 123591\n20 19fa17\n69 1b30f0\n16 c47\n24 19f9e6\n6d 1b30bf\n61 1b2f99\n65 1b2f68\n16 1cd5cf\n85 10aba7\n3a 3e69a\n3e 3e669\nf8 f2f47\n2e 3dd08\n3d 196e8f\na5 106a00\na4 cd58b\n29 19655f\n7a 1b3c95\n25 1963d7\nf7 27df29\nbd cf35a\n59 1b7bea\ncc ee40c\n7b 1b3d06\ne 1cbaa1\nb7 cf20a\nf4 2b63ef\n6b 1b33a5\nec ea265\n25 1979a9\ne7 12358a\n34 1a0339\n7d 1b3a12\ne4 ea10e\n77 1b38c2\n20 19fa09\n69 1b30e2\n29 197821\n7a 1b4f57\n61 1b2f8b\n87 10aba0\n5b 5f3df\n85 10ab99\na7 267ac3\n59 5f3d8\n8a 10afd5\n95 2956aa\nb 19a6ff\n9 19a6f8\n49 5ea77\n3 19a5a8\n1 19a5a1\nfd ebef9\nd3 280b9d\n39 196eb2\nad 29a05b\na1 106a23\na7 1069f9\n7b 5b238\n3d 196e81\na5 1069f2\n79 5b231\n5c 25c76\na6 cd584\n31 196d5b\n35 196d2a\n2a c655\n29 196551\n25 1963c9\n17 1d5bca\n4e 1b858e\n42 24f56\n25 19e9d1\n6f 63c0a\n9e 25bdd5\n17 1d591c\n4a 26361\n21 c7a4\n2d 19fddc\n88 263a9a\n36 1d1774\n2d 19e87a\na7 2607e9\n25 19e723\nb 39b9f\n37 1d1a23\n37 1d1775\n5b 1afafb\n21 1cfb2a\n75 1b4bdd\n53 1af9a4\nca 2b2cd6\n74 1bd186\n88 d15aa\n7c 22da1\n71 5c0f0\n34 1982f9\n53 258aa\nb 1d3e2f\n5f 1b8ee2\n70 299f7\n7c 1bd02f\n27 45c52\n35 1982fa\n23 1cfb31\n77 1b4be4\n35 19804c\n8a 296145\nce ef497\na5 d58da\nfa 2bfb20\n23 1976c3\n4c 265d7\n4a 1e9bdf\n13 439ab\n1b 90be\ne3 285699\n42 1f1ab8\n7 1d3fa9\nad d5783\na5 d562c\nfa 2bf872\n4a 1b84fd\n13 436fd\nbc d6147\n18 1cc43a\n3a 3f6ac\n29 dbbd\n29 c65b\ne7 f2218\ne0 2b57af\na3 ce5ca\nb7 10892c\n96 104784\ne5 ea11d\n33 3f804\nbf 1087d5\n37 1982a1\n92 25bf5f\n3b 4f17\n89 264abf\n27 1cfe02\n90 10444c\nd7 2ba2e2\nb7 10867e\ndb e6a04\nca 278038\n65 1b326a\n6a 1b4356\n3b 3f6ad\nf4 f408f\n95 d33aa\na 1cd04e\n11 a282\n30 60da\nfc f3f38\n6c 1ecaf0\nf4 f3de1\n64 1ec999\nd7 281e22\n9d d3253\n4b 5fd40\n95 d30fc\na 1ccda0\n5b 26cc3\n78 2ae10\ne2 2b57b6\n58 1af8a9\n7a 22b1b\n1e 4256e\na7 d5687\na0 298c1e\n16 42417\n30 5e2c\nb7 2613f8\nc4 ee2b5\nb5 cf203\n94 cb05b\n10 1f33\n67 1b3271\n6b 5bb99\n7b 1ee678\n8a 25b4a7\n25 1966d9\n10 1c85\nb4 26139e\n97 25d251\n9f 10cc38\n1d 3a4ba\nf 3ae30\nb7 cdc9a\n86 ca703\n11 1f34\n1b 1d5cfe\ncc 120864\n15 9a1\nc6 2b28a2\n8c 103cd3\n74 1bbc6a\n10 1c87\n1a 1d5a51\n42 1f1dc8\nf7 2be6e3\nb6 1073cb\n3a 1d18aa\n7b 22b1c\n10 19c1d1\nd2 efc10\n42 1e87c8\n76 1b4e3f\nfe 125373\n53 1b0cc8\nac d57d8\ne8 f35fa\nd3 e7b6f\na6 ce598\n84 25b326\n11 193e82\n15 193e51\nd3 e78c1\nb0 ceed1\n93 cad84\n75 1ee2ab\n97 104731\n46 1f1df9\n17 3b63a\n13 3b609\n77 1f55ec\nba 1087b3\nad cd6d7\n50 604e2\n67 1b2f61\n93 1044b4\n97 104483\n79 2b0af\n17 3b38c\n13 3b35b\n90 d3388\nb4 2997ee\n52 26dc7\ne 1cbb03\n1a 1d5c8f\n90 d30da\n52 26b19\n14 9fb2\nf2 ebd79\nd4 efc2c\n99 29eb40\nf6 ebd48\nd3 e7bd1\nf5 28501c\n8c 103cd5\n44 1b86d0\n76 22c51\n72 22c20\n19 1d59e7\n53 1ea78\n34 60b9\n54 1e9151\nfa ebc22\nf2 ebacb\n3a 1d1ae8\ndb e7a7a\nd3 e7923\n1b 1cd940\ndc f003f\n64 5b75b\nb0 cef33\n75 1ee30d\n8 3ab49\na9 ce72a\n36 198052\n7e 22afa\n72 22972\n7a 22d69\n29 5633\n5f 1b0b94\n53 1d55c\n7c 1b4ce1\n70 216a9\nd5 128347\nb 3adff\n1d 193ffc\n11 9c4\n6f 1f4de2\n17 99a\n8e 103ccc\n63 1f5cc4\nd8 e7a64\n44 1b00e4\n4 193553\ncc e7144\n9f 10c98a\nc4 e6fed\nda 2b3637\n46 1b00dd\n13 4243b\n1f 1d5a73\n30 46588\n3c 1d9bc0\n44 1b00d6\nee f231c\n4f 57713\n4 193545\nf 3ab82\ncc e7136\nc6 e6fe6\n9f 10c97c\nbc 110ac9\n17 19c448\nc4 e6fdf\nda 2b3629\n8e ca5ac\n86 ca455\nf2 27df67\n19 1ddd\n48 1b020a\ne8 eb4f6\n0 193522\n4 1934f1\n78 29ba2\n13 1d5ba7\n36 1d19ce\n11 1c86\nca 2b9d62\n40 1b00b3\n1b 1d5a50\n56 26b3c\n13 1d58f9\n3a 1d18a8\n32 1d1751\n36 1d1720\n3a d2c6\n33 1d1a00\n16 1cd827\nb3 1088a7\n15 1d5bd1\n92 1046ff\n3b 1d18a9\n76 22995\n33 1d1752\nc0 12098a\n58 1b0e19\n84 25c5da\n16 1cd579\nb3 1085f9\n3b d2c7\n15 1d5923\n92 104451\n1a 911f\n76 1b4e3d\n30 1982d6\n88 10c29e\n34 1982a5\n7a 1b4d17\n81 10be8e\n72 1b4bc0\na 3988e\n76 1b4b8f\nd 39b59\n51 60545\n30 198028\n88 10bff0\n34 197ff7\nb0 ceecf\n52 1b0cc7\n56 1b0c96\n86 294d4f\ne9 f35f9\n44 5f904\nc8 ef451\n10 19412f\n23 471ff\n55 1b9040\nd2 e7b6e\n20 5477\n10 193e81\n14 193e50\nd8 eed9e\nf3 eba68\n23 46f51\n55 1b8d92\nd2 e78c0\n27 1966de\n94 10c7bb\nb6 1088d7\n17 43988\n36 3f7e0\n57 1b9039\n3 1d3f86\n61 222ab\n93 10c802\n36 3f532\nb3 108909\n95 10c7bc\nb7 1088d8\n96 104730\n37 3f7e1\n16 3b639\nbb 1087b2\n51 604e1\n77 1b4e92\n23 1cfddf\n41 1b86a2\nde 280f70\nd3 2ba2bf\nb3 10865b\n27 1cfdae\nd7 2ba28e\nb7 10862a\n18 3a4fa\n96 104482\n3f 3f68a\n34 19804d\n9a 103348\n37 3f533\n16 3b38b\nc9 281650\n24 574a\n48 1b019a\nf6 ebd46\n53 26dc6\nf 1cbb02\n76 22c4f\nbf d638b\n72 22c1e\n57 26b49\n7e 22af8\n15 9fb1\n46 1b7175\n11 9f80\nd2 e7bd0\nd6 e7b9f\n3f 19f482\n90 cb038\n35 60b8\ne 19a6cb\nf3 ebaca\n25 549d\n3b 1d1ae7\nd2 efebe\n8e 294bfa\nc 144c\n4 12f5\nbb 261520\n1a 1cd93f\nce 11f5a9\nd6 e78f1\ndd f003e\n58 1af8a7\nfa 124320\nb1 cef32\ne7 2b5788\nad 106bb9\nfe 1242ef\nb5 cef01\ne9 27d4a7\n7f 22af9\nc 18a\n77 229a2\n4 33\n73 22971\n35 5e0a\n48 1b01fc\n42 1b00ac\n65 5a74a\ndd 1281f0\n2d 3dd10\n25 3dbb9\nd5 128099\n0 193514\nb 3ab51\n6f 1f4b34\nb5 25fe2f\n42 5789a\n4 1934e3\nd2 279b10\nb9 10fa83\n94 cadbb\n4 19253f\n94 c9da9\n76 229a1\n44 575b4\ne5 eb195\nfb 2b77df\n46 1af0c9\n43 1f1d77\n9 431a8\n14 192e92\n1f 3a4cf\n94 cadad\n4a 579f1\nd6 e6933\nc6 e5fd2\n96 c9da2\n94 c9d9b\n86 c9441\n90 cad8a\n23 46eef\n55 1b8d30\nb 431b1\nc6 2b15e0\n8c 102a11\nfb 2be85f\nc6 2b18ee\n91 1046f9\n11 1cd5a4\na4 107fc3\n57 1b0cf9\n12 19c478\ne9 f2345\n58 1b8eb9\nc0 128a2a\n53 1d56a\n5f 1b0ba2\n1a 19c321\n3d 469bf\n57 1b0a4b\n12 19c1ca\n35 46868\nd8 efd60\ne7 2b6a48\n51 1b0cc1\n32 1982d1\n13 194129\n30 198276\n18 dca\nb7 ceefa\nf4 2b60df\n17 1940f8\nbc 108779\nd1 e7b68\n78 1b4d12\nd9 f000d\n59 1b0b6a\n51 1b0a13\n3a 19817a\n32 198023\nd7 2bb852\n1b 193fd2\n28 c95e\n38 19811f\n13 193e7b\nd5 2bb7f7\nf8 ebbb9\ndb e7a6c\nf0 eba62\nd3 e7915\nd1 e78ba\n36 1d1a24\n93 cad22\n3c 19f1dc\n1f 19b08f\nff 2be890\n95 10472a\nb4 d623c\n65 1b2f5a\n91 1044ad\nff 2be5e2\n95 10447c\nb4 d5f8e\nb8 2a1a18\nd4 efee8\nd7 e7c02\n92 d3381\n57 1f275b\n1a 1de3\n57 1eaa9\n12 a228\nd8 efdc2\ne7 2b6aaa\n92 d30d3\n57 1f24ad\n29 19fb6d\n74 1f65fa\n97 29d9ab\n5c 26c9a\nfb 2be5b1\n91 10444b\nc6 2b1640\n58 26c69\n67 1ed951\nc2 2b160f\n54 26b43\n50 26b12\naf 108132\n62 2908f\n6e 1bc6c7\nd1 e7bca\n93 cb032\nf7 285015\n6d 2a77f\nc8 ee43d\n8e 103cce\n97 cb001\nd4 2b21e6\n51 1ea71\ndb 281fb8\n36 60b2\n56 1e914a\n67 1bb5cb\n32 6081\nf5 2be6dc\nc 192694\n52 1e9119\n20 1d8125\n17 1f0a\nf8 ebc1b\n38 1d1ae1\nd9 e7a73\nd1 e791c\n19 1cd939\nba cf083\n39 1d88ce\n50 26db2\nde f0038\n8d d2902\nd3 129387\nb2 cef2c\n77 1ee306\n9b caedb\nb8 cf028\nc9 2b2cd0\n34 19804b\n70 2296b\n53 1e81e\n51 1e7c3\n67 1bb31d\n32 5dd3\n52 1e8e6b\nf5 2be42e\nc4 2bae97\nc 1923e6\nd7 129602\n28 1d7fce\n1f 1db3\n20 1d7e77\ne 19a9db\n17 1c5c\nf5 27ca22\n54 1e8e41\nc6 128d02\n13 1c2b\n10 192e61\n1b 3a49e\n12 1d5b9a\ne9 12ba67\n1a 1d5a43\n12 1d58ec\n50 1b8d54\n5b 60391\n36 1d19c2\n13 1cd84b\nb2 268454\n17 1cd81a\nb6 268423\n70 1b4e5b\n7b 5c498\n8 39b29\n74 1b4e2a\n51 1b0cb3\nfb f2ef9\n8c 294ead\nd1 e7b5a\n3a 1d189c\n32 1d1745\n1b 1cd6f4\n28 46080\nba 2682fd\n2c 4604f\nbe 2682cc\n13 1cd59d\nb2 2681a6\n24 45ef8\nb6 268175\n78 1b4d04\n8c 294bff\nf8 ebbab\n9a 29d886\nf0 eba54\n92 29d72f\n37 1d9a71\n52 1b9009\n56 1b8fd8\n5 19b8a2\n4a 2661d\n10 19c471\n1b 43aae\n52 1b8d5b\n75 633f9\n56 1b8d2a\n18 19c31a\n10 19c1c3\n1b 43800\ndd e7a42\n98 d31c1\na7 299ea9\n7b 1ee6e8\n8 1cbd79\n76 1b4e31\n25 1976fb\ndc 2b20f3\nfe 125365\n14 1c54\n53 1b0cba\n70 1b4e07\n34 198299\ncc 27831c\nd3 e7b61\nfd 2b629b\nb4 2a2bc2\n7a 1b4d0b\n8e d18e4\n5b 1b0b63\n68 294ef\n78 1b4cb0\n38 198173\n30 19801c\n3b 3f659\na 976e\ncc 27806e\nd3 e78b3\nb8 cf01a\nb0 ceec3\n7e 1bbdd6\n92 10caa3\ned 285a74\ne1 f243c\n9e 10462d\ned e9fc6\n16 1940f9\n96 10ca72\n16 4397b\na8 d5753\n9 3ab4a\n6d 1f4b2d\nd8 efdb4\n95 10ca78\ne7 2b6a9c\n65 1f49d6\nd0 efc5d\n1e 43824\n1a 437f3\n58 26c5b\n67 1ed943\nc2 2b1601\n50 26b04\naf 108124\nb2 1088fc\nb6 1088cb\n3e 19f1d5\n97 104723\n37 1d9d81\ndb 2bb6da\n36 3f7d4\nde e67ea\nf6 2b76aa\n39 1d1b50\n55 1ea94\nb9 2685b1\n51 1ea63\nba 1087a5\nbe 108774\n2d c680\nb2 10864e\n9b 1045fd\nb8 10874a\n9f 1045cc\n69 5bb92\nfb 27de0f\n88 25b4a0\n97 104475\nf8 ebc0d\nfc ebbdc\n9e 29d8b7\n37 1d9ad3\n3a 3f64c\n6a 1bc952\n32 3f4f5\nc 1cbb08\n1e 1d47c0\n21 1d1036\na3 267af2\n70 2295d\n12 1d4638\neb 12ccd0\ne3 12cb79\nbb 2a1a2e\nd2 eff12\nbf 2a19fd\nd6 efee1\n85 d27ab\nd5 e7bfb\n90 d337a\n94 d3349\n3b 1d88d5\n52 26db9\n1 9683\n14 a252\n55 1eaa2\n10 a221\nb5 2696df\nd2 efc64\ndd e7aa4\n98 d3223\na7 299f0b\n9c d31f2\nc 1cbdaa\nd5 e794d\nbc 268573\n90 d30cc\n5e 26c93\n52 26b0b\n5d 1e94b\n18 a0ca\n27 1d0db2\n82 294a70\n55 1e7f4\nf2 ebd6b\n99 29eb32\nf6 ebd3a\nbb 29ac4e\nd3 e7bc3\nf0 ebd10\nd7 e7b92\n95 caffa\n3b 1d1b57\n72 22c12\n4c 1af225\n21 54dc\n57 1ea9b\n53 1ea6a\nd9 281fb1\n34 60ab\n1 15e1\nca 280392\n30 607a\n15 1f03\nfa ebc14\nf2 ebabd\nb8 cf07c\ndc f0031\nb0 cef25\n53 1e90c6\n40 1b83ff\n9d caea3\n7e 22aec\n23 1d103d\n72 22964\n4c 1aef77\nfe ebeff\n5f 1e944\nf6 ebda8\n57 1e7ed\nf2 ebd77\n53 1e7bc\n1 1333\n30 5dcc\nd5 281e29\na3 10ffe8\nce 2b9d31\nd4 efc3a\n44 1e87f2\n4c 1aef79\n94 d30a9\n4 1cbc61\nf5 2be430\nc 1923e8\n52 1e8e6d\nd7 129604\n4 192291\ndc e67e3\n2d 19fb9e\n21 c566\n1 42fef\nc6 11f444\nd6 efc33\n46 1e87eb\n4c 1aef6b\n52 1ea12f\nb0 cf1e1\n46 1aee1b\n43 1f1ac9\n9 42efa\n84 1028ac\n1f 3a221\n94 d309b\n4 1cbc53\nc 1923da\n2f 19fb97\n23 c55f\ndc e67d5\n81 294d26\n5b 26f1d\n59 26f16\n9 9a26\ncc e5e74\ne 1d5111\n2 41ad9\na6 2a11f2\nd5 efee9\nd0 efc09\n67 1bb5bf\ncd ef6df\n90 d3078\n55 1f2452\n27 19ea2e\n8d d2b4e\n22 54d2\n0 192260\n85 d29f7\n63 2a66e\n34 1d19c7\n8e 10c01a\n37 19f07d\n6 19bae6\n5f 26ca0\n15 19af41\n30 1d174a\n34 1d1719\n31 1d19f9\nb5 261401\n14 1cd820\n96 2642dc\n32 196d5f\n31 1d174b\n35 1d171a\n97 d1e41\n90 2953d8\n86 264c3f\nb7 2681d6\n39 d2c0\n27 1cfe00\n18 9118\n23 4719d\n55 1b8fde\n74 1b4e36\n13 19c477\n7a 22d77\nc6 2b15d0\n32 1982cf\n8a 10c297\n36 19829e\na5 d58cc\nce ef489\nf0 ebd0e\na1 107fa3\n74 1b4b88\n59 25cb8\na0 107c82\n85 d1479\n1b 19c320\n38 1a046d\nd9 efd5f\n29 587f\n32 46891\n3e 1d9ec9\n0 1d523e\n49 1e8917\nf8 ebbb7\n71 1b4e68\n9 39b36\n12 194128\n16 1940f7\n21 471f8\nd0 e7b67\n15 19c4a1\n92 cafcf\na5 26787c\nd8 27899e\n50 1b0a12\n58 1d969\nf7 eba99\n84 c912a\n57 1b0c97\n1a 193fd1\n3d 3e66f\nf9 ebbb8\nd6 ee97f\nf1 eba61\n63 1f5c70\n29 470a1\nd8 e7a10\n6f 1b33c6\n21 46f4a\nd0 e78b9\n67 1b326f\nb3 ceec9\n15 19c1f3\n92 cad21\n67 1ed9a5\n15 43981\nd6 11fda5\n91 10c7fb\n15 436d3\nb5 1088d1\n5b 1b0b71\n68 294fd\na3 268db4\n36 196d90\n75 1b4e8b\n21 1cfdd8\nd1 2ba2b8\ndc 280f69\nb1 108654\nbc cf305\nd5 2ba287\nb5 108623\na9 25f8f4\n97 29691f\nf4 ebd3f\n93 d3380\nd0 2ba565\n90 25bf66\nb2 cf1d8\n55 26df0\n51 26dbf\nd 1cbafb\n10 192e0d\n32 607f\n21 196408\nfa 124072\ndd efd90\n2d 58b0\n4 1d526f\n4d 1e8948\n73 5c335\n9b d3229\nb8 d7376\n93 d30d2\n74 1b4e8a\n20 1cfdd7\n75 1f65f9\nd0 2ba2b7\n59 26c68\n55 26b42\n51 26b11\nfb eac0d\n13 9f79\nf1 284d3f\n50 1f115e\n1c 192ce7\n3e 5f59\n63 2908e\n6f 1bc6c6\nca 280384\n7f 1bc013\nd0 e7bc9\nd4 e7b98\n92 cb031\n96 cb000\n4e 1b725c\n71 22c18\nfc f2cca\n4a 1b722b\n35 197048\nc6 11f692\n54 1eaa1\n30 1d8a16\n16 1f09\n69 1b43b2\nf9 ebc1a\n4c 26337\n1e 1d49fe\nd6 ee9e1\nfd ebbe9\n61 1b3247\n44 261e0\n16 1d48a7\n39 1d1ae0\nd0 efeb7\n5a 1af8a0\n23 1966af\nd3 280b8f\nb3 cef2b\naf 106bb2\nd7 280b5e\n92 d331f\n57 1f26f9\n9a caeda\n92 cad83\n27 45c60\n74 1ee2aa\nb5 2a2e71\n8e 102a0a\n96 cad52\n7d 22af2\n79 22ac1\n56 25888\n75 2299b\n71 2296a\n5c 1e94a\ne5 12bbf1\n58 1e919\nd3 e68ad\ndf 279ee5\ned 2bf1f8\ne1 12bbc0\n54 1e7f3\n30 1d8768\n3f 5f5a\n7c 1ed13f\n9a c9c18\n37 5e03\n74 1ecfe8\nc4 2b9c37\n33 5dd2\n1e 1db2\na7 10f059\n16 1c5b\n12 1c2a\n5e 1af871\n52 1af74b\nd7 efee2\nd5 efedb\ncb ef709\nd2 efc02\n42 1e87ba\n4a 1aef41\ncf ef6d8\n4c 1aef09\na5 2a11de\nce 2bad9b\nc2 127763\n67 63aa5\n8 193679\n42 1aedea\nc7 ef581\nb0 cf17f\n46 1aedb9\n9 42e98\n27 3ebc4\ndc 2ba68d\n18 192d0a\n25 3ebbd\n9d d34a1\n86 102851\n1c 192cd9\n84 10284a\nd7 e78f2\n2 1cbc29\n57 1f244b\nd5 e78eb\n90 d306a\n73 1ee591\n0 1cbc22\n8 1923a9\n7b 1b4d18\n8d d2b40\nc 192378\nda 2789a5\nc4 2b9bd5\n87 d29f0\n0 192252\n73 1b4bc1\nb 3988f\n4 192221\n77 1b4b90\nd2 27884e\n13 1d5b99\n17 1d5b68\n36 1d19c0\n16 194159\n37 1d9d1d\n92 29d9db\n1b 1d5a42\n38 1d9b8f\n13 1d58eb\n17 19af3a\n32 1d1743\n7b 1b4fc6\na7 260787\n8 192657\n0 192500\nb 39b3d\n73 1b4e6f\nd3 2bb831\n8f 294e45\n10 1cd851\n37 1a064f\nd9 efd51\n29 5871\n16 19c447\ned f2314\n9e 10c97b\nf8 ebba9\n9a 29d884\n33 1d19f2\n37 1d19c1\n92 29567f\n12 1cd84a\n61 1b31e3\n22 1d7e7e\nb7 2613fa\n16 1cd819\n71 1b4e5a\n9 39b28\n17 19415a\n34 1982a7\nf1 ebd01\n58 26ccb\n93 29d9dc\n17 1d5b76\n16 1d4915\n33 1d1744\n37 1d1713\n92 2953d1\n1a 1cd6f3\n69 1b308c\n12 1cd59c\n61 1b2f35\n3a 623a\nb7 26114c\n16 1cd56b\n11 1cd852\n13 9cb\n1f 194003\na4 107cc1\n30 4b18\n3c 198150\nd6 ee971\n9b 29d885\n17 193eac\n34 197ff9\nd4 eec24\nf1 eba53\n93 29d72e\n17 1d58c8\n10 9f81\n72 1b4e60\n25 1976f9\n11 19c470\nfe 12404f\n15 19c43f\n78 22d70\n5b 1ec23\n30 1982c8\n34 198297\n72 1b4bb2\n12 1cd858\n1e 1d475c\n19 19c319\n16 1d4605\n11 19c1c2\n38 198171\n12 1cc2dc\n97 10ca73\n9 1cbd78\n73 1b4e61\nb 39b2f\n90 25bf56\n10 194121\n20 19e755\n23 471f1\n55 1b9032\n1 1d3f7f\nd2 e7b60\ne5 28440d\n90 cafc8\n4a 265bb\n18 193fca\n10 193e73\n23 46f43\n1 1d3cd1\n55 1b8d84\nd2 e78b2\n96 d1de0\n23 1d90d1\n98 cae71\n90 cad1a\n36 1d1776\n93 10caa2\n90 295688\n97 d20f1\nb2 1088fa\n74 1b4bde\nc 398ac\n7f 5c21b\n96 1044e6\nbf cf0b3\nfc 2b6298\nbb cf082\n16 3b38d\n3 1d3f78\n93 10c7f4\n9e d34a5\na9 d5752\nd9 efdb3\n21 1963fa\nbc d60e5\n1d 3b4dc\n14 1cc2b2\n36 3f524\n59 26c5a\na3 ce568\nb3 1088fb\nb7 1088ca\ne 3ae2f\n96 104722\n4c 1e297\n41 575e6\n28 1d820c\n37 3f7d3\n6c 1bc730\n60 290f8\ne2 12ce26\nbf 108773\n23 1cfdd1\nd3 2ba2b1\n8d 26382c\nde 280f62\n96 10b81e\nb3 10864d\n27 1cfda0\nd7 2ba280\nb7 10861c\n18 3a4ec\n92 10ca41\n9a 1045fc\ne9 e9f95\n12 1940c8\n53 1b7d48\nf8 12c3c9\n9e 1045cb\n96 104474\nd6 ee9d3\n9 148a\n54 57f17\n61 1b3239\n38 19f20d\n65 1b3208\n91 10475b\nf5 2be73e\n97 29d75f\n3f 3f67c\n9a 10333a\n12 192e06\n37 3f525\n16 426c5\n33 3f4f4\nea 12cccf\n58 1e90b\ne2 12cb78\nd7 279d80\nb3 1085eb\n96 10b7bc\n91 d3379\nfe 2b629f\nb 1d533d\n53 26db8\n2c 1cfc41\nfa 2b626e\nee 2847aa\n21 54da\n15 a251\nbc 299707\nf8 f3f07\n68 1ecabf\n99 d3222\n9d d31f1\nd 1cbda9\n96 10b50e\n1 19a84f\n91 d30cb\n1a 3b7c1\na4 298bed\n34 5dfb\n4e 1e29e\n43 575ed\n2a 1d8213\n90 cb02a\nc 1706\n1 3aa55\n94 caff9\n2b 1d1196\n73 22c11\n6e 1bc737\n62 290ff\na7 106a5d\n18 19b002\n2c 19fb9f\n20 c567\n14 1f02\n10 1ed1\n56 57f1e\n67 1b320f\n93 104762\nf7 2be745\n58 1d95b\nd2 efeb0\n81 d277a\n42 1af098\n46 1af067\n9 43146\nf5 2bfa02\nce 11f59b\nf3 27cca4\nb5 ceef3\nf4 f2e21\n42 1b7382\nd5 e7b99\n90 d3318\n23 1d9133\n98 caed3\n90 cad7c\n1a 19affd\n77 1b4e3e\n94 cad4b\nb 431a3\n1b 3b7c2\n7f 1f57a5\n13 3b66b\n77 1f564e\n16 8ce9\n18 1d7a\nad 2a2659\na1 10f021\n10 1c23\nec 12ba99\n97 25d1ef\n83 10c151\n84 294cf6\na6 107f68\n16 1d4917\n33 1d1746\na3 107faa\n59 6069c\nc3 ef5c0\n3 4305a\n43 264c9\n11 1d58e6\n81 10c14a\n50 2584e\n5c 1b8e86\n83 10c143\na4 107f61\na3 107f9c\na7 107f79\n5d 6066b\na3 107f48\n59 6063a\ne3 eb3b7\n7 43029\ne9 2bdf03\n23 3ee51\nd3 129331\n8c 10bfbf\n63 222c0\n21 3eb9a\n6c 22432\nef f25bb\nbe 1087e2\n2e 1cff66\nde 2ba446\n1f 1cc71f\n97 10ca7f\n12 1cc2e8\nf 1cbdbe\n52 1f1469\nfe ebc51\n6e 2a4d9\n7e 29b78\n23 1d80c9\n1a 4284f\n6e 29217\na 41eee\n8f 103f89\nae 106e6f\n9f 103628\n8f 102cc7\n3a 3e6a8\nff f2cde\nce ef747\n94 29665b\n7b 29bb6\n4a 2661f\n94 295399\n1a 3b7b3\n97 10c825\n69 2114c\n5a 25cbe\n86 d147f\n4a 2535d\n8 87c5\n4b 1e2d0\n85 29d043\nbc ce0a7\nac cd746\n9d c9eff\n8d c959e\n7a 21b17\na6 cd2d8\n5c 259ca\n1 1d3f1b\n20 c559\n2c 19fb91\n9d 295561\n5b 1d96f\n87 c9130\n76 229a3\n4b 1d00e\n34 5e0b\n1e 3b790\na5 107f72\na7 107f6b\na1 107f41\na3 107f3a\n5 43022\n7 4301b\n27 3ee74\nd7 129354\ncb ef469\nb 42f03\n4b 26372\n89 10bff3\nda 129729\n8b 10bfec\n84 10be5a\n81 10be9c\n83 10be95\na9 107e4c\nfa 125582\na4 107cb3\n78 5c4f2\na1 107cf5\n8f ca859\nac ce9a6\nf2 12542b\nae ce99f\n85 10aeb9\na5 106d12\naf 107e22\na7 107ccb\n5d 603bd\n38 196e4f\nef eb291\n3 42d4a\n2f 3ed2b\n27 3ebd4\n9e c9c4b\nc8 e73b1\nd4 e794c\n44 1af0d0\n1f 1d4a6d\n2f 1cff65\n1e 1cc71e\ne 1cbdbd\n5f 1b7edc\n4f 1b757b\n1d 19b344\n47 5fbba\n3c 19719c\n66 5ba12\n2c 19683b\n72 1ed2c0\n85 d2a4b\n3a 19f4b4\n8b 10bf98\n2a 19eb53\nf9 286158\n58 1f2577\n6f 1b33d4\na8 d6a77\n14 19aedc\n4e 1e290\n60 5b72c\n43 575df\n5e 1afb8d\n19 2037\n4e 1af22c\n1b 1d59e0\n67 5ba13\nac 1101ca\n7 19bb49\n35 ce98\n2d 19683c\n73 1ed2c1\n8f 10c2d7\nd6 2820db\nb 431af\n32 5e33\n10 192bc1\n52 1b8fb3\n8f 10b015\n1f 3a213\nbe 1077ce\n3a 3e6a6\n50 26b10\nd7 2820dc\n8e 103f88\n53 1b8fb4\nbf 1077cf\n9e 103627\n8e 102cc6\n53 1b7cf2\nca 2bb024\ncf ef746\n4b 2661e\n6a 22476\n4c 26329\n91 2652c9\ndf eede5\nd8 efd52\ne7 2b6a3a\n7a 21b15\n5c 259c8\na6 cd2d6\n6a 211b4\n4c 25067\nce e73f7\naa 2a136c\nba d63bd\n38 3f901\n1b 3b7b4\n7f 1f5797\n90 25cf7a\nde e6a96\nf7 27cc75\nbd ce0a6\n86 c9193\n77 5c364\n5a 1d96e\n86 c912f\n4a 1d00d\nc8 2818fb\n8d 10bfc2\n8f 10bfbb\n89 10bf91\n7d 1b3d2e\n8b 10bf8a\n7f 1b3d27\nfb ebe5f\n87 10be64\nb0 cf1d1\n5b 606a3\n83 10be33\n77 1b3bd0\nad 107e1b\naf 107e14\na9 107dea\na5 107cc4\na7 107cbd\nce 2b19d5\nd 42ecb\ne6 124af9\nc4 2b1887\n9 42e9a\nfd 2bfb59\nf1 12c521\n5 42d74\n21 1d7e76\n2d 3ed24\n8a 1029db\n29 3ecf3\n25 3ebcd\n27 3ebc6\n9f 29ee28\n93 10b7f0\n26 3dbbd\nbf 29ac81\nb3 107649\n95 10b4fc\n8e 102c66\nf6 27df98\n9b c9bb7\ndf 282297\nd3 eec5f\n66 2102c\nc5 ee00a\na4 10f05f\n5 41aa4\ne4 f24ce\n45 24f13\n9d 29ee21\n91 10b7e9\n24 3dbb6\n9f 29ee1a\n93 10b7e2\n5e 1b7bbd\nbd 29ac7a\nb1 107642\n2f 1d7f97\nbf 29ac73\nb3 10763b\n7e 1b3a16\nb 42ea1\nef 2beeef\ne3 12b8b7\n56 1b8fe6\nb5 10f95d\nde 12951a\nd7 280b6e\n9d d1f9f\nd2 2bb822\nb7 107618\nbf 29ac1f\nb3 1075e7\n62 1f4caf\na7 106cb7\nff 27e08e\nf3 eaa56\n37 3e521\n3f 1d1b28\n33 3e4f0\n27 3dbc0\nd7 1280a0\n2f 1d11c7\n77 22c42\n23 3db8f\ndf 2bb6a7\nd3 12806f\na9 d6a68\n4 42d73\nea 124c91\n16 1cd889\neb 12cfd2\ned f25b4\n2c 1cff5f\nbc d73a7\n1d 1cc718\nd 1cbdb7\n9d d31ff\n33 3f7a4\nf8 f2ee5\ne8 f2584\ne 19a9dd\n86 295d0f\n7c 1b3d2f\ne4 1238a0\nfc ebc4a\nf7 2be437\nbd 10f868\n8c 10c2d1\n67 1b2fc3\n30 3f7a8\n15 8f9f\n7c 29b71\n18 42848\nb7 110978\n8d 103f82\n28 3f002\n50 1b8fae\nac 106e68\n9d 103621\n8d 102cc0\n37 1d19cf\n92 29568d\n38 3e6a1\nfd f2cd7\ncc ef740\nbf d613f\nb8 2996d6\n8e d2ba8\n79 29baf\n48 26618\n3b d017\na 9a80\n8e d18e6\n57 1b8fe5\n58 25cb7\n84 d1478\nf7 f3de7\n67 1ec99f\na 87be\nec eb599\nbe ce0a0\nae cd73f\ndc 121163\n9f c9ef8\nbd 29a9cc\nb1 107394\nbc ce045\n8f c9597\nac cd6e4\n36 5e04\n12 1c8c\nde 1211ce\nb5 107611\nb7 10760a\n8f 10ad5b\nbf 29ac11\nb3 1075d9\na7 106ca9\n5 41d60\nf5 27df84\n7 41d59\n35 3e51a\n12 193e7c\n27 3dbb2\ndd 2bb6a0\nd1 128068\nb8 2695b8\n27 471ce\na7 cd339\n1e 1d4a60\ne 1d40ff\na6 25f7d6\n67 1b4231\nc2 277eef\n4c 1b7567\nc4 2b2899\nc 3ae2a\n3d 3e3c1\nae 267c7b\n3e 1d08b9\n90 d3076\nd9 e674f\n36 1982a0\n2e 1cff58\nbe d73a0\nde 2ba438\n1f 1cc711\n3c 1d085e\n97 10ca71\n12 1cc2da\nf 1cbdb0\n9f d31f8\n46 261e7\nac 10eea6\n8f 10ad59\n5e 1b7ecf\nd6 2b3201\nc6 2b28a0\n8b 10bffa\n1c 19b337\n84 10aea8\n94 296669\nc 19a9d6\n84 295d08\n61 1ed97d\n7e 1b3d28\ne6 123899\nae 10eead\n4 1ccc67\n7c 1b3ccd\ne4 12383e\n71 1ed01c\n52 1af749\n6c 1b336c\n61 1ec6bb\n1d 192fe8\n95 d3348\n10 192bb1\nbc 108a8b\na 1ccfec\nd 192687\n79 29ba1\n48 2660a\nfd f2f25\n9e 10b969\n48 1b01a8\n8e 10b008\n1a 42841\na 41ee0\n58 25ca9\n67 1ec991\n8f 10bfc9\nd0 120fdb\n8f 103f7b\nac 1080c8\nec eb58b\ncd e73e3\n68 22463\nae 106e61\n9f 10361a\nbc 107767\n8f 102cb9\nac 106e06\nec ea2c9\n11 1d58f2\n1b 3a4f2\n38 3e63f\nd8 e7a72\n63 1f5cd2\n36 3f526\nb 39b91\nd8 1281be\nd2 278aee\n78 21b02\n74 2298e\n16 1d4669\n39 1d18a2\n0 9920\n49 1cff9\n7b 29ba8\n4a 26611\nff f2f2c\n73 1bbbdf\n39 d010\n8 9a79\nce ee477\n7d 5af52\n4c 579bb\n9c d2240\n4a 2534f\nec eb537\ne1 124886\nac ce9fa\nee ea2d0\nfc eabd6\nfd 2b755d\nf1 123f25\n86 d2a4f\ncf e6128\nec ea275\ned 2b6bfc\ne1 1235c4\n3a 3f968\nac cd738\n9d c9ef1\n8d c9590\n78 21aae\n34 5dfd\n9 468\n9b 10b699\n9f 29eb7a\n93 10b542\nbb 1074f2\nbf 29a9d3\nb3 10739b\n79 22b15\ndb eeb08\n6e 20ed5\na1 d590b\nad 268f43\nf3 2bf9c8\n84 d1484\nd9 282251\n3a 3e3ec\n1b 425a2\n4b 250b0\n63 1f5f70\n99 10b692\n2c 3da5f\n9b 10b68b\n9d 29eb73\n91 10b53b\n24 3d908\n9f 29eb6c\n28 19fdba\n93 10b534\n9e d21e5\n35 196d38\ndd efde4\n89 10ad31\nda 128467\n7f 1ee719\n73 5b0e1\n8b 10ad2a\n8e d1884\n53 1e82c\n85 d29e9\n3a 19f452\nb9 1074eb\nbb 1074e4\nbf 29a9c5\nb3 10738d\nbe ce03e\nae cd6dd\n19 4259b\n9 41c3a\nc6 e5fe0\n5a 5f370\nb 41c33\nf 1d5114\n3 41adc\ne 878d\n44 1b0074\n3c 4f4e\n56 1b8d38\n69 2a7b0\n3e 4f47\nd2 2bb574\n29 3da93\nc8 2bad71\n7a 5b1c9\n9f 10b668\n9b 10b637\n97 10b511\n9f 29eb18\n93 10b4e0\nbf 1074c1\na 3aba6\nbb 107490\n0 3a9f2\na1 ce5d3\n6a 1f4b58\nb7 10736a\n2 3aa4f\naf 106b60\n3a 196e56\nb8 110d38\na7 106a09\n5d 5f0fb\nef e9fcf\nf8 f41a7\n1f 42571\n1b 42540\n17 4241a\n77 1bcf42\n37 3e273\n2f 3da69\n27 3d912\n30 47aea\n8c 10c021\n63 22322\nc6 2babf0\n9a 103594\n1d 1d4a66\n33 47af2\n2c 1cff5d\n2d 1cff5e\nbd d73a6\n5b 1b8ebf\nc 1cbdb6\n32 3f7a3\n9c d31fe\n73 2af61\n7c 1b3d2d\n88 10bf90\n6c 1b33cc\n3e 197195\n38 19f4ad\n28 19eb4c\n6d 1b33cd\n4c 1e289\ne5 2b6cf1\n31 5e3d\n3f 197196\n6d 2a4d1\nc8 ee18f\n97 cad53\nd4 2b1f38\n50 1b8fac\nc7 2b9bdd\n8d 10b00e\nd4 280e12\ne7 2b6cf8\nad 108129\n15 c4f\n63 1edbd0\n29 3f001\n51 1b8fad\nf7 2b6397\nbd 1077c8\n4b 1d000\n2 9927\ne7 2b5a36\nad 106e67\nc6 2b188e\n8c 102cbf\n63 1ec90e\n29 3dd3f\n2b 1d94e2\n51 1b7ceb\nc8 2bb01d\n49 26617\nb6 10f9b9\n43 1b00ab\n60 1b41f8\n68 2246f\n48 1f0c64\na 193610\nb9 110a89\ndd e7a34\nbe ce09e\n59 25cb6\n85 d1477\n78 21b0e\n68 211ad\n1b 911e\n38 d26b\n3a 4f76\na8 d6d17\ned eb598\ncc e73f0\na8 2a1365\n8e ca858\na0 107cf4\nb8 d63b6\n19 3b7ad\n15 9fa3\nfd eac37\na8 d5a55\n9 3ae4c\n9b 25d0c9\ncc e612e\nce 2b9ad7\n99 10c8e2\nbf ce09f\n9e c9ef7\n8e c9596\n58 1d967\n48 1d006\nda 282255\nca 2818f4\n85 29cd89\n9d 10b661\n99 10b630\n9b 10b629\n95 10b50a\n9f 29eb0a\n93 10b4d2\n8d 10ad00\n8f 10acf9\ndd efd82\n89 10accf\n8b 10acc8\n87 10aba2\n57 1e7fb\nbd 1074ba\n8 3ab9f\n53 1e7ca\nb9 107489\nb5 107363\n0 3aa48\nb7 10735c\nbf 29a963\nb3 10732b\na5 106a02\na7 1069fb\n1d 4256a\n19 42539\na5 d6950\n15 42413\n6a 20e98\n3b 1d1b49\nd 41c09\nf2 eba69\n75 1bcf3b\n50 1ea136\n73 1bcf03\n3f 3e3bc\n39 3e392\n35 3e26c\n37 3e265\n2d 3da62\n2f 3da5b\n25 3d90b\n27 3d904\n2e 1cff56\n7c 1b3d1f\n6c 1b33be\na9 1103e4\n3c 3e3c0\neb 12ba6e\n2f 1cff57\n98 1045a1\ne 1cbdaf\n9e d31f7\n7d 1b3d20\n89 10bf83\n34 1d1a2b\n6d 1b33bf\n31 5e2f\n7b 2186a\n16 1d5bd9\n6b 20f09\n2c 19682d\ne9 f3853\n7c 2182f\n60 1ed97c\n73 1b4bb3\n56 1b7d84\n19 2029\n4e 1af21e\n23 54d5\n60 1ec6ba\n67 1b2f6f\ne5 12ce51\n3d 19718f\n67 5ba05\n2d 19682e\nc 192686\n8f 10c2c9\nac 110416\nec eb589\n10 192bb3\n1b 3a1f0\n95 d334a\n1 19b86f\n52 1b8fa5\nbe 1077c0\ncf e73ea\n95 264036\n53 60290\nae 106e5f\n85 2636d5\nd6 280e0b\ndd eedd0\nfc eac28\na3 1069ca\naf 29a002\nec ea2c7\n3a 3e698\n5c 1afb78\n2a 3dd37\n1 19a5ad\n52 1b7ce3\n59 25ca8\n78 21b00\n68 2119f\nd7 2820ce\n8e 103f7a\n17 c48\ned eb58a\ncc e73e2\nef 285a7b\ne3 f2443\n53 1b8fa6\n77 646b4\na 3ae52\nbf 10776d\nb4 260130\n69 22462\n56 1b09ea\n48 1e2ba\nfd eabd5\naf 106e60\nd7 280e0c\n9e 103619\n8e 102cb8\nfd eac29\ned ea2c8\nd2 2b1f62\n77 1ee2a4\n8c 25b4dd\n3b 3e699\nc4 ef32d\nda 2bb977\n53 1b7ce4\nca 2bb016\n1a 3a4f1\na 39b90\n79 21b01\n30 3f80c\n48 1cff8\n12 439ba\n7a 21b07\n6a 211a6\ne0 124885\nef 28456d\n4e 1f098c\n49 1b7289\nff eabdc\nbd ce044\nb6 10893b\nff ebc44\nab 106b91\n94 2652f9\n90 25cf6c\nde e6a88\nec 2b6bfb\ne0 1235c3\nc6 27815e\n8c c958f\n32 3f813\n5a 1d960\n4a 1cfff\nc8 2818ed\n94 25bc79\n1b 3a490\nbc 2999a9\nb7 10f956\nd 41bf9\ne6 2bdd83\nf6 12545c\nab ce9cf\n37 4685f\n66 1f4c8c\nbc 299947\n21 1d90d8\n6f 62bf4\n1b 425a0\na3 d68b6\n14 19af32\n7e 1bbd66\n1a 1d83\n1a 1d4a3d\n78 22d62\n3b 1d183b\n1e 1d4a0c\n56 25b26\nf0 27cc9e\n2b 1d0eda\ne 1d40ab\nac 1101bc\n35 ce8a\na 850e\n15 192be3\nfb f2eeb\n3a 1d0896\n1c 1d4749\n3e 1d0865\neb f258a\nba 1087b1\nc 1d3de8\nda 2ba415\n1f 1cc6bd\nf 1cbd5c\nd0 eff17\nf4 285fc1\ne6 ea123\n8b 102c34\na3 10ffd8\nd5 281e19\nf3 27df66\n80 25b5f7\n7b 1bbda4\n4a 1b880d\n5b 25a0f\n5f 259de\n43 1f1b2b\nfb f2c4b\nca ef6b4\n53 1f11ca\n4a 1b754b\n1c 3b4db\nbd cf0bc\n87 29e060\n43 1f0869\nd5 e7b8b\n78 22d60\n1a 1d4a3b\n88 d185a\n4d 1f0c34\n55 1e7e6\nf4 ebda1\n63 1ed984\n4b 1b04be\n3f 1d08c6\ncb e7365\n7e 1b3cd4\ne6 123845\n73 1ed023\nfa ebc20\n2c 55f3\n6a 1b33a4\n4c 1b7257\n5b 1afb5d\n87 25b31e\n5f 1afb2c\n76 1b4b91\nd3 efebd\n8f 294bf9\n4b 1af1fc\n4f 1af1cb\ncb e60a3\ned eb2dc\nb8 ce014\n9b c9ec7\na8 cd6b3\n6d 1eca8d\n89 c950b\n9e 29db55\ne4 f24c0\nf4 27dc81\nd7 279b34\nee f35d0\nbf 10f80d\n8e 10c276\n94 d1e3b\n3f 46716\nf0 ebd72\ne 4317f\n29 1d827b\n1c 19c34b\n10 8d13\nfe f2c6f\n9a 10b946\n3f 47c88\nee f230e\n89 d2b71\n1a 427ed\n9 9a7a\ne5 2bed8f\n76 1bd1ef\n8f 103f27\n95 c9aec\nde 1294b8\n3c 1981a4\n30 4b6c\nda 129487\nba 10779f\n9c 10b652\n84 c9448\n18 427d8\n8f 102c65\n70 229cb\n9a 264156\nf7 27df97\n84 25b628\nad 110415\n28 1cfc7e\n3a 3e646\nc4 ee077\nad 267c75\nc 1d4094\nfb f2cad\nca ef716\nff f2c7c\nce ef6e5\nc3 128a34\n4f 1af21f\n6 19bb46\n7f 29b85\n4e 265ee\n43 5f93d\n7b 29b54\n4a 265bd\ndd 1211c6\n78 22dc2\nda eedb5\n7f 2b0f7\n28 3dcde\n7c 22d91\n3b 1d189b\n1e 1d4a6c\n66 5a4a0\na5 2a250e\ndf 2bb70b\nd3 1280d3\n68 22461\nca ee454\n6f 2a796\n98 d221d\na7 298f05\n3d e55f\n88 d18bc\n2d dbfe\n73 64683\n16 19b1e5\na1 d691f\n12 19b1b4\n5a 25c5c\n6 19a884\n2 19a853\n4a 252fb\n8 8763\nee eb53e\ne3 12488d\ncb e73c7\n12 9f7a\nfa eac0e\ndc eeac1\nfe eabdd\nff 2b7564\nf3 123f2c\ndb e6a66\nf6 eba9a\ncb e6105\n99 c9ece\n4c 1b7565\nb4 cef02\n97 cadb5\nd4 2b1f9a\n8d c953c\nd 445\n9 414\n1e 1d4a5e\na7 d6b97\nc7 2b9c2f\ne4 2bdd7c\na9 ce9c8\n86 d178f\n47 1f0b38\n64 1f4c85\nef 2b5be1\na6 2a2508\n61 2a605\n82 103b54\nf8 2b6267\nce ef739\nff f2cd0\nd8 11fed2\n7d 5c214\ne2 f2496\nee 285ace\ndc 2b33b3\n8f 10ad69\nd0 11fd7b\n75 5c0bd\ne6 285977\n35 60aa\na 3abb4\n2 3aa5d\na4 298e8f\n88 103ca4\nec 2bdc87\n69 2a74e\n3c 1d8b9e\n93 cafd0\nf7 284fb3\n8a 103c9d\n80 103b4d\n84 102b6a\n8e 103c7a\n86 103b23\nef 2b5b7f\na6 2a24a6\ne6 2bdad5\nc4 e5fd9\n9f 10b976\n60 5b78c\nce e70e9\n6 3aa2c\n41 26450\nef f236f\ne8 2b5906\n2 3a9fb\n66 1f49de\n46 1de9b\n8c 103c73\n88 103c42\ncf 2b9ad8\nec 2bdc25\nf4 f2b1f\n55 57f16\nf7 284d67\nc6 2817d0\nee 2bdc1e\n84 103b1c\n80 103aeb\nc7 2b9981\ne4 2bdace\n7b 2b128\nc 3ab7c\n8 3ab4b\n6c 1f4b2e\n74 29a28\n77 1bbc70\n46 1b86d9\na 3ab44\n4 3aa25\n0 3a9f4\n64 1f49d7\nd0 e791b\nb9 261519\n18 1cd938\n1b 1d4a3c\n56 25b28\n79 22d61\ncc 11f5a2\nd4 e78ea\n1f 1d4a0b\nbd 268328\n1c 1d4747\n66 222e2\n53 1b9016\n2 399e5\ne 1cd01d\na2 2a0f75\na0 260a60\n3f 1d0864\n2b 1cff34\nd 1d3de7\n66 21020\n2f 1cff03\n19 194279\n1e 1cc6bc\n64 2a637\nbb 10773c\nb0 2600ff\na0 25f79e\nf5 285fc0\n4b 1b880c\n63 1ed982\n5b 1b7eab\n87 26366c\n5f 1b7e7a\n1e d9e\n1f 1cd725\n13 3a0ed\n3c 1d1872\n30 3e23a\nb7 299806\n4b 1b754a\n4f 1b7519\n7e 1b3cd2\n85 10ae49\n6a 1b33a2\n4c 1b7255\n6e 1b3371\n63 1ec6c0\ndb 280fa4\n19 19b313\n43 5fb89\n1d 19b2e2\n38 19716b\n62 5b9e1\n3c 19713a\n28 19680a\n2d 45da2\n2c 1967d9\n21 1cfb28\n72 1ed25e\nb8 ce012\na8 cd6b1\n4a 1b04bd\n43 1e97db\ne2 2843e4\n7b 1b3d04\n2b 45d76\n5d 1b7bb7\na7 25f4c5\n6b 1b33a3\n4d 1b7256\n6f 1b3372\n5a 1afb5c\n86 25b31d\na8 d6a15\nc8 2b9aad\nc7 ee32f\ne2 124b38\n43 5757d\n5e 1afb2b\n53 1e8e7a\n4a 1af1fb\n4e 1af1ca\n43 1e8519\n63 5b9e2\nac 110168\n8f 10c01b\n7 19bae7\n73 1ed25f\n8 192663\nc 192632\n1 1cb981\nfb eabab\neb ea24a\nd9 e7d13\na9 cd6b2\n8f 10c275\na 1cbade\n95 d1e3a\nb4 cdc92\n3b 1d18ab\nf1 ebd71\nf 4317e\n65 29366\n86 1028b5\nb 4314d\n31 19f0a7\n0 19bb10\n36 5e02\n14 192b90\n9b 10cc09\ndc f00a1\nb7 260136\n8f 10afb3\n84 263976\nf7 2862e5\n1b 3a1e2\nba 10779d\nb9 cf329\n96 d20f0\n77 1bd1ee\n3e 3e675\n7f 1bd345\n73 29d0d\n32 5dd1\n10 192b5f\n2e 3dd14\n9b 10b947\n8d 10bfb2\n76 1b4bf3\n49 252e5\ne 398c1\nd3 eff1f\n5 98f2\n8e 103f26\n94 c9aeb\n24 19799a\n8a 102c95\n20 197969\nd0 281e49\nbb 10779e\n9d 10b651\n8d 10acf0\na1 2a0f6f\nf5 286022\naf 106e0c\na4 25f7cf\n99 cb182\nfd 285165\n3f 3e676\n34 197039\n3b 3e645\n30 197008\nc4 ef2d9\n2f 3dd15\n24 1966d8\nd4 280bb8\na 39b3c\n72 1b4e6e\ncc ef420\nee eb53c\ne3 12488b\n7b 1b4d1a\n8c ca5b3\nb5 29aab3\nd2 121038\n1 98cf\n4b 265bc\n6e 22445\n63 5b794\n6a 22414\n2c 45df\ndc eeabf\n25 3d8fd\ncc ee15e\nee ea27a\n8c 102a03\nc6 2b15d2\nbc ce043\nff 2863dc\nf3 f2da4\n9c c9c52\nb4 29ab12\n5b 25c5b\nd1 12002a\ndd 2b3662\na4 29a1b1\nde 2b33ae\nb5 2997f1\nd2 11fd76\n77 5c0b8\n66 1f4c7e\n4b 252fa\n30 197fc8\n7e 21ae4\n62 1edc31\n7a 21ab3\ndb eedb6\n6a 21152\n3c 4f4c\n20 1d1099\nd0 2bb579\n67 1bc5df\n2c 45eb\nc0 2bac18\n63 1bc5ae\n58 1ec1b\nca e73c6\nce e7395\nc3 1206e4\n4a 1e26d\ncd ee15f\naa d59fa\nb 3adf1\n6f 1f4dd4\nef ea27b\ndd e7d44\nad cd6e3\n3d 620f\n2d 58ae\n8c c953b\n3a d264\n26 1d814f\n63 1edc32\n22 1d811e\n6b 21153\n73 1ed021\n42 1e9a8a\n5a 1d90c\n0 1934c0\n4e 1cfdc\n4a 1cfab\n2d 45ec\nc1 2bac19\nc0 ee292\ncc 2818ca\n57 1b8d99\n1c da5\n31 1d0489\n0 1ccef2\n1d 1cd72c\n11 3a0f4\n27 19fcf0\nde 279f48\nd2 e6910\n77 22c52\n67 222f1\nbc cf04b\n9e 296acb\n92 103493\n37 3f7d5\n3c 5f54\n96 103470\n9e 296a77\n15 1d4911\n92 10343f\n86 102b0f\na4 107d17\nf2 1241c9\nfe 2b7801\ne4 eb186\n16 3a379\nbb ce06e\n6 39a18\n56 1d7e8\n46 1ce87\n94 103469\n96 103462\n9c 296a70\n90 103438\nb7 d6236\n84 102b08\n34 3f7d9\n86 102b01\nb9 ce067\n16 3a36b\na9 cd706\n6 39a0a\n7c 1bbd5f\n18 1d4a36\nb7 2a2b66\n39 1d1834\n1c 1d4a05\n6c 1bb3fe\n8 141b\na7 2a2205\nfb 125581\nf9 f2ee4\nbe 1074c0\n1f 19b33d\n3c 19f48a\n38 1d088f\n1d 1cc6b6\nd 1cbd55\n28 dbbe\n71 1b38fa\ne4 ea11c\n89 102c2d\n79 1bbd9d\n48 1b8806\n59 25a08\nf9 f2c44\nc8 ef6ad\ncf e7396\nbb d60ac\n8a d2b15\n48 1b7544\ne7 285674\n5f 1afb8e\nd7 efeee\n4f 1af22d\na 19a9ac\n94 d20e7\n1b 1d5d00\nd8 eed4c\ne7 2b5a34\nc8 ee3eb\ndf e6a35\n63 2a3b4\n5f 1f158e\ncf e60d4\n8a d1853\n4f 1f0c2d\n68 1b465f\n48 1e2ca\n79 21861\n49 1b04b7\n5a 1b7bfc\n8 16d7\n39 4c6e\n28 197a6c\n3d 1d08bf\nc9 e735e\nca 2b1a06\nec f2377\n8b ca7c6\nef 2847a9\n78 1b3cfe\na4 25f4bf\nf8 ebc19\n68 1b339d\nfc ebbe8\n85 25b317\n3a 197166\nce ef437\n7e 1b3a24\n85 10ab9b\n59 5f3da\n9a 10c94c\ne9 f22e5\n1b 192fbe\n38 19710b\n9e 10c91b\n1f 192f8d\n31 1d0429\n21 d828\ndc e7aa5\n80 d29c3\nc9 e609c\nbe d764e\nb3 11099d\nde 2ba6e6\n6f 1eca86\n9b c9e65\n28 472ee\nb 431a1\n8b c9504\nd6 121075\nfc f2c68\n98 10b93f\n3d 47c81\n74 1bd1e8\n52 1ead9\n1d 3b78a\nbc d6393\nbd 2a2d1a\nb1 10f6e2\nb8 107798\n56 1eaa8\n32 1d8a1d\n8d 102c5e\n2c 3dd0f\ndc 1281ef\nf9 f2ca6\nc8 ef70f\ncf e73f8\nbb d610e\n8a d2b77\n3 42d9e\n3b cfb5\na 9a1e\n9d 104627\nd8 eedae\ne7 2b5a96\n7d 2b0f0\nc8 ee44d\n6d 2a78f\ndf e6a97\n9a d2216\n5f 1f15f0\n7c 1f573d\n38 1d1aef\n3f e558\ncf e6136\n8a d18b5\n4f 1f0c8f\n6c 1f4ddc\n10 19b1ad\n58 25c55\n67 1ec93d\n1e 90ee\na 875c\n9d 103365\nc9 e73c0\na8 ce975\n8f ca7f7\na1 107c93\n2a 5877\nda efd57\n4a 1e890f\n6f 1bb6b2\nf4 eba93\nd7 e7946\n80 d2a25\nc9 e60fe\n9f c9e96\nbd 29a96a\nb1 107332\nb6 ceefb\nd6 2b1f93\n0 98cc\n49 1cfa5\n3e e557\n95 cb06a\n5e 1f15ef\n33 478a6\n2e 45e6\nde eeac6\nc2 2bac13\nc6 ef342\n79 1bbd8f\n48 1b87f8\n59 259fa\n48 26368\n1a 1d4a2f\na 1d40ce\nc6 ee080\n58 1b7e97\na2 25f7a5\nf7 285fc7\n48 1b7536\ne7 285666\n5 1ccc66\n9a 10b8d6\n8a 10af75\nf2 2862a7\nc8 ee3dd\n22 3db80\n54 1af9c1\n2e 1d11b8\nd2 128060\n7b 22dca\n10 19c47f\nde 2bb698\n11 1f42\nb 1cd041\n8 3adf9\n39 3e390\naa 267c4a\nae 267c19\n68 1b4651\n48 1e2bc\n79 21853\n49 1b04a9\n5a 1b7bee\nf5 f2dcc\n29 1d7fcd\n40 264b1\n8b 103ee8\nef 2bdecb\ne8 eb4f8\nc9 e7350\nce 128b59\n84 263920\n47 562fa\n32 19826f\n2a 1cff27\nba d736f\n73 63111\n42 5fb7a\n7f 1f6749\nda 2ba407\n2e 1cfef6\n1b 1cc6e0\n3f d296\n38 1d082d\n1f 1cc6af\nd7 e6692\n36 1d1714\n64 29367\n93 10ca40\nb 1cbd7f\n9b d31c7\n78 1b3cf0\nf8 ebc0b\n68 1b338f\n42 261b6\na8 10ee75\n8b 10ad28\n74 1b4b7c\n1c 1cc409\n3e 3f67b\nd1 efea8\n96 104484\n73 1b38f3\n8b 102c26\nf3 27df58\n9e caf0b\n96 d3350\ne8 ea236\n21 d81a\n81 295fe8\ndc e7a97\n7b 1bbd96\n4a 1b87ff\n78 29b4e\n5b 25a01\n39 19f1fe\n8 19bc67\nda 282263\nbd 268580\nda eeb05\nc 84d8\n7f 2ae47\ncd e738f\nb9 d60a5\n88 d2b0e\n5a 1b7e9e\n4a 1b753d\n18 19b306\n25 471b9\nb7 269436\n1c 19b2d5\n84 10ae46\n11 1d4624\nd5 efee7\n4 19bb4d\n4d 1af226\n9c 10c912\n73 22c13\n8 19a9a5\n7b 1bd314\na7 268ad5\nc 19a974\nda 280fa1\n1 1d3cc3\n89 d160f\nca ee3e4\n84 d29f4\ncd e60cd\nb7 1109ce\n88 d184c\nfb f41bb\n4b 1b04b0\n68 1b45fd\n43 1ce47\n4f 1b047f\n61 1ed91b\n72 1bbefc\n28 197ac0\nd8 281fa0\n3f 1d08b8\ncb e7357\n6f 2243a\n35 19f086\na0 10ed80\nac 2a23b8\n0 1ccc36\nb5 299551\n78 1b3c9c\n71 1ecfba\n38 19715f\ncc ef430\nab 107df1\n7c 1b3a1d\n3c 19712e\na4 106c9f\n31 1d047d\nce 280353\nb8 cf07a\n45 1cbd1\n28 1967fe\nd8 280cde\n19 192fb7\n1d 192f86\nbc 2a1a57\n1 19a5af\n23 d821\n83 295fef\n82 d29bc\ncb e6095\nac 2a10f6\na8 cd6a5\n3a 3f6bc\n89 c94fd\nf9 f2c98\nc8 ef701\n3b 466d7\na 43140\n79 29b3f\n48 265a8\n9e 10b907\n8e 10afa6\nf6 2862d8\nd8 eeda0\ne7 2b5a88\n7d 2b0e2\n1e 42810\n1a 427df\n76 1bd1e1\n72 1bd1b0\n48 252e6\nf5 f2e2e\ned 284566\n4c 1f0985\n8b 103f4a\na8 108097\n8f 103f19\ne8 eb55a\nec eb529\nc9 e73b2\n2a 4617\ncd e7381\n5 19b840\nda 129479\ndf 2bb949\nd3 128311\n75 29cd5\n96 103224\n1c 1cc6a9\n49 1e259\nba 107791\n24 1963d4\nb3 d7279\n91 264007\ndc 278c71\naa 106e30\na3 d6918\n81 2636a6\nae 106dff\na7 d68e7\n85 263675\n9b 1035e9\nb8 107736\n9f 1035b8\nb6 10861d\n8b 102c88\nfc ebe88\na8 106dd5\na3 298ed6\nf7 27df89\nf8 eabf9\n3f 1d8c16\nec ea267\nf4 eba85\n96 29d760\nb9 29a999\n80 d2a17\nc9 e60f0\n84 d29e6\ncd e60bf\n3a 3e638\n15 1d58c1\n6a 1b4346\n18 19c5d6\nda 1281b7\n23 d7bf\n1 19a54d\nc4 ee069\nc5 2b2b48\n65 2a37a\n9b 104849\n6c 21170\n16 a259\n68 2113f\n4 98ef\n4d 1cfc8\n0 98be\n49 1cf97\nfb f2c9f\nca ef708\ncd e73f1\nb9 d6107\n88 d2b70\n7b 29b46\n4a 265af\n3d cfdf\nc 9a48\nda f0075\n9e 10b665\n1 42d97\nda eeda7\n58 582eb\nca ee446\n98 d220f\na7 298ef7\n3d e551\n9c d21de\n24 3d8fa\n9d 29eb65\n91 10b52d\n8c d187d\nff f41ec\n8d 29e204\n81 10abcc\na1 d6911\n5a 25c4e\n9 8518\n4a 252ed\n1c 90e7\n71 1b4bba\n9 39888\nc 8786\nda eedb3\n7f 2b0f5\n4 98fd\n4d 1cfd6\n8 8755\n7b 2b0c4\nb4 26817c\nee eb530\n81 29e338\n14 1cc314\ne8 eb506\ne1 124824\n34 19f085\n6e 22439\n63 5b788\nd6 e6693\n76 29cdd\nfe eabcf\nc 398b8\nad cd499\nff 2b7556\nf3 123f1e\nee ea26e\nef 2b6bf5\n9d 10cbd1\ne3 1235bd\nf8 eaba5\nfd 2b74fb\nf1 123ec3\nf6 eba8c\nbb 29a9a0\n82 d2a1e\ncb e60f7\ne8 ea244\n86 d29ed\ncf e60c6\nac cd6d6\nad 29a05d\na1 106a25\n99 c9ec0\ndc e7a35\n40 1b73eb\n9d c9e8f\nb4 ceef4\n89 c955f\n7e 21ad8\n6e 21177\nf2 2be707\n3c 1d8960\n28 45ae\nd8 eea8e\n1d d98\n9 406\n79 29dfb\n9a 10334a\n3f 3f68c\n89 d185b\n9e 29682b\n92 1031f3\n37 3f535\n6d 1bc97b\n61 29343\n70 22c0b\n12 1d48e6\n7f 22afb\nde 279c9a\nb5 2600dd\nd2 e6662\n77 229a4\nf2 2bf9c9\n67 22043\na 1cbd1e\n47 1b7418\n62 1edc21\n3d 3f685\n79 29ded\n9a 10333c\n52 1af9a3\n3f 3f67e\n9c 296824\n90 1031ec\n35 3f52e\n89 d184d\nd5 2820d7\na3 110296\n36 3e272\n9e 29681d\n92 1031e5\n37 3f527\n6d 1bc96d\n61 29335\n30 3f55c\nb7 29ab28\n17 1cd57a\n24 45f06\n18 3a24c\n1a 3a245\n9 8756\n1e 1cd726\n12 3a0ee\ne 1ccdc5\n2 3978d\n9e 103319\nd9 eed3d\n1d 1d47ba\n9a 1032e8\n96 1031c2\nff ea930\n60 5ba3c\nf2 27dcb9\n86 102861\na5 299f12\nf6 2b7648\na 1d50ee\na4 260a3b\ne6 2b6ce7\n8c d2b4d\nbd d60e4\n8a ca829\nbb cddc0\naa 260bbe\n5e 1e943\na 39890\n6 3976a\n46 1cbd9\n85 264c47\n21 1d913a\n72 1f6870\n5f 1b7c22\n7a 1ee42b\n14 192bf2\n81 264c16\n9c 103312\n94 1031bb\n6b 1bb681\n96 1031b4\n9c 2967c2\n90 10318a\nb7 d5f88\n84 10285a\n34 3f52b\n86 102853\n5a 57092\n7b 29e66\nb7 29aac6\n27 1d10d2\nd7 2bb5b2\n18 3a1ea\na8 260bb7\n3c 46710\n5c 1e93c\n8 39889\n70 1b4bbb\na 39882\n72 1b4bb4\n4 39763\na9 cd458\n6 3975c\nc 1ccd6a\n54 1e7e5\n0 39732\ne 1ccd63\n2 3972b\n19 1d4a35\n2e 47318\n54 25b21\n8a 10c235\nbe cddf2\n1d 1d4a04\nd 1d40a3\n38 1d088d\n3c 1d085c\n28 1cff2c\n71 1bbef4\n2c 1cfefb\na8 106dd3\n29 1cff2d\nb9 d7375\na9 106dd4\n49 1b8805\n61 1ed97b\n50 1b7a90\n6 1d3fb4\n23 1d0de3\ne8 eb504\n59 1b7ea4\n85 263665\n49 1b7543\n78 1b3cfc\n4f 1f098f\na4 25f4bd\nb5 10f6af\n84 10c118\n7c 1b3ccb\n68 1b339b\n6c 1b336a\n61 1ec6b9\n1b 19b30c\n38 19f459\n27 1cfb62\n30 1d9d3a\n1f 19b2db\n72 1bd1be\n2e 1967d2\n23 1cfb21\n9b 264405\ne8 ea242\ne9 eb55b\nc8 2782eb\n48 1b04b6\n41 1e97d4\ne0 2843dd\na 19391e\n3 1ccc3c\na2 267845\ne9 eb505\ne3 eb0fb\nc6 ee2cc\n3c 1d08be\nc8 e735d\n15 19af3f\n79 1b3cfd\na5 25f4be\n7d 1b3ccc\n69 1b339c\n58 1afb55\neb 2b6bd2\n84 25b316\n48 1af1f4\n4c 1af1c3\n41 1e8512\n21 547a\n3b 197165\ncf ef436\n3f 197134\na 19265c\n34 3e26b\na1 11028f\n3 1cb97a\n74 1b4b7a\n27 c530\n20 1cfac7\n9e 10c98b\nc8 e609b\nbb ce00c\nab cd6ab\nf6 12521c\n8d 10afac\nf5 2862de\nbc 107765\n9 3987a\na8 106e35\nb 976f\ne9 284535\n48 1f0954\nd 41eb5\nf2 ebd15\n75 1bd1e7\nd5 12100d\n9 41e84\n71 1bd1b6\n3c 3e66e\n26 19e77f\n38 3e63d\n89 d18bd\n2c 3dd0d\n99 10b940\n28 3dcdc\nbd 107766\n9a 25d0c8\na9 106e36\n4b 1cf9e\n2 98c5\nad 106e05\n8a 25c767\n9c 1035be\n20 46f3d\n2d 3dd0e\na 193670\n29 3dcdd\nd6 279d81\n9c cb1b2\n27 1d9412\nec eb535\ne1 124884\n79 1b4d13\n6c 2243e\n61 5b78d\nd9 f0071\nb 9a1d\ne9 2847e3\n48 1f0c02\n90 cafd8\nd7 280e6e\nf4 284fbb\n55 1b0a36\n23 3ebf5\n9b d34d9\n1f 1d475f\n24 3ebcc\nfc eabd4\na7 106a6b\n52 1ea77\n9f d21e4\nbd 2a2cb8\nb1 10f680\n16 1cc30d\n83 29e331\nd9 e7a03\nba ce06d\nbe ce03c\na2 29a189\n59 25c54\n49 252f3\n7c 21add\n78 21aac\n50 1b0cc0\nb 875b\n3e 4f45\n22 1d1092\n3f 1d18cc\n2 3acfd\n33 3e294\n80 d14b5\n8c 264aed\nd2 2bb572\n9b d2217\nc2 2bac11\nae d57df\n7d 1f573e\n2a 45b3\ne9 eb567\na8 d6cb5\ned eb536\nc8 e73bf\ncc e738e\nc1 1206dd\n8a ca827\n8e ca7f6\n83 103b45\na0 107c92\n6d 2243f\nc8 e60fd\na 16ce\n63 1f4a00\n46 1b7167\n11 9f72\nf9 eac06\nb8 d6354\na8 d59f3\n9 3adea\ncc e60cc\nff 1240a6\nb6 1109cd\nbf ce03d\na3 29a18a\n9a c9ec6\n3f 6208\n9e c9e95\n8a c9565\n2f 58a7\nd2 eff20\n8e c9534\n38 d25d\nce 280363\n7d 21ade\n61 1edc2b\n79 21aad\n28 c8fc\n6d 2117d\n5c 1d936\n88 c956e\n67 1bc881\n71 1ed01a\n40 1e9a83\n4c 1cfd5\n48 1cfa4\nc3 2bac12\nc2 ee28b\nce 2818c3\n2b 45b4\n52 26e29\ne 43d\na7 298ea5\na 40c\n2b 3ecfa\nf9 ebbaa\ndc eed7b\nc7 ef341\n20 1d0deb\n44 1af060\n4b 5fd34\n3b 3e399\n1f 1d49fd\nd7 ee9e0\nce 128b57\n3e 1d0855\nf6 ea838\n2a 1cff25\n73 1bbeed\ne6 e9ed7\n59 1b7e96\n49 1b7535\n78 1b3cee\n5e 26f4f\n68 1b338d\n6c 1b335c\nd9 280f8f\n74 1b4bdc\n20 1cfb29\na5 1102c0\ne8 ea234\n2f 1d8251\n8a 294e75\na 1cd040\n38 3e38f\nbf 29995b\n53 1b9008\n70 1bd155\na2 2a0f67\nc8 2782dd\n48 1b04a8\nfd 27cdc3\n73 1ed26f\n39 3e6a0\nd8 2bb97e\neb 12ba0c\ne0 2843cf\ne9 eb4f7\nc6 ee2be\n8b 29d1d2\nc8 e734f\n3b 1d0887\ncf 128b58\nc4 28151b\n3f 1d0856\n2b 1cff26\nbb d736e\na 1cbd7e\n9a d31c6\n79 1b3cef\n7d 1b3cbe\n17 19c4a8\n30 1d19fa\n69 1b338e\n6d 1b335d\nc7 127731\n5 19bb50\nde 1297ba\n58 1afb47\n48 1af1e6\n4c 1af1b5\nc8 e608d\n62 20fed\n6e 1b4625\n9 19bc66\n4 1d3fad\n21 1d0ddc\n5a 1d652\na8 ce965\n5b 1b7e9d\n78 1bbfea\n12 1d5ba8\n4b 1b753c\ndf 2ba43b\n68 1bb689\n7e 1b3cc4\ne5 2bf03f\n2d 19658e\n6e 1b3363\ndb 280f96\n19 19b305\n43 5fb7b\n9 19a9a4\n28 1967fc\n70 1bd1b7\n2c 1967cb\n21 1cfb1a\n99 2643fe\na8 cd6a3\nca 2782e4\n4a 1b04af\n78 217fe\nff 27cdca\n3b 3e6a7\nc4 ef33b\nda 2bb985\n43 1e97cd\ne2 2843d6\n80 10be29\n77 1bbeca\n88 25b74c\n52 1b7d53\n8 193917\nda e67b7\nbd 260232\neb eb4fe\n1c 192f93\n11 1cc2e2\n3e 1d08b7\na9 ce966\n86 d172d\nde 282232\nd2 eebfa\n88 ca7be\n7f 1b3cc5\n36 1d19d0\n6f 1b3364\n4a 1af1ed\n4e 1af1bc\n43 1e850b\n23 5473\n39 19715e\n63 5b9d4\n29 1967fd\n1 1cb973\n85 10aba9\nfb eab9d\neb ea23c\na9 cd6a4\n3c 4f40\n8f 10c267\na 1cbad0\n81 264966\ne8 eb558\nec eb527\n8b d2b78\nc8 2b9d5d\n14 192b82\n5 19b83e\n9b 10cbfb\n10 192b51\ndc f0093\n1 19b80d\neb eb252\nce ee423\ncf 2badaa\nc3 127772\n6c 22430\nd9 f0063\n68 223ff\na 1d40da\n8f 10afa5\na3 2a1224\nf7 2862d7\nba 10778f\ncb e73b9\n91 264005\nbe 10775e\ncf e7388\n95 263fd4\n3b 1d05db\n85 263673\nfc eabc6\ne8 ea296\nde 278c22\n2f 1d82b3\n7a 1b4d19\nc7 ee081\ne2 12488a\nf 1d53d0\n3 41d98\n50 1ea3e2\n73 1bd1af\n3e 3e667\n80 10abd7\n63 1b4510\n8c 29e20f\n3a 3e636\n4b 1e260\n11 19aeac\n70 5c39d\n88 29e1de\n6d 1f4ddd\n5 19a57c\n9b 10b939\ndc eedd1\n31 1a0315\n1 19a54b\n52 1ea0db\n18 3b50c\n93 1034a0\n9f 296ad8\n7c 21acf\n68 2113d\nd5 e68c9\naf 1080c0\n8a 103f49\n1f 19424f\n13 c17\n76 1b4be5\ne 398b3\nf0 f405e\nac 298d9a\n8e 103f18\ne9 eb559\nc6 ee320\ncc e7380\n8a 102c87\n70 2af67\n2c 1cfca3\nb4 2600ce\n48 1e258\nbb 107790\nbf 10775f\n9e 1035b7\nf9 eabf8\n9 476\n54 56f03\nfd eabc7\nfd eabd7\nb4 d74fe\nd4 2ba596\ne9 ea297\n44 565a2\ned ea266\n3e 1d8ba5\nf6 f2b88\nc8 e60ef\n6d 22431\nf 1d410c\ncc e60be\n8a 10bff7\n95 2966cc\n3f 3e668\n3b 3e637\nc4 ef2cb\ne2 eb418\n7d 21ad0\n6d 2116f\n30 3f7aa\n13 3b65d\n77 1f5640\nf5 28501e\n54 1f143d\nbe 29970e\n69 2113e\n67 1bb2bb\n5c 1d928\nea 12bcbb\nf5 2b6390\n4c 1cfc7\ne5 2b5a2f\n48 1cf96\nb0 2a18c1\n1a d6f\n96 d30a2\n96 1044d6\ne5 e9e6f\n8d d2b3e\n8 1923a7\na8 ce9c7\n63 5b786\ndb f006a\nd 9a47\n2c 589f\n21 3ebee\n99 d34d2\n3f 479ca\n1d 1d4758\n28 586e\nfe eabcd\ne2 2b6d1a\nea ea29d\n95 10b818\nee ea26c\na5 106a64\nab cd70d\nf6 12419a\n50 1ea70\n9d d21dd\nb8 ce066\nbc ce035\na0 29a182\na8 cd705\n9c c9c44\n5b 25c4d\n1a 3b513\n12 43958\n8c c92e3\nff ebc52\n4b 252ec\n62 1edc23\nc0 ee046\ncc 28167e\n6a 21144\n1d 90e6\nd 8785\n3c 4f3e\n40 1e97d3\n63 1bc5a0\n28 45ac\n1c 192ff5\nc3 1206d6\ne0 124823\n25 c7d7\neb 28453c\nfc 12c3fa\n4a 1f095b\nad ce997\n4 862f\n77 2af9e\n88 ca820\nf3 eba5a\nd6 eec2b\n8c ca7ef\n81 103b3e\n4 19baed\n35 19f084\n6f 22438\nca e60f6\n77 29cdc\n2d 58a0\n88 c955e\n73 22963\n2b 1d0ee8\n56 25b34\nc 16f8\nac 268c88\na0 d5650\n1 3aa47\n87 10aec0\nd4 2b350a\n8 16c7\n85 10ac0b\n56 56f0a\ne3 2b6d1b\nb2 10890a\n94 10c7bd\n46 565a9\nb6 1088d9\nde e6a26\n8d c92f0\nce e60c5\n63 1edc24\n5 41ab2\n10 19b15b\n7b 21aa6\n36 3f7e2\n32 3f7b1\n0 19a7fa\n6b 21145\n73 1ed013\n42 1e9a7c\n5a 1d8fe\n0 1934b2\n4e 1cfce\n4a 1cf9d\n88 d28ce\n60 1ed918\n45 1b710f\n67 2a381\nc7 2b2b4f\n3d 4f3f\nc0 ee284\ncc 2818bc\n29 45ad\n3 1d3cd8\n57 1b8d8b\n74 1bced8\n1c d97\n40 57831\ne1 eb412\nc4 2babe9\n61 2231b\nc1 ef5ab\n14 192ea0\n59 26f6c\n43 264bb\n1e 19c59c\n12 8f64\n94 29696b\n3 992a\n40 1f0b0f\n60 1ec968\nf0 f3db0\ne1 eb3b0\nf6 2bf9fa\nc5 2b18e8\nb7 1073be\n61 222b9\n76 1f6903\n25 5759\nd5 efc39\n45 1e87f1\ne7 eb3da\ne5 eb3d3\n87 29d0ae\ne3 eb3a9\ndb 281fb6\n36 60b0\n14 192e3e\nd6 2b1f41\n9c 103372\ne1 eb3a2\n84 10284c\nf7 1251bb\n58 5708b\na7 ce849\ne4 2b5a2e\n5d 26f3b\nc7 2b18e1\nf3 12518a\n7b 29e58\na3 ce818\nc3 2b18b0\n59 26f0a\n45 26483\nec 2b5939\n67 222e3\n77 1ee562\n65 222dc\n7 1d3fb7\n8c 10bfb1\n63 222b2\n44 1f0ade\ne9 2847d3\n4 39755\n77 5c0c4\n64 1ec937\n47 1e87ea\n37 1d19d1\n92 29568f\n25 574b\nf3 ebd78\n18 9126\n27 1cfe0e\n2 41ae9\ne 1d5121\ne 1d3e5f\n6 1d3d08\n64 2202d\n29 1d0f41\nc0 120928\n10 8f6b\n1c 19c5a3\n62 1b2f8f\n26 1d10d1\nd6 2bb5b1\naf 2a134a\nf7 f2dc5\n36 1d0770\ne7 f2464\nb6 10868b\n26 1cfe0f\nd6 2ba2ef\nef f230d\nc8 11f50f\n8b 10afe4\na2 ce817\n25 19fce9\n69 1bb6dc\n46 1b8439\n4 19b8a1\n63 1b4254\n11 a230\n46 1b7425\n69 1b465e\n5e 1b7c2f\n61 1b44a5\nc6 1277a0\n56 1b7ad8\n79 1b4d11\n14 19af40\nc 19a736\n52 1f11bb\n4 19a5df\n66 1b4540\n76 1b3bdf\nec f2369\n34 197047\nb4 cef62\n24 1966e6\nd4 280bc6\ne6 1235f9\nbf 29a9c3\nb3 10738b\n82 103df4\ncb ee453\n99 d221c\na4 106a61\n2c 19658f\n72 1ed014\nf7 12d7ab\nd7 2ba290\n9d 10b6c1\nd5 2bb7f9\n1d 192d48\n7a 1b3cf7\n15 192bf1\nc7 2b992f\n8d 10ad60\nde 128496\n95 d30a8\n5 1cbc60\n5e 1afb1d\n53 1e8e6c\n5 192290\n56 1af9c6\nb7 10f718\n86 10c181\n66 2a382\nd7 11fd52\n33 465f0\n3f 1d9c28\n2 43059\n8e 10c02a\n35 e3f8\na 42f02\na3 107cee\n86 10aebf\na9 1080f8\n76 29a21\n1e 1d5d30\n12 426f8\n2c d94f\n4c 1f09e7\n72 643d4\n9e 10b6c9\na1 107f3f\nb 4313f\n96 10b572\nb9 1087ab\n8e 10ad68\n29 197a6d\n86 10ac11\na9 107e4a\n21 197916\n1a 425a1\nff ebe92\n87 103e32\n87 10c120\n56 1b0a3c\n2 1cb989\n8f 103cdb\n3 42ff8\nb 3abb3\n6f 1f4b96\n3 3aa5c\n67 1f4a3f\nb6 107679\na6 106d18\n97 1034d1\n87 102b70\na5 110320\n3e 1d1b89\n32 3e551\n4 41aa3\n77 64412\nae 106bc1\n44 5e8f0\ne9 f25e5\n9c d1f9e\nd6 280b6d\na6 106a6a\n97 10b7bf\n9f 10337a\n97 103223\n87 1028c2\ne7 2bed98\nad 1101c9\neb ea29e\n80 263953\nd9 28225f\n3a 3e3fa\na5 110072\n16 1d4907\n1a 90bb\n49 2660b\n6 1d3fa6\n1b 3a252\n95 d33ac\n1f 1cd733\n13 3a0fb\nb5 d5fef\n84 d2a58\nbc 2a1cf7\nbb 1089f0\nb3 108899\n84 d27aa\n9a 29edf4\na4 2a11dd\nbd ce046\ne4 2b6a40\n9b 29ede7\n6c 1bb70e\n84 d1732\na1 ce561\n8c ca5a5\n4b 265ae\n64 2a629\nd5 11fff9\nbb 10772e\nce ee1d7\n25 19e9d3\n4e 1b8590\n42 24f58\n38 47c41\na4 ce8b1\n62 22321\n9 1d50e8\n44 261d4\n43 1e179\nbd 2999a8\n63 2a60c\n46 1b86d7\n62 22073\nfb 286151\nc6 2791e0\n5a 1f2570\n43 26467\n4b 1e022\nf6 eaae8\n9d 29d8af\n8d 29cf4e\nb3 11093b\nde 2ba684\nb4 cdf50\n9c 29daec\n44 24f12\n9 1d3e26\n5f 1b0e50\n53 1d818\n4f 1b04ef\n43 1ceb7\n85 29cd95\nee ea030\n88 10c23c\nbc cddf9\n7f 1bd2e3\n73 29cab\n29 586f\n6 8636\nbf 2a1cff\ned f3638\n7a 21869\n1 1d3c6d\nb7 2a1ba8\ne5 f34e1\n88 29d1bc\n56 1b7d76\n7e 1b4d4a\n72 21712\n6a 20f08\n11 a220\n46 1b7415\nfb 12c3cf\nca 128e38\n5b 1d6c1\n7b 1f6a28\n4b 1cd60\n48 576da\ne9 eb2bb\n52 1af9f9\n40 57583\ne1 eb164\n12 192e68\n6 1d3d06\n64 2202b\nd1 efc5e\n49 2636b\n41 26214\n0 9682\n4b 26364\n43 2620d\n63 22066\nb 97d3\n48 1f09b8\n97 d3351\ncd ef431\nad 29a309\na1 106cd1\n39 197160\nc1 ef2a9\ned eb28a\n56 1af9c8\nde 11fefc\ne5 eb133\n16 192e37\n9e 10336b\n6e 1f4b27\na5 ce5a2\n45 261e3\n5b 1f282d\n41 261b2\n65 2203c\n7b 1ee686\n25 54ab\n45 1e8543\n3b 1d1af5\ncd ef423\naa d5a5c\n28 3efa0\nb 3ae53\n6f 1f4e36\nd8 129480\nc9 ef3f2\nc7 ef2d3\n0 1cb912\na6 d5936\n24 3ee7a\nd4 12935a\nc3 ef2a2\ne5 ea173\nef eb283\ned eb27c\nd0 2b1f69\nd7 ee9d2\n8f 29cf57\ne7 eb12c\ne5 eb125\n87 29ce00\n13 439b9\na0 107f92\n85 d1789\n11 439b2\n8d d2892\n89 d2861\n59 26f7a\nc8 11f7bd\n81 d270a\n33 3f812\nca 2b1766\naf ce6f2\nec 2b58d7\n31 3f80b\na 1d3e1e\nad ce6eb\na5 ce594\n84 d2a48\ncd e6121\nc3 2b1602\nce 2782b3\nc2 12770f\nce 2bad47\n6b 22479\n61 1bb2e5\ne2 f3766\n43 261ab\nd7 2bb7fe\n6f 2218c\n4c 1f0987\nd3 efc71\n1b 1d5c8e\na1 268dad\n34 196d89\n2f 55fb\n6c 1ec7e0\nd7 128340\n42 1e97ce\n8 469\ne4 2b577e\n9 41bd8\n5d 26c8b\na7 ce599\nba 1074f1\n7c 21ad1\n86 10aebd\nd5 279b2d\na3 107cec\n3b 19817b\nf8 124079\n53 1af9f8\n70 1b3b45\n45 5e8ef\naf 106bc0\nd7 280b6c\n22 1d1030\n80 d1453\n74 22c4a\n8c 264a8b\n7e 1b3d26\n8a 10bf89\n1c 8e47\nf4 12419f\n45 261d3\n8d d188c\na8 108095\n95 10b7a8\nbb 26025c\n3d 19f23f\nce 127889\n19 3a1e9\n8a 263aa3\n15 426b1\n4e 5e792\nd0 eff19\n8c c952d\nfc ea926\n5 41d50\n97 263fcd\n1e 3a4d0\n7d 64564\n29 3dd41\n45 261d5\ne4 f3790\n3d 469af\n4c 1aef15\ndb 12821a\n14 19c4a2\n2b 3dd3a\nf8 12c367\ne6 f3789\n5c 1f12e8\n18 1cd69a\n1f a103\na6 298bf6\n87 102852\n5e 1f12e1\nb 1d50f1\n53 26b6c\n70 2acb9\naf ce754\ne 3ab73\n15 3b377\nb4 108932\n50 1f11c2\n5a 57090\n13 9fdd\n0 3aa54\n87 296020\nd4 e68d8\nae 1080cf\n75 6440d\n18 1d5a4a\nf8 ebe57\n33 4683c\n3f 1d9e74\n9a 29db32\n2 3aa4d\n86 103dd1\nb7 107368\n50 5f220\n5c 1f2858\na6 29a166\n98 10c953\n19 192fc5\n77 64406\nc4 12095b\nf5 123ef2\ne4 2b6cf0\nc7 2b2ba3\n19 1d5a49\n72 22c82\n54 26b35\n53 1f11ba\n5e 1b7e6b\nd 19a735\n70 1f5307\n99 10c952\nd6 278873\nde eed74\nf3 2be458\nc2 2baec1\n55 1f1190\n8d 103c64\n57 1f1189\n49 1f09b7\n29 d91f\n4e 57706\nda 1281b9\neb 124c20\ndc eed6f\n7a 1ed169\ndd 2ba68e\nff 12d900\na3 d6bc6\n81 263954\n5d 1e93d\nfc ebef8\nb9 ce015\n6 19a576\n76 1ecfe1\nc6 2b15de\ncd 2b9d2d\nef 12cf9f\n15 1c55\nb4 cf210\n2a 1d9297\n50 1b7aa0\nbe 108a92\n0 1332\n6c 1ec7de\n2f 55f9\nbd d6394\n6e 1ec7d7\ndb 2ba40a\n2e 1d9266\n22 45c2e\n54 1b7a6f\nfe 12c6a1\n4f 1b048d\n43 1ce55\n78 1b3a50\nfd f41e7\n64 1ec687\naa 10f12a\ndc 280f6b\nf3 12d52c\nd1 2ba2ba\n4d 1b7258\nec 284813\n70 22c79\n65 1bc816\nc0 2804d4\n9c 25d3a2\n90 c9d6a\n35 60ac\n4a 5ea6f\nfb 27de11\n5a 1ea230\n9b 29edf5\n6c 1bb71c\na5 d6bf0\n3e 198459\n32 4e21\nd7 2b34b0\n5e 1b7bcd\nc6 12773e\n2a 45d85\n5c 1b7bc6\nc4 127737\nca e6104\n6f 22446\n75 1ee5bb\n18 1d477a\n52 1b7a43\n50 1b8d62\nc 192376\n34 198309\nf6 123eea\nb9 d6115\n88 d2b7e\na6 106a08\n5d 5f0f9\na7 106a07\nde 1281ea\n5e 5f0f3\n2f 3dd09\nd8 1281c0\n58 5f0c9\nd9 1281bf\n7d 22d92\n29 3dcdf\n6a 1b45f4\nc6 e5d32\n5a 5f0c2\n14 19c440\n2b 3dcd8\n89 d160d\nd4 12809a\nd8 e7d12\n63 1f5f72\n2 1937c7\n55 5efa2\n75 1f5337\n44 1f1da0\nb 850f\n56 5ef9c\nfb ebc21\n88 c92b2\n2d 55f4\n7b 22b2a\n8 1bb\n98 10c8f1\n8c d15dd\n18 437fa\nc 84e6\n8d d15dc\n2f 1d11b9\n23 3db81\ncd 12788f\nfc eabc8\n4d 5e798\n38 1a070d\n38 4f0f\ne1 2b6a72\n9c 10c922\n1d 192f94\n8b d1606\nd6 128093\n23 19fcbd\n48 5e768\n1d 1d5cb8\n11 42680\na0 ce880\n8f 294ba5\na1 ce87f\n6 42d7a\n2b 3ed5c\nfc 124048\n57 1af9c7\n21 1963a6\nf4 123ef1\nc6 2b2ba2\n5d 1f12e7\n98 d1f0d\na7 298bf5\n5f 1f12e0\n70 2afbb\n65 20d7a\n7b 1ed3c4\nec 1236e7\n47 1af066\n6b 221c9\n68 5a5bf\nef 2b5b8b\n6a 5a5b8\n78 1b4fbe\naf 106e70\ne4 123590\n7c 1b3a1f\n4d 1f0986\n88 d15ac\nf8 f2ca7\n83 295fe1\n78 29bb0\n3 1cceea\n27 daa0\n20 1d1037\na2 267af3\n44 1b86de\n53 1b8cf8\n60 1b44f8\n64 1edbf9\n44 57864\n75 5adfb\nb3 cdc69\nbf 2612a1\n82 ca6d2\n47 1e9aac\n66 1edbf2\nc3 120984\ne0 124ad1\n47 1e9a4a\ne6 284653\na 3aba4\nb4 25fe82\n8a c9567\n2f 58a9\n6c 1eca8e\n6 1d5268\n4f 1e8941\n46 1e979d\n96 d1e42\n21 3ebf0\nbe 1074be\nbc 1074b7\n17 192e36\n19 2099\nfa eabaa\n7d 1bc07c\n5f 1b0e42\n53 1d80a\n7c 1b4f8f\n70 21957\n4c 1e28b\n7d 21822\n99 caed4\n9e 103317\n54 581c5\n9 1738\n4f 1b04e1\n43 1cea9\n6c 1b462e\n60 20ff6\n5f 5f100\nd2 2820fe\n62 1bc7fb\nf5 2b7404\n6 8388\n28 55c0\n3e 479cb\n80 ca41d\nef 12ba3d\ne4 284400\n51 1e817\nea f385b\nec eb27b\nd6 ee9d1\n8e 29cf56\ne6 124afb\nbc 260285\n49 1b87f7\n2 3979b\ne 1ccdd3\n22 1cfb2e\na7 1102c5\naa 298d70\n89 294bcf\nc4 e5cbb\n9 1cbad8\n44 1cbc4\n6a 1b4664\n6e 1bb405\na 1422\n68 22401\nc7 120953\n8b 294bc8\n8a 294bc7\nc1 120929\n41 57832\nec 285a73\ne0 f243b\ncf 281926\nc3 ee2ee\nec f3877\n9 19b9c8\n5a 1b90fe\n43 1b7139\n19 1d49c7\na7 ce8a9\ne4 2b5a8e\nb6 2a1b53\n5 3acc4\na4 d58cd\n97 25cf41\n63 1b4260\n84 ca3ec\n58 1ec2b\na5 ce8a4\n4 3acc3\n3b 466e7\n96 25cf40\n63 22004\n46 251d5\n69 2240e\n9e d3509\n1 3ac93\n60 5a46a\n6c 1edaa2\n8a ca57b\n43 5631d\n4f 1e9955\n79 21a9f\n63 5a462\n6f 1eda9a\nca 2b1758\nb9 10f7d5\n88 10c23e\n2a 19e8a5\n8 467\n42 1af036\n9f 10b6ca\nc6 e5d24\n1 19bb1d\n8 1d5395\nb4 cdeee\n97 c9da1\n4e 1dff0\n2a 1d7f65\nae 106b5f\ne9 f2583\n2 1d3f75\n94 c9d47\n45 1e878f\nc0 2814f8\n2d 197852\n21 421a\n7e 1b4f88\n72 21950\n68 1b43b1\n1d 19b338\n9a c9e66\n38 1d1b41\nc9 ee19e\nee 2844fc\nc6 2b2b50\n8c 103f81\nf7 2b60e7\n56 1b7d78\n79 1b4fb1\n68 22471\n28 1d0f34\n9d 26568f\n91 d2057\na8 107e3d\n20 3ebfb\na7 29a1c7\n9e d2247\ne2 f3704\nea eb2bf\n86 10ac03\n5e 1b7c2d\n21 197908\ne2 eb168\n82 295fe0\ned 1249aa\n76 1b4b83\n1e 1cc410\nd3 efeaf\nac 298d38\n2 19bb15\n4b 1af1ee\n67 5b755\n5a 1af8a2\ndf f0039\n1 992f\ndb f0008\nfd 285175\n5c 1f1594\n11 43704\na6 298ea2\n66 1f4a40\n31 4784b\na4 cd2df\n87 c9192\n24 41e8\nbf 26832f\n1e 1d474e\ncc 120802\n28 1d953e\n73 1f530f\n8 431a9\n39 46740\n89 c956d\nd4 11fffa\n99 d1f00\ned eb2ec\n4c 5770b\nce ee1c7\ncf 1207fc\n26 41e1\n8b c9566\nd6 11fff3\n8a 10bfe9\n95 2966be\nf2 27df59\n44 1af0c2\n6 43026\n4f 566ff\n99 26541e\n3b 3e3fb\nda 2bb6d9\na4 cd2dd\n5c 1afb86\n2a 3dd45\n3f 479cc\n52 1b7cf1\nbb 2682fe\n1a 1d471d\nc7 127a4f\n4e 5fa54\nc5 1279e6\n89 29cf0f\n6c 1edd4e\n60 5a716\n84 c919a\n75 5c36b\n18 4252a\ncd ef42f\n9f 29daf6\nb7 2681d8\n16 1d45f7\n9d 2653e1\n91 d1da9\n39 198420\n16 19b1e7\n3e 1d0863\nb5 26012f\nff 125368\n6e 1b4635\n62 20ffd\n9 1d3dc4\ne6 124b69\n47 575ae\ne6 f21b7\nc2 ef5af\nab 2a10cb\n41 1b86b0\n35 19805a\nc6 1206a4\nc3 120674\nb8 d6108\n31 198029\nc2 120673\n5a 1b0b02\nbb 2a1cce\ne9 f3607\n86 294d5d\nf4 f2dcd\n8f 29d205\nd0 2b2217\nd7 eec80\n55 581c4\nfc ea988\nac 107e0c\n24 3ebca\n91 10c7fd\na8 107ddb\n8e d28f8\n51 5f21f\n5d 1f2857\na7 29a165\ne9 ea299\na0 d6bc0\n71 1ed2c8\n2a 19e843\naa d574c\n6f 1f4b26\nb5 25fe21\nbc d7655\nb1 1109a4\ndc 2ba6ed\n6 1d4fba\n4f 1e8693\nb9 108a59\n35 ce96\n4 98ff\n40 1f1ac1\n77 216e2\nee 124a14\n29 19680b\n74 1ed298\n9e 25d3a9\n92 c9d71\n57 1e914b\n37 60b3\nb 1d3e1f\n28 1d7f6c\ne1 2b6d22\n47 5fbaa\n38 1d05d1\n9f 25d3a8\n93 c9d70\nbc 2614f5\nb0 cdebd\n18 1d5cf8\n2 3acfb\nb7 107616\n2b 4308\n25 1cfb5b\n1a 43863\n61 1ec967\nf1 f3daf\n7c 1b3cbd\na5 25f4c0\nfa ebece\n5b 1e913\n2b 3effa\n5d 1b0e3b\n51 1d803\n9d 103311\n2c d941\nac d6cf4\na1 110043\na8 1080f9\nee 2b6ea2\ne2 12386a\n71 2afbc\ne4 124b02\nc7 1209b5\nc 8784\nc3 277ee0\nd 1d50b9\n55 26b34\n1 41a81\n29 1d11ef\n23 1d0de5\n6 1d3fb6\n64 222db\n63 1ec960\nf3 f3da8\nd7 e6940\n57 1f1499\n30 1d1998\n37 e401\nee e9fce\nb4 cdeec\n5b 259ad\nd1 11fd7c\ndd 2b33b4\n94 c9d45\n3f 19f236\ne 19bc9f\n2 8667\n23 5471\n1d af8\n99 1048b2\nfd 2be895\n9f 29d8b6\n14 c4e\na 160\n9f 264434\nbc 268581\n57 1e90e9\n7a 22dc9\n58 1afb57\nf7 27dc87\n84 25b318\n73 1f5361\n42 1f1dca\nde eed82\n41 264b4\nfb ebbb1\n7b 1b4d0c\n5e 1b7edd\ne3 12487d\nc6 127a4e\n64 21017\nda efd59\n4a 1e8911\n75 2af8b\n1a 1d478d\n78 22ab2\n8f 263ad3\nac 267c20\n47 1e8788\n97 10b50f\nb7 2a18a4\n86 29e30d\nbd 29ac6a\nb1 107632\ndd efd92\na 39b2e\nab cd70f\nd9 121133\n87 294a40\n19 3b50d\nb8 d6116\n3e 1d1b17\n32 3e4df\n94 c9afb\n9 16c8\n6d 1bb6ab\n5f 1b913c\n53 25b04\nca 128e36\nd5 2b350b\n9c 296ac2\n90 10348a\n35 3f7cc\n78 21aa0\n7 1d5279\ne9 1249db\nc6 1277a2\n61 1b44a7\n7d 29e1e\n79 217ff\n48 1e268\n9e 10336d\n0 1937c0\nff 125314\n3d 3f933\nfc ea91a\n80 294d17\ne6 eb3e5\n9c 25c0ec\na9 cd714\nf4 1241a1\nd7 120054\n98 10cc01\n75 5b0a9\n52 1b0a0b\nbd 108a28\nf7 124199\n3d 3f931\n77 5b0a2\nc5 2bac4a\n17 1cc5c8\n25 d7f9\n3b 1d9e43\nd5 e793f\na5 cd2de\nfa 2b7524\n63 1f4c5c\n29 4608d\nd8 e69fc\nec f35c9\nb9 10f837\n88 10c2a0\n1d 19c5a6\n63 1b2f92\n11 8f6e\nba ce00b\n3d 19f4dd\n67 63d53\nad 1080c7\ne7 123838\n7 1cbc67\n30 1a05c4\n79 1b3c9d\n7e 1ed138\n5f 1b0dee\n53 1d7b6\nca 120ae8\n6d 1edd4f\n61 5a717\ned 284814\n4c 1f0c33\n21 46eea\na9 108096\na3 107c8c\n86 10ae5d\n6f 1edd48\n63 5a710\n47 1ce86\nd4 11fff8\n18 192fc6\n3a 6238\nc2 2b2b73\nf3 2b610a\nad 25f685\nfe 27cdbb\n8d 29e210\n81 10abd8\na9 ce6b8\n8c d1889\nde 2bb946\nd2 12830e\nb7 cdf48\n87 103b84\ncc e5e82\n86 26492f\n5a 1b916e\nc2 128cdf\nc 444\n42 5fbe8\n63 1bb2ec\n58 1d959\n9a 10b696\n3f 479d8\n8d 103f20\nc7 11f691\n74 1b4bec\nc 398ba\nd1 eff18\n8c 103f1f\n5e 1afb1f\nc3 e7270\nd 84e5\n18 19c328\n35 3e50a\nc2 ef2ad\n6e 5a5eb\n95 10345a\n87 29e2fe\n15 3a363\n5 39a02\n97 25bc7f\ne6 f3735\nee eb2f0\ne5 2846bb\na5 cd5e2\n4 39a01\n96 25bc7e\n83 ca6d1\na0 ce81e\n17 1d4918\n71 1b3b46\n58 1d907\n10 192e5f\n7a 1ed16b\nc4 280203\nff 12d902\nfa 124074\nea 2b6e71\n7c 1ed141\n9a c9c1a\n16 1d591b\n5f 1e8ff4\n3f 5f5c\ne9 2b6bcb\n4f 5fa53\n8d d188a\n7b 21b08\nde 2ba3d6\n10 19b1bd\n0 3aca2\n31 3e239\n3d 1d1871\n9f d1f98\n98 29552f\n8e 264d96\n82 d175e\nbf 26832d\n11 19b1bc\nb0 cef23\n5b 603f5\n4a 1b729d\n17 1d592a\n69 1ec810\n97 10c833\ne9 123719\n12 1cd84c\n61 1b31e5\nb8 cf018\nec 1249ab\ncf 12085e\nc7 2b15df\n50 1ead2\nfa 124012\na 1d538e\nbf 2a1ca9\n34 60b7\n25 41db\nc4 2814b9\n9 147c\ne2 27d606\n3b 1d0825\nf3 ebd16\nda eea95\n2a 45b5\n40 2519b\n4c 1b87d3\n41 1f1b22\n20 1976c7\n5b 26f7f\n87 d2740\na8 260909\n65 1ec688\n3d 4c9f\nc 1708\n52 5818d\n46 1ce79\nb 1cbd8d\n13 8d19\n1f 19c351\n30 ce66\n3c 1a049e\n38 1d18a3\n13 1d48e5\n73 22c1f\n72 1bbc4e\n65 1b44d8\ned 124a0c\n28 197812\n0 41ae0\nc 1d5118\n5a 2599e\n92 10c7f3\ndb 11fecc\nf8 124019\n87 10bed4\nc 192626\nda 278c53\n8c 1029b1\n6 41ab8\nf4 27dce3\n46 1e9aab\n64 20d77\n51 1e8e73\n2a 3dce3\n5c 1afb24\n45 26491\n8 97cb\nbd d60e6\na8 1103e5\n8b 10c298\nea 123711\n28 460e\n62 1b31dd\n75 2aceb\nb1 2681a0\n10 1d45bf\na5 cd2d0\ndc 2ba3dd\n79 21b0f\n24 19f9d8\n6d 1b30b1\n99 104604\ndc 2b3663\nd0 12002b\n15 1d489f\n21 19e9a2\n4a 1b855f\nae 11016f\nd4 ee978\n3a 198178\n78 21b10\na4 cd2d1\n11 3a092\n1d 1cd6ca\n9f 264186\nbc 2682d3\n82 10be92\ncb 11f56b\ne8 1236b8\nc8 e73b3\nf9 ea94a\ne8 27d748\nd9 eeaf3\n6c 20ec0\nf 1452\nf5 eaae0\nd8 eeaf2\n95 10b7b6\ne7 2b57da\n7d 2ae34\n60 2230c\nd 19a729\n28 1d0f32\n8a c9257\n65 2a628\nca ef3f6\nc7 1279df\n7a 1ee677\n5f 1b7e6e\ne3 eb417\nc5 ef2ca\n65 1ed94a\n83 ca423\na0 ce570\neb 124c2e\naf ce9a0\nec 2b5b85\n8c 1029a3\nff 125312\n64 20d69\nc2 ef29f\nb8 1074dc\nb7 299866\n53 5819a\na8 ce967\nfa ea95e\n2c 4331\n84 d279c\n9a 29ede6\n89 d2b7f\n2c 3efcf\n99 10cc02\n66 5a740\ndf 2bb9ab\nd3 128373\nd7 2ba52e\nb0 110be1\n32 196cff\n0 1ccee4\n31 1d047b\n3c 19712c\na0 ce810\n24 41da\n6b 29247\n29 1cfc71\nee 285a7a\ne2 f2442\n43 57839\nea e9ffd\nc7 2b9bcd\n19 19b2a5\n24 1cfaea\nf2 2b6117\ndb eea98\na1 d589b\nca ef458\nad 268ed3\n33 1982d0\n10 1cc2d3\n9 1cbaca\n89 1029d3\n21 3d8ca\n2d 1d0f02\n88 294bc0\n17 1cc5ba\n66 1b422e\nfb 1252e1\n41 5fbe4\n62 5a711\n6e 1edd49\n17 1d48b6\n1f 1cc471\ncb 2b2cc9\n2a 197ac7\nda 281fa7\n10 192dfd\n5a 58036\n48 5798a\n79 5af21\n4b 1e9bd2\n6f 2a788\n68 1edd1f\n97 d20ef\n90 295686\nb7 268484\n7a 5af1b\n6a 1edd18\n7 1d4fc9\nc0 e6fac\nc9 120834\ncd 2bada3\nc1 12776b\n65 1ec6ea\n7d 1ee402\n40 57833\n71 5adca\n43 1e9a7b\n67 2a631\n60 1edbc8\ne2 284684\n38 3f653\n1b 3b506\nba d610f\nfd 284ec7\n5c 1f12e6\na6 298bf4\n2c 433f\n96 10b510\nad cd435\ncb 12082d\ne8 12497a\nce ee415\n58 259fb\n15 426bf\n67 1ec6e3\nfa 2b77d0\ndf 280fc7\n2d 433e\nc0 edfe4\ncc 28161c\n9a 10c8ea\n35 47b1c\nc3 120922\n6d 5a5f1\ncc 128bb4\n35 e408\n36 1d1712\n7 1d3f55\n97 10c7d1\nf 1cbb10\n18 1d5ce8\ne9 1236b7\nc4 1206ab\n0 19249c\n4a 576d5\n49 1b74d3\n94 10321b\nbd 10877a\n17 436da\n69 5a5c0\n18 1cd9a8\n33 d16e\n3f 1a07a6\na1 107f95\n79 1b4fbf\na5 260780\nc8 128b83\n65 5a49a\nc9 ef462\n6c 5b8b2\n24 196438\n9c 103310\n7 1cb9b9\n10 1d5b91\nfe f2cdf\nff ebe9e\n8c c952f\ndc 11fea1\n33 47b62\n87 10be72\n18 194288\nda 11fe69\n7b 5b23a\n32 47b61\n43 1f1b1b\n42 25194\n4e 1b87cc\n60 1f5c68\n7 42d7b\ne9 2bdc55\n99 29ede0\n2a 46025\n5c 1b7e66\n9a d1f06\nf7 ebd47\n84 c93d8\n47 1cbd8\n2a 196805\nda 280ce5\nf9 27de0a\n5f 26c92\nb 41bdf\n58 1ea229\nd4 11fd4a\n18 192d18\n3a 5f8a\ncb ef459\n4 2e1\ne8 27d746\n77 1ecfe2\n46 1e9a4b\n7c 1b3d21\ne6 285915\n66 1bc81e\nd7 2b21ee\n8d c928e\nd 197\n88 d15fe\n2d d940\nc5 ee2c4\n24 197946\n7 1937f9\n8c c928d\n9a d1f66\nc 196\n91 29ec89\nc7 2b2b41\n65 5ba0a\n8 41bc9\n70 1bcefb\n7b 64538\n8f 29d195\n38 1983bf\ncc e6120\naa d6cbc\nca 2b9d54\nef eb53d\ncc 11f540\n58 25c47\nd0 1212ed\n2a dbc5\n9b 103595\nf7 2b739d\nbd 1087ce\n81 ca6d8\n4a 1f0c5d\n90 25bf58\na4 d6951\ned ea02a\n4c 56449\nc5 ef5e8\n18 19c2b6\n80 10be37\nc9 11f510\nbe 110ac2\na7 ce59b\ne4 2b5780\nc7 2b1633\na4 29a151\n9d 1045d3\nd7 11fd44\neb eb50c\n6b 22415\n45 251bf\n82 10be30\ncb 11f509\n63 2a660\n41 1b73ee\nd1 efc6a\n8c 103c71\nde eeb38\ne1 eb3ae\nef ea26f\na6 d6b96\nbc 1074ab\nb8 10747a\n3c 1d1870\n30 3e238\n2 1ccee9\n7c 22af3\nb7 299804\ndb e7cc6\n38 3e383\n6a 5a5ba\na7 267b23\n4c 1dfdb\ne5 2b6a43\nc9 e5dee\n80 d2715\n2c 3da53\nfa 124080\n48 1dfaa\n24 c536\n41 1e9a82\nb5 d7551\nde eeb2a\ne1 eb3a0\n5c 5806e\n80 295fdb\nb1 299572\nbc 260223\n9 1d40d4\n44 251c0\nbd 268574\nda eeaf9\n58 5803d\nb8 d60a6\ncc e7390\nfd ea927\n48 1e00c\n23 1d109f\nde 279f3a\nd2 e6902\ne8 eb24a\nad d5775\ne7 27d5d4\n22 1d109e\n4d 2632a\n7d 1f548e\n4c 1f1ef7\n40 5e8bf\n47 1b0398\n99 d31c0\nce 2803b5\n73 2ad21\n3c 3f682\n8e 1029aa\nf6 27dcdc\ne3 12ce17\n76 5adf3\nb8 1087aa\n9d d1fa1\nb5 2a2e61\ndf 281fdb\nd3 ee9a3\n66 20d70\na5 268dde\n3f 46708\nf0 ebd64\ne 43171\nb6 25fe25\n9d 29ee13\n91 10b7db\n35 465b8\n4 43021\n58 1af899\n21 1966a8\n94 d20f5\nb1 cef24\nb6 107367\ne 1451\na7 299eb9\n5e 1b7bbf\n51 1e9121\n66 1b327e\n31 6089\n41 1e87c0\nd1 efc08\n21 5728\n23 c7ab\n2f 19fde3\n8a 263aa1\nad 2679c7\nc 1d3de6\n2a 1cff33\nb5 d628f\nc5 127a48\n9b 103587\n22 45c82\n54 1b7ac3\n2e 1d92ba\nb5 2681d1\n14 1d45f0\n32 1d073d\n5 1d3c90\n23 1cfddd\n38 19817f\n72 1b38f0\n8f 10c277\nd0 121289\nf6 ea7d6\nb 41eed\n18 90b6\n27 1cfd9e\n90 10475c\nf4 2be73f\n2d 3efd2\ndd 1294b2\nec e9fb9\n1 1937cf\nd8 e67b2\n7d 22af4\n75 2299d\n1a 193f6f\n47 1f1dfa\n64 1f5f47\na5 ce84e\nb8 cddc8\n64 5a499\ndd 2bb704\n27 197940\nd1 1280cc\na8 cd467\n6d 1ec841\n61 29095\n6d 1bc6cd\nc8 28038b\n77 1bcf34\n98 c9c21\n3d 5f63\n65 1bc576\nc0 280234\n9c 25d102\n90 c9aca\n55 1e8ea4\n35 5e0c\n2d 1d824a\n4a 5e7cf\n78 21862\n66 1b44de\n46 1e149\n77 216e0\nee 124a12\n8d ca7f0\nd7 eec2c\n62 5a4d1\n6e 1edb09\n28 4370\ncd 127b9f\n64 1bb2b5\n0 12d2\n18 193fcc\ne8 f38a8\nac 267964\n51 1b8d53\nad 267c83\nc 1d40a2\n29 1d0ed1\n98 25bdff\n13 1cc589\n37 d13f\n30 1d06d6\n7f 22aed\n36 1d87ae\n37 1d0701\n65 2203a\n65 1b4228\nc0 277ee6\nfb 1255e5\n40 1b83f3\n4b 5fa30\nc4 279425\ncf e714a\n59 1f2828\n8a d28c9\n25 1d0dab\n80 294a69\n86 295faf\n2e 3dd16\nde 1281f6\nfb f3f01\nc6 e6f90\n48 5fd38\nda 281fb5\n47 1b00ea\n2 19b869\n25 45f07\n25 54a9\n25 197697\nbb 108a54\n80 25b355\n24 1d0daa\nbc cf0bb\n51 1f146f\n66 1bb5cc\n31 e3d7\n51 1f26d1\n82 d2772\n47 1f1b4c\n64 1f5c99\n28 58d2\nd8 efdb2\n1c a0fb\n86 294aa1\n43 1e8757\nd2 281e5e\ne9 27c1e5\na5 267b2c\n4 1d3f4b\n21 1d0d7a\n65 1bc5d8\ne2 eb106\n7e 22d98\n5c 1afb26\n2a 3dce5\n74 1bceca\nda 1281c5\nb8 cf08a\n46 5e63b\nc 42eca\n75 1b4b7b\n21 1cfac8\n8e 10bfba\n5d 259c9\na7 cd2d7\nb3 2681a7\n12 1d45c6\n81 29cdb8\n8c 263a69\nf0 ebab6\ne 42ec3\ne3 284693\n42 1f0ab2\nf4 12c551\nec eb2dd\n82 25b5fc\n2 192505\nb9 110afb\ne4 27d382\ne2 2b6a7a\nc4 2791db\nde e6786\n47 1b8438\n3c 1d05a0\n33 4dbe\n3f 1983f6\n9a 25c0b4\n88 d285e\nce e5e25\n8 1ccfd9\n39 1d0570\n38 1d056f\ne5 1238a1\n26 1d106d\n88 295e82\n40 261a3\n84 d1788\n51 1ea0d3\nf7 f40f7\n8e d2898\nd5 12131d\n8c d2891\n63 5a780\n6f 1eddb8\n4f 1b87db\n43 251a3\nc5 2b2baa\nd5 2b1f39\n64 1bc569\nca 127864\n34 1d0449\n8c 102a13\n95 c9d46\n44 1b740e\n3e 1d08c5\nb 4314f\nf7 f2e35\n51 1e8e11\na 1ccfe0\n3b 1d0577\ne2 27d358\nee ea2de\n6 964a\neb e9ffe\n62 5b725\n38 196eaf\nbd d7646\n9d 2643cd\ne6 e9e75\nd9 eeaf1\na5 268d7e\nc2 ef303\n85 295fa9\n9c 2643cc\n91 29d71b\n1a 1cc441\n9f 10cbd8\ndb eea96\n2b 45b6\n21 1976c8\n72 1b4dfe\n1 193521\n52 1b0c57\n6b 29505\n70 1bbee7\n7b 63524\n2e 4338\nad 10f153\n72 22c74\n8a 264ab5\ne 1d410d\n2b 1d0f3c\n4b 2535e\n74 1bbc08\n41 1b8390\n40 26211\n12 1d48d8\n37 47b23\n92 25cf71\nf4 123ef3\na9 cd466\nac 268ee2\na0 d58aa\n8f 264d95\n83 d175d\n65 1f4c84\nb9 2a1a27\nd0 eff0b\na8 cd465\nc8 ef461\n47 1b8686\ncf 128bba\n43 1cc09\n4f 1b0241\na 19b9c0\n2d 4605e\n69 20f00\n44 1b83c2\n7f 1b4d49\n42 1e17a\n73 21711\n62 1b450f\nf 19bc9e\n3 8666\n30 1d174c\nbe 1077d0\ne2 124aca\nb8 260254\n84 10ae54\na1 107c83\n1c 19b2e3\n39 198112\n1d 19b2d4\n7a 5af19\nb9 2a2f87\n40 1b73ed\n8 19b955\n3f 469b6\n50 58194\n42 1b73e6\n57 1afa37\n12 19b1b6\nc9 ee3ea\n6c 1b30be\nec e9fc7\n44 1b83c0\n62 1b450d\ne9 f2585\n12 1cd5aa\n47 1af0d6\n2 19a855\n87 10bec4\nff f2f2a\n4a 2660f\n6f 5b8bc\n5a 57022\nd5 e7bed\nfa 2b77d2\n1b 1d4790\n31 19f363\nce 2bafe5\nc2 1279ad\n5a 1b7e3c\na7 cd5e7\n5a 26f72\n95 25bf26\n8a d1851\nd8 e67a4\n1f 1d47c1\n2 1ccc3d\nb7 299558\nc0 12067a\n48 25348\n58 1b0b09\n7b 1bd2a6\n46 1b0335\n50 1b7d3e\n7f 6455b\n5d 1f12e9\na7 298bf7\n88 c94fc\nc3 2814f0\n8 405\n43 1b83f9\n60 1bc546\n11 1cc2d4\n1c 192f85\ne4 28464c\nb8 1074e8\n13 192e67\n30 196fb4\n49 1b854b\n1a 19affb\nc7 ee32d\n97 10b503\n49 1dfb9\n55 56f02\n84 25c5e8\nbc d6139\na 19a69a\n18 19aff4\nc5 ee326\n51 58193\nf8 ea957\ncb ef3f9\ne5 f378f\n5b 5f371\n7e 1b4ff8\n72 219c0\n19 1d4787\n2e 4706a\n54 25873\n8 19a693\nca ef6a6\nfb f2c3d\n3a 1d05e8\nbf 110d7f\ne8 e9ff6\n32 19f35d\n80 103b3d\n77 1b3bde\n83 10be41\n30 19f356\n3b 46993\n75 1b3bd7\n81 10be3a\na1 ce5d1\n8 167\n1 1d3c5f\n7a 2185b\n57 1b8d37\n58 25a09\n67 1ec6f1\n62 290f1\n6e 1bc729\n54 1b7a5f\n72 1b3bac\ned f2368\nc6 11f690\n35 197046\nde 280cc2\n41 1b83f4\n12 19aea4\n41 1de62\n56 1f24ac\n5a 25cb0\n55 1f24b4\nd2 120fe2\n1 19a53d\n78 22b22\n8c 25b77f\n9b cae79\n72 1b4b60\n38 5f91\naa 260910\n2d 1977e2\n21 41aa\nc7 e5d31\n5b 5f0c1\n58 1d6ad\n36 1d070e\n45 1b0391\nc5 ef578\n40 1aede1\n5e 1b0de1\n52 1d7a9\nc 979a\n48 1cd4c\n21 19796a\nd1 281e4a\nb6 108629\n26 1cfdad\ne7 285668\nad d6a99\n77 21990\nee 124cc2\n3a 4cca\n91 29e9dd\ndf 1284f9\nfc 12c646\na5 d6942\n5e 25c7f\n3e 1981ab\n32 4b73\nd7 1283a2\n67 2102f\n2a 4369\nec 12bce5\nd3 11fd21\ndf 2b3359\n4e 2531e\n7a 1ee6d9\n5f 1b7ed0\nc7 127a41\ne4 12bb8e\nd7 2b3202\n18 b1c\ncc 127b3e\n65 5a748\n42 1b00aa\n98 10b691\n3d 479d3\n92 10c803\nc 19369c\n0 64\n73 229d3\n6f 1ec7d8\nff 12c6a2\nd8 eeb00\n7d 2ae42\ne7 2b57e8\n77 1ee5c2\n1a 1d4781\nda e7d19\n2d 3efd0\n67 5a741\nb1 269460\n1c 1d4757\neb 12ccc2\n12 1d4628\n5 1cceb2\na4 267abb\n2c 19e86d\n3a 1d0824\n85 103dbb\n1 19a85b\nea f384d\n35 4787c\n12 1d462a\n4 1ccec1\n35 1d0458\n37 47875\n84 103dca\nb5 107361\na4 29a15f\n0 3aa46\n87 296012\n61 1edc29\n95 c9da8\nd6 2b21df\n85 294aa9\na 42e92\n17 3a378\n25 46f1b\n4c 1b7265\n19 1cc677\n8e 29e20a\n82 10abd2\n27 46f14\ncd e70d3\ne9 f22e3\n9a 10c94a\n12 19c416\na0 107c84\na4 260a91\n79 1bbd2f\n44 1aedbe\nda 12017b\n2b 45d84\n5d 1b7bc5\n18 a12c\n27 1d0e14\nf9 eaba6\nb0 d74cd\n46 1b7107\n19 194287\n75 2acdd\n92 10c7f5\ned 2857c6\ne1 f218e\n85 d14e9\na0 107cf2\n38 198181\ndf 128259\nb2 2613c6\n0 2be\nc 1938f6\n3d 196e8d\n54 26df1\n68 1b433f\n48 1b0198\n27 c52e\n20 1cfac5\n28 1977ae\n8 193607\n1e 19b02e\n86 10ab9f\n5a 5f3de\n5 19a5de\n56 1b7d14\nd5 12966b\n57 1af789\n12 19af08\n57 1f11eb\n29 19e8ab\n74 1f5338\n0 1324\na4 260a3d\n87 25c8f0\nc1 ef30b\n4e 1b751a\n6b 1b4349\n52 1b7a99\nc9 1278c0\n44 1b0330\n75 1b38c7\ne0 1235c1\nec 2b6bf9\n82 25c612\n2 132b\n7b 1bbd36\n46 1aedc5\nc4 128ca7\n20 1cfab7\n98 d3231\na7 299f19\n70 1ee27b\n77 2ace4\n50 57ee6\nc4 e7239\nf5 ea7d0\ne4 27d5ce\n40 1deb5\nc7 279481\n6a 20efa\n25 1979a7\n16 19aed7\n14 19aed0\nf 1938ee\n3 2b6\n91 295687\n56 1b7a68\n6 12fa\n55 1f2454\n22 45c20\n2e 1d9258\n54 1b7a61\n72 1b3bae\n4 12f3\n9 16d6\n6d 1bb6b9\n12 426e8\n1e 1d5d20\n21 1d108a\n5a 1d900\nc4 ef2c9\na1 107f93\n39 198422\n1 42d43\n63 1edc22\n4a 1b728f\n81 103d8a\n6d 1b30bd\n62 1bc5a1\n59 25c56\n11 19b1ae\n5b 603e7\ne3 124b2b\n14 1cc5c0\nad 267c13\nca ee198\n48 576dc\na8 d5745\n48 1b7288\n50 1b9000\n2 19a5a7\n25 1963d5\nbb 107792\n5a 56d74\n97 d1ddf\n65 1b2f66\n16 1cd5cd\nac 268c26\na0 d55ee\n85 294ce7\n7d 5b200\n6 2e8\n8a 10ad27\n2 19a7f3\n95 2953fc\nc4 278163\n65 2a37c\n12 9fdc\n46 1b73c1\n63 1b41f0\n32 1d073f\n14 1d45f2\nf1 285fff\n75 1f55e5\nb8 cddc6\n8b d2868\n9c 103374\nf4 ebae7\n42 1b0048\n1b 427ec\n10 19b1af\nb4 cef56\n2 1934b7\n17 1d48b4\ndc eeacf\n14 19af30\n5c 259d8\n10 9fd5\n10 19aeff\n58 259a7\n67 1ec68f\nac d6a38\n27 471c2\n7c 1bbdc1\n18 1dde\nce 12086b\na5 106cae\n82 25c610\n9c 10b660\n5a 26c62\n9e 10b659\n1b 3b760\n10 194123\n7f 1f5743\n1e 42562\nc4 277eb5\nff 1255b4\n98 10b62f\n18 42538\n9f 29db04\n94 10b509\n3c 19718e\n66 5ba04\n41 5fb80\n51 1d563\n5d 1b0b9b\n2b 3ed5a\n53 1b8d06\n16 4240b\n31 d167\nba cf2cd\n3d 1a079f\ne4 e9e62\neb eb2b4\nce ee485\n4a 576d3\nf4 27c9b1\ncc ee16e\n1e 1d5a12\n12 423da\n8c 10acff\nf4 286031\ne3 eb15d\nc6 ee32e\n42 5757c\nc4 ee017\ndc efd81\n88 10acce\n5c 26c8a\n8 41bd7\nf2 285ff9\n6d 1b461f\n61 20fe7\n72 1bcf02\n68 1b465d\n84 10aba8\nf0 27dca4\ne 1d50b1\n2 41a79\nac 299ffa\na0 1069c2\n40 1de53\nc7 27941f\n55 5f250\n85 c9129\na 1d50e2\n2d 3ed22\n58 25999\ndf 280f65\n21 3d8d8\n2d 1d0f10\naf 2679cc\n1c 1cc717\na6 107fda\ne2 f36f6\ndf 2ba6f5\n89 103c41\ned 2bdc24\n2a 1964f3\nf6 1254cc\nd4 e68ca\nae 1080c1\n85 264937\nd6 121325\n38 196ebf\ndd 2ba6ee\n5a 1af83e\n4c 1b7575\n69 1b43a4\n84 c943a\n8 19a9b3\n89 25b49f\nd4 12131e\nd 19a9e3\n53 1f1468\n33 e3d0\n70 1f55b5\n85 10be69\n3d d28d\n21 1d93da\n1e 8e40\n1b 427de\n68 1b43a3\n1c 8e39\na5 ce5f6\n4 3aa15\n32 4e1f\n3e 198457\n78 1b39de\n58 1af837\n18 192ca6\nc5 e5fd8\n23 1d913f\nde eeac8\nc5 ee078\nd 1d4095\n5e 259d1\na5 107f62\n98 25c0af\n8c d15cf\nff f3f3e\n6f 1ecaf6\n85 102af9\n47 1b8684\n96 29ecd0\nc9 ee192\na1 107f31\n9e 10b6bb\n67 5b765\nd8 eea90\ne7 2b5778\n26 3d90f\nc5 281774\n7b 1ee6dc\n8 19b9b7\n45 1b867d\n85 29cd87\n59 1f15c6\n35 4b4a\nac 107e7c\n0 19a59e\n48 25046\n9c d3200\nc 1cbdb8\n2a 1cff35\n72 219b0\n7e 1b4fe8\n1c a109\nbb 2a1a20\n86 294aaf\n56 2587a\n1b 1d478e\n5c 25c84\n79 22ab3\nfa ea952\n4 193801\nc6 11f3e2\n35 196d98\n98 d31cf\na7 299eb7\n2a 47039\n5c 1b8e7a\n50 25842\naf 106e62\n9d d223f\nd7 280e0e\ne7 124afa\nda 278c47\n97 10b821\nab 298d6f\nce ee167\n95 d2086\nc0 277ee8\ne2 eb15a\ne2 eb16a\ncc ee160\n68 1ec80f\n68 291df\n5 19baec\n4a 2503f\n93 104754\nf7 2be737\nc8 ee12f\n7 15a9\n48 25038\ncf 280604\n38 19f1ff\n2a 3efa7\n5c 1b0de8\n50 1d7b0\nee f38e0\nc4 ee009\nce 278015\n60 1ec6b8\n23 54d3\nd8 280f9c\n68 1b464f\n30 19f0a8\na 4314e\n3b 466e5\n44 1b0392\n75 1b3929\n2b 1d8274\nb 1d40cd\n28 1d821a\nd2 eff1e\ne5 eb187\nfb 2b77d1\nb6 cdc37\n52 1af6e7\nb4 26816e\nc1 277ed9\n81 25b348\naf 2a10fe\nf0 2b6110\nf7 f2b79\nc6 ef5e2\n60 1b4508\n2f 45db5\ne0 eb411\n39 3e630\n86 29e05d\ncf 2b1736\n64 1b44d7\n47 1b038a\nec 124a0b\n2f 3da67\n24 19642a\nc9 ef70e\n6a 1b4658\nf0 eba60\n60 1b31e4\ne8 123718\n43 1af097\n7a 21859\n14 1d5bd2\n56 1b7a66\n73 1b4b61\n39 5f92\n62 1b4501\n60 1b4506\nf5 1254c4\n98 10b683\n50 1b7cea\n3d 479c5\na8 106b25\nb5 cdf43\n14 3a362\n3 1924a4\n37 1d070d\n31 1982c9\n36 1d070c\n37 1d1721\n92 2953df\n38 3e3f3\nbf 2999bf\n27 1cfdac\n8c 29e203\n80 10abcb\nf3 12d53a\n25 46f0d\n97 10b501\n53 581fc\na8 ce9c9\n84 295cfa\n16 19aed5\n87 264bde\n45 1de93\n41 1e9a20\nc3 2804dc\na2 cd5b7\nae 260bef\n25 3dba9\nc1 280235\nea 1239bf\n1e 194002\n12 9ca\nd4 128346\n34 1d8809\nc0 ef2a8\n1a 193f71\n24 1d1068\n4 3acd3\n35 3e26a\n7 1ccf1b\na6 267b24\n71 22c7a\nd4 2bb548\n26 1d1061\n1a 3a49f\n56 1af718\n1a 3a1e3\na4 ce601\nca ee134\n41 1b0360\n58 1d64b\n68 1bb43d\nca 128dd6\nfb 12c36d\nf0 284d30\nc1 12098b\ndf 12824b\nfc 12c398\nff 2be5e0\nc2 127a11\nce 2bb049\n12 1d4636\nf7 27df27\n7c 29b7f\n7 1cceb9\n89 10bf81\na0 ce872\nd3 2bb583\n8f 294b97\nc7 ef5ef\n1a 19c2bd\nd6 281e1f\n4c 25317\nca 128b26\nd5 2b31fb\n4b 1ccfe\n5 19a56e\ne2 eb40a\n89 29e1d1\n18 d76\n2 39739\n56 1e7ec\ne 1ccd71\n49 1b8795\n94 1044dd\nc5 ee2b6\n0 961e\n49 1ccf7\n3e e2a9\n95 cadbc\n5e 1f1341\n60 1b44fa\n6b 5bb37\nc7 279171\n2f 45da7\ne0 eb403\n66 1b44d0\ncc 2b1792\nee 124a04\n6c 1b43d4\n60 20d9c\n64 1b44c9\n4d 1f0c96\n83 d2711\nca 11f7c4\n5a 606a0\n49 1e992b\ne8 284534\n20 196407\na5 d6b9e\nc5 2b9c36\n85 10be5b\na7 268d85\n59 6069a\n90 29d9c6\n3c 19719e\n55 1b0cf2\n23 3eeb1\n3 1937c8\n20 197915\n87 c9440\na4 cd58d\ne4 2b5a90\n4f 1f1eef\n43 5e8b7\nc3 e7262\ne0 eb3af\n7 193797\nac 107e18\nb1 cf1d2\nce 279339\nc2 e5d01\na5 25f77c\n41 1b0352\neb f2598\n2f 196835\n23 1d90df\n98 cae7f\n60 22008\n32 1d06cf\n9c 26412a\n6 19354a\nc7 2b15d1\n7b 22d78\nc1 e725b\n48 1cffa\ne7 eb12a\n47 1b0328\nec 1249a9\ne5 27c30d\n52 1b7a97\n45 1b0321\n97 103461\n81 10be2a\nb 19a9ab\n56 1f1438\n7b 1b3cf6\n19 8e17\n31 1d9cd7\nf5 1241a0\nd2 279b02\nb8 1074da\n30 196fa6\n38 19716d\nd9 efdc1\n56 1d53a\nc3 ef55e\ncb e7119\n88 103eee\n2a 196555\n5a 1afaee\n9e 10b657\n20 196395\n21 1cfdd6\nb1 d721e\n2c 19e87b\nf 19a72e\n72 1f5300\n37 47881\na6 2607ea\nef 2bdf2f\n3a 1d0832\n85 103dc9\n21 1d0dea\n5a 1d660\n57 1b7d17\n72 1ee520\n66 1b320c\nd3 280e3f\na4 d58db\n80 294d25\n87 d178e\n2e 19e874\n75 1ed2f9\n87 103dc2\n7 3accb\na6 d58d4\n58 1b8eab\na2 2607b9\neb 2bdefe\n81 103d98\ne5 2bdd7b\n62 1b31db\n2a 3dcd5\n5c 1afb16\ncd 2b2d01\nc1 11f6c9\n9a 10b626\n99 10b620\nc0 ef30a\n65 1b421c\n82 10be3e\ncb 11f517\nc0 277eda\n95 29539a\n7b 1b4f58\n98 10b61f\n6b 1bb443\n22 1d0de4\n9f 29daf4\n8d ca852\nd7 eec8e\ne6 eb3d9\naa 25f64e\n40 1b737d\n7e 1bbd74\n91 25bf57\n8f 29d193\n6f 2219a\n60 222aa\n42 1e87b8\nd2 efc00\nf 19a9dc\n2c 19eb29\n72 1f55ae\nfe 2b7553\nf2 123f1b\n5b 1f1311\n3b e279\n78 1f545e\n7e 1b4d48\n72 21710\n20 5719\nd0 efbf9\n96 10b500\n43 1b0359\ncd ee1d1\ne8 1249da\n60 1b44a6\nc4 11f3e9\n95 10b4fa\n15 42403\n3f 1d1b88\n33 3e550\nd2 2bb82e\n0 1d4f90\n49 1e8669\n29 55d1\n32 465e3\n3e 1d9c1b\nec ea01b\ne2 124b2c\n5 41aa2\nc2 2baecd\n8f 263825\nac 267972\nac 106b56\n7 1924d5\nc7 e7231\n28 1d9290\n89 c92bf\nd4 11fd4c\n47 1e13a\n93 d3070\n3 1cbc28\n74 1b4e28\n27 c7de\n20 1cfd75\n28 1964fc\ncd 2b9d2b\n57 1ea3b9\n1d 8e38\nf6 284fc2\n45 1e133\nf5 27dcd6\n7 41aab\na6 10f066\nc6 28025e\n91 d3069\n1 1cbc21\n9 1923a8\n1a 3a1e1\nbb cddc2\n85 263923\na7 d6b95\n22 1963fe\n67 1b44d1\n84 10ab98\n1c 19b027\na6 267ac2\n58 5f3d7\n81 d2778\nd2 efeae\n42 562ca\n4e 1e9902\n22 1cfd6e\n1 9681\n87 ca6a0\nc4 2b1885\n2 3ac9b\n33 3e232\n3f 1d186a\n9a 295528\nc1 e71f9\n41 1e102\n17 1cd828\n5 15a2\n81 ca668\ne5 28464b\nf6 27dc88\nbc cf0b9\n2c 19683d\n72 1ed2c2\ndc 280d1d\nae 298daf\nf6 ea82a\n31 e3d5\n9d 29eb01\n91 10b4c9\n1d 1d5a0a\n11 423d2\nfd f2c75\ncc ef6de\nc1 128a2d\na0 ce5d2\n3f 1a0498\n33 ce60\n76 1ed291\n2b 196804\n73 1b4e01\n39 6232\n9c 29eb00\n90 10b4c8\n35 1d1770\n5c 26c8c\n97 29d99d\n85 29e2f7\nfe ebef3\ndc e6781\n8 1d50e7\ncc e5e20\n9b 10b635\n90 263ff8\ndf f0047\n71 646ea\n7c 1ed3ed\n35 1a033a\nbc cdd97\n9f c9c4a\nb8 cdd66\n9b c9c19\nbb 25ffae\n8a 25ca17\nb4 cdc40\n97 c9af3\nac cd436\n15 1d5b6f\n8f c92e9\na8 cd405\n6d 1ec7df\n11 1d5b3e\nd6 11fd45\n8b c92b8\nc9 1207d2\n9c c9bf0\nb4 29aab0\n4 964f\n4d 1cd28\n2 1d3cc7\n94 c9a99\ne1 f36fc\n74 216d8\n46 1b0389\n8c c928f\na4 29a14f\nde 2b334c\nd2 11fd14\n84 c9138\n45 1e84e1\nfd ebc3d\na9 106b8a\nfa 1242c0\n43 1decb\n2a 19eaf1\n88 c956c\n67 1bc87f\n2a 1d7fd5\ne7 12484c\nda 278999\n24 1976fa\n81 d2a26\n49 1b8559\ne7 eb43a\n58 6038b\n64 222e9\n28 19655e\nad d6cf5\ncd 2b9d8d\na 1732\n3b 4cc9\nf3 12d77a\n8 42eeb\n42 1f1aba\n87 10be70\n99 29d870\n48 1cfa6\n0 1924fe\nbc cf0ad\na 19360e\n34 1d19b9\n84 103b0c\na6 260a36\n58 5834b\n89 103eef\ned 2bded2\nc 193648\n0 10\n9a c9bb8\n1d 19b08a\n38 1d1893\n18 1cd6fa\nde e7cea\ne8 eb2ba\neb f234c\nd4 279b2e\na2 107ced\ne0 eb163\n28 1d1180\n3b 1d8b75\n69 2a4ae\n6 1cbc04\n1 1937c1\nad 107e0b\n97 1034c3\nb4 107610\n27 19fc8c\n82 26394a\na5 267870\n4 1d3c8f\n22 1cfddc\nc8 e5e51\n6d 22193\nac 107e0a\n96 1034c2\n41 1b73e0\n8c 103c63\n1 1cbc2f\n5a 1afaec\n19 d67\nae 26796b\nf5 2b63f0\n1 15d3\nb 1d539d\n1 1ccc43\na0 26784c\nf3 27df5a\n52 1ea379\n81 103b4c\n8c ca7fd\nd2 121282\n4f 1cfdd\ne5 2bdb2f\nc1 e6fbb\n9 1ccfd8\n5c 1d92a\n62 22071\ne8 eb2ac\ne7 2843a6\nad d57d7\ne5 eb3e1\na8 ce71b\n4c 265e5\n41 5f934\n1d 19c34a\n11 8d12\n9a cae78\n76 1bbc1d\n92 1044a5\ned 27d476\ne1 e9e3e\n1c ae9\n89 d2b0d\n68 5bb91\nfa 27de0e\ndf 2ba693\n43 1f1b1d\n22 1976c2\nc4 128ca9\n46 1aedc7\nf5 12c240\nc7 2baef1\nda f0005\nc3 128a26\n32 1d19ff\n7f 1b4f95\n73 2195d\n4b 576d4\ncf ee486\nea 124c8f\n9 84b4\nbd 10f806\n8c 10c26f\n2e 19e8d6\n1d 1cd978\n11 3a340\ne2 eb15c\n0 3a9e6\n49 25045\na0 ce5c4\ne0 eb155\n64 1b421b\na5 107cb4\n31 4b0b\n3d 198143\n79 5c4f3\n3d 4ca1\nb0 29981f\naa 11013e\nd0 ee947\ndc 281f7f\n2c 1d11af\n20 3db77\n28 1977be\na0 107ce6\n84 103dba\nbb 10f7de\n4b 1b0202\n68 1b434f\nbe 10fb10\n23 1cfb2f\n60 1b41e8\na4 107d23\ne9 f2591\n9a 10cbf8\na0 107f32\n49 1b01fb\nab 267c4b\na 1d406a\nbc 10fb09\n3a 1d0578\ne7 1238aa\n3 19351a\nc5 2bae96\n6e 21183\n20 197667\n4 19379f\n35 196d36\na0 106a30\nac 29a068\n89 10ad2f\nda 128465\n23 da6d\n1 19a7fb\na4 107cb5\n78 5c4f4\n27 1cfafe\n18 8e16\n3b 1d88c7\n6 1cb956\n1 193513\ne4 f2220\n2c 1d823d\n83 ca66f\ne7 284652\n2e 196834\nc3 e6fb4\ne0 eb101\n5 19bade\n7 1934e9\n8d 10acfe\nf5 286030\n6a 211b6\n4c 25069\n41 1b00a4\neb f22ea\n26 19fa41\n6f 1b311a\n38 3f8ff\n1d 90f6\ne5 2856d1\n44 1f1af0\n40 26203\nc7 2817cf\ne4 28591c\n32 1d0421\na4 260a9d\n5 1934e2\n76 1bbf1f\n12 1f3c\n81 103adc\n46 24f27\nce 280665\n99 d3470\n24 1d80f4\n7 1d3fa7\n2c 1cfcaf\n30 3f79a\n15 8f91\nad ce6f9\ne7 e9e6a\na9 ce6c8\n80 29e087\nc9 2b1760\nb2 10f6da\nbe 2a2d12\n9 1d5078\n62 222b1\neb 12cf6e\n7e 5af4a\na0 107f3e\n85 d1735\ndb eeafa\n6e 20ec7\na1 d58fd\nad 268f35\n22 197970\n46 1af075\nf5 12c4ee\nd2 281e50\n14 8f90\naf 106b5e\na4 25f521\ne2 eb416\n43 1de5b\n40 1b73dd\nbc 299697\n42 1de68\nac 29a30a\na0 106cd2\nd4 efc9c\n10 42681\n1c 1d5cb9\n54 1b09e1\n22 3eba0\n43 1b73e5\nd7 2ba2e4\n60 1bb532\nef f231b\nda eed45\n45 1b00d5\na6 107cbc\n7a 5c4fb\n44 1aee22\n1f 1d47bf\n29 3da31\n7d 22ae4\n63 1f4a02\nd8 e67a2\n6f 29224\n26 3ebc5\n4c 1cd1b\n6b 291f3\ne1 1235c2\ned 2b6bfa\nd1 12938e\n8d 1029a2\n73 1b3b4b\ne0 28593f\n59 1f1318\na3 298c26\n65 1b2fba\nb 1ccda1\n46 1de8d\n63 1bc85c\n9d 103303\n38 1970fd\ne4 eb194\nc0 277ed8\n65 1b421a\n1b a37e\nf9 285144\n58 1f1563\n23 3ec05\nde efd96\nbc 1074a9\nbf cf2ff\nb5 269431\nfc 2b64e4\n9c 103302\ne8 f38b6\n49 262fb\na7 267877\nb9 107735\n6 1d3c96\n8f 103c6b\na6 d5626\n7a 29e65\n76 1bbf2d\na0 107c90\n85 d1487\nae 268c2d\na2 d55f5\n67 1f49cf\nc 41bfa\n74 1bcf2c\n7f 64569\n99 1032d2\nc3 ef310\nb8 107796\n19 3a1db\nc0 e6fbc\n43 26219\n2b 5876\n21 19f9a8\n6f 294c4\n68 1eca5b\n51 1b8d55\nac 25f924\n3d 4670f\nc 43178\n46 5e8e9\nfd 2be833\ne7 2b57dc\n95 10b7b8\n85 c9137\n32 196cfd\n2e 55fa\nbb cf2ce\nc5 e5d1c\n98 1032d1\n1 1d4f83\n0 85fc\nc 19bc34\n38 3e381\nbf 29994d\nf7 eaae7\n5 1d3f3e\na8 d6a07\nbf 29994f\ned eb288\nf6 f2b26\n57 57f1d\n30 3f80a\nac ce6ea\n6f 1b4634\n63 20ffc\n24 54a8\n91 d30db\n95 25d24a\n76 29a2f\nf1 2b73d5\n57 6025d\n39 466de\n8 43147\nb0 29aae1\n95 2642d8\n2c 197af1\n20 44b9\nfe ea991\nca 128dd4\nd5 2b34a9\n2a 55c9\n99 d221e\n4a 1e8661\ncf 128df8\n53 57eec\na8 ce6b9\n88 c950a\n67 1bc81d\nc2 2804db\n5d 25c79\na7 cd587\n2b 3efa8\ndb 129488\nd0 281e4b\n65 1ec936\nf4 2b7405\n28 55c2\na3 cd556\naf 260b8e\ne2 12481c\nb8 25ffa6\n85 d2a49\nc 19a6c6\n48 5ea76\nda 280cf3\n8b 103ef6\nef 2bded9\n26 54a3\n59 5f0c8\nb7 10f9b8\n61 1b41f7\ne6 eb12b\n75 1b392b\n41 1f1d6e\nd1 278848\nab 29a03f\n5f 1b7c2e\ne4 eb124\na9 29a038\n86 29cdff\na9 ce976\n69 29240\nb2 10f988\nbe 2a2fc0\n1d 19b096\n47 5f90c\n24 549c\nf2 ebac9\n16 1cd87b\n65 1b3214\nc6 e723e\nfb f41af\n5b 1b7bfd\nfc f41e6\nad 106b49\nf1 12d535\n8a 295e89\n62 22003\n34 197045\n4 19a56f\n77 1bcede\nd2 280b9c\ne0 eb0f3\n59 1b7bf6\n60 21ffc\nad ce9f9\ne7 27d5c8\n22 5472\nc7 128ca1\na0 ce562\na6 10f058\nb6 29a819\n19 19b065\n43 5f8db\n20 546b\n59 5834c\na7 260a37\n26 c52f\n79 647df\n88 d160e\nca 11f516\n4d 1f09e8\n2d d950\n73 643d5\n3 1ccc4a\n95 1031ac\n24 1d105a\n8 8517\n31 1982d7\nc2 120921\n9b 10c8e9\n6c 29210\n7 1d5215\nc0 e71f8\nd2 2b34e0\n85 10284b\n91 d3387\n86 102aff\n1e 192f8e\n8e 10bfb8\na4 106d0f\n5 39754\nc2 2b2b7f\nca 2b2cca\nfb 2b6261\n1d 1d4748\n3b 1d0895\nc4 281529\n84 c93e6\naa d6d1e\n63 62ac0\n6f 1f60f8\nef eb59f\nc4 1279e7\n60 1b425a\n99 10b8ce\n37 60b1\n74 1ed296\nc6 ef334\n4 1937f3\n35 196d8a\n8a 294e83\n51 1f11c1\n79 1b4ca1\n5c 1b7e72\n66 1bb31e\n31 e129\n89 103c95\n64 1b4529\n8e 10b016\nab 107e45\n64 1bc887\n20 1d1029\nbc cdd95\n13 43957\n8d c92e2\na0 107f30\n85 d1727\nb8 cdd64\n8a 25ca15\n2a 196803\nbc 10faa7\n21 1cfac6\nb4 cdc3e\n86 25c8ef\n0 19a84c\nff 12c3a0\n48 252f4\ndd e7a96\ncf e5e88\nd8 f0060\n65 222da\n7 1d3fb5\nff 28612e\nf3 f2af6\nbd 2a19f6\nd4 efeda\nfb ea961\n5a 56d80\n99 29edee\nac cd434\n7a 1b3d05\n2a 45d77\n5c 1b7bb8\n43 5f8cb\nd9 e7a65\na9 cd404\nd0 efea9\na8 cd403\n4a 1b01a1\n40 1af091\n2 42ff5\n4b 566ce\n1b 1d4a2e\n27 54a2\n38 1d8b7b\n6e 1bc975\n62 2933d\n6a 20ef8\n5 19b8a0\n9 1d3db6\n6e 1b4627\n62 20fef\n5c 1b7bb6\na6 25f4c4\n8d 29e4b2\n81 10ae7a\n2c 1d0f01\n20 3d8c9\n98 d21ad\n6c 22184\na7 298e95\n8 39887\na9 cd468\ndb efdc8\n14 c50\ne3 2b6d19\ndd eeb30\n89 c925d\n9e 29d8a7\n38 1d05d3\n3f 196e88\nbd 110d6a\n96 29d750\n70 1b3ba7\n43 264b9\nd7 1293b8\n60 2a606\n59 1b8eaa\n6b 221bd\na1 d6b6b\n34 4b47\n6 1937f8\n88 29d1ca\nbc cdd89\n1e 1cc6b0\n4c 1dfe9\n28 1d7f5e\nb8 cdd58\nba d767f\n73 63421\n7f 1f6a59\na 8510\n2c 433d\n99 d1f70\nb1 2a2e30\n25 41e7\n7a 1ee42d\nc4 2814c5\n58 1d905\n63 1bc850\n0 1ccc44\ne9 f25e7\n87 29d04a\n4c 26339\nb2 25fe56\n9e 2954f7\n8d d28a0\nd3 129325\n48 26308\n89 d286f\nc5 ee06c\n1c 193fa7\n10 96f\n43 1f0aa5\n98 c9bb1\naf 260b8c\na3 cd554\n59 25c46\n7c 22ae5\nb7 2997f6\nf5 eaa7e\n74 1b3b76\n57 1afa29\n56 1f118a\nb 19a6fd\n28 19e84a\na1 29a191\n7 43019\n7 1d3ca5\ne 42ecf\nf0 ebac2\n60 1b3246\nf4 eba91\n4 862d\n6b 63ee9\n64 1b3215\n47 1af0c8\nec 123749\n18 43aa8\n6a 1edacc\ndf 282227\nd3 eebef\n1c 19b025\n2b 1d11f6\n51 1af9ff\n14 1940fe\n62 1b4253\n10 a22f\nc1 e7269\n8c d288f\n4b 1e9924\n6f 2a4da\n68 1eda71\n87 29e052\n4e 1b750c\n43 1f085b\n11 1c94\nb 1ccd93\n2f d949\n70 2295b\n28 1d0ee0\naa 26799c\nc 9798\n18 192f56\nd7 11fff2\nf7 eaa79\nbd cf04a\n8c 10afab\na9 107dda\n86 10aba1\n5a 5f3e0\n30 6078\nc6 27917e\n5a 1b0dae\n43 1aede9\n60 1b2f36\n15 1cc55f\n50 1b09b0\n4a 262ff\nce 128e05\nb8 d7624\n4e 1b725e\n99 caee2\n5 1cbbfe\na4 d68ef\ned e9fc8\n9e 10462f\n9d 2656f1\n91 d20b9\nc9 28038c\n24 4486\n5b 1f257f\n32 19700f\nc6 ef2e0\nda 2b20b9\n29 55c1\nc 8792\n5f 1b7bce\n7a 1ee3d7\n9c c9be2\n47 1f0ad6\n8a 10afd7\n55 25b22\n2f 47319\n36 196fde\nbe 107512\n86 25b62f\n48 262f8\ncb 120a87\n8 9767\n4e 1cd2e\n81 d1764\n8d 264d9c\nd3 2bb821\nfa 124010\n38 19842f\n72 1b3ba0\n4e 1aef72\nb8 cf338\n71 5b0da\n7d 1ee712\n9d 10c921\ne2 12487e\nd7 278810\n56 1b09e8\nbc 2996a7\n58 1b0b69\n9e c9e97\n30 3e53a\n3c 1d1b72\nae 106e0d\n85 295cfb\n59 1ea53a\n37 1d8abf\n77 216d4\nee 124a06\n4f 1f0c9f\na4 25f7cd\n22 1966ae\n23 1d7e1d\n98 c9bbd\n77 1bced0\nd2 280b8e\nf1 27dcb3\nf 1d50c0\n57 26b3b\n3 41a88\n50 1ea0d2\n4f 1b726d\n6a 1eda76\naa 10ee7c\ndc 280cbd\n5a 26f7e\n16 193eab\nd7 2b1f32\nea 1236af\n28 197ace\nd8 281fae\n62 1b323f\ncd 2b9d8f\nef 12d001\n80 10281b\n8c 295e53\nc7 277eaf\n1e 43ae0\ned 12bce4\nca 281646\n82 25c8be\n28 58d0\n62 1b449f\ne5 12383f\nc2 2791a1\n34 1a05f5\n7d 1b3cce\na8 106b79\n20 196645\n44 261d2\n62 2231f\n12 3b3bc\n9 9a24\n99 cb190\nfd 285173\n2b 1d11e8\n51 1af9f1\n9d 2954ff\ndb e7a0a\n67 1b321d\nef 123751\nf0 ebd00\naf d6cee\ncf 2b9d86\nec 2bded3\n96 10c7c4\n88 c924e\nce e5e89\ne6 2b6d49\n65 1b3216\ned 12374a\n16 1cd87d\nf5 eba92\n28 196550\nad d6ce7\ned 1236e8\n16 1cd81b\n42 261a8\n9d cb15f\n40 251ef\n4c 1b8827\nd7 e68d0\nad 106b57\n6d 294bd\n8e 102a0c\n97 c9d3f\na5 2a24a0\nc2 128a25\ned 284812\ncf 2795d8\nc3 e5fa0\nec 27d725\ne0 ea0ed\n84 ca44c\n18 19b064\n42 5f8da\n45 1af05f\n5b 582e3\n95 d1de6\nef eb52f\nb5 26817b\n84 264be4\nf1 2b63c1\n57 5f249\n86 10be63\n5a 606a2\nc0 ef298\n40 261a1\nc7 28176d\n44 1b7100\n62 1b324d\n87 264bdc\n7c 22af1\n95 c9d38\n85 264c45\nf4 eaa7f\nd7 e6932\n2c 3ed15\ndf eeb39\nfa 125342\n38 623f\n72 1b4e0e\na4 106caf\n87 102b62\n20 1d80b5\n8 1923b5\n8d d2b4c\nd3 1295d1\n8e d15d4\n0 835c\nc 19b994\nf3 285ffa\n52 1f2419\nf9 eaba4\n46 1b7105\n22 19765e\n4 41d51\n96 263fce\n77 646c0\ncb e70b7\n94 10477f\n5e 1b0ded\n52 1d7b5\n8c d15cd\n1d 1cd96a\n11 3a332\n2 1d4f89\n4b 1e8662\n2b 55ca\nee 1249a4\n4f 1f0c3d\na4 25f76b\nbb 110a90\nc8 ef3f1\na0 ce80e\n4b 5fce0\n1a dd1\nf6 2b60e6\nc7 28176f\n8d d2ba0\ne7 12359a\nea 124973\neb f258c\n61 21ffd\n44 251ce\n9c 296ad2\n90 10349a\n35 3f7dc\n2d 196520\n69 5a8d0\n3 19a7f4\n14 426b2\nc1 11f3b9\ncd 2b29f1\n89 10acbf\nf1 285ff1\n61 1b3245\n9 41bc8\n71 1bcefa\nc7 1206a5\n68 22153\ne3 ea0e7\nef 27d71f\nf 398b2\n53 6029e\nae 106e6d\nd6 280e19\ncd 120803\na1 25f4ef\n9a 1045fe\n94 10447b\nfe 2be5e1\n15 3a371\n4d 5770c\nec f2315\n8 42e8b\nc2 120983\n5a 1b0e12\n17 3a36a\nc5 2b9bd4\n20 1963a5\nde e6778\nc2 2b28c5\n69 21140\n20 da67\n29 3ef91\na4 10effd\n87 10aeb0\n3a 1d1b48\n1f 19b33f\n97 296671\n53 1d7a8\n5f 1b0de0\n7 192537\nd4 280b64\nce ee165\n97 10cad3\ne0 27d34f\n1f 1cd971\n13 3a339\nac 2a2604\na0 10efcc\n8f 29e4b7\n83 10ae7f\n1b 19b30e\na8 106b87\n3 192506\n20 196653\n7e 22d9a\n8a d15a3\n95 25bc78\n4a 2503d\naf 267c7a\nda efd67\n4a 1e891f\n75 2af99\na 84ac\n80 10287b\n8c 295eb3\nc9 ef454\n9a d1f04\n9f d2246\n98 2957dd\nbf 2685db\n77 1ed290\nb9 107797\n6 1d3cf8\n49 2635d\n1a 8e0d\n89 d15ff\n5 39a10\n87 102b00\n7 39a09\n9c 29d83e\n73 1b3b3f\n39 4f70\ncf 278076\nd8 28224e\n55 1af774\n23 3d933\n2f 1d0f6b\n56 1b7a74\n7b 21808\n9 192346\na8 25f901\n5f 1b7e6c\n16 1d5b77\nbb 26986c\n6b 20ea7\n4f 1b750b\n6b 1f6073\n99 29d872\naf d577c\ne 1d50bd\n2 41a85\nf0 27dcb0\n17 1cc566\n65 1bb554\n1 1571\n34 1d177b\ncc e5e1e\n89 25b74d\na0 cd55c\nac 260b94\n83 c940f\n8f 25ca47\n45 1e141\n4a 1ccff\n1c 192d49\n89 264d6d\n80 10c139\n2 192257\n87 d29ee\nc4 2b9bd3\n84 10c108\n6 192226\nb1 2a2e40\n8a 1029d9\n55 1d524\nf4 eaadf\n2f 3ed1b\n39 4f0e\ncf 278014\n89 10afd1\nb5 10f9bf\ne4 eb132\nd8 2ba6be\nc7 e6fe5\n7a 1b4f55\nea eb2c1\n7e 1b3d34\n8a 10bf97\n85 10aeab\nb9 108749\nee 2b593e\nc8 120a7f\na9 106b7a\n3a 3e388\n8 97c9\n42 1b8398\n6 1924d4\nbd 110aca\n97 10b7af\nc7 11f3e3\n8d 103c72\na 1cbd8c\n95 d20e8\n6e 1b30b5\ndb 280ce8\n41 1e164\n5 193800\n4d 25068\n28 3da20\nf3 2bf9d8\naf 298fec\nae 268f3d\na2 d5905\n20 3ee49\n3 3acfc\n67 1f4cdf\nd0 129329\nc1 ef29b\n14 1cc5c2\ne3 124b2d\n70 1b3889\ndb f0078\n63 1ec8fe\n1e 1d5d22\n12 426ea\ncb 2b2a29\n3 12ca\n67 1bb2ad\nae 110161\nd4 ee96a\n99 29d87e\n46 575bd\n39 1970fe\n42 1b0366\n6d 1bc721\n61 290e9\nea eb24f\n95 10c7ca\n35 5dfc\n1f 1d5cbf\n13 42687\n2a 47047\n50 25850\n5c 1b8e88\nc4 1289f9\nd 3ab7b\nac d5784\n88 294bce\n8f d1637\n8e d2b46\nbf d60dd\n72 22970\n86 25b5cd\n0 1cb972\nc4 e5cb9\n7b 29b56\na4 106ca1\ned f3876\n68 1b30df\n7a 21aa7\n29 4371\nc8 28164f\n2c 1cfefd\n74 21978\n5e 5f101\nb2 10865a\n76 1b4e91\n22 1cfdde\n4 1d3c91\n77 1f6600\nd2 2ba2be\na 3adfe\n1c 193ffb\n10 9c3\nb4 2600dc\n97 25bf8f\n18 194286\n52 1af9f7\n6 1d3f46\n29 1d117f\n7 1cbbf7\na6 d68e8\nef e9fc1\n78 22ac0\n8c 25b71d\n4d 1b72ba\nca e5de8\nba 1074e1\n32 196fad\n25 5749\nf0 123f24\nfc 2b755c\n80 25b5f5\n81 294a6a\ndd 281f80\nab 11013f\nd1 ee948\n39 19f44c\n4 1924db\n4e 57714\n39 4698c\n20 197657\nc1 2814f7\n41 261a2\n85 10ae55\n96 29ec60\n58 1d6bb\na7 d5935\na0 298ecc\n1 1cb911\nb4 10760e\n8e 25ca46\n82 c940e\n64 1ec935\n12 436fe\n22 19639c\n60 1b424c\n6b 5b889\na1 299ee1\ncf 280354\n42 1f1b1c\n76 1bbc7f\n56 1af71a\n89 d2b6f\nef 2b6ba1\ne3 123569\n20 420b\n2c 197843\n73 1bcf01\n1a 1cc6df\nbe 108a22\n0 12c2\n87 25c88e\n28 3da84\naf 299050\n19 1cd9a9\n3d 1d05b1\n54 1ea95\nd8 278990\n53 1e911a\nd 192695\n33 6082\n9d 10b6c3\n76 1bbc0f\n12 1c2c\n60 2206a\n32 1d0731\nc6 128a02\n9c 26418c\n51 1b900f\na0 2a0f6e\n8e 263ad2\ne7 27c368\n46 1e8787\n4a 1f1f1f\nff 2be83a\n43 1b0367\n5 41d5e\na7 107d2d\n9f d34a6\ne6 2bdb37\nb1 110942\ndc 2ba68b\n10 436a3\n49 25037\nad ce9a5\nf3 12542a\ne7 ea116\nd5 279ddb\na3 107f9a\n3b 198429\n85 d2a57\n8e d15d6\nd5 12005b\na7 107f69\n9a 25c0b6\n11 1d4622\n1c 19b2d3\nd2 2ba560\n98 10358f\n77 1f68a2\n15 1d45f1\n33 1d073e\n86 103dc1\nd4 2b31fa\n87 10abb0\nb0 108901\n11 3b346\n46 1e853b\n95 d20f8\na7 11006b\n85 29cdf9\ne1 eb402\ndc 279f33\nd0 e68fb\naa 1080f2\n3f 1983f8\n33 4dc0\ncf ee484\n81 264968\n30 e3d6\n79 21aaf\n31 197007\n7b 5c240\n18 90c4\n6a 1b30e8\n9e 2967c9\n92 103191\n15 1d4663\nef f387f\nf9 124018\nc8 120a81\na7 ce8ab\n6 3acca\n29 19654f\nec 27d475\ne0 e9e3d\n19 42537\n8c ca54f\nd2 120fd4\nc6 e5cc0\n7a 22dcb\n35 196d8c\n1 1d51cf\n4 19a829\n21 197658\ndd 2b3600\nd1 11ffc8\n60 2a5f8\n67 222e1\nce 2795d7\nc2 e5f9f\n8d 29e1a0\n81 10ab68\nb8 2685b2\n19 19aff7\nee f3632\nb6 10f6a9\n5e 1b0e4f\n52 1d817\nea f3601\n13 1d4637\nd5 e661b\naf 107e12\n3c 1d05a2\nd 1cbaa7\n99 d1f62\nea f2599\n4b 57990\nf5 27cc6e\n38 196e5d\ndd 2ba68c\nc8 e70a1\n5 1ccf22\n20 1d0d79\n5 19a570\na4 267b2b\n43 1b73e7\ne8 12ba68\n8e 103c6a\n13 1d45c7\n66 1bb2bc\n82 103b44\n3d 3e671\n1a 193fd3\nd0 128317\ndc 2bb94f\n0 19a84e\n73 1bd1bd\nd2 2ba2b2\n98 1032e1\n77 1f65f4\nd6 e693f\n14 1d490e\n31 1d173d\na1 107fa1\nca 120a7a\nfb 124011\n39 198430\ndd 121412\nda 129789\n86 103b13\n5a 58352\n0 19a53c\n32 1d0491\nb7 110c28\ne5 f3781\n38 6241\n84 294a9a\nb5 261153\n14 1cd572\n96 26402e\nb0 110bef\n46 1f0829\nf9 1242c8\n8a 2637f3\n7e 1b4fea\n72 219b2\n19 1d4779\n88 d28c2\ncd e7143\na5 107f70\nba cf32f\n98 25c0bd\n39 e280\n6e 1bb475\n64 1bb563\n47 1b7416\n80 103deb\n18 19427a\n6c 1b311e\n3a 3f65a\n3c 1d1b82\n30 3e54a\n29 1d0f43\n71 229be\ndf 281fe9\nd3 ee9b1\n66 20d7e\na5 268dec\n8e 1029b8\nf6 27dcea\nd0 2ba567\n93 d3382\n2d 1d0f12\n21 3d8da\n75 2298d\n17 1d4668\n41 5e920\n4d 1f1f58\nca 120a86\n9b d1f05\n8d d18ee\n9 3ab3c\na8 1080f7\n78 1b4d10\ne4 27d5c0\n51 1b8d61\n6a 211a8\n43 1b00b9\n17 4240c\nb6 10f9c7\n46 5e8f7\n63 5b726\n9 8764\n85 10becb\nd6 129601\n92 25bf5d\n37 19829f\n9b d3477\n26 1d80fb\nc1 2804e3\n2e 1cfcb6\ndc eeddf\nf9 ebc0e\nb6 25fe27\nec e9fb7\n7e 1ed13a\n17 1cc31a\nc3 2b2b72\n72 1b3b4c\n38 4f7d\nb0 1088a1\n46 1e84db\nb4 107602\nea eb2b3\n95 10c82e\n17 1d4606\n84 d1486\na6 107f6a\nf2 27cca3\n53 1af6e8\nca 2b2a1a\nef e9fcd\n44 1de84\na4 1069f3\n78 5b232\nbe 1074b0\n2c 3da61\nc9 ee130\n2d 1cfcb0\n7e 1ed3e6\ned f362a\ncb ee3f1\nc6 120952\n35 198308\nd6 e6691\nad 107e19\n36 197ff2\n72 5c3a2\n93 d331e\n28 1967aa\n8a 10bfeb\n61 2230b\n8b c9256\n12 1cc588\na1 107cf3\nac ce9a4\ne5 f34d3\ncf 2818d2\nc3 ee29a\n38 5f93\n5f 1af8e0\ndd 1297c2\n7e 1b4d3c\n72 21704\n82 10be94\na7 106ca7\n3f 197136\n6a 21146\n29 1cfc0f\n6d 1bb46d\nea e9f9b\n18 1cc6e6\n3f 19f4e4\n7c 1b3a0f\nad 29a30b\na1 106cd3\nf9 ebe66\n46 1b83c7\n6d 1bc72f\n61 290f7\nea eb25d\ne5 ea171\n49 1f1c19\n22 19e74e\nc 192386\n2e 55f8\n9b d322b\ne0 27d5f1\n2f 1d7f95\nd0 2ba2b9\n4 19222f\n26 54a1\nda 120187\n93 d30d4\n75 1f65fb\nb9 110a8b\nee 2bdc80\n84 103b1a\n61 1b4259\n40 1aede3\n2 42d47\n4b 56420\n1b 1d4780\n38 1d88cd\n5a 1afafa\ne7 f24d4\n41 1e84b0\nd 8793\n5f 1f2850\n53 5f218\ne5 ea10f\n17 1d4916\n3 1d4f8a\n2 8603\ne 19bc3b\n86 29cdf1\nb4 2681de\nee eb592\nd9 effff\n9e 1045db\n61 2a3ad\n2b 58da\n82 10ab70\n8e 29e1a8\n5f 1f285e\n53 5f226\nf2 27dca9\nc4 e5f67\n37 3e511\n85 29e0b9\nd6 2bb7ef\ne3 eb409\n14 192e9e\n27 3dbb0\nc6 2bae8e\nbc 29ac6b\nb0 107633\nc1 ef299\nf4 f2b11\n9a 10c8f8\n95 10b80c\n35 47b2a\n68 1b307d\nb8 cf2c6\nc1 e6fad\n8b 103c3a\nbc 108a27\ne6 eb199\n58 1f12b7\n1b a0d2\nde e67dc\n19 19c5d5\n53 1b7d46\n70 1bbe93\n11 a290\na7 cd5e9\n6 39a08\n72 1b3b3e\n38 4f6f\n99 cae80\nce 278075\n97 d208d\n60 1b4496\nf 19a730\n50 1af742\n2a 1d0f39\n2 1ccef7\n66 1b4220\nf5 ebda2\n98 d1f61\na7 298c49\n3d e2a3\n65 21018\n8e 10acf8\nf6 28602a\n48 1b854a\n2f 1d1227\n23 3dbef\n55 1afa30\n4a 2535b\n84 10ac08\n67 1b4541\n7 19a577\n22 1d0d80\n58 1d659\n96 29d9fe\nc7 1277a1\nd5 2b325d\n6d 1bb3fd\n0 1921ee\n9e 10cbd7\n2d 3ed14\n88 1029d2\n73 1b4e0d\n0 19249e\nb6 29aab9\n88 ca7cc\n4b 1cfac\ne1 2bdafe\nd6 eff51\n95 29ea1a\n84 ca3ea\n58 1ec29\n89 ca7cd\ned 2847b0\n92 10b7df\n9e 29ee17\ncf 279576\nc3 e5f3e\na4 2a0f9f\n46 1e97ab\nce 1278f7\n69 1b45fc\n9e 29edb7\n92 10b77f\n9e 103309\ndc efd91\nbc 29ac69\nb0 107631\n4c 1e8949\nf7 123f4f\n1d 3a21a\n54 1ea93\n0 399e0\n3d 1d05af\nc 1cd018\n8e 263ad4\nc5 279418\n24 1cfae8\n9c d3262\n44 1f0b3e\nca 281648\n2b 436a\ned 12bce6\n69 1b4650\nc4 ee007\n75 1bcf2b\n21 1d7e78\nf2 eba59\n8f 102a19\n1a 192d0f\n98 10cbf1\n25 3ee6b\n8c 296161\n80 102b29\n18 192fb8\nb7 2610e8\n99 c9bb0\n8c d289d\nd2 129322\nc6 ee00e\nc4 128a5b\nd4 128098\n15 a25f\n67 1b4283\nec e9fc5\ne5 27d5c1\n6 1d526a\n37 1d8801\n2 1ccc4b\nc3 ef2a0\nf6 f2b18\n37 47b31\nad ce74d\nc 3ab6c\n52 1b0c65\nc7 27815d\n6a 22416\n22 19796e\n4e 1f1c42\n42 5e60a\n8 42e99\n81 d2718\ne3 27d5f7\n9 1cd048\n2d 1cfc50\n75 216cb\n44 1e134\n43 1e87b9\nd3 efc01\nd9 1294e5\n23 5721\n61 20d39\n6d 1b4371\nc8 27802f\na6 10ed56\n45 1b83c3\ne4 28597e\n16 3a0bd\n83 10c0e1\n8b 103c9c\na8 107de9\n66 1bc88e\nff 2b7562\nf3 123f2a\nc2 120993\n30 3f4fa\n15 8cf1\n8c 10c023\ndc 2ba43f\n26 19667b\n93 2642ae\n60 1b2f28\n40 1b838f\nf7 125211\n34 1d177d\n91 10caa9\n6d 1b3111\nfc 12404a\n96 10c824\n68 2114b\ndf 11fefd\nda 280f95\nd 19a737\n28 1d0f40\na3 298c18\n6e 1bb467\n4 1301\n39 e272\nbf cddf3\n8b 10c236\n1e 3a212\ncc 2bada2\nc0 12776a\n65 63aac\nfc f2c76\nd8 2b20c0\ndf eeb29\n5d 5806d\nff 284ebe\nc2 ee2ef\nce 281927\n87 295fb0\n8b d28ca\nc8 2b9aaf\n9b 10c94d\n22 1d10a0\nd2 2bb580\n53 1b8d08\n19 a139\n6e 1ec7d9\n48 1aeed8\nf6 eaa84\n4 1303\ndc efde5\n14 1cc312\n81 29e336\n4 1ccf23\n35 1d04ba\nc1 e6f59\n26 1d10c3\nd6 2bb5a3\nd1 e7bbc\n94 d1e39\n1b 1d5a52\ne 19a6cd\n9c 25d3b0\n90 c9d78\n55 1e9152\n35 60ba\n4a 5ea7d\nbb 10fade\n8 8515\n42 1b70e4\nec 27d779\ne0 ea141\n95 10c7cc\n62 2200f\n77 1ecff2\n27 1d1060\n18 a378\n82 294d1e\n40 1e163\n29 1cfc7f\n93 104444\n64 20d6b\n7a 1ed3b5\n4 19a5cf\n4c 25077\ncb 1207cb\n11 9fe2\na8 cd713\n9 158\na6 107f76\n33 1d1754\nda 1284c7\n8e d1638\n5d 1f1597\n7f 64809\n91 d30d9\ndb efdba\n14 c42\ndd eeb22\n89 c924f\nc9 12785e\nb6 10f957\n87 10bed2\na8 29a2d9\na0 107fa2\ndf e67e9\nf7 ebda9\n9a d1f68\n5f 1f1342\n7c 1f548f\n38 1d1841\n3f e2aa\n95 2655a6\n4b 1f09b0\n2b d918\n68 1f4afd\n27 1d0db4\n82 294a72\na8 1080eb\nd4 e668c\nae 107e83\ne9 f38a7\n75 63149\n44 5fbb2\n9c d3260\nd6 281e2f\n94 1031b9\nde eed84\nfb ebbb3\n83 10c141\n1b 19c5d0\n7a 1b3a49\nff f41e0\nd8 1213e2\nc9 ef3fe\n8e 10c2d8\nbf 10f86f\ne4 e9ed2\n2c 1cfeef\n3b 1d9b87\n48 1b84e8\ncd ef741\n4 3aa17\n4d 25076\ne6 eb18d\n41 1f1dd0\n25 1d0db9\na7 267875\n2d 3da60\nc1 ef2a7\n88 1029e2\ndc e7a95\n67 1f5cf5\n30 5e3a\n73 5c333\n8e 25c79a\n82 c9162\n64 1ec689\n27 54a4\nb1 108902\n47 1e853c\n1f 1d49ff\n4d 26338\n9 1428\n6d 1bb40b\n12 4243a\n1e 1d5a72\na1 25f79d\n60 1b4258\nf5 28500e\n96 10346e\nff 27e0ee\nf3 eaab6\nf5 284cfe\nc4 281767\n25 4489\n46 1af0d7\nf5 12c550\na5 298e8e\nc2 11f413\nce 2b2a4b\neb 2b5900\n99 10b8dc\n1f 1cc463\nb3 cf177\n54 1b0a43\n22 3ec02\n53 5f288\n5f 1f28c0\n32 3f555\n10 1cc2e3\ne7 2b6a9e\n95 10ca7a\n37 19f0e1\n8d 10c270\n90 10c79a\nd6 278abf\n9c c9ef0\n27 1d8150\nc9 ee12e\n28 1977b0\nec eb597\n4d 1dfdc\n6e 2a4e7\n81 ca6ca\ne5 2846ad\n96 1044e4\nc7 ee2bd\nec 2b6e47\ne0 12380f\ncf 2b2cfa\nc3 11f6c2\nc0 2791aa\n8 413\n43 1b8407\n39 3e63e\n27 19e780\n6e 21175\n20 197659\n56 1d7da\n1b 1cc6ee\n8f 2637c3\n77 21982\nc5 28152a\na5 268d8c\nc2 ef311\n1b 42530\n96 10b502\n1a 1cc67f\n48 1dfb8\n38 196e4d\ne5 ea17f\n1f 19c353\n13 8d1b\n10 1d48d1\n96 1031b2\n99 103334\n1 98bf\naf d57de\na8 298d75\ne 19bc3d\n3f 19f1d4\n2 8605\n95 10320e\ncc 127b3c\n85 d1725\n28 3da22\naf 298fee\n5e 259cf\nf8 27cb47\n66 1bb55c\n24 3d8fc\n61 2200b\n76 1f6655\n45 1b866f\na3 cd308\naf 260940\nfc 27cb16\na 431b0\n3b 46747\nd6 120001\n6c 1b43e2\n60 20daa\n5d 1ebf9\nf4 27c9bf\nd7 278872\ne 191\n2a 1d9537\n50 1b7d40\n5b 5f37d\nba 10f7dd\n10 1cd597\n0 15d2\n89 c92b1\n14 19c1a0\n7f 22aeb\n2b 3da38\nc7 ee07f\nc4 e6f7b\nae 268edb\na2 d58a3\n3 3ac9a\n67 1f4c7d\ne7 ea124\n8 1cbd85\n2f 19eb83\n98 d31cd\n6c 1b30ae\nd9 280ce1\n31 1d0729\nc5 1289fa\n0 1334\n24 1cfe08\nfd ea929\nb4 d7250\nd4 2ba2e8\ne9 e9fe9\n44 562f4\n6 1cb9ac\n1a ac1\n8d d2890\nc9 e7104\n10 1f41\n59 26f08\na3 ce816\n1e 3b4e4\n44 1b867c\nb8 1089f8\n4e 1e8632\n40 1b0041\n86 d14df\n55 1f143e\n5b 1b7e3d\n74 2298c\n20 3d8d9\n2c 1d0f11\n3 3978c\nf 1ccdc4\nae 2679cd\n66 1ec682\n71 1bbee6\na3 2a24e6\n36 1d04c2\nb7 cdf4a\n16 3a369\n69 1edad2\n69 291ec\n1a 43853\n20 3eb8d\na7 29a159\n41 1aedf0\n3b 197167\n15 1cc551\nd6 e68dd\nc 1938e6\n0 2ae\n28 58e0\nd8 efdc0\n9 1d3e18\n86 2636cd\na0 11028e\ne9 123967\nf4 ea7c3\n5 19253e\n84 10becc\nc3 ef5b2\n8d d2bb0\nd3 129635\n4a 1e2c3\n7b 2185a\n10 19af0f\n7c 1b3cbf\na2 cd553\n25 19ea25\nae 260b8b\n50 1f140e\n13 a229\nba 2996df\n45 1b73af\n4 19b891\n4d 1aef6a\ncb ef6a7\n5e 1d683\nba 10fa7d\n25 1d0e0d\nb5 107673\n3 1cbbd4\n4c 1b8517\n40 24edf\nc7 2804ab\nc0 280236\n65 1bc578\n6e 1bc6d5\n62 2909d\n81 25c8b8\n28 19654e\nad d6ce5\n6f 5b8ac\n4 1d4f61\na9 268c56\nb7 10766a\ne6 285923\nd 9799\nab 10f12b\ndd 280f6c\nff f41de\ne9 12bd17\n30 ceba\n3c 1a04f2\n20 1976bb\ne3 eb109\nc8 ee43f\n6d 2a781\n18 192d16\n42 5758c\n8f 29d1a3\nd7 eec1e\n2 1d3cc9\nc0 2bac1a\n5b 1d65f\n9b 25be07\n9 39b8a\n24 19768a\nf2 27dcb7\n4a 1f1c11\n4f 1b8583\n43 24f4b\n85 d2749\nd0 279db9\n1f 1d475d\n41 1b83f2\nc 42ebc\n82 295fe2\nb3 299579\nbe 26022a\n4d 5fa5c\n0 1570\nce 2b2d09\nc2 11f6d1\nff 2b62a0\n2e 3ed2a\n54 1d533\nc1 ef557\nc9 e7112\nac 29a06a\na0 106a32\nf4 ebae5\n11 1cc520\n0 1326\nb5 cdc41\n6f 62946\n6 193796\nbe 1077c2\na4 25f77b\n87 25b62e\n19 192d09\n41 1b8400\n48 1e8918\n6a 5bb8a\n26 1d105f\ned f25c2\n9e 10cc29\n19 a387\n4e 1b757c\n6b 1b43ab\na4 107f63\n9 42e8a\n69 1bb42e\n93 25bcb0\n26 3ebd1\n93 10c804\n9d 29eb71\n91 10b539\naf 106b52\n42 1b83fa\n65 62a98\n58 1af89b\ndd f0032\n5 43020\n6c 1bc97c\nd7 1280f6\n60 29344\n4f 1b882f\n43 251f7\n53 1b09b8\n68 20eff\n76 1bbebd\n12 1eda\n25 19e787\ndc 121411\n69 291de\nb2 10f926\nbe 2a2f5e\n9f 25d098\n93 c9a60\na9 106b88\nfa 1242be\nfd eabc9\nb4 d74f0\n43 1f1dcb\n7d 1f6752\n71 6311a\nb5 cdf51\n14 3a370\n7b 22d6a\n8d ca7fe\nd3 121283\nc7 e5f6f\n4c 1af227\n94 d1dd9\n77 1ed300\n39 19f20e\nca 127858\n53 26b18\n2d 19fb90\n21 c558\naa ce6be\na8 2a2635\n9 1d507a\nb0 108891\n95 d2088\n9d c9c43\nc6 128cae\n12 436aa\n4b 2503e\n84 10be68\ncd 11f541\nfb 12d62f\nf0 285ff2\n71 1bd156\n4a 1ccef\ne6 27d317\n80 d2707\nc9 e5de0\nf7 f2b27\ne7 eb43c\n46 5785b\nde e67da\n94 c9aed\nbf 1074bf\n8e 103f28\nca 128e28\n40 1b0043\n11 1d48d0\n80 29d073\nb4 cdc32\n4b 5fd42\na0 ce870\n59 1b7be8\n15 8f8f\n4e 25070\n46 1e84e9\nc 19a6d4\nb8 2996ca\nce e70e7\nc6 2b9980\n91 10c78b\n5 862e\n83 103df3\na0 107f40\n6e 1eda9b\n62 5a463\nbd d6384\n28 3ecf2\nb 3aba5\n79 1f570d\naa d57ae\n6f 1f4b88\nf7 ebd39\na3 106c86\naf 29a2be\neb 27c1de\n99 d21ba\n3c 196e8e\na4 1069ff\n1c da7\na4 299ea1\nda e6a05\n5d 1b7ed7\n2b 46096\n78 1ee6e0\n61 1b44f9\n8a 29e1d9\n13 19aea7\na 1420\n5 98f0\nac 298da6\n2 1d3f77\n1 1cbbcd\ncc ee47e\ne9 eb2ad\n44 1b70fe\n62 1b324b\n42 261aa\nbf 26154f\nb3 cdf17\n30 3e4da\n3c 1d1b12\n89 10af7b\nf1 2862ad\n85 d29e7\n0 192250\n1e 194250\n12 c18\n5 1d4f60\n2 12c9\n17 426c6\n18 8e68\n52 1b7a37\n85 103b0d\n3a 1d0576\ne7 1238a8\n95 10b81a\nea ea29f\na9 298d68\na5 268dee\n4 1d520d\n4d 1e88e6\n7c 1b4f8d\n70 21955\n59 25998\n7d 29e2c\n61 1f5f79\n9e 10337b\nd9 eed9f\n0 1937ce\n60 1b41ea\n69 1bb43c\n20 1d0ddd\n51 26e21\ne7 ea17a\n46 56599\nfe eabdb\n61 2230d\n3e 1983f5\n32 4dbd\ne6 ea115\n9 42efc\nc6 e72a2\nf7 ea839\n5a 60632\nab 10ee7b\ndd 280cbc\n4a 1cd61\n2d 1967dc\ncb ef45b\n4c 1e8639\n6e 5b8ab\n91 2642a5\ndb 1294de\n83 103ae3\ne7 2bdac6\n9b d1f69\n7d 1f5490\n8d 1029b0\n1b 1d49ce\n49 26307\n7a 64849\n48 1b8558\nae cea01\nc 192688\n9 19a9b2\n54 1f143f\n10 1cd7f1\n17 a25a\nbe 299710\nf9 285134\ne8 e9ff8\n61 1b2f27\nf 1d53c0\n3 41d88\n2c 1d950d\n20 45ed5\n13 1cd549\n0 1d3cc2\n73 1f6631\n84 294cf4\nb5 2696ed\nd2 efc72\n8e c9286\nff 12d652\n31 e439\ndd 2ba3e0\n40 1b70cf\n14 19b1ee\n31 19801d\n51 1b09b1\n4b 26300\n44 1cbc2\n9b 29d877\ndc 1281f1\nc 193948\n0 310\n41 1b713e\n6a 5a8c8\n5a 1b7b8e\n28 d910\n99 1032e0\n3f 1d0858\n6d 22191\nc8 e5e4f\n21 1d9138\n5a 259ae\nea ea2ab\n2 9617\n4b 1ccf0\n23 dacf\n1 19a85d\ncf 2b9a76\ne0 f3751\ndb eed44\n7e 1b3a18\n24 1979a8\ne6 123589\n65 1b2fc8\n24 197944\ncc 2b1a40\nee 124cb2\nc5 281528\n87 10ac10\n58 26f77\ne9 f25f3\na0 107f94\nd7 278abe\n99 d1f0e\n19 1cc42b\nd1 efbfa\n21 571a\n0 19b860\n49 1aef39\n1d 1dba\n80 ca3b9\n95 10b508\n6d 1bb45f\nea e9f8d\n88 ca51e\n8c d2901\nb5 2a2e01\nd2 129386\nc6 ee072\n44 575b6\nbe 107760\nb7 d7248\n95 263fd6\nc 193638\n0 0\n87 25b5cc\nbf cf361\nb5 269493\n14 1d58b2\nfc 2b6546\n5b 1b7bf1\nce ee413\n94 10b7a9\n2c 4605d\nd3 2b1f63\n8d 25b4de\n4d 2633a\n82 25c8c0\nb3 25fe57\n8 192665\n2a 58d7\n79 1f6a31\ne5 ea181\n44 565a0\n27 1d7eae\nd6 27881d\n68 2948d\naa 25f64c\n40 1b737b\n1 1d3cc1\nc 19a972\n20 1d7e07\na4 106a01\n87 1028b4\ncc e70d4\n66 1bc570\nd7 2b1f40\n33 1d87de\n2 1d5247\n1f 1d5a81\n13 42449\n9c 25d34e\n90 c9d16\ne4 eb3de\n71 1b4bbc\n0 15d4\n31 4b6b\n3d 1981a3\ne2 2b6d18\nc7 28050f\n64 2203b\n64 1b4229\n47 1b00dc\n4d 1b0478\n41 1ce40\n36 1d06fe\na6 106ca8\n54 1afa31\n22 3dbf0\n2e 1d1228\ne3 124ac9\nfc ea928\n64 290b9\n93 10c792\nb 1cbad1\n80 25b605\na2 ce877\n58 26f69\nb9 d7677\n4d 25316\nd 19a6c7\nac 267c82\n28 1d0ed0\nc0 2814e8\n99 d3230\na9 d59f2\n4e 1e86a2\n29 42ff\n7d 22d94\n89 d159d\nf4 27df2f\nd7 279de2\na8 cd3f7\n40 1b839f\n6 2e6\n22 4210\n2e 197848\n86 c93dd\n25 41d9\nc4 2814b7\n3e 1981a9\n32 4b71\n1b 19b05e\n38 19f1ab\n99 1035f0\n28 dc20\ncc e7382\nfd ea919\n26 19642f\n4b 1b0210\n1f 42563\nbe 10fb1e\n9e 264185\n7a 21b09\n1 1d3f0d\n21 196396\n15 42411\n6a 20e96\n1d 1d5a18\n11 423e0\ne 1d410b\n2b 1d0f3a\n51 1af743\na 41c32\n1e 43832\ne0 124b31\nc5 ee328\n75 1b4e37\n18 19aff6\n64 1b3213\n3f 1d8ba4\nc6 ef5f0\nf7 f2b87\n47 1b83ca\ne6 285985\n90 10475a\n96 29d75e\n6b 63edb\n64 1b3207\n37 47873\n15 1d4601\n6a 1b3086\n20 5717\nea eb56f\ncc ef422\n6f 20ed8\ne2 2b5a56\n78 2b0b0\n89 102971\n11 1cd596\nb0 26819f\nbe 25ff7e\n2c 3dd01\n58 1d8f9\ncc e70d2\n4c 1b0485\n40 1ce4d\n68 1b43af\nb8 107478\n58 1b7ea5\n84 263666\n65 63d58\nf7 285fd5\na8 cd3f5\n9 1ccd8c\na8 267995\n87 26391a\nc 19b986\n0 834e\n58 1f1319\n1b a134\nf6 27dcda\na2 298c27\n42 1e977a\n8 3abab\n60 2230a\n83 294a71\n16 426b9\nb4 10f95e\n41 1b0050\n97 10b811\n17 192e98\n63 1b324c\n45 1b70ff\n24 1976f8\n7f 5c4bd\n8b 10acc6\nf3 285ff8\n58 1af845\n5e 25c8d\n7b 22abc\na8 260bb5\n5d 25c77\na7 cd585\n58 1b9167\n84 264928\na8 ce6b7\n58 1afaf5\n7a 22d67\nc6 e5f6e\n5b 1b7bef\n78 1bbd3c\nc6 28176c\n36 1d8802\n2d 1977f0\n21 41b8\nc2 ef2a1\n45 2648f\n5a 1b7e3a\n19 90b5\n7c 22ae3\n28 3da30\nb 398e3\n9b 1035f7\n2a dc27\n35 1982fc\n9f 10cbd6\n1a 1cc43f\n41 1b86a0\n6 1ccc7c\n7d 1b4f8e\n71 21956\n9a 10b636\n4e 2532c\n6b 2215b\nbb cf330\n16 3b63b\n98 103333\n3d 3f675\n10 192dff\nad 2a10f5\nc4 ef5d9\n88 d28c0\nc5 ef586\n40 1aedef\n7b 5c4ee\n32 4b11\n3e 198149\n7a 5c4f9\neb ea23e\na2 d6b65\nc2 2b9bfd\n8a d15a5\ne7 eb3e6\ndf eed75\nfd 2bf849\n8 1c9\nf1 12c211\n10 1cc581\n2d 5602\n4 1d4fc1\n4d 1e869a\n56 5f258\n73 5c087\n7c 1b4d41\n70 21709\nf7 27ccd5\nf8 ea94b\n3f 1d8968\n29 3da21\n52 1e90c7\n18 3a4f8\neb f22dc\n7e 1b4d3a\n72 21702\n2a 1cfc87\n3 19a544\n14 42402\nb5 d5fe3\n23 1d7e0f\n98 c9baf\n6 1d3ca4\nd1 efbfc\n49 26309\n1 19b861\n67 2936d\n60 1ec904\n4e 1b72cc\n19 a0d7\n82 103af0\n5 1d4fc2\nf7 2862d9\n56 1f26f8\nea 27d74f\n48 1aeed6\nac 10812a\n35 4df8\n95 d1dd8\n4c 25315\nda efd65\n3 19b86a\n4a 1e891d\n48 1cce8\n2c 1d0f03\n20 3d8cb\na7 298e97\n56 25886\nc 144a\na5 299eb2\n59 1ec2a\n3a 196e54\n85 ca3eb\n8a 25b4a5\nc8 ef3ef\n71 1bbc38\n40 1b86a1\n4b 5fcde\n2 1cb979\n87 10c110\n13 8f67\n1f 19c59f\ndd eed6e\ne6 eb137\n87 103b14\n5b 259a1\nfa f2f5c\n5b 58353\nde efd8a\n4e 1e8942\nae 110471\nd4 eec7a\n21 dad6\ncd 2b9a7d\nde e7a9c\n8a 1029e9\nb5 cdedf\nd6 e6685\n11 19c47e\n69 2a4a0\na7 25f7d7\n6 1cbbf6\n34 19f0e7\n49 1f0c03\nc0 2804e4\ned 12cd5a\n65 1bc826\nc2 27944f\nc9 ef69e\n5c 1d67a\nea 12ba0d\nf5 2b60e2\n18 3a1e8\nb9 cddc9\n71 1f662c\n2d 1cfc40\nee e9fcc\nfb f2c4d\nb4 107360\n86 296011\n4c 1b0487\n40 1ce4f\n7d 1b3a1e\nbc ce037\nbd 29a9be\nb1 107386\nd 41c07\nf2 eba67\n75 1bcf39\nef f237d\na6 107d1e\ne1 eb404\nfd 12c645\n4e 1af1cc\n3 1ccef8\n68 20ef3\n67 1b4221\nc2 277edf\n3e 4c97\nab d6cbb\n6b 1b45f7\n88 10acbe\ne8 124c88\ncd ee47f\n17 1d48a6\n99 10b690\nd0 eff09\nb9 2a1a25\n88 29e48e\nc 192384\n59 259a6\n70 1b38f7\nf5 f408e\n8 9769\n4d 1dfea\n1e 19b02c\nc4 281527\n55 1af9c0\n2f 1d11b7\n23 3db7f\n19 1cc6e7\n2e 3efca\n54 1d7d3\na3 107c9a\n59 6038c\n68 211af\na 19a69c\n67 1b44dd\naf 107e76\nc2 27819b\n2a 197817\ne 1938ed\n2 2b5\n16 99b\n83 d29bf\n61 5a469\n6d 1edaa1\na8 ce6c7\ncf ee1c8\nc8 2b175f\n2b 19e842\n9 19366a\ne 1cbaad\n53 1b7aa6\n1e a102\n65 1b3206\n2b 4618\n8e 29cee6\nb5 1088d3\ne1 124b32\n79 1b4fc1\na5 260782\nc8 ee19f\n6d 2a4e1\n37 1d879f\n6 1d5208\nde 280cb6\n58 25997\ndf 280f63\n36 3f534\n29 1d94dd\n71 2af58\n4 19a57d\n6f 20ec8\na4 260a2f\na7 25f529\n6 1cb948\n1 1ccee3\ndd 280f6a\nab 10f129\na0 267aec\nc2 e726f\n33 1d0732\n61 2206b\n1f 1d5a11\n13 423d9\nb6 29ab1b\n68 211a1\n67 1b44cf\nc2 27818d\n88 ca82e\n29 587d\n6f 1b4626\n63 20fee\n24 549a\nc6 ef2d2\n4a 1e9b71\n7b 1ed108\nbe 10776e\n99 c9c12\nb9 25ffa7\n88 25ca10\na6 1069f8\nc8 e5ded\n40 1b73df\n4b 5ea1c\n4a 1b84ef\nc3 ef2b0\ncf ef48a\nc8 2b2a21\ne3 f21e7\nef 28581f\n19 1cd6fb\n54 1e7e7\nb8 268304\nd 1d50a9\n1 41a71\nc 1d3df6\n54 25871\n13 1cc2db\n37 ce91\n30 1d0428\n1e 192f8c\n7b 6459c\n14 8ce0\nf8 ebeb9\n1 f\nd 193647\n68 20e8f\nef 27c45b\n7 1d3d07\n65 2202c\n2a d979\n9b 103349\n35 19804e\n44 5e642\ne9 f2337\n97 26427b\nc1 2b1609\ncc 2782ba\nff 2b6294\nb6 2a2bbb\n5a 1b7b8c\n8f 10acf7\na3 2a0f76\nf7 286029\n8c d18ed\na9 ce71c\n8 3ab3b\n0 39794\nc 1ccdcc\n15 1940ff\ncd ef421\neb eb56e\na6 d694a\nef ea023\n75 1f68a9\n4e 56442\nc5 ef5dc\n10 1edf\n7a 1bc045\n18 192fb6\n73 5adc3\n7f 1ee3fb\n42 5782c\n96 d33b2\ne8 ea298\na9 ce974\nef 27d71d\ne3 ea0e5\ncb 128dd7\n5e 56db3\n27 448e\ndd 128252\n75 1b4e99\n18 19b058\n25 46f0b\n5b 1f2891\nb0 2613bf\na5 107cc2\nba cf081\n98 25be0f\n8a d1605\n59 1f1564\nb6 29ab29\n6f 2a4d8\n4d 1b7266\n68 1eda6f\n5d 259d7\n41 1f1b24\n12 439ac\n63 1edbc0\nc5 ee016\n32 478a5\n7b 5af7e\n50 1af744\n2a 1d0f3b\n94 d310b\ne5 27d31f\nc2 2814f1\n2f 19784b\n23 4213\ne5 12bb8f\n87 10c180\n33 196fae\na9 cd406\nbe 2a1a50\n40 1b02fd\nce ef6d5\nc3 128a24\n5b 1b8eb3\ne0 12cb71\n58 26cbb\nf0 f2d9c\nfc 2863d4\n28 4362\nd1 11fd1a\ndd 2b3352\n65 21028\n27 197690\n82 25b34e\na3 ce878\n59 26f6a\ne7 f24c6\ne0 2b5a5d\ne8 f3846\n1c 193fa9\n10 971\n4a 5e761\n5 1d520e\n94 c9a8b\n56 1b9048\nad 298fe7\nca 11f56c\n6f 5b8ae\ne4 eb432\n96 104794\nd9 f0061\n9e 10463d\n5a 1afafc\n64 2a37b\nd5 11fd4b\n29 1d928f\nf5 2b60e0\nc4 2b2b49\n6a 2215a\n15 436d5\n8a 10c2a7\nbb 10f83e\n28 1cfebe\na7 ce847\ne4 2b5a2c\n79 1ee681\naa ce722\n63 5a4c4\n1d 43ad8\n6f 1edafc\naf 11016e\nd5 ee977\nf5 284d0c\nc4 281775\n25 4497\n1b 3a500\n30 19802a\nd5 2bb859\n1a 192f5d\nbe d6140\n3c 3f684\n96 10cae2\n1b 19affc\n1 1ccef1\n36 1d87a0\nf7 eaadb\n56 56efa\ne8 e9f94\n5 8380\n62 22313\n9 1d50da\n9f 2656f8\n93 d20c0\na6 d5688\n75 1f55e7\n80 ca3bb\ne4 28439e\n34 1d0457\nbc 299699\n42 1de6a\na0 106cd0\nac 29a308\nbd 299948\nda 11fecd\n7f 5c20f\ne4 f372e\ne0 124b33\nf4 27cc6d\neb eb570\n4a 5798f\nd7 278b20\n9 1ccd38\n7d 29b70\n4c 265d9\n20 54cd\n6 1cceb8\na7 260a99\nc4 ee319\nce ef429\n8 165\n96 c9a90\n73 643d3\n51 1f1161\ne 3ab83\n49 265a7\n2c 19fb2f\n20 c4f7\n65 20d78\n5 1cbbf0\n2 15db\n33 4b72\n3f 1981aa\na4 d68e1\ned e9fba\n8 97d9\n49 1b8549\n40 1af03d\n62 222af\n6 3acda\n37 3e271\n26 1d106f\n73 22c81\nd6 2bb54f\n33 19f04e\nc5 11f698\nf3 12d786\n11 436f8\na6 298e96\n2 1d4f8b\n8 16d5\n4e 1b047e\n42 1ce46\nfb ebe6d\n88 c94fe\nd8 11fe70\nce ef6d7\nff f2c6e\n66 1b44dc\n8d 10c016\n42 1e8766\n8 39b97\nb4 2997fe\n97 2956b1\n6e 1f4de3\n44 1af06e\n66 222e0\nd3 eff13\n4b 1b7290\naf cea00\na5 268b32\n4 1d4f51\nec 2b5be5\n84 10ae48\n72 1ed020\nf7 12d7b7\n46 1ce77\n8e 296168\n82 102b30\n27 3ee72\n1a 192fbf\nac ce6e8\n96 c9da0\n28 42fe\n69 22402\n94 25bc85\n26 1cfd9d\na5 d6b8e\n20 1963f7\n3e 1983f7\n32 4dbf\nc7 e5cc1\n8d ca550\nd3 120fd5\nab ce6c1\n6b 1eda77\n8f d2897\n69 211ae\ncc 2b9a7c\n85 d1787\n95 25cf48\n52 1b7d47\n75 1b3b75\ne0 12386f\nec 2b6ea7\n2 15d9\n58 1e96d\nf0 eaa4e\nfc 27e086\nd3 e6901\ndf 279f39\n28 430c\n56 26df8\n60 1b3236\nac 298d44\n29 19e83b\nb5 cdf4f\n2b 1d9296\n51 1b7a9f\n7a 5b229\n20 197907\n2 3a9ed\n66 1f49d0\n4b 2504c\nc1 11f41b\ncd 2b2a53\n48 1b7224\n42 1f085a\n50 26b74\nc 188\na5 298bf0\nc1 2814e9\nb9 d60b3\n88 d2b1c\nce 2818c5\nff 284e5c\nc2 ee28d\n4 19b83f\n6f 2218a\n2 1577\nf8 27cb45\na6 29a158\n29 3da2f\nf8 ea959\ndd eed7c\n6 98f6\n4f 1cfcf\na0 260a6e\n2 1cb987\n87 10c11e\nde 279c8c\nb5 2600cf\nd2 e6654\n77 22996\nb7 10766c\n2a 1d9545\n50 1b7d4e\ncd ee41b\ne0 eb3a1\n9c cb15e\n27 1d93be\nc0 281796\n2b 55c8\n6f 29216\n68 1ec7ad\n9c d1f30\n7f 1ed457\nb 15f\n59 5f12c\n9b d21b3\n25 196437\n76 1b3b6d\n58 1b7b85\n1 1cce81\nc0 278196\ne2 eb408\nb2 2a18d6\n86 103e33\nb7 1073ca\na6 29a1c8\n37 4def\n85 264997\nd6 2820cd\n8 3ae4b\n42 1e9a1a\n73 1ecfb1\n2d 19652c\neb 12bcae\n9d 103373\n58 1b90f7\n45 24f1f\ncd ee161\n25 4487\nc4 281765\n86 10abad\n81 d276a\n26 197691\n62 5ba41\n10 43951\n62 1ed975\nb1 d625e\nbd 269896\n22 c560\n2e 19fb98\n9f 295568\na4 ce5f3\nc1 2b9957\na1 d68bf\ncc 280608\n85 d1795\n74 22c3a\n2c 1d11bf\n20 3db87\nf 1cd072\n3 39a3a\n79 22dd1\ndc 2bb69f\nd0 128067\n4f 579b3\n15 1d45ff\n6a 1b3084\n2a 4305\nce 12085d\na5 106ca0\nc5 e722a\n7a 1b3c93\nf7 f2dd5\nb9 cf089\ne2 27d5f8\nce ee1c9\n4c 5770d\n8 157\nd3 28210f\n8f 25b723\nb6 10766b\ncd 2b2c9f\nc1 11f667\n96 103460\ne9 2b6bc9\n9d 10b65f\n5b 26c61\n3 98c6\naa 298d7c\ne1 28468c\n40 1f0aab\n42 1e9a7a\n2f 45db7\n7c 1ee401\n70 5adc9\nf7 2b6395\nb4 107352\n2 1ccc49\n17 1cc558\nab ce96d\n88 102970\n9 1cbd16\n91 265575\n2c 58ad\n21 3ebfc\n66 2101e\ndf 282289\nd3 eec51\n9d 29ee11\n91 10b7d9\nc0 e6f4c\n7 1d4f69\n6 15a8\n11 1d58f4\ncb 2818f5\n5e 1af8d1\nf5 12d504\nc4 280205\naf ce752\n45 26481\nec 2b5937\n84 10ab9a\n58 5f3d9\nf7 12d509\nc 1458\n46 1cbc9\n85 264c37\n2e 197a96\nd8 128222\n22 445e\nde 281f76\nd2 ee93e\nec ea2d5\n4 9641\n4d 1cd1a\n86 10ae5b\nd5 279acb\na3 107c8a\n28 1964ec\n66 1b2fc2\n31 5dcd\n14 8f9e\nb4 1088d0\n50 1f1160\n26 3ee81\nd6 129361\n10 1d4623\n6d 2921d\n24 3ebbe\nd7 ee9e2\nf2 1251eb\n94 295655\n9e c9be7\n82 295d34\ne1 f3440\n4f 26341\nc2 2baebf\n89 103f51\ned 2bdf34\ned eb27a\n8f 29cf55\n9d cb1c1\n43 261a9\n70 1b3897\n53 1af74a\nd 1707\n53 5818c\nad 268c97\nc 1d50b6\n0 41a7e\na1 d565f\nfa ea950\n87 10abae\n6c 1b461e\n60 20fe6\n40 1ce41\n7d 1b3a10\n4c 1b0479\neb 12ba0e\nd1 12103e\n55 1f2702\n86 d27a3\n31 1a05c5\na 15e\na8 ce973\nee 27d71c\n65 1bb5b6\ne2 ea0e4\n90 10b7e8\n9c 29ee20\nee 27c1ac\n3c 5f52\n1e 1cd9d2\n12 3a39a\n88 102c8e\n67 1f5fa1\n59 1f1566\n7b 647d8\n8a d1607\ne7 eb448\n3a 198116\nbc cf049\n2c 1967cd\ndc 280cad\n21 1cfb1c\n1e 1942b0\n12 c78\nc7 ee011\ne2 12481a\n7a 1b4ca9\nbf 2a1c9d\ned f35d6\n7a 21807\n28 c64e\n6d 20ecf\n1b a0d0\nf9 284e96\n58 1f12b5\n7 1d4fbb\n9a cb126\n1d 19c5f8\n11 8fc0\n83 d2771\ne9 e9f97\na0 d68be\n65 1f5c98\nc0 2b9956\n4c 1cd19\ne5 2b5781\n3a 198118\nd3 12830f\ndf 2bb947\ncc e5e10\n74 1b38c6\nfc 27cb08\n90 10b53a\n9c 29eb72\na8 ce6c5\n9 19a694\nb5 10f711\n84 10c17a\n2b 46024\n5d 1b7e65\na7 25f773\n11 1cc582\ne8 eb256\ndd 2bb94e\nd1 128316\n29 19655d\n74 1ecfea\n9e 25d0fb\n92 c9ac3\n57 1e8e9d\n37 5e05\n7e 1b3d36\n47 562ec\ne6 1238a7\ne1 2b6a74\n47 5f8fc\n19 3a4f9\ne 84df\n11 1d45c0\n24 1963c8\nf2 27c9f5\n9e 25bdd7\n30 1d047a\n78 5af22\n12 436fc\n5b 56dd5\n7b 1ed16a\n4a 1e9bd3\n60 5b72a\n45 24f21\nb8 d73ca\n5b 1f156b\n8c 10acf1\nfe ebef1\ndc 278c7f\naa 106e3e\nff 12d660\nf4 286023\ncb 2bad79\n26 3ee73\nf5 28627e\nce e5e17\n28 1cfebc\n6c 1b4680\n60 21048\n14 9a2\n81 d29c6\n5 41ab0\nb4 cdee0\n52 5f219\n5e 1f2851\nb0 cf17d\n46 1aedb7\n0 e\nc 193646\ncb ef715\n62 20db1\n6e 1b43e9\n8b 296146\n45 5e641\nf4 eaa71\nb9 299985\n50 1b09b2\n4a 26301\n1e 19c59e\n12 8f66\nd2 1295d2\n1a b21\ndc 12849d\nb3 299589\n57 1d7e7\nce 120b19\ncc 281680\nc0 ee048\n65 2a38a\n8 1d4065\n7b 1f69d4\n4c 1b01d7\n40 1cb9f\nc7 27816b\nc8 e5de1\n10 3b3b5\n97 296981\n2 42d9d\nb7 10f6b8\nc7 ef57f\n42 1aede8\na9 267c42\n2f 1d7ff7\nde 278966\n3b 5f8b\n78 1ed170\n1c 1d5a0b\n10 423d3\nc5 ef2d8\n97 29d99f\n55 56c54\ne9 27d747\n75 1ecfe9\n44 1e9a52\n92 104761\n17 1d4608\nd8 1294e2\n0 1924ac\n22 571e\nc1 edfe5\ncd 28161d\nd9 efd53\n9 19b9b8\neb e9f8e\n85 29e307\n96 c9af2\n3c 6210\n31 3f55f\n19 1cc439\n21 3d93a\n2d 1d0f72\n72 5b070\n7e 1ee6a8\n15 1d5b71\n6a 1b45f6\n48 262fa\n1c 19c597\n10 8f5f\n2 39a3b\ne 1cd073\n3f 1d060a\nbc 260233\n9f 25c0e6\n17 4240a\nb1 299582\n2f 3efd7\n55 1d7e0\nb5 107663\n68 1b43a1\n4c 1b0477\n40 1ce3f\nf2 27c9e7\na0 cd54e\nac 260b86\n46 261db\nb 1d50ef\nef 2b5bef\na6 2a2516\n7 1d4f5b\n1d 19c598\n11 8f60\n39 196eb0\n8 193919\nda e67b9\nbd 260234\n49 26609\n5f 1f15fe\n1e 19424e\n12 c16\n27 1d9110\n9c caeb0\n48 1ccf6\ncf 2782c2\n84 10aba6\n67 1b44df\nef 124a13\nc2 27819d\n2a 197819\ncc 128e00\n4e 1aef1e\nfd 12c397\n10 192b4f\nb8 cf2c8\n6 3aa1c\na7 ce5fd\ne4 2b57e2\n6a 221ca\nd7 278acc\n86 d273f\ncf e5e18\na6 107d2a\nd6 279ad3\n9c caf04\n27 1d9164\n8b 103f58\n1c 3b78b\nef 2bdf3b\n1a 3a1f1\n8d 10bfc0\n41 264b2\n4d 265e6\n60 1b4498\n23 1d938d\n98 cb12d\nc0 2baec8\n5b 1d90d\nf1 2be45f\n1d a108\n77 1bbc0e\n46 1b8677\n71 5c08c\ne2 285946\n94 d1de7\ne5 f24bf\nf5 27dc80\nc8 1207d1\n38 4f7f\n8b 10c2a6\nb5 25fe91\n14 1cc2b0\nc6 1279ec\ne3 12481b\n5e 1b7e7b\n7b 1b4caa\na8 cd457\n37 19703f\ne4 eb3d2\na9 29a2e6\n5b 25cbd\n87 d147e\na8 25f647\n8 16c9\n39 4c60\n28 197a5e\n9c d1f90\nd6 280b5f\nae 106bb3\n6a 2a50a\n75 1b4bdf\ne1 eb102\nf6 2bf74c\n26 3d901\nc5 281766\ne9 e9ff7\n12 19412a\nec ea2d7\n4 9643\nbb 26986e\n1a 1d5c8d\n7a 21805\nb9 269873\n68 20e9f\nef 27c46b\nf 19a97a\n9f 10330a\n3a 197104\n41 1b70ce\n84 d1786\n53 26e1a\nb 1d539f\n94 25cf47\n8e d2896\na4 cd5ed\n5 32\nc2 27945d\n86 ca69f\n73 1ed2cf\n95 29ecba\ndc 2789c3\naa 106b82\nc9 28164e\n24 5748\n91 d337b\n58 1b7be9\na2 25f4f7\n8d 29610e\n81 102ad6\n59 26cbc\nee 27c45a\n16 1cc559\n44 1de92\n79 2ae03\nee eb5a0\nae 1080cd\nd4 e68d6\n2e 58a8\n23 3ebf7\n19 3b74b\n4e 1e8940\n5c 1b8edc\n2a 4709b\n50 258a4\n7b 63276\n4a 5fcdf\nf2 2b7679\nd7 280e70\n1b 1d82\nce 128e59\nd 1d3df7\n2f 47069\n55 25872\n75 1bbc07\n44 1b8670\nd6 2ba28d\n7f 1b4ff7\n73 219bf\nac 2608d8\na0 cd2a0\n2f 47325\n55 25b2e\n19 1d89\n4e 1aef7e\ncc 128e60\n25 3ee7b\nd5 12935b\nbc 25ff75\n60 29088\n6c 1bc6c0\n62 2104f\n6e 1b4687\nc 97a8\n52 6022d\n46 24f19\nb 1d3e2d\n4c 25323\n69 22152\ndf 279c29\nd3 e65f1\n9 141a\na8 ce9d5\nea 2bf1cd\n4b 1f1c12\nf2 125429\nd7 eec20\n12 3b3ae\nb6 29aac7\n1 1cbbbf\n0 19250c\n2d 1d11c0\n21 3db88\n75 22c3b\n4a 57745\na9 106b18\nfc ea918\n60 1bb2e6\nbd cdd8a\n63 1b2f2e\n1e 19c352\n12 8d1a\n80 295fd9\n0 15e0\ne9 ea2a5\n1 9611\n2a 197ac5\n46 1e87e9\n11 3b5f4\nb8 cddb8\nd4 279dea\na2 107fa9\n84 10be5c\n58 6069b\n18 193f6a\n4 1cb9b3\nc5 ee008\n18 ac8\n28 197abe\nc 19b988\n0 8350\n73 2acbf\n87 26391c\ncb e710b\ne8 eb258\n76 1ee551\n26 1cfd9f\nea f38af\ndc 28221d\nd0 eebe5\n85 d27a9\n8 39879\na9 cd45a\n40 1b0351\nf5 27cc6c\n67 5ba03\nce 2b2cf9\nc2 11f6c1\n5a 1afb50\n2b 1cfc78\n9 19b956\n8a 10c243\n35 e14a\n95 296918\ncd 128dff\n4f 1aef1d\n58 1b90f5\n20 5469\na7 260a35\na6 298be8\n7a 1ed427\n32 1d0483\nb7 110c1a\n2f 3efc9\n55 1d7d2\nef 2bf19d\ne3 12bb65\n7 39\nbd cdd96\n8c ca7ff\nd2 121284\n74 22c48\n65 20d6c\n7b 1ed3b6\n66 1b4290\n1d 19c2e8\n11 8cb0\n19 19b057\n1 9621\n63 1b4500\nc7 2804ad\n8d d18de\n91 c9a67\n9d 25d09f\nea 1236b1\n40 1e171\na8 106b19\n42 1de5a\n2b 562c\n68 1ec811\n7f 1b3d35\n88 103c94\n31 196cf7\n0 193760\n6f 1bb404\nb 1421\nb 1d40db\n46 251c7\n69 22400\n3d 5f61\n1f 1cd9e1\n13 3a3a9\n9 19a704\n54 1f1191\nc8 2818ef\nf9 284e86\n25 19fce7\na2 ce815\n4f 1cd2f\n58 26f07\ncc ef6ce\n59 1b8eac\nb4 108678\n2 1ccbd9\na3 2607ba\n4b 5fa94\na0 ce5c2\n9 39b98\n6d 20ec1\nf4 f2e2f\n42 1b7390\n55 25874\n2f 4706b\nd1 2bb57c\n8d 294b90\nc8 28038d\n61 29097\n6d 1bc6cf\n89 102c8f\ned e9fb8\na1 ce871\n16 c49\nf8 12407b\nb4 cf202\n2a 1d9289\n50 1b7a92\n5b 5f0cf\n3d 1d18d3\n31 3e29b\n0 3ad04\n64 1f4ce7\n63 63a80\nbe 108a84\n28 47040\nb 42ef3\ne1 27d350\n2c 1d11b1\n20 3db79\n5a 1e982\n31 4dc5\n3d 1983fd\n98 25c0bb\n77 1b3b6e\ne2 123868\nee 2b6ea0\nc8 e73c1\nf9 ea958\n78 21800\nff 27cdcc\n5f 1b9130\n53 25af8\n84 ca44e\n9a 296a98\n43 26457\nea 2b590d\n43 1b86b5\n65 1b452c\n82 10c14e\ncb 11f827\n36 196fdc\n2b 1cfc88\n7f 1b4d3b\n73 21703\n42 1e16c\n40 1aed81\n1b 1d471e\nc2 2814ff\n5 19a81c\n5d 1b9135\n2b 472f4\n51 25afd\n34 46867\nda 2ba409\na9 106b26\n84 d279a\ncd e5e73\nc5 ee2b8\n11 19ae9e\n88 29e1d0\n4 19a5d1\n77 1bcf40\n80 ca41b\n3e 3e677\n4 863b\n21 546a\n24 19e9d2\n7 19a885\n5f 60670\ne4 2bf03e\n2c 19658d\n72 1ed012\nd5 2ba537\nf7 12d7a9\n27 19fcee\n0 1ccef0\n4c 5e799\n18 8e06\n9f 2643d2\n11 1d48de\nd4 279ddc\na2 107f9b\n86 29d0ad\na3 299edc\n5b 25a11\n21 c814\n2d 19fe4c\n73 1f68d1\na8 106b17\n37 1d06ff\n16 426b7\nb7 d6298\nb0 29982f\n6b 1b3085\ne6 124b6b\n1a 8e71\nec f2307\n2d 47320\n43 1f1ab9\ne0 f36fd\ne 1cbb11\nc3 ef5b0\n3e 1d8905\ncc 2818cc\nfd 284e63\nc0 ee294\nef 2b593f\n21 19e994\n4a 1b8551\n6d 62bef\n61 20d47\n6d 1b437f\nc8 27803d\nb0 cf171\n1 42d41\n53 1b8d5a\n70 1bcea7\n18 d66\n19 8e07\nda eea97\nb 16cf\na1 2a2221\ncd 28167f\nc1 ee047\n9 1d4064\n20 1976c9\nc5 2baef8\na4 d562d\n80 294a77\n87 d14e0\na5 268b40\n4 1d4f5f\n4d 1e8638\n7c 1b4cdf\n70 216a7\n42 1b0358\nf7 27cc73\n86 1028b3\nf8 ea8e9\n56 26dea\n3f 1d8906\n94 d33b9\na8 260907\ne5 27d5cd\n50 1af9f2\n2a 1d11e9\nda 2bb6c9\n1f 193fa1\nc6 127a40\n13 969\n8a 103c9b\n34 1d8ab7\ne3 1235c9\nef 2b6c01\n7b 5c4fc\n14 c40\nae 2a260b\nd4 280e14\na2 10efd3\n6b 221cb\n5d 1b0b39\n2b 3ecf8\n51 1d501\nc8 120833\n71 1b3896\n40 1b02ff\ne9 ea237\na0 d6b5e\n68 5b881\n4d 25078\na8 ce719\nc5 e5f76\nf3 f4064\naf 298da0\n88 c92b0\n2d 55f2\n21 41a8\n2d 1977e0\n88 25b49e\nb8 110d9a\n3a 196eb8\n98 104851\ndf 2ba6e7\nbf d764f\nfc 2be834\nd0 281e57\nfc f3f8c\n4a 1b84ed\nc3 279450\n2d 4332\nc0 edfd8\ncc 281610\nea 27d75d\n59 1e96e\n29 430d\n1a a133\n61 1b3237\n2b 1d8212\n9c 26442c\ne7 2b6d58\n8a 29cf17\nb1 108904\n82 29d06e\n9c cb150\n27 1d93b0\n7c 1bd2dd\n70 29ca5\n5f 1b9190\nb 1d40dd\n53 25b58\n78 21860\ne6 2856d7\n90 1044ac\n64 1b2f59\n9c 25d0a0\n90 c9a68\n55 1e8e42\nb6 2997f7\ne4 eb130\n59 1e90c\nf8 ebec7\nc5 128ca8\n47 1aedc6\n62 1edbc1\n28 3eff2\nba 26126f\n50 1b8f9e\n2 19a545\nd 9a55\n53 604da\n6c 1edd50\n60 5a718\n4f 1e9c03\n43 565cb\n45 24f11\n94 c9d39\n73 1b3bad\n2f 1d9257\n23 45c1f\n55 1b7a60\n54 26b33\nc 1d50b8\n0 41a80\n87 29d04c\n62 1ec95f\nef 12bceb\ne4 2846ae\nd6 2b3203\n9c 104634\n14 194100\ncd ee40d\n29 3ed55\n7a 5c48b\n48 1b01fa\n34 d145\nda 280ce7\nc5 2b9928\ne7 12cb9a\na9 d6a14\n76 1b4be3\n22 1cfb30\na7 1102c7\n13 19aea5\n64 1ed949\n3d 4f4f\naa 1103ec\nd0 eebf5\ndc 28222d\n21 3ebfe\n2c 58af\ndc efd8f\n72 5c334\nc4 279427\nf5 27c9be\n40 1b00a3\nd2 280b90\n98 c9bbf\n77 1bced2\nc6 e6f92\n97 10b81f\nb 3ae61\n6f 1f4e44\n14 1cc55e\nb5 26013f\n42 1b86a8\n73 1bbc3f\n53 1af6f6\nd1 1295d8\n9b 29d879\n7f 21829\n4e 1e292\n14 19aede\ne3 f3449\n64 21025\n8f 296107\n83 102acf\nd1 27883a\n8e 29d202\nab 29a031\nb2 299836\n8e 294e54\nd6 e68cf\n19 42529\n85 c9199\n8a 10bf87\ncf ef748\n95 29665c\nc 1cbaa8\nc1 ef547\n2e 3ed1a\n54 1d523\nbd 2612aa\n10 3a091\n1c 1cd6c9\nb1 cdc72\n6b 62977\n98 d1f5f\n3d e2a1\n91 103437\n9d 296a6f\n60 5a4ca\nce e5e27\n6c 1edb02\nb6 107359\nd2 129640\n96 265290\n28 3da2e\naf 298ffa\n56 1e7fa\n32 1d876f\nbc 1074b9\n28 45ba\n9f 10336c\nd7 e6622\nde 282226\nd2 eebee\n8d 264af0\n81 d14b8\nd 43177\ne1 2b5a60\n47 5e8e8\n40 26205\nf5 f2b20\nc7 2817d1\n6b 1ec809\n19 427e5\n0 1934b0\n15 1d48ad\n6a 1b3332\na1 298c11\nc1 ef549\n54 1d525\n2e 3ed1c\nc0 e7268\nc 192624\n50 1b9010\n80 103adb\n77 1b3b7c\n4f 1e98f3\ne2 123876\n43 562bb\nee 2b6eae\ne5 eb433\n45 1b710d\ndb 1284ca\nbd cf306\naf 298ffc\nf7 eaa77\n37 1d0761\nc3 e7200\ned 2b593a\na4 2a2261\n28 19e89e\n6d 1b311f\n51 1eac5\n61 20d9d\n6d 1b43d5\na7 267815\n59 5f12a\n3e 196e85\n19 4283b\nb9 ce007\ne2 f3758\n94 10b4fb\nbe 29ac80\nb2 107648\n8c 1029af\n9e 1048e9\n48 1d008\ne7 eb138\n47 1b0336\n5e 1b8e8d\n52 25855\n87 10ac12\nd4 2b325c\n8 1419\nbc cf2f7\n94 10b817\nb1 108646\n58 1b7b87\nf 19b98e\n3 8356\n85 295d5d\nbb 10fae0\n86 102b6f\n5d 1b8ee9\n2b 470a8\n51 258b1\nda e7a17\naf 108122\nd5 e692b\n48 1b84e6\n18 192fc4\n29 1cfebd\n7e 1b4ce6\n72 216ae\nb1 26971c\n6d 1b4681\n61 21049\nb4 108622\n7 1cbc59\n97 d30a1\n28 19e8ac\n81 103dfa\ne5 2bdddd\ne5 eb123\n87 29cdfe\nbe 108782\nf9 f41a6\nb8 cf088\n28 19680c\nd8 280cec\nd 192633\n53 1e90b8\n35 19f0e8\nc6 127732\n8c 10bfc1\nf5 f2dce\n68 1b30ef\ned f3886\n50 26e22\nc 436\nce 2b2a5b\nc2 11f423\na5 298e9e\n7e 1b4f94\n72 2195c\n2a 46095\n79 1b4d05\n5c 1b7ed6\n11 970\n1d 193fa8\nc4 127a47\n8a d2865\n95 25cf3a\n20 577b\n88 29e1e0\nd0 efc5b\nc8 e60ff\n6d 22441\n8e 10acf6\n9d 104881\n49 250a9\n44 24f20\nfb 28514b\n5a 1f156a\n4a 576e1\neb eb2c2\n36 1d0762\n9f 10462e\n28 587c\n38 cfaf\n15 19c4af\n47 1b86e6\n86 26397b\nec eb289\ncf e713c\n4e 26332\n52 1b7a35\nc1 280227\n83 d29bd\n16 999\n5b 1b7b8d\nd6 129673\nfe 125305\n9b 10cbf9\n4e 1f0980\nb8 110d46\nbd 110d68\n2 2b7\n3f 196e86\ne 1938ef\ncd 28035b\n29 3dd31\n63 1ec900\n49 1b7225\na7 107fcb\ne7 27c304\n15 19c44d\n5a 259a0\n0 41ae2\nc 1d511a\nca 2795b4\ne5 f3471\n24 1d0e1c\n81 10c148\ndb 1281b8\n19 19c5d7\nf5 ebd40\n98 d1eff\na7 298be7\n7b 1ed426\nec 27d723\ne0 ea0eb\n28 197810\ndc 279c93\nd0 e665b\naa 107e52\n5a 1b7eac\n86 26366d\n2d 19e8ce\naa cd3fc\n28 1cff2e\n70 219a9\n7c 1b4fe1\n41 1e875e\n29 1964ed\na4 107fd3\n1d 42568\n86 d27a1\ncf e5e7a\n55 1f2700\n9f 10b906\n66 1b4292\n64 1b4289\n8c d188b\na9 ce6ba\n84 103b0e\n58 5834d\ncd ee16d\n56 1b0cea\n2 1cbc37\n57 1f2459\n92 1044b3\n1c af7\n89 d2b1b\ncf 2818c4\nc3 ee28c\n20 41b7\n2c 1977ef\n3 6a\nf 1936a2\n2f 19e8d7\n70 1b38e9\n4f 1b751b\n6a 1edd24\ne4 f21b0\nc0 ef5a8\na9 2a10c4\n25 471c9\ne8 ea2a4\n0 9610\n49 1cce9\n5d 1f1595\n8e d1636\nad ce75b\nc 3ab7a\n28 1cfc10\n1a 19427f\nc6 ee32c\ne3 eb15b\n81 264c14\n14 192bf0\n40 24eef\n4c 1b8527\n2a 4615\n79 1f576f\ne8 eb566\n49 1dfab\n97 10b7b1\n49 1e267\n88 25b75a\n82 25b350\n73 1ee521\n67 1b320d\n96 1034d0\nd4 12960a\n56 1af728\n89 d2b7d\n20 4219\n2c 197851\n72 1ee2d6\n51 1b9001\n16 1cd5dd\n46 251d3\n63 22002\n66 1bb56a\n82 103df2\n1a 194281\n39 5f30\n6e 1b3125\n32 1d072f\nc6 128a00\n3b 3e3ed\n85 d1485\nda 2bb6cb\n3 3ad0a\n67 1f4ced\nd6 ee97d\n8c ca541\n1c 8e37\n0 1d4f84\n37 465bf\n6 43028\n1b 1cc432\n3f cfe8\n38 1d057f\nb8 1087ac\nac cd498\nb7 107608\n5 1d3c9e\n78 1b3a40\na5 ce840\nb9 cdd59\n8e 10c274\nd1 e78ac\n19 19aff5\n94 10cadb\n64 5a49b\n3c 198140\n30 4b08\nb7 2600d4\nfa f2c4c\n50 1b0a06\n5b 58043\nc2 e6fb3\na5 260a2e\n4 8381\n77 2acf0\nbb 2685ac\n1a 1d49cb\nd5 27886b\na3 106a2a\naf 29a062\n88 ca572\na 39b9e\n31 ceb9\n3d 1a04f1\nba cf01f\n0 9922\ne4 e9e70\nc7 e5d23\n6 19a886\n29 197abf\n6f 1bc6d4\n63 2909c\ne5 2b6aa3\nef eb2e5\n4e 57704\n25 c529\n3b 1d8b73\nae 106b5d\nf7 27ccc7\ncd ef6cf\n48 1aef38\n84 d1794\na1 ce5c3\na6 106a06\nba 1074e3\n4b 5e760\n95 c9a8a\nbe 10faae\n41 1af090\n8d 294b9e\ndb 280cf4\n36 4dee\n5d 25c85\n41 1f1dd2\n72 1ecfb0\n2c 19652b\naf ce6f0\nec 2b58d5\na5 cd5e0\n46 1af065\nfd 12d65b\n99 10cc00\nd6 278b21\ndf 2bb9a9\nd3 128371\nc7 2804bb\n8d d18ec\nfc 2bfaf6\nf0 12c4be\n79 1b39df\n48 1b0448\ne5 28440f\n44 1f082e\n20 3eb9b\ne 16ff\n3 3aa4e\n71 1f55b6\nae 268c8f\na2 d5657\n67 1f4a31\na7 29a167\ned eb2de\nbe cdd8e\na2 299edb\n14 3a0c4\n81 10c0e8\n23 19e74f\n89 103ca3\nda 1213d9\ned 2bdc86\na3 2607b8\nb4 108676\nde efd98\n4e 1e8950\ne1 eb410\n2f 47077\n55 25880\n75 1bbc15\n44 1b867e\ncc 128bb2\ndd eeac2\nad d6a37\n3a 4c68\ndf 128497\n26 1cfb53\n2a 1d11f5\n50 1af9fe\n45 1b83c1\n63 1b450e\n31 1d173f\n14 1d4910\ne3 12ce7b\n42 1b7144\n0 3acf4\n9f c9be8\n4 1cceb3\n35 1d044a\nb8 cf026\n4e 1dfe2\nae 106b51\nd0 efc6b\n8c c927f\n38 1d081d\n90 295678\n6c 5a5f2\n38 4c5f\nbf 26022b\ne8 27d49a\nd2 129330\n1a 192fbd\nb6 10f70b\n3a 1d0888\n68 221c1\ne3 ea155\nef 27d78d\n31 1d0737\n4 15a1\n6d 1bb71b\nea ea249\n95 2652f8\n5a 1f1312\n69 22410\nbf 107515\n26 1cfdab\nf1 ebd03\n93 29d9de\n21 197968\ne7 27c312\ne8 e9f88\n70 1ecfb7\n77 29a20\n46 26489\n2f 1d7fa5\n3f 1d1b18\n33 3e4e0\n81 29e088\n80 d1701\n8c 264d39\n3 4304c\nff 27dde0\nf3 ea7a8\n2a 19fb67\n9b 295537\n10 19ae9d\n44 1f0830\ne9 284525\n14 436d4\nb9 d73c9\nca ee136\n2f 1d0f19\n77 22994\n23 3d8e1\n7 1d3f45\n45 5fbb1\nd7 281e2e\n68 20e9d\nef 27c469\n6b 62c25\n37 1d87af\n2c 3efd1\ndc 1294b1\n66 5a742\nbb 110a9e\neb ea2ac\n3 9618\ne1 2843de\n40 1f07fd\n2f 1d7fa3\n46 26487\ne0 27d5ff\n39 1d081e\nd1 efc6c\n8d c9280\nac 106b4a\n46 1de8b\na6 1069fa\n7a 5b239\n6f 5a5ea\n4 1d3c9f\na9 267994\nb6 107609\n36 1d1a22\ne6 284661\nd 84d7\n6 193798\nb5 110c11\n37 196d2f\n2b 1d9536\n51 1b7d3f\n2b 47046\n5d 1b8e87\n51 2584f\nc8 128b81\nd9 eea91\n37 465b1\n6 4301a\n87 10c172\n63 1ec660\n29 3da91\n7a 5b1c7\nc8 2bad6f\nc4 ee317\nce ef427\nc2 28179d\n5f 1b7bcc\n44 1b83d0\nf7 27df37\nbd cf368\n40 1b8401\n0 1ccc34\n84 295fb8\nb5 29954f\n83 10bea3\n14 19b1e0\n1f 4281d\ne4 e9e6e\n84 294ce6\n7d 1bbdc0\n4c 1b8829\n40 251f1\nfa ea8ee\n87 29d03c\n4c 2632b\n6a 22478\n6a 1bc6f8\n50 1b7a2e\naf 29904e\nb4 107600\n4a 1b04b1\n7b 1b3a48\n3c 1d1b20\n30 3e4e8\n1f 1cd9d3\n13 3a39b\n8e 10c2c8\n53 1f1158\nd 19a6d3\n40 1b713d\n7b 6483c\n9d c9be1\ne5 2846bd\n44 1f0adc\nae 298dad\ned ea2d6\n5 9642\n45 565a1\nd7 27881e\n73 6468f\n9a 295536\nc1 e7207\nd6 2bb851\n35 1d0768\nc8 ee12d\nd9 eeaff\n6 1ccc1a\n41 1b863e\n1c 194257\n10 c1f\n8 1ccd2b\nae d6d4f\n8 193925\n42 1af096\nf9 12d68c\na0 106c70\nac 29a2a8\n49 25357\n28 19fb0c\n9c 1045d2\nad 2608d7\na1 cd29f\na8 29a02b\n31 196cf9\n28 55ce\n39 4cc4\n80 d2709\n63 1edc30\n6c 1b462c\nd7 11fda6\n60 20ff4\n75 22c49\n18 8e08\n27 1cfaf0\ne4 f3472\na9 2a2386\nbc cdd87\na6 107f78\n38 1d081f\nd 1cd017\n1 399df\n54 1b7d6f\n7c 1b4d43\n70 2170b\n44 1e132\n2d 1cfc4e\n6f 1bc736\n63 290fe\n1c 19b335\ncc 127890\n98 d1efd\n47 1b7176\n30 19f0b6\n6a 2246a\nb4 cf1a2\n5 42d72\nd6 128341\n8b d18b4\n9 3adf8\na8 d5a01\n6d 1f4ddb\nbd 1074b8\n8c 103f21\ne 3ab75\nc1 ef5b9\n64 5ba09\nf6 27dc86\n48 1e25a\n79 217f1\n68 1b45ef\n43 1b86a7\nd7 2bb5a6\n60 1bc7f4\nc6 e6f82\n3e 1d88f7\nc8 e5e41\n6d 22183\nf 1d3e5e\ncc 2818be\nfd 284e55\nc0 ee286\nb 84ad\n81 10287c\n8d 295eb4\nc7 27942d\n8d d15ce\n58 1d8f7\n5 9900\nbc cf2f9\nd 42ec9\n22 3dbe2\n54 1afa23\n2e 1d121a\nc9 e70b0\nd2 1280c2\nb5 2a1b3d\nde 2bb6fa\nb5 260131\n14 1cc550\nc5 280204\nee 12398e\nc8 ef453\ndf e7a49\n9a d31c8\na 1cbd80\n53 5ef6a\n5f 1f25a2\n35 e3fa\nfb 28615f\n5a 1f257e\naf 108130\nd5 e6939\n2c 197a8d\n20 4455\nda 279f15\ne0 eb0ff\neb 124c30\ne5 eb435\nb6 cdee5\ncc e5e12\n50 1e7c2\nd7 279d8e\n9d d1f2f\ncf 127b98\n81 29e07c\nc5 11f3ea\n29 58d3\n8c 29e1a1\n80 10ab69\nf3 12d4d8\ne0 27d5ef\n8 1427\n14 192be2\n81 264c06\n99 d1f60\nc7 127a51\n14 8ce2\n73 643e1\n87 29d03e\n45 562f3\n9d d1f93\nb8 10879c\n30 198268\n34 1d0705\n25 19fc95\n42 2621a\nbc 2a1a49\n88 ca830\nb9 cddc7\ne 9a4d\na8 260bc5\nd3 1295d3\n1b b22\n38 4c6f\n3b 196eb7\nb9 110d99\na 193920\ndd 12849e\nc0 edfd6\ncc 28160e\n84 295fa8\n9 19a6f6\ndb 2ba418\n36 3e512\nef 124a05\n84 29e0ba\nf4 27cc5d\nf3 27cc96\n9d 2653ef\n91 d1db7\nfe ea91f\n90 10ca48\ne2 2b6a6c\n1d 19b098\n38 1d18a1\n3a 1d189a\n1f 19b091\na4 10ed4f\n87 10ac02\n44 1ce70\n64 1bc817\nd5 2b21e7\nca 127b12\n90 10ca9a\n12 192bb8\n97 d334f\nd4 2ba534\n8a 10c237\nbb 10f7ce\n9c c9bee\n27 1d7e4e\n6a 5b87c\n20 196643\nc7 27917f\nfc 27cb06\nba 29998b\n1b 1cc3d0\nc2 2791b1\n5d 1b0de7\n2b 3efa6\n51 1d7af\nc8 120ae1\n80 c90f9\n8c 25c731\n4f 1f1ca3\n43 5e66b\n6c 1f5df0\n60 627b8\nd8 eea9e\ne7 2b5786\n82 ca6d0\n64 1edbf7\nd2 280e3e\ndd 2ba442\n27 19667e\nff 12d6b4\n98 c9e6d\n77 1bd180\n1b 42594\n58 25cb5\n53 26b0a\nb 1d508f\nd8 2ba410\n22 19664c\ncb 2b9ab7\n26 3dbb1\nee e9fbe\nee 285a6c\ne2 f2434\n43 5782b\nea e9fef\n95 10b56a\ne6 12484d\nbc 25ffd7\n61 1b2f97\n66 5b756\na9 268f04\n3c 196ee0\n81 10be9a\n8c d2b4b\n19 19c329\nd2 1295d0\n9e 29d847\n3b 4f79\nda 282257\na9 cd3f6\n69 1b30ee\n6e 5b8ad\n24 196674\n91 2642a7\n65 1f4c86\n35 197ff8\n90 25bcb6\n89 10bff1\nda 129727\n1 19babd\nc6 277f12\n22 1cfd6c\n4b 1b8550\n68 1bc69d\n3c 4c90\na9 d6cb4\n90 2953da\n97 d1e43\nb2 10864c\n22 1cfdd0\n98 1032df\n77 1f65f2\n8c 26382b\nd2 2ba2b0\n25 549b\n25 197689\n80 25b347\n86 25c88d\n12 1cc2ea\n97 10ca81\n9 1cbd86\n44 1ce72\na7 107cbb\n7b 5c4fa\n9a 25be08\n8 39b8b\n3e 1d0857\n6c 22190\nd9 efdc3\ncd e7135\n85 29e359\n35 19f0e6\nc6 127730\n4 19bb4f\n5 19a880\n82 c93ae\n20 1d1089\n8e 25c9e6\nbf 25ff7d\n71 646ec\n2d 3dd00\n9e 104887\n21 1d1098\n64 1bb2a7\n0 12c4\n34 4df5\nda 278997\n37 1d8aaf\n66 1b4222\nc8 e735f\nf9 ea8f6\ncb 2795a7\nc1 ef2fb\n9f 10cc2a\nbc 110d77\ne8 27d4a6\n6d 5a89f\n11 194130\nc9 ef452\n3d 198141\n31 4b09\n0 1572\n64 1bb555\n64 22039\n36 1d0700\n45 1b0383\nef f25c9\n86 10bed3\n92 104753\n96 10c834\ne8 12371a\na4 cd2cf\n4f 5e7a1\n40 1b02f1\n71 1b3888\n11 8cbe\n1d 19c2f6\n57 1b7a67\n84 25c8ea\nb5 25fe81\n19 19c327\n53 1b7a98\n70 1bbbe5\na5 ce5a0\n16 192e35\n1d 8e48\n38 3f651\nf2 27dcab\n96 10cad4\ne5 f246d\n6d 1b30af\n23 1966ad\nce e60d3\n5c 25c78\na6 cd586\n19 3b7bb\n19 8e09\nb8 d63c4\ne 19394f\n2 317\nf7 2bf997\nb7 d5fea\n16 42409\nb0 299581\n9e 25c0e5\n17 19c1fa\n7d 1b3a20\n34 1a0347\n69 1b30e0\n6 19bae4\n4f 1af1bd\n61 1ec659\n30 196cf6\ncd 2bafed\nc1 1279b5\n33 19700e\nc7 ef2df\nde 281f84\nd2 ee94c\n88 ca510\n40 1af08f\nfc ebc3e\n4a 1b019f\n93 104452\n64 20d79\n33 1a0310\nc5 12095a\n7a 1ed3c3\na9 107de8\n86 10abaf\n79 1bbfdd\n44 1af06c\ndd 2bb9b0\nd1 128378\n95 263fc8\n40 1af09d\n62 2105f\n6e 1b4697\n87 10ae4e\n3a 1d1ae6\n1f 19b2dd\nbd cf058\nac ce9f8\ne6 27d5c7\n69 20e9e\n1a 3b505\n71 1b3ba6\n3c 3e670\n80 ca667\n94 10478b\nd6 2b34b1\n9c 1048e2\nab ce6bf\n96 c9d3e\n4 15a3\n35 4b3a\nd 1d4105\n8a 102c33\n24 197938\n66 1ec930\n24 1979a6\n1 41d1f\nd 1d5357\nc1 127707\ncd 2bad3f\n83 d2a21\nc0 2b9c06\n65 1f5f48\n93 10caa4\n24 41e6\n9d 265451\n91 d1e19\ne9 eb257\nc6 ee01e\n44 1ce80\na9 298d76\nc0 e725a\n7 1d5277\n95 10321c\n15 c41\n4e 1cd22\n81 d1758\n8d 264d90\nc7 11f701\n89 294bc1\n6b 1b4657\n9 9778\ne2 f24a4\nee 285adc\n4f 1b8521\n43 24ee9\nc2 279451\nf3 27c9e8\n68 29241\n88 c925c\n67 1bc56f\nc2 28022d\n3 1d5246\n96 c9af4\n9c 25d340\n90 c9d08\ne4 eb3d0\na3 299eda\n86 29d0ab\n16 436db\n99 26415e\nbb d73d0\n68 5a5c1\n2 42d9b\n4b 56474\n27 1963ce\na5 1102b0\n98 2643fd\na 398f2\n35 4de8\ne6 2b6ce9\nac 10811a\n6e 20ec9\ne0 eb401\nbd 261558\n10 3a33f\nb1 cdf20\n1c 1cd977\n9e 264433\ne5 124853\nfa ebc12\nd8 2789a0\n5 1cb9b2\n4a 1b0201\n21 196644\nac cd426\nf0 f3e12\n84 103e2c\nb5 1073c3\na4 29a1c1\n5e 1e951\n85 10be5d\n3a 1d88c6\ne7 12bbf8\n60 1b31d4\n44 1aedb2\nd0 279dab\n1f 1d474f\n79 21b03\n30 e42a\nc6 281530\n25 41e9\nc4 2814c7\n3b 1d0833\nab 268c5f\nbc 110b1d\na 1d507e\n43 25207\n4f 1b883f\nba d60ad\nba 108a5f\n1b 3b4a4\n2d 197a8e\n21 4456\n4a 1e013\n25 daa7\na6 106a5c\n69 1b43b0\n4d 1b0486\n41 1ce4e\n7f 21ad9\n36 e400\n56 1f1498\n5 1d5270\n82 103d9e\n23 19796f\na4 298e9d\n1d a3aa\n87 294d50\na8 ce729\n61 5a4cb\n6d 1edb03\n40 1b035f\n19 a3db\n61 1b4507\n19 8e6b\n67 2101f\n1 1d4f91\n0 860a\nc 19bc42\nf3 2862a8\n52 1f26c7\n40 261b1\n43 1f0859\n4e 1b750a\n60 1f49a6\nac 260b92\na0 cd55a\n9d 29eb63\n91 10b52b\n9c d21dc\n15 8ce1\n35 19f076\n4 19badf\nc6 2babe2\n63 22314\n8c 10c013\nd2 28210c\nf8 ea8f5\nca 2795a6\n80 d2769\nc9 e5e42\n48 1ccf8\n16 1d45f9\nbd 2685e4\n1c 1d4a03\n39 1d1832\n1e 1d4750\n7b 2b11a\nc9 ee13c\n2e 1d82a6\n36 1d8ab0\ndb eeb06\n1a 3b7b5\n14 3b632\nb5 cf213\n7e 1f5798\n21 1966a6\n6b 5b8df\nc5 e5cba\n40 24ee1\n4c 1b8519\n6a 1b4666\nd6 129603\n8b d2b76\na8 d6cc3\nc8 2b9d5b\n22 4212\n2e 19784a\nd9 eea9d\n29 45bd\n3e 1d8c07\n26 1963cd\n93 264000\n6a 20ea6\n9b 264157\n9 41eda\nb4 299550\n57 56efb\n65 1b427e\n82 10bea0\ncb 11f579\n36 196d2e\n25 1963c7\n86 25b5cb\ne4 eb184\n4b 2504e\n63 1f5f0e\n3 19a5a6\n20 19e6f3\n71 1bcf0a\n2d 19651e\ne4 f3720\n6a 5a5c6\nec eb2db\n4d 1b7574\nca e60a2\n17 19aed6\n82 10abd0\n8e 29e208\n89 10ad21\n1 19a7ed\n96 25bf2c\n5b 259af\n4a 2636f\n21 c7b2\n2d 19fdea\n88 263aa8\n13 19af07\n6a 22408\n30 19f054\n41 1aede2\n1f 19b02d\n64 1b2f65\nfa 124322\nec 27c1a7\n69 1bb6ea\na 3adf0\nab ce9d1\n87 295d02\n5b 1ea541\n0 2b0\nc 1938e8\n3d 196e7f\n3a 4f78\n63 222b0\n3e 197197\na6 106d08\n9f 2967c8\n93 103190\n42 1f0858\n2e 3efd6\n54 1d7df\nbf 25ffe1\n1e 1cc400\nd 97a9\n80 29e327\n2a 58d9\n1c 194247\n10 c0f\n8a 10c299\nbb 10f830\n4d 1b87d4\n41 2519c\n3a 198426\n1e 19c2f0\n12 8cb8\n4 1ccc05\nd2 2b3232\n4d 1b7264\na5 29a150\nc2 1206d5\n31 196fa7\n5a 1b0b64\n49 1b7295\n2f 1d9265\n23 45c2d\n55 1b7a6e\n5e 1af86f\n1d 4382a\nb8 cf328\n45 1ce7f\n85 10c109\n7 192227\nc6 e6fe4\n4 41\ncf 2b9d88\n1a 1cc68b\n37 1d8811\n31 19f303\n5a 1b8ec0\nab 10f17d\ndd 280fbe\na6 260a34\n83 d2a1f\n65 1f5f46\nc0 2b9c04\na8 ce727\n4f 265ed\n5 19a88e\n20 1d1097\n3d 1983ef\n5a 1e974\n31 4db7\nc7 277ebd\n80 25b603\n32 1d1753\nb4 cef64\nc9 120a80\nca 1207ca\n39 198180\n42 1b035a\n73 1b38f1\nd1 12128a\n42 1ce48\n4e 1b0480\n7f 1b3a17\n15 19aecf\n63 1b4502\n80 10abc9\n8c 29e201\nde eead4\n41 26206\n94 2966bd\n84 d2a56\ncd e612f\n88 d18ae\nfb f421d\n2d dbf0\n8f 264d33\n83 d16fb\n65 1bc568\nc0 280226\n9c 25d0f4\n90 c9abc\n35 5dfe\n2d 1d823c\n4a 5e7c1\nee 2bdeda\n75 1ee55b\na6 ce5fc\na 1ccd92\n8e 296116\n5 1d3fb0\nd6 e7b91\n82 102ade\nbf 2996ad\n3b 3f95b\n40 1b70cd\naa cd70c\nc9 e70a2\n9c 104880\nd6 11fff1\n85 1028bb\nfe eac3f\n16 9fab\n68 20e91\na6 268d84\n58 60699\nef 27c45d\na1 11028d\n34 3e269\n6 1ccf1a\n9e d1f35\n82 29e082\n4a 1aeedf\n65 1bb2a6\n1 1d3f7d\n6a 21154\nb4 10f950\n22 1966ac\nc8 e7351\nf9 ea8e8\n5 40\nea 123713\nff 27e07e\nf3 eaa46\ned f3884\n68 1b30ed\n7a 21ab5\nac ce75a\nf2 1251df\n21 19fcc4\ne6 e9ecb\n41 1f0b0e\n21 da76\n3 19a854\n5a 1afb4e\n7c 22d93\n88 d159c\n85 d14d9\n13 19b1b5\n30 19f302\n83 103d91\ne7 2bdd74\ned 12cfa6\n6f 1b30c4\nc 1938f4\n0 2bc\n28 19781e\neb e9f9e\n65 1b44c8\nc0 278186\n7b 1f69c6\ne7 27d636\n8a 2637f5\n50 1e7b4\nf 97a2\n8 1ccd39\n6b 63ee7\n74 1b3b74\nf8 12533b\ndd eeb32\n20 1963f9\na5 d6b90\n70 1b3ba5\n41 1b73ec\n67 22041\n68 20f01\n67 1b422f\nc2 277eed\ne9 e9f89\na0 d68b0\nb7 2997f8\ne5 eb131\n32 3f503\n5 1d5210\n36 1d0450\n18 19b056\n42 5f8cc\n23 45edb\n2f 1d9513\n55 1b7d1c\n32 1d0481\n4 98f1\n35 ce88\n95 295656\n8a 10af81\n2b 1d9544\n51 1b7d4d\ne4 eb3e0\nc7 e7293\na7 298ea3\n6c 22192\n24 1976ea\ncc 2bafee\nfd 2be585\nc0 1279b6\ne 19262b\n52 1b9017\n25 19fa39\na2 ce567\nd3 eebed\ndf 282225\ndf efd89\n8b 10acd6\n61 63d8b\nf4 27dcd5\n6 41aaa\n78 1b39ee\nc1 2b9c05\n34 4b49\na1 d6b6d\n3d 5f53\n23 1d7e71\n98 c9c11\nf4 eaa7d\n78 1bbd90\n39 19710c\n83 d270f\n84 ca698\n3c 1d18d4\n30 3e29c\nc8 e7103\n9d 10cbcf\ne3 1235bb\nef 2b6bf3\nf 19a6cc\n1c 192ce5\n86 10be61\ncf 11f53a\n53 1f1416\n19 42847\n48 1dfba\n0 193512\n84 25c896\nb5 25fe2d\n44 1b0082\nf7 2b764b\nbd 108a7c\n6a 1b3394\n85 10ae47\n38 1d1adf\n1d 19b2d6\nff f2f1c\n3e 1d08c7\nb2 108908\n13 3b34d\n46 1f1d99\n77 1f5330\nc6 e7240\nf7 ea7d7\n25 197937\n42 1debc\nf4 2b638d\nb7 cf1a8\n64 1b4227\nfa 1255e4\ne0 e9e31\nec 27d469\n60 20d46\n6c 1b437e\n48 56418\ne9 e9ff9\na0 d6920\n56 1afa36\n2 1ccc3b\n86 295fbf\nb7 299556\nf8 ea949\ne 1d53cf\n2 41d97\n3f 1d8966\n58 25ca7\nc1 e6f4b\na2 298c19\n7f 1bbdd5\n42 25206\n4e 1b883e\n41 1b73de\n6 1cb9ba\ne6 ea125\n41 2518e\n4d 1b87c6\n69 22162\n4 19a88b\n21 1976ba\n26 1cfafd\n93 29d730\nae 298d3f\n3a 1d05da\n84 263672\nbf 110d71\n45 1b83cf\n4c 1e88e7\n6e 5bb59\ndb 12978c\n7 1ccf29\ne1 124ad0\nde 12825a\n23 1d7e6f\nc8 ee13d\n5 1d3f4c\n6c 1b4372\n60 20d3a\n50 60542\nad cd737\ne7 27c306\n34 1d044b\n49 1b8557\n25 1cfaf7\nbf d764d\n9d 2643db\n82 10be32\n76 1b3bcf\nd9 128221\n23 445d\n2f 197a95\n8a 25b753\nb5 cdf41\n2b 1d9288\n51 1b7a91\nc5 2804b2\nd 192385\na1 ce80f\n9e d1f99\ne0 eb3ad\n37 47883\n18 194278\nc4 ee325\ne1 eb154\n52 1f272b\n73 299fd\n7f 1bd035\n42 26466\nd5 12106f\nbc 2a1c95\n21 4464\n2d 197a9c\n4a 1e021\na4 298e8d\ne3 eb3a7\n14 192e3c\n5e 58075\n7 19bae5\na9 268c58\n8 1d5077\nb1 d7520\n78 1b3a4e\nfd f41e5\n85 c93d7\n64 1b44d5\n21 5718\nec 27c1a5\nda e69f7\n5d 1b7ec9\n2b 46088\na2 298ec7\n3f 1d18da\n33 3e2a2\n2 3ad0b\n66 22034\n95 10ca6a\n17 192b88\n82 102882\n8e 295eba\nc7 ef335\nc0 2b28cc\n89 10297f\nf 1d50be\n3 41a86\nf1 27dcb1\nd5 2ba535\n30 196d06\n99 d1efe\nf4 ea7cf\nc6 279480\n6b 62c27\n6d 2920f\n43 1f0ab5\n9 41ee6\n97 c9a91\nb7 25fe26\n86 25c88f\n94 295647\n4c 1af217\n21 54ce\n22 197660\nc7 2bae8f\n59 259fc\n59 1b7b86\n88 c9250\n41 264c2\nfb ebbbf\n2f c687\n7c 1b4cd1\n28 1cfc1e\n70 21699\nf7 27cc65\n94 d33ab\n1a 3a251\n92 10c7a1\ncb ee135\n20 1966a7\n21 1d7e16\n75 1bcec9\nd0 280b87\n2a 1977b7\nc3 1279ae\ncf 2bafe6\n88 103ef0\nb9 107487\n8b 296138\n24 1963c6\n91 263ff9\n2e 3efd8\n54 1d7e1\n88 10bff2\n1d 19c2f8\n11 8cc0\n70 1bcf09\n2c 19651d\n99 264150\n34 196d27\n1b 43854\n38 479a1\n40 1deb7\nf5 ea7d2\n3c 196e7e\n18 90b4\na1 d6b5d\n34 4b39\n9b 10cba7\n1d 19b026\n80 ca6c9\n23 1cfabf\ne 1936a1\nf7 2bf6e9\n2 69\nc4 1279e5\n31 1d9a9b\n38 1d1b51\n17 426c8\n2b 1cfc16\n6d 1b462d\n61 20ff5\n12 3b65c\nde efd88\n8a 10acd5\nff f2c7e\n60 63d8a\nae 25f61b\nf2 286007\n1a 1cc6ed\ne0 27d351\n46 261d9\n35 e15a\n30 3f55e\n69 20ef2\nb2 10763a\nbe 29ac72\ne8 27d4a8\n4e 26330\n48 1e9b78\n4f 265e1\n79 1ed10f\n5a 1b90fc\n43 1b7137\n60 1bb284\n3 9928\n40 1f0b0d\n1c a3b5\n6e 1b43d9\n62 20da1\n58 1ebc9\n94 c9a97\n58 1ea539\nb 41eef\n12 1d58fa\n4b 1b728e\n68 1bb3db\n78 1ed172\n3b 5f8d\n7f 1b3a27\nfd 12d909\n75 1b3b67\n4a 1e8671\ncf 128e08\n3c 5f60\n1e 1cd9e0\n12 3a3a8\n20 3d92b\n2c 1d0f63\n8e c9288\naa 2a262e\nd0 280e37\ndb 128474\n75 1bd179\nb5 26973f\n24 1d0dba\n7 1ccc6d\n39 196e50\ndb 129738\n8c c9281\nff ebbf0\n1c 1cd9d9\n10 3a3a1\n1d 1d5a7a\n11 42442\n9a 1045a8\n95 1034bc\nac 298d46\nf4 ea7c1\n6b 62c19\n1 98c1\n42 1b00b8\nb 1d50e1\n28 1d922e\n3e 1d8959\n29 430f\nfd 284eb7\ncc 281920\nc0 ee2e8\n65 2a62a\nca ef3f8\n57 1f1437\n23 3eba3\n7 19b839\nef 27c4cd\n4e 1e88ec\n7 1d5207\n28 1cfc0e\n24 19fcf6\n6d 1b33cf\n3c 1a074c\n30 d114\n1f 19c5ff\n13 8fc7\nb 9771\n48 1f0956\ne6 27d5c5\n21 3eb9c\na5 2a2252\nf 1700\naf 268c90\n2 41a77\ne 1d50af\nf0 27dca2\na3 d5658\n5 1d5200\ndc 279f41\nd0 e6909\naa 108100\n8c 10bfb3\n4e 1aef0e\nb8 cf2d4\naf ce99e\na5 268ad0\nec 2b5b83\n12 1cc598\na9 d6d16\n76 1b38c1\n39 479a0\n6d 294cb\n8e 102a1a\nc9 ee43e\n53 57ede\nd7 eec90\nf2 125499\nb6 2610e9\n88 103f50\n1d 194256\n11 c1e\nec eb279\n8e 29cf54\nd6 279d8f\n27 1d9420\n9c cb1c0\n2a 1977b5\n20 1966a5\n6a 5b8de\n30 1d1988\n15 19b17f\n1d 192d3a\n1b 43b02\n38 47c4f\n29 1cfc81\n7d 1b4d34\n71 216fc\n40 1e165\nf5 eaa80\n50 1e816\n8 1ccd9b\nc9 ef3f0\n69 2a75c\n8e 10c2ca\nbf 10f861\n37 19f32d\n17 8f98\n32 3f7a1\n10 1cc52f\nb5 cf1a1\n8a 103cab\ne4 e9ec4\nfa 2b650e\n6b 63c3b\n4 837f\n49 252f5\nf1 284feb\n1 19a84d\n4b 5fa86\n6c 20ece\nd9 eeb01\n2a 1cfec3\n49 1b0209\n38 19842d\n1c 19c2f7\n10 8cbf\n72 1b3b9e\nc 97a6\nb8 ce074\n19 ab9\n39 196e4e\n8 1938b7\n84 295faa\nb5 299541\n4e 5ea4c\n6b 5b87b\n0 1d4f30\nef f262d\n18 b2a\n9e 25d347\n15 19b1e1\n92 c9d0f\n30 1d19ea\nc5 ee06a\nbb d761c\n81 d2a18\n24 1976ec\n55 1d532\n2f 3ed29\n8a 1029e7\n81 d2708\n64 1b2f57\nab 2a2381\nd1 280b8a\nb9 ce013\n6 19a574\n85 c9439\n8f ca549\n57 1b7a75\n65 1bc5ca\ne2 eb0f8\n84 d2738\ncd e5e11\n5a 1af840\na1 107c91\n29 c95f\n1 19bb11\nee 1236f0\ne8 1236b6\nf5 eaad4\n43 1af035\ne 1cbb0f\n23 1d0d83\n6 1d3f54\n29 1d118d\n3d 469bd\n1a 19c31f\n24 197688\n1e 19c600\n12 8fc8\n63 1b31dc\n40 2518d\n4c 1b87c5\n68 22161\n4b 1e014\ne3 ea0f5\nef 27d72d\n20 1976b9\n2 1cbc27\ndb e6748\n92 d306f\n20 41a7\n2c 1977df\n74 1b3b68\n52 1e7c9\nb8 107488\n9b 10333b\n0 43042\n9f d1f36\n83 29e083\n97 10b7bd\n41 1b864c\ne6 f3727\n64 1b3205\n63 1b323e\nd 19b997\n1 835f\n9b d1f07\n39 1d9be2\n65 1bc878\ne2 eb3a6\n24 197936\n5e 1b0b33\n52 1d4fb\n49 265b7\n23 1d0d81\n6 1d3f52\n3e 3f67d\nd1 efeaa\n28 c90a\n9f 10b6bc\n1 19bb0f\nee 1236ee\n66 1b44ce\n6c 1b43d2\n60 20d9a\ne7 27c366\n48 1b04aa\n79 1b3a41\n63 22074\n47 1b86d8\n64 1bc825\n44 26490\n75 29a27\n96 29ecc2\n98 d1f6d\n3d e2af\nc5 e5f68\n48 1b04b8\n79 1b3a4f\n8b 102978\n49 25355\n9f 10b658\nb8 107486\n13 192e05\n8a 296137\n7f 2182b\n36 e152\n56 1f11ea\n11 192e60\n9d 29eb0f\n91 10b4d7\n99 10b62e\n0 9672\n49 1cd4b\na4 268d8b\ned 27c464\n31 1d9ce7\na 39880\nab cd461\n44 1aedb0\ne4 f3782\n6a 5a628\n4c 1aef07\n2b 4306\nd7 2ba53e\n9d 10b96f\ncc e70e2\nbb 10fa7c\n96 cadb4\nec 27d717\ne0 ea0df\n9c c9e9c\n27 1d80fc\n38 19841f\n1c 19c2e9\n10 8cb1\n97 26427d\n2a 1d94d5\n50 1b7cde\nba 25ffaf\n28 3dd32\na4 25f7db\n5 192220\n14 19c202\n2b 3da9a\n19 8e77\n53 1b7a46\nc4 e6fdd\n27 1cfb52\n18 8e6a\n2a 197a65\n65 1b2f58\n66 2a390\nc6 27815c\n61 1b2f89\n1 2bd\nd 1938f5\n53 1ea37a\n9c 26417e\n62 2a3c1\n11 1cc590\n44 1b0322\n75 1b38b9\n4b 2535c\n27 1d814e\nd6 278abd\n19 1d4717\n85 25b387\n24 1cfaf6\n9c 2643da\ndb eeaa4\n40 1b0353\n71 1b38ea\n6b 1edd25\na8 298d69\n40 1b86af\n71 1bbc46\n2c 1cfc4d\n60 20da8\n6c 1b43e0\n3c 1d05ae\n72 1b3b4a\n99 10b684\n89 d1601\n38 1d05df\nbd 110d76\n67 2a63f\n7 1d5217\n19 dd7\n3 3979a\nf 1ccdd2\ne9 124979\nc6 127740\n9e 10330b\n0 19375e\nd 1d3df5\n66 2102e\na4 107cc3\n87 103b76\n89 103c33\n67 1b2fc1\n15 8f9d\nf7 2be435\nc6 2bae9e\n8c 10c2cf\nb9 ce075\n6 19a5d6\n4e 2507e\n51 1f115f\n7e 21828\neb f384c\n39 198110\n1c 19b2e1\n2c 19fddd\n20 c7a5\n65 21026\n89 d28c3\n2c 3ed13\n72 1b4e0c\n99 10c946\n8e 10ad06\nf6 286038\n73 1bbbed\n39 d01e\n8 9a87\n7f 1bbdc7\n4e 1b8830\n42 251f8\n1d 1d4756\n76 2198f\nac 107e1a\n8f 103ccd\nc7 e6f83\n6 1cceba\n37 1d0451\n8c d163d\nc6 28020c\nbf 26028d\n75 21987\nec 124cb9\n9e 10b667\n18 a3e8\n27 1d10d0\n5e 1b9191\n52 25b59\nf3 ebd08\nea 1249d5\n7f 1b4cdb\n73 216a3\n9c d1f2e\n8e d2ba6\n21 1966b4\n4 31\n6b 5b8ed\n22 1cfdce\n42 24ef6\n4e 1b852e\n45 261e1\n17 1d48a8\n47 261e8\n1a 1cd9a1\n8e 10ad58\n6 19a824\n29 197a5d\na 1cba70\nbf 261551\n1e 1cd970\n12 3a338\nb3 cdf19\nb3 cf1d9\naf 2a13ac\nf7 f2e27\nf0 2b63be\n9c 296814\n90 1031dc\n35 3f51e\n24 1cfaf8\n7 1cb9ab\n88 d2860\n6b 1edd87\ncd e70e1\n27 46f22\n58 1b7b95\n69 22470\ncc 2bad3e\nc0 127706\n39 466d0\n8 43139\na5 cd340\n76 1b4e9f\ne 39b6d\nd5 278ab7\naf 29a2ae\na3 106c76\n3b 197105\n34 1d0459\n17 1cc30c\n3f 3e3ca\n83 ca3c1\ne7 2843a4\n9b d21b5\n39 1d9e90\nb6 10735b\n7a 1b3a47\nc5 e6fde\n62 21051\n6e 1b4689\n2e 1d11b6\n22 3db7e\n54 1af9bf\nf5 284d70\n54 1f118f\n69 20e90\n9c 2954fe\ne0 124821\nc5 ee018\n3d 19712f\n5a 1d6b4\n41 26452\n24 1cfda4\n71 1b3b98\n2a 472f5\n5c 1b9136\n50 25afe\n58 1d6b9\ndf 278c85\nd4 e7b8a\n80 102ad7\n8c 29610f\nbd 2996a6\n8 1ccd8b\n37 3e263\n6 3accc\n1 19b80f\n4f 2532b\n20 1cfdd5\n37 47b21\n15 1d48af\n6a 1b3334\n1d 1cc46a\n61 1b41e9\n44 1b73ba\n74 1b3b66\nca 120a78\n39 19842e\n73 1b3b9f\n1a 3a4ff\ned ea01a\n93 25bf5e\n6b 1b43b9\na4 107f71\n87 103e24\n8 193677\n69 1ec812\n20 1d9139\n2 8665\ne 19bc9d\nc0 e6f4a\n7 1d4f67\n41 1b86ae\n7 19a5d9\n19 3a497\n22 1d0de2\n9a 2656c6\nd 19a6c5\n24 41d8\n9d 265443\n91 d1e0b\n1e 3a222\n8b 10c246\n37 4b43\nd4 e667e\nae 107e75\nbc cf057\n64 1b3275\n31 1d9a29\n62 20d4d\n6e 1b4385\n49 5fd3b\n81 10be38\ne4 123592\nc7 11f445\nfa 28514a\n5b 1b7b8f\n89 10bf8f\na4 106a63\n44 1b0072\n89 ca511\ned 2844f4\n1a 42591\nbe 2a1caa\na4 298be1\n78 1ed420\n5c 1b0b2a\n2a 3ece9\n50 1d4f2\n8 1cba77\nf 84e0\n50 1b0a14\n33 1d0730\nc7 128a01\n53 25858\n5f 1b8e90\nc0 ef2fc\n72 22c72\n50 1afa00\n2a 1d11f7\n0 41a72\nc 1d50aa\nda 2bb6d7\n24 1d0db8\n33 3f564\n4 39753\na5 cd334\nbb 29997e\nde 280f64\nd3 2ba2b3\n20 3d8d7\n2c 1d0f0f\n48 25098\n41 56570\n4d 1e9ba8\ncf 280664\nec 2847b1\n70 22c17\n62 1b425f\n98 d347d\nea 27d4a1\n9e d1f37\nea f25ed\n68 5bb31\n11 1d4630\n4b 579e4\nf5 27ccc2\n8e 264a86\n82 d144e\n66 222f0\nd8 282250\n23 3d935\n2f 1d0f6d\nd6 e662f\n9a c9e64\n1d 19b336\n47 5fbac\n7a 1ed109\n39 4cd2\nd8 281fb0\n1a 90bd\n6 1d3fa8\n29 1d11e1\nd5 279d79\na3 107f38\nf5 2bf6f2\nc 1936aa\n0 72\n24 1d1066\nc8 ef3ff\n28 430e\ncd 127b3d\ncf 1278f8\nba 26986d\nd7 efc40\n47 1e87f8\na2 260a65\n58 1b9157\nbd ce036\na1 29a183\n2b 3ed4c\n51 1d555\n5d 1b0b8d\n23 1d1091\n8b 29e486\n1e 1cc462\n49 262f9\nf8 ea8e7\n64 1b2f67\nb1 cf1e0\n47 1aee1a\n6e 1b33d3\n39 61de\n25 1d10c9\nc8 1278c1\n6d 63c03\n74 1b38c8\n57 1af77b\n4e 1e8640\n45 57863\ne4 f246c\nc0 2b18b6\nc7 ee31f\nd7 279ae0\n56 26b4a\n60 1b2f88\ne5 f371f\n5e 26ca1\n3e 1d1b79\n32 3e541\n74 1b38b8\n7f 5c20d\n14 1d58c2\nb9 2695b7\n49 1b7287\n8 19a6f5\n45 1b73bb\n26 1cfaef\n9e 2643d3\n93 29d722\n8c c953d\nde 2b35fa\nd2 11ffc2\n74 21986\n7 1d3f53\n3e 1d05a7\n1f 1942b1\n13 c79\n3c 1983fe\n30 4dc6\n7 1d3c97\n81 29d066\n1b dd0\n38 4f1d\n17 1d45f8\n13 1d4629\n1e 19b2da\n7c 21821\ne9 f3845\nd4 e662a\nae 107e21\n70 22c6b\n28 1d11f0\n62 1ec961\nf2 f3da9\ncc 2803bc\nac 29a05a\na0 106a22\n38 196eb1\nbd d7648\na6 29a156\n3d 19f1db\nc 19bc44\n0 860c\n2f 1cfcb7\n4e 1e88ee\n9 1ccd9a\n44 1de86\na8 2679a3\nfb 27e0b1\n5a 1ea4d0\n44 24f10\n6e 1b4695\n62 2105d\nf1 2b63bf\nc9 ee3ec\n81 263944\ncb 128b7d\n9d 29d84d\nf6 eaa86\n21 c4f6\n2d 19fb2e\n3 1d3f76\n20 1d80c3\n8 147b\n42 1b004a\n13 1d48d7\n30 1d8a24\n69 2949a\n1a 43b01\n20 3ee3b\n52 1ea3db\n11 1ed2\n46 1af0c7\n4b 1f1c1e\nfd 12d6bd\nc 19a6c4\n64 1b4219\n60 1b424a\n8 1cd047\n23 c80d\n2f 19fe45\n60 20d38\n6c 1b4370\nad 260b95\nca e711a\na1 cd55d\nb6 2a1ba7\n64 5a749\nbc cddf7\nf6 27c9c6\n3c 1983ee\n30 4db6\nc6 277ebc\n26 1d0db1\n92 d205d\n9e 265695\n64 222e7\n8d c952e\nd1 eff1a\n38 4f0d\nce 278013\n22 3d8d0\n54 1af711\n2e 1d0f08\ne5 eb3df\n88 d159e\nfb f3f0d\n6b 1ecac5\n84 d2746\ncd e5e1f\n36 1d1722\n71 1bd146\n64 29375\n93 10ca4e\n6f 1b30b6\n44 1aee14\n6 42d78\n4f 56451\n3a 1983c6\n4 19a87d\n69 22154\n4c 25325\nc7 ee2cb\n64 1b44c7\n16 1d465b\n2c 1977e1\n20 41a9\n9 9768\n67 222ef\na 84ae\na 3ab52\n81 294d18\n6e 1f4b35\n55 60264\n2 1cb97b\n87 10c112\n89 d285f\ncf e5e26\nd8 efffe\n6f 1bc974\n63 2933c\n32 3f563\na4 cd333\nba 29997d\n8e 10c268\nbf 10f7ff\n1 41ae3\nd 1d511b\n8a 103c49\nee 2bdc2c\n8d 264d8e\n81 d1756\n8b d2866\na9 d57b6\n8 41bd5\n8f 29d1a1\n28 4300\n2a 4307\ncf 127b36\nb4 29a866\naf 107e84\n45 5fbb3\nc3 1289c2\n7c 1b4cd3\n70 2169b\nbd ce098\nf7 27cc67\n79 1ed3bf\n30 1d9ce6\naa cd460\n7d 1bbd5e\n4c 1b87c7\n40 2518f\n1d 42816\n6f 1ec83a\n83 10be31\n1b 19c2c0\n3c 198142\n30 4b0a\nb7 2600d6\na5 d68e0\n79 2b11f\ncd ee40f\n93 29d9d0\nc1 ef309\n87 10be62\n13 8cb9\n1f 19c2f1\n5b 606a1\n4c 1b7503\n9e 296ad9\n92 1034a1\n37 3f7e3\nd2 e7bc2\n8a 296147\nbb 2996de\nc8 12785f\n98 25d0c3\n7 3acd9\n80 103add\n77 1b3b7e\n27 1d93bc\ne0 eb39f\nb6 cdc39\n11 1d487c\n7 19a887\n22 1d1090\nb4 107354\n9b 29db27\nc9 ef460\na 41c40\n35 d136\n8f 10bfb9\ne0 f36ef\nbe cdd90\n19 1d49d3\n1 19250d\n80 10be9b\ncd 2792cf\nc1 e5c97\n26 1cfe01\nff ea922\nb6 d7249\nd6 2ba2e1\n1c 1db9\na5 10f060\ne 1cbaaf\nc3 ef54e\n56 1d52a\n95 265598\n36 1d0452\nd 1ccd69\n1 39731\nc 16fa\n3d 4c91\n20 4457\n2c 197a8f\n5b 1b0b01\nfa 27e0bc\nd0 ee937\ndc 281f6f\n2c 1cfc4f\n74 216ca\ne1 f36ee\na6 107cca\n5d 1f25a9\n51 5ef71\nd0 278849\nf2 ebabb\naa 29a040\n7a 1b4d09\nee eb2f2\nac 260be6\na0 cd5ae\n4b 5ea80\n2c 19682f\ndc 280d0f\n8d 295e60\n81 102828\n8c 264a7f\n80 d1447\nf3 f3db6\n63 1ec96e\nc5 e5cc8\n4c 1cd27\nd9 eed4b\n5 39762\n85 102859\n7f 1b3cd3\n88 103c32\n7b 2ae16\n34 196fd5\n7e 5c20e\nd5 279ad9\na3 107c98\n59 6038a\n78 1b4fb0\n2b c966\n36 197ff0\n11 439a6\n8c 2637c9\n74 21988\n96 10c7d2\ncf ee166\na0 298c10\n94 c9af9\n9b 10b8d5\n62 1b4261\n3e 1d05a9\n25 1cfb59\n76 1ed28f\n45 1b8671\ne 1ccd61\n2 39729\na3 cd30a\naf 260942\n41 1cbf4\n4d 1b022c\n97 1031b3\n6c 1b3110\n97 2956bf\nc9 ef6ac\n5c 1d688\n79 5c491\n88 c92c0\n67 1bc5d3\n40 1e97d5\n7a 1b39e7\nd3 129395\n8f 1029a9\na3 298c28\nf7 27dcdb\n54 1af713\n2e 1d0f0a\n22 3d8d2\n5f 1f1590\n33 1d0420\n2 1cce89\n85 d1733\n3b 3e69b\nda 2bb979\n3e 46717\ncb e5de7\n82 d270e\n4 9651\n1a 1d5c9b\n66 1b4530\na 19a6fe\n67 1b453f\n58 1d657\ndf 278c23\n63 1bc5a2\nad ce6e9\n8f 296169\n83 102b31\nf4 ebd31\nac 29a2b6\na0 106c7e\n44 1b866e\n60 2200a\n43 1debd\nd6 2b21ef\n9c 103620\nab cd3fd\n7a 21aa5\nc8 28164d\n64 1b4537\nab 107e53\n41 5fb82\n3c 197190\na4 106d01\nf5 27dce2\n7 41ab7\nb6 cdee7\nc5 2b9926\n30 4b6a\n3c 1981a2\n1 1cb91f\n94 25bc87\nb6 ceef9\nae 106bb1\nd6 280b5d\n8 9775\n4e 1b851e\n42 24ee6\n76 1bcf33\n9b 1032d9\nc 41c08\n74 1bcf3a\n44 1b73ac\nae 25f67d\n6c 1b4380\n60 20d48\n4f 1b0233\n43 1cbfb\nad cd745\ne7 27c314\ncd ee46f\ne5 2b6cef\n9 1cba76\n85 29d035\nfe eac31\n41 1deb6\ne5 27d5cf\n48 1dfac\n35 196d28\n4 193791\nc6 2b2894\n8c 103cc5\nd2 279dbe\n0 1cbc20\nd9 e6741\n90 d3068\n5 19a88c\n8d d187c\n96 103214\n40 1e10f\n87 d172c\n24 3d90a\n87 294cee\n4c 1dfdd\n34 1d0767\nc0 e7206\n5c 1af86a\n2a 3da29\neb eb2c0\nb0 110c43\n32 196d61\n90 1046fa\nd7 2ba590\na9 267c50\nf4 2be6dd\n54 1b7d0d\nbe 25ffde\n47 1ce78\nd4 278b1a\nae 29a311\na2 106cd9\nd5 2ba289\nf7 12d4fb\n43 1b73f5\n1c 192ff7\n8e 103c78\nf7 eaa85\nf4 ea7d1\nd7 e6684\nc6 ef2d0\nb4 107362\n20 4463\n2c 197a9b\n97 103215\n3a 3e38a\n28 3dd40\n2a 1d94e3\n50 1b7cec\n2e 197a94\n22 445c\nd8 128220\n7d 64562\n27 1d106e\n6a 1b43aa\n18 a386\n82 294d2c\n49 1e00d\n5c 1b7e64\n11 9fd4\na6 25f772\n30 196d58\n7b 2b118\n59 1b7ea6\n85 263667\n5f 1b8ef0\n53 258b8\nea f258b\nf5 27cc60\n1 192251\n60 1b2f26\n40 1b0361\n71 1b38f8\neb ea000\n4a 5641f\n1c 1cc469\n89 29e48d\ncc e7134\nef 2857cd\ne3 f2195\n57 1b0c89\nfc 12530a\n2e 197af6\n22 44be\n71 1f5618\ne0 eb40f\n41 1de54\ncc 1278f0\n3d 3e3c3\n79 22dd3\nd0 128069\ndc 2bb6a1\n81 ca3ba\ne5 28439d\n42 5e8b8\n7f 1f5487\n4e 1f1ef0\naf 2a109c\nf7 f2b17\nc6 ef580\na5 cd57e\nce e713b\n5a 1d962\ncd ef731\na6 106a68\n1c af9\n89 d2b1d\ncf 2818c6\nc3 ee28e\n82 d2770\n51 1f26cf\ncb e5e49\ne8 e9f96\nfb eab9f\nb2 d74c6\n86 2636cf\n98 10358d\n77 1f68a0\na9 260908\n8c 263ad9\nd2 2ba55e\n30 1d048a\n1e 192fee\nb5 110c21\n11 192bb2\nd 19b987\n1 834f\nca e5e58\nad 25f8d3\n2f 19e875\n70 1b3887\na6 260786\n7a 1b4fc5\n81 10c13c\ne4 ea11e\nc7 e5fd1\n72 1b388e\ne1 27c080\n6e 2218b\nd8 2ba40e\n22 19664a\n2b 19781a\nb6 1073c9\n6c 1b4620\n60 20fe8\n41 1b0042\ne0 27d5fd\n17 19b1e8\n32 1d19f1\n9 84a6\nd4 e692c\nae 108123\n37 4df1\n85 264999\n94 103459\n98 1048b1\n9e 29d8b5\n6c 1b335e\nc2 2804dd\ncd 2b9ae1\nef 12cd53\n88 c950c\n67 1bc81f\n47 2648a\n71 1ecfb8\n40 1e9a21\na 1d5390\n3b 1d8927\n60 1b2f8a\ne5 f3721\n28 197820\nc1 127a17\ncd 2bb04f\n10 8f6d\n1c 19c5a5\n62 1b2f91\ne7 f3728\nd2 281e52\nf5 12c4f0\n30 1d072a\nc4 1289fb\n1a 3a48f\nbb ce070\n97 2953a1\na4 ce8af\n5 12f4\ne4 eb440\n45 1de85\nf4 2be42f\na9 2679a2\n98 104603\n10 1940cf\n77 5c0b6\n6c 1b30b0\n21 196646\n4a 1b0203\n60 1edc2a\nff 27cb1e\n2c 197aef\n20 44b7\ncc ee40e\ncb e5df5\n94 1034bd\n4c 1b7505\nca 280634\n7d 1bbd6c\n4c 1b87d5\n40 2519d\n6f 1ec848\n48 1cd58\ncf 278324\nc4 e7229\nad 298d45\n30 3e29a\n3c 1d18d2\n97 25bf2d\n1c 193f99\n10 961\n16 19c1fb\n68 1b30e1\ned f3878\n6f 1bb412\n26 1d0db3\nb7 cdc9c\n16 3a0bb\n83 10c0df\n3c 1983f0\n30 4db8\n8c c92ef\nc6 277ebe\n2c 19651f\n21 d81c\n68 5a8cf\nfa 27cb4c\n48 1b0208\n70 1b38eb\n4a 57991\n7b 5af28\nf5 f4082\n6a 1edd26\nf2 27df57\n72 1b38f2\nf7 f4089\nd0 12128b\nc 198\n24 1d1058\n23 1d93ed\nde eed76\n66 5a492\na8 107e49\n8d d1640\na5 2a2500\ndf 2bb6fd\n68 1bc94b\nd3 1280c5\n5 1cb950\nb7 cdc3a\nb4 2997f0\n31 4dc7\n3d 1983ff\n5a 1e984\n1c 192cd7\n6c 1b30bc\n80 103d89\n3d 19f1cd\nc 19bc36\n0 85fe\n2f 1cfca9\n73 2af6d\n42 1aed88\n8 1b9\nac 25f8d2\n8f 25b785\nf5 27dcd4\n7 41aa9\na1 298c21\n28 19fb60\nae 107e13\nd4 e661c\n99 295530\n61 20da9\n6d 1b43e1\nba cddc1\n2a 472e7\n5c 1b9128\n50 25af0\n8f 29e457\n83 10ae1f\n8b 1029da\nfc ebbda\na8 106b27\nc 84d6\n89 1029e1\nda 120117\nfa eabfe\n84 10ac0a\n30 5dca\nb7 261396\n64 1ed94b\n82 ca424\n47 1e97fe\n9c 29d84e\n39 4f80\n45 1b7171\nce 2792d7\nc2 e5c9f\n60 1ed97a\nd8 28225e\n23 3d943\n2f 1d0f7b\na9 cd3f8\n40 1b02ef\n41 1b7130\n21 1d80b6\n4a 1f1c73\n7 2e7\ne2 2b5a64\n43 1e84a9\n4d 1b04da\n41 1cea2\nea eb56d\n4b 1dfb2\n1d 1d49f8\n1c 4381b\nb 19b95d\nbd d73fc\n45 1f082f\nf4 27cc5f\nf7 ea7cb\n26 19f9df\n6f 1b30b8\n9b 10460b\nef 124c5f\ne4 27d622\n8b ca518\nef 2844fb\n6 38\n77 64404\n55 1f1192\n61 20d3b\n6d 1b4373\nc8 278031\n38 5f21\nb3 cdeb5\nbf 2614ed\n1d 1cc408\n27 c7dc\n20 1cfd73\n9f 29edb8\n93 10b780\n5a 1d65e\n99 2656cc\n28 3ece4\na3 106c78\naf 29a2b0\nb 1d406d\n61 2205d\n46 1b73b5\nce 1278e9\n69 1b45ee\n19 1d7b\nb8 cf336\n4e 1aef70\ne5 12cba3\n0 19b86e\n49 1aef47\n32 cec1\n3e 1a04f9\n5c 1d68a\nc9 ef6ae\n58 1b0b5b\nb 8511\n8f 29e455\n83 10ae1d\n1b 19b2ac\n26 1cfaf1\nb6 10862b\n3c 1d05b0\nf1 f404f\na9 d6a06\n22 1cfb22\na7 1102b9\n52 1b7a45\n18 8e76\n45 1ce71\n61 20d9b\n6d 1b43d3\nb 87bd\n56 5f24a\nc8 28163f\na 1d50e0\nc6 ee2ca\ne3 eb0f9\n14 192b8e\n40 1aed7f\nb2 10865c\n46 562fb\n85 29e369\nb8 cf32a\n2e 1cff64\nf9 ebebc\n41 562c2\n4d 1e98fa\n88 ca520\ncf 2803b6\nec 284503\n8 1d5079\nd 19b9eb\n8a ca519\n1 83b3\n49 1b84e7\ne 1d53c1\n2 41d89\n3f 1d8958\n35 198298\n52 1e81d\na 1ccda2\ncb ef3f7\n6f 5a5f8\n91 cad7d\n96 1031c0\n4 1ccc75\n26 19768f\n93 104700\n64 21027\n8b 10af74\nf3 2862a6\n2e 1cfc48\n43 261b9\nec 1236e9\n86 10bec3\ncf 11f59c\nb8 cdd56\n58 60639\n68 1edad3\nf9 27cb46\nc8 2795af\nee eb59e\n4f 1dfe3\n27 3d910\n63 22320\nc6 2babee\n6c 1bc96e\n60 29336\nf2 27df65\n53 1b09aa\n68 20ef1\nef 27c4bd\nc5 e5fca\ncf e70da\n60 1b2f98\ne5 f372f\n6 3a\nc1 ef2fd\n8 159\n7b 22ac8\n41 24ee0\n4d 1b8518\n6b 1b4665\n1c 193f9b\n10 963\nfe ea92d\n61 2205f\nab 29a2ed\nd1 278af6\n6b 2a763\n1e 193fa2\n12 96a\n27 1979a2\n83 29cdcf\nd7 281e82\nf4 285fcf\n18 aba\na 3ae60\nb4 26013e\n4c 1aef17\n6e 22189\ndb efdbc\n8b d2b16\n1e af2\n18 193fd8\n8 1429\n6c 1bb40c\n4e 1b01d2\n42 1cb9a\n14 192be4\n81 264c08\n53 26e28\ne1 f3750\n2c 1cfcb1\nee ea2ce\na0 2607b2\nd1 e65f8\ndd 279c30\nab 107def\n4a 1e2c1\n10 19af0d\n48 1ccea\na4 1069f1\n3c 196e80\ncd ef6dd\n48 1aef46\n12 192e66\n5 19379e\n4a 1ccf1\n1c 192d3b\n89 264d5f\nc5 ef5ea\n7a 1bc053\n58 5f129\na6 267814\n1d 194248\n11 c10\n88 103f42\na4 cd57f\nc3 ef550\n56 1d52c\n1b 1cc440\n2f 19eb85\n70 1b3b97\nbc 2996a5\n34 5e09\n5a 1d970\n79 1bcff1\n44 1b0080\nda 12143d\nd 3ae29\n88 294e7c\n8f d18e5\nac d5a32\n47 5659a\n91 2652b9\na4 298bef\n87 294aa2\ne4 eb122\n86 29cdfd\n67 1f5f3f\n88 102c2c\naf d5a2a\n70 1b38f9\nf5 f4090\n9b 10b627\n1e 1cc710\n67 5a493\nfa 125580\ndf eed77\n13 a227\nba 2996dd\nf1 284fed\n50 1f140c\na 42ef4\nbf 10f80f\n88 264d5e\nb9 2682f5\ne3 12cb6b\nf 1cd01e\n3 399e6\n9d d3503\n38 3e391\n1b 3a244\n3b 1d05d9\na 1cd042\nbf 29995d\n63 1f49ae\nd8 e674e\n29 45ddf\nc4 11f699\nb4 299540\n1e 3b4e2\n98 d34df\n20 1cfd65\n2f 1977e7\n23 41af\n9f 10c928\nab 106b83\n94 2652eb\nc4 e6f8b\n40 1b7131\n4b 5e76e\na4 ce8a1\n8f d2b45\n88 10c290\na 1923ae\na1 10ffe1\ncc 2b9d2a\nee 2b6e40\ne2 123808\n5 19b894\n4c 1e8947\n82 ca3c2\ne6 2843a5\n5d 1b0dd9\n2b 3ef98\n51 1d7a1\n69 5a870\n20 47197\n3 4304a\n50 1b8cfe\n8f 25c9e5\n83 c93ad\n94 263fc7\n84 d14e8\n75 646b9\n3d cfed\nc 9a56\n1 42da5\n52 604db\nec ea027\ndf 279ed7\nd3 e689f\nee 124cb4\nb4 2a1900\n2 1cceeb\n33 1d0482\n3e 197133\n5 2e0\n46 1b86e7\n77 1bbc7e\n21 420a\n2d 197842\ne 19b98d\n2 8355\n84 295d5c\nff 27ce2c\n17 19c198\n69 1b307e\ne4 124b64\n5 19bb4e\n88 d286c\nce 281615\nc2 edfdd\nac 29a05c\na0 106a24\nd 1d5365\n1 41d2d\n84 102af8\n1c 192f87\n68 1b308d\n37 197ff1\n92 25bcaf\n38 4cc3\n1e 1cc6ae\nbf 26028f\nec 124cbb\n1c 90e5\n52 1b0a19\nbf d6141\n1e 42560\nb8 2996d8\n46 1b7417\n11 a222\n1d 2068\n57 1d7d9\nd4 27886c\nae 29a063\na2 106a2b\na 398e4\nbd 2a1a58\nd4 eff3c\n3d 1d085d\n5a 56de2\n69 2948c\nb3 107647\nbf 29ac7f\n9f 25d346\n93 c9d0e\n28 587e\n6f 1bb714\nd8 efd5e\n40 1b00b1\n9 3ae5a\ne7 2bf036\na7 d5689\n6 41aa8\nf4 27dcd3\na0 298c20\n8e 25b784\nbf 10faad\ne4 ea110\na9 299024\n8e 10ad04\n89 d28c1\ne6 ea117\n8d 29cede\nab 29902b\n28 1cfc70\n73 1bcf11\n2f 196525\nad 110407\n53 1f1168\n19 42599\n42 1f0b08\ndb 2bb978\n96 d1dde\n1b 19affe\nfb ea8ff\n86 d147d\n5a 25cbc\n30 60e8\ndc 2b208f\n9f caeaa\n44 1b710e\nf6 eaa78\n9d 29d83f\nbb 29998c\n8 84a5\n8f 263a71\n48 25036\ncf 280602\nc7 2bae9f\n8d 10c2d0\neb f384e\n7e 2182a\n23 1cfd7b\na7 107d1d\n33 4b74\n3f 1981ac\nff ebf00\n8c c9591\n89 d18bb\nd4 128348\n27 41e0\nc6 2814be\n9c d3510\n24 1cfd96\n91 29d9c9\nbf 107523\n55 5f252\n21 197976\n16 43989\n99 26440c\nbb d767e\n6d 1f5df1\n61 627b9\n2f 197849\n23 4211\ne5 12bb8d\nc2 2814ef\n7d 1bc01c\n34 1d06f7\n6a 1b43b8\n38 4c61\nbf 26022d\n8b 10bf88\n8d 29e4b0\n81 10ae78\n19 19b307\nf1 2be70d\n1 1d3f6f\nbd 2a1a48\n8c 29e4b1\n80 10ae79\nf3 12d7e8\n25 471bb\n20 3db85\n2c 1d11bd\n5a 56d82\nde f00a8\n66 1ec92e\nd3 2ba561\n2e 19fb36\n22 c4fe\na4 299f05\n67 20d7f\n99 d1f0c\n6f 1b43e8\n63 20db0\nf7 284d15\nbd d6146\n8c d2baf\nd2 129634\nda f0077\n62 1ec8fd\nc0 2817a4\n4b 1dfc0\n4d 1b04e8\n41 1ceb0\n5b 1e921\n5d 1b0e49\n2b 3f008\n51 1d811\n80 c915b\n8c 25c793\n7a 22ac7\n20 1cfdc7\n8e 25b724\n7c 1b4fef\n70 219b7\n23 1d0d91\n9e 2957b3\n7d 1b4ff0\nd4 2ba286\n71 219b8\na0 cd302\nac 26093a\n9a 10b698\n3f 479da\n39 4c62\nac 260bf4\n1 1\nd 193639\na0 cd5bc\n51 26b03\n9 1d5088\n30 3e4e6\n3c 1d1b1e\n2f 19e873\nea e9fff\n1c 1d5a19\n10 423e1\n97 29d9ad\nb4 25fe20\nab ce723\na 3ab42\na1 d68af\n46 1f0ad7\n0 1937c2\n31 196d59\n30 e128\n79 21801\n45 1f0add\nc6 e7292\naf 298dae\n62 5b731\n47 24f28\nba d73d1\nc8 28037d\n98 c9c13\n3d 5f55\n43 1f0b07\n23 da6f\n60 1f4c54\n12 1d48e4\n2f dbf7\n28 1d118e\n70 22c09\nfd 2be5e7\ncc 2bb050\nc0 127a18\n65 63d5a\n4f 1b8591\n43 24f59\n63 1ec6b2\n11 4268e\n1d 1d5cc6\n84 10aeb6\na1 107ce5\nac ce996\n1c 19b345\n39 198174\n9e 264125\n74 1bd1da\nc 41ea8\nda 1284d5\n7f 64817\n75 646bb\n50 1f1470\n13 a28b\n45 575b5\ne4 f21be\nc0 2b1608\nc7 ee071\n87 29e300\n5 1924ce\nc8 e609d\nca e6094\n4d 1b7566\n20 d829\n69 20f02\nc3 ef304\n3e 1d1869\n32 3e231\n72 216a2\n7e 1b4cda\n2 1d3c67\n1a dcf\n95 29d6ea\nb3 299837\n52 1af9a5\n18 dd6\n37 1d87ad\n6 1d5216\n17 42418\n31 ce57\n0 98c0\n3d 1a048f\n98 26414d\nf0 ebd02\n92 29d9dd\n37 1d9d1f\n41 5f8d2\nc1 1289c9\n40 5f8d3\nc7 1289f3\nb7 ceefc\n60 1b31e2\nd7 2b1f94\nf4 ebd33\nb3 29a83d\n96 29da0e\nb9 29ac47\n45 5f903\n78 1b39e0\n9c 25c0de\n95 cad4c\n4b 5fa22\n5f 1b7bbe\n78 217f2\ncb 128b19\n4a 5fa23\n0 19a7ea\n39 1a04d0\nca 128b1a\nf8 ebe59\n9a 29db34\n46 575ad\ne7 eb18e\n49 5fa29\nc9 128b20\n48 5fa2a\naf 2a23b2\na3 10ed7a\nd5 280bbb\nc8 e5def\nc8 128b21\n4a 1e9931\n21 1cfd74\n1 9931\ncf 128b4a\nfc 2b6238\nbf cf053\nfc ebe8a\nbb 29a994\n9e 29db65\n4d 5fa5a\ncd 128b51\n39 1d0880\n4c 5fa5b\nb2 299578\n4 19baeb\n4d 1af1c4\n37 1d9ac5\ncc 128b52\n38 1d0881\n48 1e98c8\ne9 27d4a9\n4f 26331\n5 19a5d2\n8e 25c738\n82 c9100\n20 1d0ddb\n98 2656bf\n88 10acc0\ne5 124b01\nfa ebec0\nd8 278c4e\ne9 2b5907\n9c 2957ae\nbe 108a20\n95 265296\n19 8e79\n9f 265696\n93 d205e\n31 1d9d39\nf 1d40aa\nf1 27cc9d\n1 1924ff\n59 1e91a\n84 295fb6\n35 197fea\n90 25bca8\n89 10bfe3\n1 19baaf\n34 197feb\n1d 1d47b8\n9a 1032e6\n9 192656\n9f 1045da\n95 1034ca\n8 1d50db\n15 1cc5c1\n59 1b0b5c\na 41eec\n2f c935\n28 1cfecc\n7c 1b4f7f\n70 21947\n78 21a9e\n62 5a461\nbd d7656\n6e 1eda99\n43 1e117\nda 11fecb\nbd 299946\n60 5a468\n6c 1edaa0\n7d 1bd02e\n71 299f6\n40 2645f\n63 5a4d2\n6f 1edb0a\na1 1069cf\nad 29a007\n96 10b564\n40 1e101\n29 1cfc1d\nc7 ef2d1\n1b 4284e\n4a 1dfc1\n2 193519\n25 3dbb7\nc4 2bae95\n4c 1b04e9\n7d 1b3a80\n40 1ceb1\n12 193e7a\n35 3e518\nd4 2bb7f6\n7a 22ac9\n32 198021\nd7 e6630\n47 1f1aea\n65 1bc824\n45 1f1af1\n80 d2717\nf5 f3de0\n65 1ec998\n44 1f1af2\ne9 2857e7\n7c 1b4d35\n70 216fd\n6a 5bb38\n56 1e90e8\nf7 27ccc9\n12 3b34c\nb3 cef2d\nde 278c76\nc1 128a2b\ncc ef6dc\n59 1b8eba\n41 1af09e\nc0 128a2c\n40 1f1b23\n5c 259d6\n43 5e609\n4f 1f1c41\n8a d2867\nd1 1212ec\n88 d286e\n40 5e611\n4c 1f1c49\n13 1cd859\nd2 278840\n78 21854\ne8 12ccc8\n9 9a78\n43 1b8647\n51 26db3\n4b 1f1c72\n68 1f5dbf\n75 1b4b89\nc9 128b82\n41 1b864e\n49 1f1c79\n3e 3f689\nd1 efeb6\n8d 294bf2\n0 19bb1c\n49 1af1f5\n32 d16f\n3e 1a07a7\n5 2ee\n5a 1ea534\n48 1f1c7a\n1 42fe1\n24 1d0dac\nb5 cdc95\n3 1921f6\n14 3a0b4\n81 10c0d8\n3b 1a0713\n5 43012\n97 26528f\n4b 1b8552\n57 1b7ad9\n72 1ee2e2\naa 298d6e\n8e 103c6c\n2 1d3f15\ncd ee1cf\n43 5fb79\n11 8f6c\n63 1b2f90\n1d 19c5a4\nea 1249e3\n1 1921ef\na0 25f7aa\n57 1b7d15\nc2 127a0f\n1b 193f70\nce 2bb047\n14 1d48a0\n8e 10c01c\n66 1ed944\nf7 ea82d\n56 56c4c\nc3 128c70\n8 1729\n73 1b388f\n42 1b02f8\n98 2643fb\n39 1d088e\nf7 ea7d9\n25 197939\n42 1debe\nb7 cf1aa\nd7 2b2242\n34 197fe9\n8 192655\ndc 281fd1\nd0 ee999\n88 29cf1e\n21 45c28\n75 2acdb\n2d 1d9260\nbc cf369\nf6 eaada\n9d 29d8a1\n21 19e754\nfe ebc43\naa 106b90\ndc 2789d1\n5d 1b7c27\nda e6755\n2b 45de6\nc6 11f6a0\nb6 299547\n38 5f23\n3 192258\n81 10c13a\n19 19c5c9\n1 1d5231\n9b 29eb47\n6c 1bb46e\nf1 12d4d3\n8 148b\n59 1ebc8\n4a 5672f\n3 967c\n40 1f0861\nd7 280e1c\n9d d224d\n25 1cfda7\n4e 1e9964\n42 5632c\n66 1edbf0\n4e 1cd30\n23 1cfd6d\n9f 29db02\n0 42d42\n87 29e30e\n94 10472b\n34 ce89\n63 22072\n47 1f1d98\n45 1f1d9f\nd5 278879\na3 106a38\naf 29a070\n47 261ea\n7a 22abb\nc3 128cd2\n5b 1b9161\n69 22160\n3b 1d0827\n43 1f1dc9\n7d 1f6750\n40 5fb81\n71 63118\n60 1f5f16\n30 19801a\n59 1b9168\ncc 2b19dc\n85 264929\ndc 279c85\naa 107e44\nd0 e664d\n17 1d466a\nb7 cdee6\n43 2621b\n37 196fdd\n72 2af6e\n4a 1ccfd\nd4 2bb7f8\n1c 192d47\n89 264d6b\n91 d20c5\n9d 2656fd\n42 1e97dc\n6b 20ea5\n6f 20ed6\n77 29cce\n2f 1d8253\n70 1ed265\n33 6080\n7f 1b4ce7\n42 1e118\n73 216af\ncf ee1d8\nea 1249e1\n28 58de\n62 1b44ad\n13 1cc527\nb2 d7218\nfb ea8f1\n22 1d7e70\n6a 1b4604\n26 1d7ea1\n3a cfb6\nb0 107385\nbc 29a9bd\na9 ce968\n4e 1b048c\n42 1ce54\n6a 1b43b6\n6b 22469\n0 19bb1e\nce 2bad37\nc2 1276ff\n31 19f0b5\n20 d7b9\n69 20e92\n24 d7ea\n6d 20ec3\n66 1bc82c\n46 26497\n77 29a2e\n62 20daf\n6e 1b43e7\n56 1d7e6\n2e 19fde4\n22 c7ac\na4 29a1b3\n79 1f54c1\n67 2102d\nb4 29ab14\nf2 12d779\n3a 4cc8\n77 2198e\n4f 57705\nee 124cc0\n13 3b5fb\n77 1f55de\nbe 108a30\n6a 1bb436\n0 12d0\nfa 12c670\nb4 cf1ae\n28 3da92\nf7 eaad9\naf 29905e\n21 5478\n4a 2509f\n2d 19eb1a\n45 1b8423\nc2 e6f51\nad 106bbb\nfe 1242f1\naf 2a235e\nf7 f3dd9\na3 10ed26\nd5 280b67\n58 1ebc7\n2 967b\n29 55cf\n36 1d8a4e\nf 1cd080\n3 39a48\n67 20d71\n2d 5600\n67 1bc5d1\n88 c92be\n47 26498\na 97d2\n8f d15d5\nd0 279b09\n5c 1d67c\nc9 ef6a0\nac 106b58\n8f 102a0b\n7b 2ae0a\n46 1de99\n22 1d7e0e\ncd ef6d1\n48 1aef3a\n84 d1796\n0 3a9e4\na1 ce5c5\ncc 27830e\n4c 1b04e7\n40 1ceaf\n4a 1dfbf\n63 22010\nc4 2b9925\n6d 1b468f\n61 21057\n6b 22167\n2a d917\n9b 1032e7\n71 5c39c\n35 197fec\n81 ca42a\n4a 1f09af\n90 25bcaa\n6f 22198\nac 106bba\ncf 279584\nc3 e5f4c\ne5 eb185\n80 ca429\naa 2a238e\nd0 280b97\n75 1bced9\n98 10b8db\nea 2b58ff\ne 1cd011\n3f 1d05a8\n2 399d9\nd8 280cee\ne5 12cba1\n71 299f8\n7d 1bd030\n6a 1bb6e4\n0 157e\nec 27c1b3\n21 5726\n36 1d0760\n33 cec0\n3f 1a04f8\n2 9929\n8f d1883\nad 2a2357\nf5 f3dd2\na1 10ed1f\nd0 279db7\n42 1e116\nbc 299945\n58 5f377\n7b 2b0b8\n46 1e147\n22 1d80bc\n0 3ac92\na1 ce873\na6 106cb6\n60 1ec656\n67 290bf\nc7 2b188d\n63 222be\nbb 107482\n0 9674\nb5 d5f8f\n87 264c40\n2f 19eb21\n80 ca6d7\ne3 f3703\n76 216df\nb5 26974d\naa 2a263c\nb 1d5081\nd0 280e45\n25 196685\n42 1cc0a\n4e 1b0242\nfd 12d6bb\n4e 1cd20\n75 1bd187\n29 1d7f6d\n7d 1bd020\n40 26451\n71 299e8\n60 1bc7e6\n89 d159f\n6a 29494\n75 1b3b69\n5 838e\n31 1d8a17\n5a 1f25d4\n36 1d1a30\n95 264028\n7a 29df5\n29 c6bf\nf7 285017\n56 1f1436\n19 8e15\n23 1d102f\n6a 20ea4\n62 1ed983\nb1 d626c\nbd 2698a4\na8 d57a7\ne2 2b6d28\n9 3ab9e\nf5 ebd32\na1 106c7f\nca 12083c\nad 29a2b7\n63 5b9d2\ned ea028\n16 19415b\nd 9a57\n53 604dc\n22 1d0d82\nc5 2b28fc\ncb ee137\n47 24f26\na0 298c12\nea 124971\ncf ee168\n6 3aa1e\n20 1cfb27\na5 1102be\n98 26440b\n31 d115\n5a 26cd2\n3d 1a074d\n4f 2507d\n57 25887\n77 1bbc1c\n46 1b8685\na5 10effc\nce 128bb9\n30 1d0488\nb5 110c1f\n9d d1f3d\nbd 2682d2\n80 d1703\n8c 264d3b\n99 d1f6e\nb9 268303\n88 264d6c\nc7 ee01d\n61 2a5f7\n26 3ebd3\n82 103b46\ncb ee1a5\n8d ca542\nd7 ee97e\n14 994\n81 d29b8\n8e 1029b6\nf6 27dce8\n89 ca573\ned 284556\n1b 1d49cc\n44 1b00d4\n1c aeb\n89 d2b0f\n24 1cfb58\n5 863c\n5a 1f2882\n41 1f07fe\n2 19a5a5\n4a 2504d\n6e 1b4633\n62 20ffb\n76 2198d\naa d6a0e\nf1 125493\nca 2b9aa6\n1 3acf5\n93 25cf72\nef eb28f\n47 251d4\nce ee175\ncf 281934\nc3 ee2fc\n8e 102c64\n57 1b09db\nf6 27df96\n89 ca821\ned 284804\n3b 3f6bb\n44 1b0382\n7b 1bbda6\n1 961f\n6a 2a756\n75 1b4e2b\naa 29a2e0\nd0 278ae9\n5 9650\n4f 1b851f\n43 24ee7\n9 9776\nd 97a7\n53 6022c\n6c 1b468e\n60 21056\n6a 22166\n25 5757\n6e 22197\n43 261b7\n8c 29e44f\n80 10ae17\nbd 2a19e6\n5 19a5e0\n20 1d0de9\n98 2656cd\n4f 2633f\nee f25ca\n4f 579c1\nef 2bef51\ne3 12b919\na4 ce59f\nef 27d72b\ne3 ea0f3\na0 ce5d0\ne6 e9e67\nac ce6f6\n85 d2747\n3b 3f6af\nc7 ee00f\n8d d289e\n23 3ee43\nd3 129323\nb2 299826\n7d 632a0\n4c 5fd09\na7 d6949\n85 2636d7\na0 299ee0\n4 2ef\ncb ef467\n97 1044d7\n20 5725\n27 1d0e16\ndb 129736\nd0 2820f9\nca 2792f8\ned 123996\n24 5756\n42 1e97cc\n91 d3389\n1 98cd\nc6 e5d22\n5 98fe\n41 1f1ac0\n62 222bd\n66 222ee\n8f c9535\nd3 eff21\n9a 296aa6\n43 26465\nda 128219\nbd 2a1c94\n47 26496\n1 12c3\na0 ce87e\n85 d29f5\n0 19225e\n3b 3f95d\n95 25cf9c\nc7 ef58d\nc0 128cd8\n42 1aedf6\n4d 1af218\n4 19bb3f\n7d 29b7e\n4c 265e7\nb2 260104\n41 5f936\n20 54db\n47 1aee28\nc5 128d0a\nc3 ef5be\n9c d3254\nac 106bac\n19 b1b\nf2 27cca5\naa 260bbc\naa 10ee6e\ndc 280caf\n8b 10c244\n1e 3a220\n85 10ae57\n8d 102a12\nde 120148\n2 3aca9\n3f 1d1878\n33 3e240\n22 1d103e\n44 1af05e\na 3ae00\n3b 3e397\n30 196d5a\n72 22c10\n50 1af99e\n2a 1d1195\nb5 d74f1\n81 ca41c\ne5 2843ff\n27 3ebd2\n27 1d0dc0\n18 a0d8\n82 294a7e\n4d 1f1ef6\n41 5e8be\n50 1af750\n2a 1d0f47\nb5 d72a3\na2 10efc5\nae 2a25fd\nd4 280e06\nac 106e5a\n16 3a377\ne9 27c1d7\n66 5b754\n44 1e84e2\nc6 2bae9c\n28 4360\n62 1b2f2f\n37 3e51f\nd6 2bb7fd\n14 1c62\n7e 1bbdc8\n1a 1de5\n5e 1af87f\ndc 129761\na7 106cb5\n5d 5f3a7\nc6 27941e\n6b 62bc5\n2 3a9f9\n55 1b09e2\n23 3eba1\nbb 110a92\n86 103b21\n82 103b52\na7 107cc9\n8 39b99\n5d 603bb\nc6 2babe0\n63 22312\n62 1edbbf\n47 1b73b6\ncf 1278ea\nec 12ba37\n44 1e97a4\n55 1b0c90\n23 3ee4f\n33 1d8a7e\n82 103e00\na7 107f77\n5d 60669\n4b 1cd52\n2 9679\n7f 1ed3e7\n0 19a5a0\n73 1bcf0f\n1f 1d5a1f\n13 423e7\n4c 1b01d9\n40 1cba1\n1b 4253e\n10 19af01\n44 1cbd2\nbe 2a2f6c\nb2 10f934\nba 1074ef\n8f 10ad05\n84 2636c8\nf7 286037\n8b 10ad36\nc4 e5cc9\n9f 10b666\n94 264029\n53 1b8fb6\n19 a3e7\n9b 10b697\n5b 1f2571\nc7 2791e1\n36 3e51e\n49 1e2bb\ne0 27c081\nd4 281e26\neb 1236be\nd5 e6629\naf 107e20\na4 2607e3\na5 106d10\nc9 28037e\nf1 27df5f\nf 1d536c\n3 41d34\n8 41e85\n5c 26f38\n33 198024\n8 8507\n42 1b70d6\n60 2104a\n6c 1b4682\ncb ee1a7\n69 221b4\nae d5a8d\n8 1cba69\nbd 26154a\n1c 1cd969\n10 3a331\nb1 cdf12\n6b 62c17\n7b 1ee3d8\nb6 107615\n88 2637ec\n70 219ab\n7c 1b4fe3\n25 1976ed\n8b 1029e8\n87 10ae5c\nad d6a45\ne7 f21b6\n7a 22ab9\n58 1af847\n8f 29e4c5\n83 10ae8d\n45 1b8433\nc2 e6f61\nd2 2b2210\n8c 25b78b\n3 42d48\nb6 cf1a9\nd6 2b2241\n7 42d79\nbe cf300\n96 10b820\nb3 10864f\nde 2b2398\nf1 ebac3\nf 42ed0\n4 19b893\na6 107cc8\n40 1af09f\n62 22311\n14 19b1de\n79 22ab5\n5c 25c86\n3c 1981b2\n30 4b7a\nd5 1283a9\n83 10bea1\ne5 f2211\n38 4cd1\ndd 128500\n8b 10bff8\nd4 eff4a\ne8 27d498\n71 1bd1a8\n9 41e76\n93 10cab0\n1d 90f4\n6f 1b3118\n3b 1a0467\ncd 120ab1\n26 3ee7f\n93 10cab2\n83 10c14f\nef 27d47d\ne3 e9e45\n2c 197841\n20 4209\na7 25f7d5\nce 2792d5\n45 1b716f\nc2 e5c9d\neb e9f9c\n2f 4339\n9f 10cbc8\n1a 1cc431\n10 192bbf\n4d 1b7512\n21 196406\n72 1b3b3c\n86 10be71\n63 5b734\neb 12cf70\n7e 5af4c\nc8 128e2f\n4a 1aef4d\ncf ef6e4\nd4 129608\n56 1af726\ndc 12975f\n5e 1af87d\ne5 12ce4f\n67 1b2f6d\n46 1b0337\nf5 12d7b0\n77 1b38ce\n64 21019\n93 1046f2\ne 19268d\nf7 2be6d5\n4e 1b048e\n42 1ce56\nfd 12d907\n7f 1b3a25\nff 27e08c\nf3 eaa54\n10 192e6d\n3d 1d1b21\n31 3e4e9\n5a 580a6\n14 992\n81 d29b6\n7b 5c24e\nef f388d\n7b 1bbfe4\n46 1af073\n3c 3f932\n18 1cc3ca\nbe d63ee\n8e 29cee4\n2b 4616\n76 5b0a3\n5f 1d690\nc4 2bac4b\n42 1af0a4\n56 1af9d4\n52 1afa05\n67 1b321b\n6b 5bb39\nea 28452d\n80 ca3c7\na9 ce6c6\ne3 eb107\neb eb25e\n39 1a0460\n4 1934ef\na0 d6b6c\ne9 ea245\n9a 1048ac\n94 104729\nfe 2be88f\n0 193520\n7b 1bcff8\n46 1b0087\nea 2847db\n80 ca675\na1 ce81d\ne3 eb3b5\n39 1a070e\n4 19379d\n7d 22da2\n89 d15ab\n41 1af02e\n59 25a0a\ne6 2bdac7\ncb ee143\n22 3ebf4\n0 1cb982\n54 1b0a35\ne2 2bedc8\n11 19c170\n5f 25c8c\n43 1f1dd9\n30 1d0736\nac 25f616\nea 2bef1f\nb2 cdc6a\nbe 2612a2\n34 196d35\nbc 25ff77\n1b 43862\na8 107e3b\n8d d1632\n40 1dec5\n30 196d66\nb5 d74fd\n27 3dbbe\nb8 25ffa8\n3c 196e8c\n83 294d2d\n48 1e01c\n2d 1cfefc\n4a 56481\n38 196ebd\nbd d7654\n62 5b733\n1d 19b034\n66 1b2f6c\nd3 280b9f\nc 1ccd5c\n0 39724\n73 5c093\nee 27c1ae\n98 d21bb\nea 27c1df\nb1 2600fe\n6e 1b30c3\n49 5ea79\n36 4df0\ndb 280cf6\n3e 1d1b27\n32 3e4ef\n41 1e172\n6a 1b30f4\nef f388b\n76 1b38cd\nfe 27cb0f\n72 1b38fe\n84 d1726\nf7 f4095\nfa 27cb40\na5 2a0f9e\n47 1b7114\n43 1b7145\n6 1ccc0c\n1c 194249\n10 c11\n4f 1b726b\n4b 1b729c\n24 1963d6\n2d 47072\n34 196d37\nca ee144\n40 57893\ndf e6787\n45 1b741f\nc2 e5f4d\nff 27cb1c\n60 1edc28\nce 279585\n1f 3b4e3\n14 193ea6\n4a 1e9923\n99 d34e0\n21 1cfd66\nd6 1293c5\n8a c9503\nd 19a9d5\n6a 1ec7a8\n25 45c4b\n24 196682\n6e 5b8bb\nb3 d7527\n91 2642b5\n34 196fe3\n7e 5c21c\n1b 43b10\n7d 1b4d42\n71 2170a\n40 1e173\n30 197014\n80 d29b5\nc9 e608e\n7a 5c24d\n5 19a82a\na4 ce591\n4f 5fa63\nd4 ee9d8\nbd 2685d6\n1c 1d49f5\n4e 1b726c\n66 1b321a\n8d 10ad54\nd3 280e4d\n76 1b3b7b\n8a 29d1d1\n9d 10b6b5\n47 1b73c2\n43 1b73f3\n1a 19b05d\n1 2af\nd 1938e7\n57 1b7d23\nc2 127a1d\nce 2bb055\ne 1cbd5b\n9 193918\n53 1b7d54\n1b 1d5cf0\n38 1d9e3d\na0 ce56e\nab 1103eb\nd1 eebf4\n3e 3e3c7\ndd 28222c\ndd eed70\nab 10809f\n6b 62969\n81 d2716\n89 d286d\n6 1d5278\n37 1d880f\nc3 ef2ae\n5f 26f4e\n15 19b1ef\n30 1d19f8\n66 1b2f5e\nd3 280b91\n2 1cbc35\n20 41b5\n2c 1977ed\n53 1f241a\n48 250a8\n47 1b83d6\n4b 1b855e\na0 ce81c\n24 197698\n7 19354b\n14 9a0\n81 d29c4\n65 1bc886\ne2 eb3b4\n21 1d1028\n1e 1d47b2\n4a 1e8601\n20 197975\n40 1cb93\n4c 1b01cb\n39 3e3f4\n22 19790e\nd8 2bb6d2\n3 1d4f98\na0 25f4f0\n20 54cb\na7 260a97\n58 1f2827\n11 193e74\n16 1cc2b7\n19 193fcb\n1e 1cc40e\n46 1e139\n2f 1cfc55\n42 1e16a\n2b 1cfc86\n66 20d72\n6 1ccec8\n37 1d045f\na 1cbd2a\nbc 1077c9\n62 1b449d\n2 1ccef9\n33 1d0490\nff eabd0\nb6 d74f7\nd6 2ba58f\ne 1cd01f\n56 1ea9a\n2 399e7\n3f 1d05b6\n52 1eacb\na 1cd050\n3b 1d05e7\ne3 f36f7\n76 216d3\n10 1c31\nc5 ef32e\n7a 1bbd97\n59 5f376\nbf 29ac1d\nb3 1075e5\n11 194122\nfe ea92f\n76 1b38bf\n12 1cc596\n4d 25324\n2d 197850\n21 4218\nc0 2814f6\n2f d947\nd 19a6d5\n28 1d0ede\nfa ea960\ne4 e9e60\n85 102b5b\n20 196397\n3b 1d9b89\n6 1ccc18\na2 110295\nd4 2820d6\neb 12396e\n23 1d0df1\n59 60638\n3b 1d9e37\n6 1ccec6\n87 294d5e\nec 285ad5\ne0 f249d\n41 24ee2\n4d 1b851a\n88 10ad2e\n6b 1b4667\n80 263697\ncf ef6e6\n61 63d89\n6c 1eca8c\n25 19f9d9\nf3 286006\n42 1b8406\n95 c9d9a\n42 1b86b6\n73 1bbc4d\n2e 1cfc54\ndc eed7d\nf9 ebbac\n9b 29d887\nea 2b6e7d\ncf 280674\n4b 1e98c2\n2a 1cfc85\nd5 eec25\naf 11041c\n14 19b1ec\n31 19801b\n36 1d045e\ncc ef482\n14 194160\nbe 2996a0\n59 1b9158\n12 3a0fc\n1e 1cd734\n32 1d048f\nb7 110c26\nba 2996d1\n1c 19b343\n39 198172\n16 19af39\n3e 1d05b5\nbf 110d7d\n70 21949\n7c 1b4f81\nae 25f61d\n21 c504\n2d 19fb3c\n7c 1b4ff1\n70 219b9\n88 2637fa\n73 1f65c1\n3 1d3f84\n61 222a9\n3d 197191\na5 106d02\n46 1b0079\n5e 603b5\n83 10be3f\ne5 f24c1\n3c 1983fc\n30 4dc4\n26 1d0dbf\n6 3975a\na7 cd33b\nb8 ce006\n9 41bd6\n71 1bcf08\n22 1d0df0\nd0 27883b\naa 29a032\n1 1325\nc2 e6fc3\na5 260a3e\n28 1964ee\ndc eed6d\n12 1cc278\nb3 25fe59\ncf 2b9d24\naf cd73e\n52 60549\n68 1b307f\n80 10be99\nc9 11f572\nbe 110b24\n30 196cf8\n29 5625\n28 1cfc72\n5a 1b0b62\n31 196fa5\n40 1aed8d\n7b 5c48c\n41 1aed8e\n7f 1b3a19\n36 1a0340\n41 261a4\ne0 f375f\ncf 2b9a84\n48 1aeee4\n32 ce5f\n3e 1a0497\n68 2114d\n96 10c826\ne5 f21bf\nfd 27e077\nf1 eaa3f\n73 229c5\n2b 1d0f4a\na4 cd331\n11 1cc280\na3 10ed78\nd5 280bb9\naf 2a23b0\ne6 123839\n4d 1b8528\n41 24ef0\nf 1d53ce\n3 41d96\n17 426ba\n19 1cc3d7\n95 29d996\n28 3ef9e\nd 8795\n5f 1f2852\n53 5f21a\n22 1cfac0\n7 19a575\nbc 29a9bb\nb0 107383\n4c 1e869b\n2a 1cfc17\n0 1cce90\n7 98f9\n31 1d0427\n48 1b0456\n79 1b39ed\nf 9a50\n50 1ea62\n8 1ccfe7\n39 1d057e\n84 d2748\nc4 277ea7\n9 1d5326\n3d 196ee3\na5 106a54\nc5 277ea8\n34 1a039b\n7d 1b3a74\nc6 277eae\n19 1d4725\n7e 1b4f96\n72 2195e\n4d 1b72c8\n68 1edad1\nca e5df6\nf 1d5122\n50 1ea134\n3 41aea\nd1 281e5a\n8d 25b46e\n20 45c27\n2c 1d925f\n3 41ada\nf 1d5112\nd3 281e61\n8f 25b475\n65 1b452a\nce 278005\n24 4494\n9d 2656ff\n42 1e850a\n91 d20c7\nca 278036\n63 20d40\n6f 1b4378\n25 4495\n7a 1ee6db\nc4 281773\n82 26495e\ncb 278037\n8 1d40d3\n7d 1b3d30\n34 1a0657\n1e 1d5a82\n12 4244a\nf7 ebd3b\n9c 25bdce\n9d 25bdcf\n35 4df6\n92 2652bf\ndb 278998\n99 25be00\n52 1d507\n5e 1b0b3f\n67 22035\n9a 25be06\n2 1579\n33 4b10\n3f 198148\n3d 19f47b\n5a 25a00\nfe 285119\n4d 1e28a\ne4 27c050\n86 10aeb1\na9 1080ea\n1f 1d5d21\n13 426e9\n3c 1d9e6e\n30 46836\n42 1de5c\ncc ee1c0\n15 19aedd\n4f 1e291\n61 5b72d\nd 16f9\nad 268c89\nc 1d50a8\n0 41a70\na1 d5651\n57 25b35\nce 128e67\nc3 11f3b2\ncf 2b29ea\n11 19af0e\n4b 1e2c2\n68 2240f\n90 d2064\n9c 26569c\ne2 27c088\na7 106d17\n53 25856\n5f 1b8e8e\ncb 2b2a1b\n1 311\nd 193949\n57 1b7d85\n17 3b62c\nb6 d6235\n4a 1dfb3\n49 265a9\na7 267b25\n6 1d3f44\nb4 2600cc\n5f 1f159e\n84 25c888\nb5 25fe1f\n1d 205a\ndd 28222e\n3e 3e3c9\nd1 eebf6\nab 1103ed\nc4 279419\nf5 27c9b0\n28 1d827c\n56 1b09dc\n5d 1ebeb\nfd 28617b\nf1 f2b43\n19 208b\nfa eab9c\n7d 1bc06e\n1e 3a4ce\nb0 25fe51\n8c ca851\nc6 279420\nf7 27c9b7\n1b 2092\n84 294a38\n38 61df\n7f 1bc075\n11 19c1c4\n58 1e9277\nb2 25fe58\n5f 1ebf2\n71 5c08e\nb9 110a97\n80 c93a7\n8c 25c9df\nbd 25ff76\n41 1dec4\n28 19eaea\na5 110010\n98 26415d\n31 ce67\n3d 1a049f\nb7 268486\n16 1d48a5\nf 1d3dee\nc0 27944a\nf1 27c9e1\ne4 2bdb30\n4e 1cfde\n6 192536\n59 1ec1c\n29 45bb\ne 1d3def\nf0 27c9e2\nbf 108781\nb4 261144\nb5 107671\nf2 27c9e9\nb7 107678\nc0 e5f38\ncc 279570\nfd 27cb07\nc5 277eb6\n3f 1d1b26\n33 3e4ee\nc1 277ee7\naa cd70e\n6f 1ecae8\nd8 121132\n86 294a3f\n13 19c1cb\n5a 1e927e\ncc 27800c\n84 264934\ncd 27800d\na8 cd715\nfd f3f37\na3 ce56a\n6d 1ecaef\n84 294a46\n88 10bfe2\n7f 1bc083\n8 39b35\na9 cd716\n85 294a47\n80 264965\nc9 27803e\nb2 d5fb8\nbe 2695f0\n81 294a78\n8c 25b729\n0 3aa56\n64 1f4a39\na0 25f79c\n4b 1f0c6e\n8 3abad\n6c 1f4b90\n2d 1d7ffe\naa 106b2c\ndc 27896d\nb8 ce076\nb3 ceecb\n7d 1ed450\n94 2953a7\n18 3a496\n6 19a5d8\nb9 ce077\n40 26213\nc7 2817df\n93 2953d2\n9e 25c083\n9f 2954f8\nb0 cf1d3\n9 9a16\n94 d1e49\ne5 27c05d\n96 2966c4\nc2 28022f\n88 c925e\n67 1bc571\n47 261dc\ne6 f3797\n40 1e9773\n87 10aebe\n2c 3ed21\n99 10c954\n83 29d07b\n48 2636a\nc3 ee2fe\ncf 281936\n4d 1e298\n39 cfae\n8 9a17\ne4 27c05e\nc7 277f11\n14 8f9c\n66 1b2fc0\n31 5dcb\ncd 281681\nc1 ee049\ne1 27c08e\n5b 1f155d\nc7 2781cd\na5 298bee\nc1 e5c89\ncd 2792c1\ndb e6756\n82 29cdc0\n9c caea2\n27 1d9102\n97 10b813\n49 1e2c9\n46 24f17\ne0 27c08f\na1 298c1f\n51 1b8d01\nac 25f8d0\n12 4394a\n9c d1fa0\ned 27c1b4\n92 1031e3\n9e 29681b\n24 c7e4\nca 280386\n26 45c51\n39 cfbc\n8 9a25\n7f 1bbd65\n4e 1b87ce\n42 25196\nf3 2bf72a\naf 298d3e\nec 27c1b5\na3 d6b64\n36 4b40\n90 2642a6\n9b 10b8e3\nf7 2bf6eb\ne 1936a3\n2 6b\nbd 110b1c\n52 26b6d\n35 1a05e8\n4e 2506e\ne8 27c1e6\n52 1b09b9\n18 1dea\na 1cbae0\n5e 1b0b93\n35 196fd6\n52 1d55b\nbc ce0a5\n1d aea\nf6 27cc74\n86 295fb1\nb7 299548\nc4 ef32b\nb6 299549\nd7 278b2e\n92 29d721\n37 1d9a63\nf 1d3dfc\nc0 279458\nf1 27c9ef\nb2 29957a\n80 295fe9\n87 d2a52\nb1 299580\nbc 260231\n5e 1b912f\n52 25af7\nd 19b9f9\n1 83c1\n3d 19712d\n5a 1d6b2\nfe 27cdcb\n64 1bc5cb\n6f 63c08\nd5 2b1f9b\n8a 296139\nbb 2996d0\n2 12cb\n66 1bb2ae\n69 1b4340\n4c 1b7511\n41 1f0860\n21 d7c8\n6 12fc\ne8 27c1d6\n5e 1b8e8f\n52 25857\n50 1b9002\n5b 6063f\n28 19655c\nad d6cf3\ne 1453\n1b 437f2\n40 1de55\nb9 29a99b\n1f 43823\n60 1b2f96\ne5 f372d\n4e 1dfe4\n4d 265da\n86 ca3f3\n12 3b66a\n20 3ebed\n8e ca54a\n0 2\nc 19363a\n48 579ea\nda 279c67\n56 1b7a76\n69 294ee\n79 1b4caf\n9f 10c91a\nc4 e6f7d\n89 295e91\n40 1af02d\nf9 12d62a\nee 2bdc8e\nb9 110a99\n96 103222\n5 193552\ne1 eb100\n52 1af995\n1 19225f\nc1 278187\n53 1af996\nf8 124017\nca 2b2cc8\na1 ce56f\n12 192e04\n6e 1bb413\n81 25b5f6\na 1cbad2\n8f 10c269\n92 10c793\naf d6a40\nf0 eba52\n37 1d9a6f\n92 29d72d\n60 1b31d6\ne4 124854\nc7 120707\n16 1d591d\n65 1bb2b6\n71 1b3b36\n8c 263a77\n70 1b3b37\nd4 281e7a\neb 123712\n80 29cdc7\n25 1d9109\n18 427e6\n6a 1ec80a\nef 12cfa1\n73 1b3b3d\n2a 1cfc79\n33 196fac\n11 1cc52e\n80 25c8b9\nb1 25fe50\nb0 d721f\nf9 ea8f8\nc0 1289ca\n41 1af03c\n7f 1b3cc7\n36 1a05ee\n95 d1de8\nb0 1085f1\n38 d2bf\n7 19a823\n71 1b4bac\n54 1b7d7d\n2b 1d1194\n51 1af99d\ne3 f2495\nef 285acd\ncf ef738\nc8 2b2ccf\n6 1d4fca\n1c 19c607\nde 1281e8\n10 8fcf\n33 1d06ce\n64 2a389\nc4 278155\n60 2a3ba\nc3 27818e\n44 1f0890\neb 2b6e80\n84 25b5c4\n78 217f0\nff 27cdbc\n36 1d1784\n85 25b5c5\n22 1d0df2\n7a 217f7\nb9 269865\n20 3d939\n2c 1d0f71\n66 5a494\nd3 278aef\n9e d31f9\ne 1cbdb1\n94 d20e9\ne5 27c2fd\n1c 1d47b9\na1 267aeb\neb 12cd24\ne4 27c2fe\ndf e7aab\n9a d322a\n53 5efcc\n5f 1f2604\n70 63119\n7c 1f6751\ne1 27c32e\nc9 2bb020\n5c 1e8ffc\ne0 27c32f\nd4 2820d4\neb 12396c\nc0 e5c96\ncc 2792ce\ne2 27c336\nef 1249a5\na4 25f76d\n32 196fab\nca 2b1768\nf5 27cc5e\n23 3eea5\n8a 10af83\nde f0036\nd3 129385\nf0 12d4d2\nbc ce097\nf6 27cc66\n4f 1f097f\nb9 110d45\n39 1d88c0\n4 1cb94f\na5 25f530\nf 1d409c\nf1 27cc8f\ne 1d409d\nf0 27cc90\nfb 1242cd\nf2 27cc97\nb6 2600d5\n78 5af20\n4a 1e9bd1\nff 2b64ec\n60 2205c\ne7 27d628\na 1d406c\n67 2a391\n1 1d3ccf\n29 1977af\nc 19a980\nf3 284fe6\n52 1f1405\nb3 260105\n4d 265e8\n3 1d3cd6\n44 251cc\n61 21ffb\nb2 260106\n38 1d0571\ne5 1238a3\n3b 46739\na 431a2\nbf 10fabd\nc5 278164\n88 ca574\nec 284557\n48 26306\n1a 1d49cd\n77 2acf2\nc1 278195\naf 29a2bc\na3 106c84\nd5 278ac5\nca ee3f0\n4e 1b04e0\n42 1cea8\n6e 1bb715\n4 15af\n39 e520\n2 19a543\nb4 d5fe2\na1 298ecd\n4a 1aeedd\nfc ea97c\na8 25f645\ne5 27c30b\n96 296972\ne4 27c30c\n4c 1b022b\n40 1cbf3\nc7 2781bf\ne2 ea148\nee 27d780\ne1 27c33c\n46 251c5\ne0 27c33d\n52 1b0c67\n18 2098\n5e 1b0e41\na 1cbd8e\n52 1d809\n5a 1e920\nef 2bf1ff\ne3 12bbc7\n5c 1b0e48\n2a 3f007\n50 1d810\n9f 25bdd6\n0 1ccee2\nc0 ef308\n92 29d9cf\n37 1d9d11\n78 22b16\nb3 299827\n4d 5fd0a\ne0 eb161\nb2 299828\nb1 29982e\n1a 42593\n6b 1ec7a7\nd 19a973\n83 29e32f\n16 1cc30b\neb 28452e\n4a 1f094d\n81 ca3c8\ne5 2843ab\nef 28455f\n4e 1f097e\n85 ca3f9\n89 ca51f\ned 284502\n92 10b531\n9e 29eb69\n68 1b4341\n3b 1d8bd5\n37 1d9ad1\n60 1b3238\n6a 1b4348\n15 1d58c3\nb7 10767a\n1 1ccbe1\nd 1936a9\n1 71\n5e 1b0ddf\na 1cbd2c\n52 1d7a7\n10 9fe3\n3 43058\n31 1d8a85\n18 a13a\n27 1d0e22\n69 1b434e\n46 1b7115\nbf 10fb0f\n53 1afa06\n1e 1d5a74\n12 4243c\ndb efdca\n63 1ec650\n29 1d0edf\n6 1d3ca6\n31 4b17\n0 1580\n3d 19814f\n98 25be0d\ncb 1278b9\ne8 12ba06\n46 1b0329\n77 1b38c0\n13 1cc597\nef 2b5931\n9d 10b90d\n9f 10b914\n94 2642d7\nc4 e5f77\n3d 1d0613\n81 25c60a\n9b 10b945\nb2 cf178\n35 1a064a\n65 222ea\n3f 1d061a\n2 42dab\n80 c90f7\n8c 25c72f\n3 42dac\n81 c90f8\n8d 25c730\ncc e60ce\n78 21852\nff 27ce1e\n83 c90ff\n8f 25c737\nc5 279426\n88 25c760\n89 25c761\nc4 279169\n69 62910\n1f 1d5d2f\n13 426f7\nee ea27c\nef 2b6c03\ne3 1235cb\nc5 27916a\n82 103e02\nbf 29a9d1\nb3 107399\n21 577c\n84 29e04a\n58 1f2889\na2 29a197\nc6 279170\n6b 62917\nad 2a1095\nc4 ef579\nf5 f2b10\ne4 28590e\n6a 1ec7b4\n6c 2921e\n9b 10c8f7\n46 5659b\nc0 e6f5a\na1 2a24df\n34 1d04bb\nc0 27919a\n6d 1f4b2f\nc1 27919b\nc 9a46\n1 42d95\nc3 2791a2\nc0 e5c88\ncc 2792c0\nc2 e5c8f\n45 1b7161\nce 2792c7\n11 9d2\n1d 19400a\nc3 e5c90\ncf 2792c8\n2 3aca7\nc8 2792f1\n86 103b15\n5a 58354\ncf ee174\ncb 2792f9\n57 26df7\na8 cd459\ne5 ea11f\n96 104786\nd 19b995\n1 835d\ne9 eb259\n84 263982\na1 2607b1\n48 1e00e\n83 294d1f\n4e 1b756e\n19 a379\na4 d6b9d\ned ea276\n9e 1048dd\na3 cd2a6\naf 2608de\nca 2782f2\n94 d30fd\ne5 27d311\n3e 5f5b\ne4 27d312\nb7 10f70a\n86 10c173\n96 d3104\ne8 e9fea\ne7 27d318\n48 1af1e8\n4d 1b8526\n41 24eee\n96 29ec6e\n15 1cd813\nb4 26841c\ne0 27d343\neb 124980\na5 107fd2\n90 d3326\ne2 27d34a\n49 1b7233\na7 107fd9\n9e d325b\ne3 e9e37\nef 27d46f\nc4 279177\nc5 279178\nc0 2791a8\n44 251be\n7d 1ed3ee\nae cd48f\nc1 2791a9\naa ce9d0\n86 295d01\n5a 1ea540\na8 ce9d7\n61 5a779\n6d 1eddb1\n82 295d32\n5a 56de4\n3d 1d085f\n83 295d33\n87 d27a2\n80 295d39\na7 268b37\nc8 2792ff\n81 295d3a\nc9 279300\n1c 19c5f9\n10 8fc1\n99 c9e5e\n83 102821\n8f 295e59\n8b 295e8a\n88 ca7ce\nb9 cdd65\n42 261b8\nbc 2a19e7\ne4 27d320\nc7 2791d3\na8 106b7b\nd 1d50b7\n1 41a7f\na6 299eaa\n7a 1ee6e9\n12 1cc58a\n40 1dec3\n5b 1f281f\nc7 27948f\na5 299eb0\naa cd6ac\n2d 19eb7e\ndb e7a18\nc0 2b18b8\ne2 124b2a\nc7 ee321\ne0 e9e3f\nec 27d477\nc3 e5cf2\ncf 27932a\nb9 ce005\nd5 278809\na3 1069c8\naf 29a000\n46 1cbcb\nb 1cbadf\n85 264c39\nc 145a\n52 57edf\n4d 1af1b6\n4 19badd\n24 54aa\n44 1e8542\n94 1034cb\nf8 f2f55\n59 2599a\n57 6026b\n2c 5601\n72 5c086\na7 cd2d9\n44 1de94\nfb 27e0bf\n5a 1ea4de\n87 294cfc\n4c 1dfeb\n84 d29e8\nb5 d5f7f\n2a 1cfc23\n80 ca3c9\n45 1e97a3\nc7 28025f\ne4 2843ac\n84 ca3fa\n5a 1e912\nef 2bf1f1\n3a 1d1af4\ne3 12bbb9\n2a 3eff9\n5c 1b0e3a\n8 1cbd87\n50 1d802\n8c d2b3f\nbd d60d6\na0 d589c\nac 268ed4\n70 22969\nf7 27df35\n84 25b5c6\n74 2299a\n8b 294bd6\n8c ca551\nd2 120fd6\na4 ce5a1\n1a a37f\nc4 2b1639\nd6 e7bf3\n8e 296178\nbf 29970f\n82 102b40\n27 3ee82\nd7 129362\n41 1f0aac\nc2 e7261\nab 298d7d\nac ce6f8\n8f ca5ab\nf2 12517d\ncc 2b1790\ncc ef6d0\nfd f2c67\ne0 f242d\nec 285a65\n99 10b93e\n80 25c609\nc8 e70b1\n7 3aa1d\na6 107fd8\n1 19375f\n30 4b78\n3c 1981b0\nd4 1212ba\nb4 cf204\n2 193765\n41 1b02f0\nab cd3ff\nf4 ebd95\n42 1b02f6\n17 1cc2b8\nbf 1074b3\n10 a283\n61 1b4497\n5e 1b7c21\nc6 127792\n12 a28a\n63 1b449e\n41 1b02fe\nfc eac38\n4a 1af199\n14 9fa4\n1 1cce8f\n97 29564f\n5c 1e93e\nde 1294aa\n10 a291\n84 25c886\n7d 2b0e0\n29 4602d\nfd f2f15\n3c 1d08c0\n80 25c8b7\n43 1b7385\n9 87b6\nc0 127a16\ncc 2bb04e\n19 193f77\ne9 eb507\na1 260a5f\n10 3a0f5\n1c 1cd72d\nfe eac2f\nb0 261113\nc4 279417\n69 62bbe\nc0 279448\nc1 279449\nc0 279456\n0 1921fe\n22 5470\nc1 279457\ne1 27d5f0\nde eedd8\n5c 5831c\ne1 27d5fe\ne8 27d756\n4e 265de\n43 5f92d\n60 63a7a\n61 63a7b\n8 84a7\n65 222e8\n62 1b41f1\n28 5622\na3 cd5b6\naf 260bee\n6b 21147\n22 da6e\n9f 296a76\n93 10343e\nb5 108677\n42 1f0b06\n8 1923b7\n2a 5629\n79 1f6783\n43 26459\nea 2b590f\nc2 2bac1f\n40 1aedf1\n62 22063\n63 22064\nad 267c21\nca ee1a6\n6f 2a4e8\n68 221b3\ne3 ea147\nef 27d77f\n62 2a5ff\ndf 2b3607\nd3 11ffcf\n48 1aef48\n6a 221ba\n45 1aedbf\nb8 261268\n0 19a53e\n73 1bcead\n8c d18df\na3 29a188\n13 43949\n6d 1bc6bf\n61 29087\nff 2b64e0\nb6 2a2e07\n4 19b83d\n4d 1aef16\n65 1b44d6\n8 19a695\n7b 1bd004\nde e7a48\nf 3ab74\nae 10812f\nd4 e6938\n10 19ae9f\n9 97cc\n76 1bcf41\nd5 129609\n57 1af727\n72 1ee522\n38 3f953\nc2 11f421\na5 298e9c\nce 2b2a59\n12 19aea6\n80 102827\n8c 295e5f\n60 1b2f34\n68 1b308b\n48 1b7226\n19 193fd9\na6 107fcc\n4a 1b722d\n71 22c1a\n65 1edc5a\n8 1d3e19\n7b 1f6788\n11 19b14c\n88 29e47e\n70 1b3895\n78 1b39ec\n41 1b70dc\n6a 5a866\na4 ce84d\nc2 120675\n31 19802b\n40 1b70dd\n8b d1852\nd6 1293c3\n2f c685\n28 1cfc1c\n48 1b7234\nb4 d622e\n15 3b625\nf6 27c9b8\nbc cdde9\n23 1d103f\n37 ce8f\n6 98f8\n30 1d0426\n59 1b7b94\n3f cfe6\ne 9a4f\n38 1d057d\n54 1b8d21\n6b 5a5b9\n0 1d3c6e\n99 d220e\na9 268f06\n8 1d5325\n3c 196ee2\na4 106a53\n50 25840\n5c 1b8e78\n8 1d3dc5\nb7 cf20c\n16 3b62b\n69 1edad4\nf0 286000\nac 25f614\n12 1d45c8\n4b 57982\n11 1d45ce\n2b 1d8276\n9b 10c94b\nb8 110a98\nc0 e6fae\n7 1d4fcb\nf8 ea8f7\nfb 27cb3f\nca 2795a8\nf5 27dc74\na6 10f004\nae 106bbf\nd6 280b6b\nb6 107607\n18 1d4726\n4 19a87f\nbe 25ff7c\n70 646eb\n2c 3dcff\n99 10b932\n9a d21b2\nc9 ef702\n58 5f12b\ne4 27c04e\n21 3eeac\ndc f003d\nd1 12938c\ne6 27c055\ne9 285a97\n7c 1b3a73\n23 3eeb3\nde f0044\nd3 129393\nba 25ffad\n50 1b7cdc\nd6 efc41\n46 1e87f9\ne0 27c07f\nb5 cdc93\n84 ca6fc\n4f 1e9ba1\n43 56569\nfa 2b6260\nfd ebbdb\na9 106b28\n43 1de69\n73 1f686f\n82 26369e\n63 63d90\n2e 1d11c4\n54 1af9cd\n22 3db8c\n10 1d48dd\n8c 2637bd\nc8 127b6d\n6d 63eaf\n69 2924e\nf4 27c9af\n1 3aca1\n6b 1f4e07\n95 263fc6\n7 19b89b\n4e 1e894e\n19 3b759\n9b d2215\nb8 d6362\n7d 1f573c\naf 2679ce\ne 1d3ded\nf0 27c9e0\n8f 10ad67\na1 d58a9\nca ef466\nad 268ee1\n93 263ffe\n11 1cd542\n78 22ab4\n1a 1d478f\n92 263fff\n73 646f1\n9d 26411d\n3a 1d1b56\nbd 2695ea\n10 423d1\nb1 d5fb2\n1c 1d5a09\n1e 1d5a20\n12 423e8\n9c 26411e\nd8 1284ce\n8c d163f\n7d 64810\n3e 1d1b25\ne7 12bbea\n32 3e4ed\nf8 27cb37\n16 1c5d\nd 41ea7\n25 1d10cb\nd5 2bb5ab\n9c 10b650\n7f 1b4f89\n73 21951\nb0 2a18cf\nea 124c83\nc1 2814f9\nfa 27cb3e\n99 26414e\nc3 1289c4\nf5 27df90\n7 41d65\n98 26414f\na5 110002\n79 64841\n9b 264155\n19 1cd699\nc5 2801f6\nf4 124201\ne3 27c343\n8 9a15\ne4 27c05c\nc4 2801f7\n9 1ba\ne2 27c344\nc7 2801fd\nc6 e6f84\n8b 295e98\ne0 27c08d\nf7 2bf999\n2 319\n3f 196ee8\nbd 110dca\ne 193951\n8f 10bfc7\n84 26498a\n85 10aeb7\n38 1d1b4f\n1d 19b346\nc3 28022e\ne7 f3796\n41 1e9772\ncd 28034d\ncc 28034e\n3 98c8\n40 1f0aad\nce 280355\nf5 ebd42\nc4 e5fcb\nce e70db\ne8 27c1e4\n4a 1aef3f\ncf ef6d6\ne1 12cb72\nb5 ceef5\n25 c7e3\n7a 1f6a29\ncb 280385\n60 2205e\n46 1e9a49\ne7 27d62a\n20 5779\ne 1d3dfb\nf0 27c9ee\n89 d18ad\n2c 196581\n28 3ed54\n50 1b8d00\nf7 ebd9b\na3 106ce8\naf 29a320\nfc 27cb14\nab 10ee6d\ndd 280cae\n4a 1cd53\n2d 1967ce\nb 42ef5\ndf 280cb5\n2f 1967d5\n1f 1d5ccd\n13 42695\nd9 280cdf\n29 1967ff\n35 d144\ndb 280ce6\n2b 196806\nff f424e\n59 1ea22a\ncc ee41c\ne9 eb24b\na6 298be6\na2 298c17\n38 e271\nb4 29954e\n8c 294b9d\n40 5fbe1\ndf eead5\nc2 ee29b\nff 284e6a\nce 2818d3\nf 1cd010\n3 399d8\n34 4de7\nd9 280ced\nd4 280b56\n29 19680d\nba 2996cf\n48 1b7542\n40 261af\n12 1d4876\n43 5f92f\n22 54d4\nc7 128d03\n8 1cbd15\na9 25f8f6\n28 5624\na3 cd5b8\ne 1cd00f\n2 399d7\naf 260bf0\n2a 562b\ncf 128e5a\n87 29cd8e\n5b 1f15cd\n62 22065\na3 298ec6\n68 221b5\n5a 1f15ce\n6a 221bc\n8 19366b\nbd 25ff86\n8e 263a70\n97 29d6ef\n48 1b01a6\n9e 10b967\n5a 1b7bf0\n8f 296115\n83 102add\na5 107d16\n31 4b6d\n3d 1981a5\n93 29d720\n9e 2643d1\n9c 2643d8\n35 1d9a6a\n9f 29d846\nf9 eabfa\nb0 d7521\na5 cd2e0\n67 20d81\n70 2af59\n30 196fa4\n7a 5c1dd\n98 29d87f\n31 46589\n3d 1d9bc1\n1 19a7eb\n45 1af06d\nb8 261516\n0 19a7ec\n2d 47062\n73 1bd15b\n97 10b571\n2f 1d11c5\n23 3db8d\n55 1af9ce\n4a 252f9\nc0 11f6c8\ncc 2b2d00\n10 19b14d\n7b 1b4fba\n98 10b681\n3d 479c3\n67 1edc61\na 1d3e20\n31 3f80d\n13 19b153\n8a 29e485\n57 1af9d5\n10 1cd5a3\n37 1a03a1\n4a 1f09b1\n89 29e1df\n71 5c39e\n19 a0c9\n4e 1b72be\n70 1b3b43\n41 1b738a\ne6 f2465\n47 5785c\nab 2a1379\nee ea020\n29 dbcb\n40 1b738b\nc8 1278bf\n6d 63c01\nb4 cdf40\n3b 1d1b59\n32 1d06cd\n9f 10463c\na1 298ebf\ne 1d3e4f\n37 d13d\n30 1d06d4\n77 1ed044\n46 1e9aad\ne7 27c056\na6 10eff6\n4c 1b022d\n40 1cbf5\nf6 f2dc6\n57 581bd\n4 41aaf\na5 d5690\nbb 2a1cda\nfe ea981\n39 e52c\n13 1d4875\n20 1d1027\n5 19a81e\nd3 280e4b\n95 d309a\n5 1cbc52\nbc 1087dd\na 1ccd3e\nd 1923d9\nb6 2600d3\n45 5f905\n25 1cfaf9\ne4 27c2fc\ne3 27c335\ncb 2bb027\n5e 1e9003\nb9 2a2cd9\nf0 f40b2\n2f 1cfef7\nfa 125336\n51 26b65\n9 1d50ea\ne6 27c303\n71 2acac\nb5 cdeed\nde e7aaa\nba 2a1a1f\nd6 efeef\ne0 27c32d\n84 263914\nf7 286283\n86 26391b\n26 41df\n9f 26544a\n93 d1e12\n75 1f5339\n80 263945\n8b 10af82\ne7 2bed8a\nad 1101bb\n42 2620c\n25 19fc87\nf3 2862b4\n96 103216\n7d 1b4ce0\n71 216a8\n40 1e111\naf 267c7c\ne 1d409b\nf0 27cc8e\nf2 27cc95\n94 264275\nc4 2804a5\n96 26427c\n86 294ab1\n93 2642ac\n11 1cd7f0\n92 2642ad\n37 1a05ef\ne4 27c30a\n2c 47071\n96 295412\n54 25881\n2e 47078\nd3 280e3d\n82 ca422\n51 1ea381\ne9 eb4f9\nd3 eec4f\ndf 282287\n8b 29d1d4\n66 1f4a32\n31 4783d\n2f 1d1219\n23 3dbe1\n55 1afa22\n4a 2534d\n5a 1b0b0e\na2 298ec5\n38 e51f\nb7 10735a\n86 103dc3\ncf ee422\nb6 2997f5\nb4 2997fc\ndf eed83\nb7 d6296\n11 1cc272\nb0 29982d\nf7 eba8b\naf 29a010\na3 1069d8\n59 5f0ca\n86 29d03d\nc4 e5d1d\nda 2b2367\n49 265b5\n81 29d074\ndc eeb23\n4d 265d8\n21 54cc\nf7 2be427\nc6 2bae90\n8c 10c2c1\ne 1923df\na5 110012\nbb 108750\n1 43053\nf4 27dce1\n6 41ab6\n1 9673\nbf 29a971\nb3 107339\n62 1f4a01\nc4 ef2d7\n96 29d99e\n95 29d9a4\n2c 19fdeb\n20 c7b3\n97 10b565\n41 26460\n49 1e01b\nbf 2685dd\n1e 1d49fc\nd6 ee9df\nad 110167\n3a 3e398\nc5 ef5da\n58 26cbd\n93 29d9ce\n64 1b427b\n2f 3ed1d\nf4 eaae1\n42 1af042\n91 29d9d5\n31 5e2d\na3 110294\nd5 2820d5\n36 3e270\nb5 110973\n45 1b0081\n41 1cba0\n4d 1b01d8\n8 19b957\nc1 ef5a9\nc 1cbb0a\n94 10c7cb\n15 192e3d\n80 102b37\n8c 29616f\n45 251cd\n27 d7f0\n5 19a57e\n20 1d0d87\n68 2215f\n3a 1d0826\nb1 29aaf2\n17 4397a\n60 1b41f6\n41 1b839e\n49 1b84f5\ncb ef405\n93 2966a2\n64 1b2fc9\nc4 278165\ne6 eb3d7\n56 1b7d86\n73 1b4bb5\n48 1b84f6\n43 1f07f7\n9 1d5086\n62 222bf\ne5 f21af\n8b 10bf96\n24 1cfb5a\nbd 10fab4\n73 1f52ff\n42 1f1d68\n8 43199\n9a 265416\n87 d2a50\n80 295fe7\na7 268de5\ne4 27d310\nb9 10fae5\n4b 1f1c82\na0 2607b0\ne0 27d341\n1b 1cd702\n28 4608e\n56 1e7ee\n44 1b0384\n75 1b391b\ne2 e9e36\n65 1bb308\nee 27d46e\n0 15e2\nce 1207fb\n31 4b79\n3d 1981b1\na5 29a160\nc2 1206e5\n46 1b038b\n77 1b3922\nd5 1212bb\n25 19e777\na2 cd2a5\nae 2608dd\n89 10c293\n7f 21839\nf2 2b63b7\n51 1b8d63\nd 192377\nac 25f932\nde f009a\nf4 ebaf3\n30 3e4d8\n3c 1d1b10\n5e 1d68f\ncb ef6b3\ne0 124acf\nc5 ee2c6\n34 4df7\nfb f3f6f\n2d d942\n84 d27a8\ncd e5e81\n18 b1a\n52 1af6e9\nc5 2814b8\nc9 ef400\n81 264958\nc8 ee191\n6d 2a4d3\nc7 2814bf\n82 264960\nce 128bbb\na5 10effe\n75 22c3d\n81 d1446\n8d 264a7e\n2 42d49\n8c d28f1\nc6 2814c0\n27 41e2\ne8 124c26\ncd ee41d\n77 22c44\n83 d144d\n8f 264a85\n3c 5f62\n85 10c179\n88 264ab0\nc9 ee44e\n8a 264ab7\nad 10f155\n73 1bbefb\n2e 1cff02\nf9 ebe5a\n9b 29db35\na0 d590c\nd 19b989\nac 268f44\n1 8351\nc1 edfd7\ncd 28160f\n53 26b7a\ne 3ab81\n3 42ff6\nc9 281640\n33 4658e\n3f 1d9bc6\n2 42ff7\na 3abb2\nb4 25fe90\nc8 281641\n29 4363\n4 1924cf\ncb 281647\ne4 27d31e\ne4 123840\nc7 11f6f3\n6e 22437\n4c 1af1c5\na6 299ea8\n5f 1b7ede\n7a 1ee6e7\nc6 2801fe\n8c d162f\na4 299eaf\n2c 19eb7d\n44 1b73ae\nae 25f67f\n4c 1aef69\nb8 ce004\nc3 e6f52\n6 1ccf1c\n37 1d04b3\nc5 2814c6\ncc 2b19de\nee 124c50\nc0 edfe6\ncc 28161e\na0 298ebe\n2d 4340\n34 3e519\nfb 12d691\n34 196d29\n87 29e050\n5b 1f288f\n9c 26568e\n90 d2056\n82 102b32\n8e 29616a\nbf 299701\naf 1080ce\nd5 e68d7\n23 5783\n86 29e051\n5a 1f2890\nb5 d5fe1\n84 d2a4a\na 398f0\n1c a3b7\n6e 1b43db\n62 20da3\n28 5632\n4f 1aef7f\ncd 128e61\nea 124c1f\ncf ee416\na0 298ec0\nef 1249a3\n21 578a\n84 29e058\n83 29e081\n82 d16fa\n8e 264d32\n60 2206c\na3 298ed4\n68 221c3\n80 ca42b\ne4 28440e\n98 10b8dd\nea 2b5901\nb1 299820\nf7 284d07\n8c d2ba1\nbd d6138\na0 d58fe\nac 268f36\n88 ca582\n41 56324\n4d 1e995c\nec 284565\nad 2a10f7\nc4 ef5db\nf5 f2b72\n6a 1ec816\nef 12cfad\ne4 285970\n9b 10c959\ncc ef732\nfd f2cc9\ne0 f248f\nec 285ac7\nc8 e7113\n99 d21ac\n75 1b3b77\n6a 294a2\n73 5c397\n8b 29e1d8\nbe 1074b2\n20 197905\nd8 2bb6d0\n22 19790c\n9f 10b65a\n1 19baad\n47 1af074\nba 26151d\n52 1b7aa5\n8 193669\n8c 25c9ed\n80 c93b5\nbd 25ff84\n45 1b032f\n74 2299c\n0 992e\n49 1d007\n7 19a825\n22 1d102e\n27 da9e\n5 19a82c\n20 1d1035\n4b 1f1f30\na0 260a5e\n4 19b89f\n4d 1aef78\n56 25b36\n73 22965\n9c 25d092\n90 c9a5a\n53 1b7a36\nca 2bad68\ne4 27d5be\n86 295d73\n6e 20ed7\ne4 27d5cc\na4 29a15d\n87 26397e\na2 29a187\n6 1d4fc8\n4f 1e86a1\n73 5c095\n8e 10afb4\nab 107de3\n66 22040\n8f c9287\nd3 efc73\n38 4cc1\n72 1b3890\n46 1b73b3\n6e 1b4387\n62 20d4f\n25 45f05\n4b 1aef40\n2 19b867\ncb 2b9aa7\nab d6a0f\n6b 62bb5\n66 20d80\nde e7a9e\nb5 cdee1\n21 1d90ca\n6f 62be6\nb7 2a1b46\ne5 f347f\n35 46866\n5b 1af8a1\n12 19c1c8\ndb 2ba408\n2b 1cff28\nbb d7370\nc3 edfde\ncf 281616\n30 5dd8\ne5 f34d5\nfb 2bfb1f\ncb e5e57\n6a 1edada\n43 1b8397\nf5 f3e36\ndb e67b8\nfb 27cb4d\nca 2795b6\ne6 e9e77\n2a 1cfec5\nf6 ea7d8\neb eb260\ncf 2795e6\nc3 e5fae\n21 3d93c\n2d 1d0f74\n72 5b072\n7e 1ee6aa\n8c 25c9eb\n80 c93b3\n32 e121\n7b 217fa\na8 25f8f3\na9 10f122\n99 10b682\n11 19b14e\nad 106b59\n91 d3317\nc6 28050c\nea ea2ad\n2 9619\n47 1de9a\n5 1ccc12\n87 2636ce\na4 26781b\n3f 1d9ed6\n33 4689e\n46 1b8675\n62 22011\n78 1b3a42\nfd f41d9\n66 22042\n19 d75\n22 1976c0\n6c 1b4690\n60 21058\n6a 22168\n60 2231a\n6 1d4f66\n4f 1e863f\nf3 f40c6\n25 da99\ncf e70e8\n41 1b738c\n63 2a5fe\na4 ce5f5\n47 1e8796\n9f 264436\nba 29ac3f\n8 3adf7\na9 ce9d8\n85 295d09\nb7 10f9c6\ne6 eb139\n6f 1bb716\n5 15b0\n24 5758\nd4 efc38\n44 1e87f0\nf3 12d4ca\na 1482\n4e 1aef1c\ncc 128dfe\n59 1f25dc\na3 299eea\n95 1044d0\n47 1e148\na4 ce84f\n87 ca702\nc4 2b18e7\ne6 eb3e7\n75 5c36d\n81 10ab76\n8d 29e1ae\n28 197acc\n62 1b323d\nc 19b996\n0 835e\n8 84b5\n39 61d0\n6e 1b33c5\n72 219be\n7e 1b4ff6\n3a 1d8bd6\n85 10c16d\n12 19af06\n5 193790\n6c 5a5f0\nc9 ee1a0\n16 19af37\n30 47af8\n79 5b1d1\n5e 259df\na2 107c97\nd4 279ad8\n25 1d9169\n8 398eb\n7 1ccc19\nbb cf084\n1a 3b4a3\n9c d1f3e\n98 d1f6f\nda 11fe77\n5d 1f1349\na7 298c57\n3d e2b1\n0 3ad02\n10 3b663\n8c ca543\n52 1ea389\n18 3b7ba\nde eead6\na5 2a2260\n78 22b14\nbd 268582\nda eeb07\n7f 2ae49\n21 546c\n4 863d\n6b 1b43b7\n40 1f07ff\n6 1d4f68\ncb ef407\n64 5a747\nf6 27c9c4\ndd 2bb9b2\nd1 12837a\nbd cdd88\n8c ca7f1\nde e67e8\nb9 cddb9\n88 ca822\nec 284805\n77 2afa0\n62 1b44ff\n0 9620\n8 9777\n46 261e9\n4f 1aef71\nb9 cf337\n6 19b898\n4a 5ea0d\neb f25ee\n69 5bb32\n4e 26340\nc6 ee010\ncc ee41a\ne9 eb249\n8c d289f\nd2 129324\n3d 4f41\n60 1ed91a\n67 2a383\n40 57585\n88 d28d0\n55 1af782\n23 3d941\n2f 1d0f79\nf 1ccd70\n3 39738\n9d d3255\n7 39769\nb1 d7522\n10 43941\n1f 1cd6d1\n13 3a099\n7e 22aea\n5c 1af878\n2a 3da37\n5 19b892\n82 ca3c0\n87 102860\n12 192b56\n90 10ca38\nd7 efee0\n8f 29e465\n83 10ae2d\na5 110066\n21 3ee3a\n2d 19652e\n9 19b9ba\nf0 27c9f0\n56 25878\ne 1d3dfd\n9f 29edc6\n93 10b78e\nb6 107369\n80 d276b\n7a 1b3a55\nc5 e6fec\n1 399d1\nd 1cd009\nac 267c12\n98 25be01\n7 39a17\nd8 28225c\n90 2652b8\nd9 278991\n1f 1cd97f\n13 3a347\n9b 1045a9\n6c 20ed0\n24 196428\n1e 1cc472\nd3 eff11\n8b 29e496\ne8 27c1d8\n54 1af9cf\n76 22c41\n2e 1d11c6\n22 3db8e\n7b 22dd8\nde 2bb6a6\nd2 12806e\n26 3dbbf\nd6 12809f\n36 3e520\n87 102b0e\nad ce6f7\ne7 e9e68\n8f 296177\n83 102b3f\nfc ebeea\na8 106e37\n4e 1b01e0\n42 1cba8\nfd 12d659\n42 1e178\nbc 2999a7\nb6 107617\n7a 1b3d03\nc5 e729a\n18 193f68\n3 3a9fa\n67 1f49dd\n7 3aa2b\n2c 1d1221\n20 3dbe9\ndc 2bb701\n26 19793d\nd0 1280c9\n75 6440b\n5c 1b0b3a\n50 1d502\n2a 3ecf9\n16 1cc2a9\nb7 25fe8a\n87 103b22\nf4 27cc6b\n20 3eeab\nd0 12938b\n85 102b69\n8f 103c79\n84 25c63c\n43 1e16b\n60 222b8\n16 3a0cb\n83 10c0ef\n1c 192fe7\n77 5adf4\n46 5785d\ne5 e9ec3\n8b 103caa\n1c 3b4dd\nef 2bdc8d\nd4 279b3c\na2 107cfb\n58 603ed\nc9 2792f2\n3 3aca8\n67 1f4c8b\nc6 128a54\n87 103dd0\n83 103e01\n14 3b634\ne7 2bdde4\n22 3dbee\n54 1afa2f\n2e 1d1226\n48 56728\n1 9675\n38 d01f\n65 2101a\n66 1b453e\n2e 196524\n72 1bcf10\n32 3e54f\n3e 1d1b87\n8d 2637bc\n75 2197b\n1a 4253f\ne5 ea111\nf5 eaa72\nc8 28037f\nbb cf020\n1 9923\n1e 1d5cce\n12 42696\n2a 1cfc15\n6f 1b33d6\n81 d2a1a\nb6 2610e7\n91 10ca9d\n8e 102a18\nac d6a46\nf2 12d4cb\ncc 2b9ade\n27 471d0\n9e 103379\nc 1d53c8\n3d 1d895f\nba 10748d\n0 41d90\na 42ea0\nf0 ebac4\ne 42ed1\n37 3e513\n85 29e0bb\n82 10bea2\ne4 f2212\nb5 d74ef\n8a 10bff9\nc4 2babe7\n61 22319\ne6 f3479\nd1 280b96\nab 2a238d\ncf 2792d6\nc3 e5c9e\ndf 279c37\nd3 e65ff\nb7 10f966\n37 1d8abd\n5 19222e\nf8 eabf7\n3f 1d8c14\nf5 f2b1e\nc4 ef587\na8 2a10b7\n15 192b8f\n80 102889\n8c 295ec1\n11 192bc0\n1d 192ce6\n89 d1859\n2c 19652d\n30 196d68\nd5 2ba597\n66 1ed942\nb5 d74ff\n18 dd8\ncf ef428\nc1 128cd9\n43 1aedf7\nd3 efc0f\n43 1e87c7\nba cf331\nb 42f01\ndd 129760\n5f 1af87e\n9d 2656ef\n91 d20b7\nd4 e693a\nae 108131\n94 103467\nfb ebe61\ne4 12ce50\n66 1b2f6e\ne7 f3736\n89 ca7bf\ned 2847a2\nad 2a23b9\na1 10ed81\nf5 f3e34\n4f 1b72cf\n6a 1edad8\n4c 1e8699\nf7 f4097\ndb eed52\nfc 12d908\n7e 1b3a26\ne6 123597\nee 2b6c00\ne2 1235c8\nff f41ee\ncd ef73f\n42 1b7138\n8 19b9c7\n33 3f7a2\na0 cd5be\nac 260bf6\nf2 2b767b\n84 10be6a\n5 1924dc\na0 2a120e\n90 10c7fc\nc9 ee190\n11 192e6e\nb4 cef00\nb0 cef31\n75 1bced7\nd0 280b95\ne8 f2338\nfe 2b622f\n30 197016\n43 1af0a5\n48 566c6\na0 d6bce\n1 9613\ne9 ea2a7\nf6 eba98\n66 1b321c\nee 123750\n76 1b3b7d\nfe 1240b1\n6 1ccf2a\n37 1d04c1\nc3 e6f60\nec 27d787\ne0 ea14f\n99 c9e6c\na2 2607b7\n58 1b8ea9\n37 1d9d7f\n5 1934f0\nea 27c49b\n2 19b807\n48 1cd5a\n47 1b0088\n56 1d538\nc3 ef55c\n66 1b4230\ne5 284659\na5 cd332\na0 1069c0\nac 299ff8\na4 ce841\n37 1d076f\nc3 e720e\ne2 27c334\nce 281625\nc2 edfed\n88 ca512\nec 2844f5\nde 281f86\nd2 ee94e\n85 25c887\nda eeaa5\na1 2a222f\n6b 20ef9\n0 19a5ae\n42 1b7146\n5a 1b7bfe\n67 63ab1\nb9 cdd57\n88 ca7c0\nec 2847a3\n42 1b73f4\n52 1b7d55\ne2 27d5f6\n46 1b83d5\nce 281617\nc2 edfdf\n61 1b424b\n44 1b741c\n42 24ef4\n4e 1b852c\n1a 1cd9af\n74 1b3bc8\n94 295657\nb6 1088c9\n1d 19c35a\n11 8d22\n36 1d8810\nc2 ef2af\n3e 1d8967\ncc 28192e\nfd 284ec5\nc0 ee2f6\n65 2a638\nca ef406\n1c 194255\n10 c1d\nc5 ee31a\n6b 221bb\n0 19b870\n46 1b83d7\n42 1b8408\n88 ca580\n46 1b8683\n36 1d8abe\nf3 f2af4\nff 28612c\nc2 ef55d\n9d 29eb11\n91 10b4d9\nf3 2b63b8\nad 25f933\n4c 1b8589\n40 24f51\ne2 2b6d26\n43 1e976b\nc7 28051d\n2e 196836\nde 280d16\n44 261e2\nfb 28640d\n5a 1f282c\n7 1cb957\nb1 269710\n10 1d5b2f\n3 1cb988\nf 1cbaae\nd0 efc69\nab 1103dd\nd1 eebe6\ndd 28221e\n3e 3e3b9\n64 290c7\n93 10c7a0\n13 1cc2e9\n4 19a88d\n21 1976bc\n8f 29e463\n83 10ae2b\na5 110064\n26 1cfaff\n61 1bb523\nc 19a9e4\n29 197813\nb4 1073c2\ne3 f36f5\n76 216d1\n2e 1cfc56\n69 1bb67a\n6 19354c\nb5 1109c5\n36 1d0460\n71 1bbe84\n0 19b862\nb 42e9f\nda eed53\n45 1b00e3\nfd 27cb15\ncc 27957e\nc0 e5f46\n79 1bbfdb\n45 1b867f\ncd 128bb3\n8 19b9b9\n41 1cc02\n4d 1b023a\n7 1cbc05\ncd 2b1791\n84 29e0b8\n21 41b6\n2d 1977ee\n88 25b4ac\n67 1b2f5f\n3 1cbc36\n22 44c0\n2e 197af8\n5e 58067\ne5 e9e61\n8b 103c48\nef 2bdc2b\nce e6127\n80 25c60b\n16 3a0c9\n83 10c0ed\n26 1d0dc1\n61 1bc7e5\n50 1af752\n72 229c4\n2a 1d0f49\n31 19f2f5\n5a 1b8eb2\n7d 63550\n83 103d9f\ne7 2bdd82\n9b 10459b\n6c 20ec2\nfd 27e079\nf1 eaa41\n48 1e8916\n1 19b863\n16 1d4607\n63 1f4c4e\nd8 e69ee\n29 4607f\n39 1d1840\n8a 264d65\nbb 2682fc\n1e 1d475e\n5 8630\n20 3ee39\nd6 efedf\nbf 2a19fb\n8e 29e464\n82 10ae2c\na4 110065\n9e 29edc5\n92 10b78d\n20 c505\n2c 19fb3d\n3 83b8\nf 19b9f0\n72 1f65c2\n8b 29e494\n1e 1cc470\n33 1d16e4\n16 1d48b5\n39 1d1aee\nbb 2685aa\n82 10be40\n76 1b3bdd\n2 1d4f99\n20 1cfab9\n30 1d041a\n32 46581\n3e 1d9bb9\n18 4259a\nfd ebe8b\n9f 29db66\n69 1ec7ae\n16 1d58c9\n63 1f5f10\nd8 e7cb0\n29 47341\nbb 2695be\na8 cd707\n8 39b27\na9 cd708\n85 294a39\n6e 1b33d5\n39 61e0\nd0 2bb57b\n8c 294b8f\n90 2953ca\n35 1d170c\n9c 2954f0\n9d 2954f1\n99 295522\n82 103af2\n5 1d4fc4\nb 19bc6d\n56 1f26fa\n7b 1b4fb8\na5 298be0\n79 1ed41f\n48 1cd4a\nec 27c463\n47 5e63a\ne1 2b57b2\nf1 2bf723\nad 298d37\nb4 299542\n18 1cc3d8\n3a 3f64a\n1f 8e41\nb0 299573\n1b 8e72\n58 1d6ab\nfc 27cdc4\n57 5ef9b\nf1 2b6113\n88 296132\nb9 2996c9\n62 1b2f2d\n15 194161\ncd ef483\n20 1966b3\n6a 5b8ec\n37 4b41\n6 15aa\ne8 27c484\nf1 ebd0f\ne8 1249dc\n7d 1b4ce2\n71 216aa\n1b 43aa0\nb9 29ac49\n1f 43ad1\n68 1b434d\n60 1b3244\n77 216d2\n2f 1cfc57\n46 1e13b\nb 1cd04f\n0 9612\ne8 ea2a6\naa 26090e\n40 1b863d\nb7 cdc38\n86 ca6a1\nc5 2b15d8\n8f d28f9\n88 295e90\nc4 2b9c35\n11 192b5e\na3 d5656\naf 268c8e\n86 c943f\n6c 294be\n9b 10cb97\n32 3e233\n3e 1d186b\nc9 ee44c\n9b 2967f9\n6c 1b3120\n41 1aed80\ne0 27c33b\n3d 4c93\naa 110130\nd0 ee939\ndc 281f71\nfa 27e0be\n8d 264d9e\n81 d1766\n7a 5af7b\n34 1d06f9\nfb 2bf871\nf9 27cb38\nc8 2795a1\nfa eac00\n95 c9afa\nd6 2b1f31\n10 9f73\nf8 eac07\n94 c9da7\n9d c9c51\n20 3d93b\n2c 1d0f73\n32 60e1\n10 192e6f\nde 2b2088\nbb cf022\nc2 ef30f\na5 268d8a\na0 1069d0\nf4 eba83\nac 29a008\n83 102883\n8f 295ebb\nac 106baa\ne6 2b5779\n94 10b56b\ne5 2b577f\nc2 2b9951\ndc e7a33\n88 102980\n67 1f5c93\naf d6aa2\ne 42ec1\nf0 ebab4\na8 29a039\ne4 2bddde\n31 196d07\n68 2949b\ndf 12824d\na6 cd5e8\nd4 12100c\ne1 2b57b0\nec 27c461\n2 3a9eb\na3 ce5cc\n99 cb120\nce 278315\nca 2b9aa8\nf1 125495\n91 29d9c7\n21 3ee4a\nd1 12932a\n90 29d9c8\n58 1e97b\nd3 e690f\ndf 279f47\n50 26dc0\nc 1cbafc\nc6 2b2b42\n8c 103f73\nf7 2b60d9\n81 10c0e6\n14 3a0c2\nb5 cdca3\nab 25f64d\nbc 10750b\nf6 2b60da\nf2 2b610b\nac 25f686\nb6 cdf49\nc0 2b2b7a\nc7 ef5e3\nf1 2b6111\nfc 27cdc2\n58 26f17\na 1d3dbe\n67 1edbff\nc2 2b18bd\nce 2b2c99\n31 197017\nc2 11f661\nff 2b6230\n89 10c23d\n1c 3a219\nbd cddfa\n22 3eeb0\n54 1b0cf1\n25 45c59\nfe 2b6231\nfa 2b6262\n7 1ccc7b\ne1 124822\n69 294f0\n1d 1942b8\n11 c80\n5f 1b0b40\n53 1d508\n1a 43801\n1c 1d5d29\n10 426f1\n61 1ec905\n3c 1d0850\n16 1cd5db\n6f 1b4388\n63 20d50\n1 860b\nd 19bc43\n53 1f26c8\n47 1b73b4\n4 1d3f3f\n77 1f68ae\nd2 2ba56c\na2 cd307\nae 26093f\n71 1ed266\n4c 1b04d9\n40 1cea1\n4a 1dfb1\n19 427d7\n85 c9447\nc6 2b187e\nc7 2b187f\nc7 e5ccf\nd0 efea7\nc5 2b1886\n65 290b8\ne0 e9e2f\nec 27d467\ncf 279338\nc3 e5d00\nd7 2b21e0\nd3 2b2211\nb3 cf179\n8d 25b78c\n1b 1cc67e\nf3 27c9f6\nc2 27945f\n48 1e2c8\n96 10b812\nb9 108a4b\n40 1b0051\ne7 2b5a26\nd8 eed3e\n9d 29daed\n54 1d531\nc1 ef555\n2e 3ed28\n9b 10c95b\nc2 2b9bff\ndc e7ce1\n88 102c2e\n67 1f5f41\ne7 e9e78\nf0 f4050\ne3 2b5a57\n44 1e140\n79 2b0b1\ne2 2b5a58\n42 1b70d4\nf4 f2b73\nee 12cfae\n9a 10c95a\ne1 2b5a5e\n2b 1cfec6\nf7 2b6387\na5 298c52\nf2 2b63b9\nac 25f934\n45 251c1\n6e 5a897\n20 1d0d7b\n4d 25318\n2d 197844\n21 420c\nc0 2814ea\ncb 128b27\n28 1d0ed2\ncc 28035c\n43 24ef5\n4f 1b852d\n1a 43855\n69 291ee\n67 1ec691\n6f 1b43dc\n1d a3b8\n63 20da4\n6a 1eda6a\n18 4385c\n69 1eda70\n46 1f0837\n18 1d7c\neb 28452c\n80 295d2b\n81 295d2c\n80 c93a5\n8c 25c9dd\n85 c918b\n80 102819\n8c 295e51\nb5 cdc31\n84 ca69a\n81 10281a\n8d 295e52\n89 295e83\n15 19b1ed\na5 299ea2\n73 1ee2d7\n56 1f14a8\n79 1ee6e1\na4 299ea3\n78 1ee6e2\ncf ef42a\na1 1069c1\nad 299ff9\n7b 1ee42e\n5e 1f15ff\na5 ce842\n35 4b48\n4 15b1\n3d 19713d\n5a 1d6c2\n20 44c5\n2c 197afd\n57 1b7d79\n72 1ee582\n63 5ba36\n75 216d9\n44 1e142\nb5 cdc3f\n84 ca6a8\nad 2a1343\nca 1278c8\nf5 f2dbe\n6c 294cc\n9b 10cba5\n90 265568\n32 3e241\n3e 1d1879\n79 1bd29d\n34 1d0769\nb0 2681ad\nea eb561\n95 10cadc\nc6 2b2892\nc7 2b2893\ne8 eb568\n9 9a88\nf4 2b76b3\n28 5870\nd8 efd50\n7a 21867\n48 57988\ne9 eb569\nc5 2b289a\nca e6096\n4d 1b7568\nc2 2b28c3\n34 1d9d17\nae cd491\n7d 1ed3f0\n86 ca701\nc3 2b28c4\nc7 ef333\nc0 2b28ca\ne7 2856c8\nc1 2b28cb\nc0 e5f44\ncc 27957c\nf3 f3e18\n3b 1d9e35\nc5 e5d2a\n51 1f2733\nd 1cbd47\nc0 11f3b8\ncc 2b29f0\n81 d2a28\n24 3ee78\n91 10caab\nc9 2b2a22\n16 192e99\n4d 1f1c48\n41 5e610\n37 1d8a5d\nac 107e6c\ne6 2b6a3b\n94 10c82d\n15 192e9f\ncd ee1c1\ne5 2b6a41\nea ea23d\n6d 1bb70f\nb7 d5fe8\nb0 29957f\n86 d2a51\ncc ee1c2\ne4 2b6a42\nc7 2b28f5\n49 1f0955\n95 2642d6\n68 2a75d\ndf 12950f\na6 ce8aa\n9e 10c97d\ned f2316\nf9 eab96\n21 197978\ne3 123559\nef 2b6b91\n46 562eb\ne7 e9ecc\nf8 eab97\n3f 1d8bb4\n49 5e767\ne2 12355a\nee 2b6b92\n9c 10c984\n1d 192ff6\ne1 123560\ned 2b6b98\n23 19e99b\n34 46859\n6d 5a8a1\n24 471c8\n87 c93de\nca 2bad6a\n14 19c1f4\n2b 3da8c\n44 562f2\ne5 e9ed3\n13 1d5889\nfb 2b651d\ne0 123561\nec 2b6b99\nc3 11f414\ncf 2b2a4c\n98 10cb9f\nea 2b6bc3\n39 4cc2\n8 172b\n73 1b3891\n71 1b3898\nc4 2804b3\n69 63ee0\n34 196fe5\nfb 28615d\n43 1e9a19\n10 439b3\n49 25347\n59 1b0b08\n61 1edbc7\n5e 1f1351\n63 22012\n47 1b8676\nce 2b1727\n0 19250e\n22 5780\nc6 2b2b40\nc7 e6f91\nc4 2b2b47\nb6 cdc99\nc2 2b2b71\nc3 e6fc2\nc7 ef5e1\nc0 2b2b78\ne7 285976\nd4 efc8e\n91 10b777\n9d 29edaf\ne7 eb13a\ncc ee470\n21 19f9b4\n5c 1af876\n2a 3da35\ned ea268\na4 d6b8f\ndc eeb31\ne1 2b6d20\n2b 1d1188\n20 dad7\ncc 2b9a7e\n69 211b0\n0 1d3c60\n73 1f65cf\n65 1edbf8\n8 1d3db7\n7b 1f6726\ndc 2ba3df\n30 e438\n79 21b11\na5 cd2d2\n3b 198179\n10 1d45c1\nc5 127a4a\n7a 1ed107\n7f 29b77\n4e 265e0\n78 1ed10e\n4b 1f094e\n8c 294e4d\nd4 e68c8\nae 1080bf\n1e 1d4a6e\n3b 1d189d\n85 264935\n4a 1f094f\n71 5c33c\n11 1d486e\nac 1080c6\n39 1d18a4\n6f 1b43ea\n63 20db2\n14 1cd5d4\n45 1b73ad\n6d 1b4381\n61 20d49\n12 3b3b0\n76 1f5393\n59 1f12b6\n9e 10c929\nb1 108648\n94 10b819\ne5 2b5a2d\na8 298d67\nb0 299571\nf5 2b638e\nb8 2996c8\n58 1f15c7\n1b a3e2\nf7 2bf6f7\n80 29cdb9\nf3 2bf728\n25 1d90fb\ne5 2b6d51\n88 29cf10\n34 1d0707\nfb 2bf87f\n21 45c1a\n2d 1d9252\n95 29d6e8\n78 22b24\nb3 299835\nf3 12d778\na 1730\n85 29e04b\n59 1f288a\na3 29a198\n91 29d719\n9c 2643ca\n90 29d71a\n35 1d9a5c\n39 4f72\n9c 29d840\nba 29998d\nf5 2b76b2\n98 29d871\na0 1069ce\nac 29a006\ne6 2b5777\ncd ee47d\naf d6aa0\n8d 26382e\na8 29a037\n0 19376e\n31 196d05\n78 2ae02\n8 19a705\n7a 1b39e5\nc5 e6f7c\n27 1d7ea0\nd6 27880f\n4b 250ae\ndd eedde\nf2 2b6109\nac 25f684\nd5 e78dd\nb6 cdf47\nb2 10f996\n13 423db\nbe 2a2fce\n1f 1d5a13\ncc 2782ac\n9 9a18\n15 993\nb4 cdf4e\n11 423e2\n1d 1d5a1a\nfc 2b6236\nbf cf051\nc7 2b991f\n7b 2b0c6\nc3 2b9950\nce 280601\nac 29a318\nf4 ebd93\na0 106ce0\n46 1b0089\nf5 12d502\na5 106d04\nc0 2b9958\n65 1f5c9a\n24 3dba8\nc9 2b9aae\na9 d6a16\n3 1cbbc6\nb5 107665\n4a 1e2cf\nc5 2babea\n95 d20f6\ne5 e9e71\n96 1044d8\n34 3e509\n23 19664b\nd9 2ba40f\n30 1d06c6\ne5 27c04f\n53 1b8d6a\nf 19237e\n8d 10c260\n0 1d3f0e\n73 1f687d\na 19b95c\nbc d73fb\n5f 1f159c\nae 1101d1\nd4 ee9da\n1c 1d49f7\n4f 1b852f\n43 24ef7\n7b 1f677a\n53 1f1406\nd 19a981\n9e 10463b\n96 10ca80\ncf ee414\ned 2beee8\ne1 12b8b0\n84 263974\n5b 25cbf\n84 29d036\nf7 2bf9a5\n94 29d997\n39 4697e\na5 cd5ee\ne6 2b5a25\ne7 e9e76\n2b 1cfec4\n98 d21ab\ncc 2b172e\n20 5787\nf6 2b6386\na5 298c50\n60 22318\nde eeb36\na1 ce811\ne2 124828\n65 1f5cfa\nc7 ee01f\nf3 12c26a\nc2 128cd3\nff 2bf8a2\nc3 2b9bfe\n36 4b42\na3 d6b66\n2b 1d7f74\n7f 1bd027\n42 26458\n73 299ef\n62 1bc7ed\n61 2a65b\nd3 2ba55f\n8d 263ada\n8 1cbac9\nb3 d74c7\n8f 1029b7\n1a 192cad\n98 10cb8f\nf7 27dce9\n84 25b37a\nd1 2ba566\n1d a3b6\n6f 1b43da\n63 20da2\n4b 1f1c10\n49 1f1c17\n4d 1b858a\nca e70b8\n41 24f52\n48 1f1c18\n50 604e0\nad cd6d5\nf1 f40c1\n12 1cc27a\n81 29e07a\n1a 1cc3d1\na4 2607ef\n13 4370b\na0 107ce4\n85 d14db\n9b 29db25\n88 29e1d2\nb0 26845b\n11 19aea0\n45 1e9ab3\n45 1b7101\ne4 2846bc\nad 2a13a5\nf5 f2e20\n9b 10cc07\nd1 282108\n32 3e2a3\n8d 25b71c\n3e 1d18db\n79 1bd2ff\n19 3a489\n4b 26370\nce 2bad99\nc2 127761\na5 2a11dc\n40 1b713f\ne5 e9ed1\ne0 12355f\nec 2b6b97\n32 3f803\n10 1cc591\n37 19f38f\n74 1b38ba\nc7 2babe1\naa 1103de\ndc 28221f\nd0 eebe7\nce 2b2cfb\nc2 11f6c3\nff 2b6292\nc5 2babe8\n3 8604\nf 19bc3c\n28 3dd30\n62 1ec8ff\ncc 28035a\n7c 1b3a11\n1 19bb1f\nc3 127700\ncf 2bad38\ne0 124883\nc5 ee07a\ndb 2ba6c4\nd 1d4097\n24 3ee6a\nc9 2bad70\n10 1d487d\n7b 5b1c8\n85 c93e5\n8e 102c58\nf6 27df8a\n3c 3f930\n76 5b0a1\n19 3b49d\n4e 1e8692\nb8 108a58\n9d d224f\nc4 2bac49\nb8 cddba\n77 1b3924\n43 1f1d67\n84 c918a\nc6 277eb0\n8c c92e1\n94 d2087\nad 110409\n2f 196527\n1a abf\n6 1cb9aa\na0 cd5b0\nac 260be8\ne 1cbb01\n52 26dc5\nd6 279ae1\n27 1d9172\n9c caf12\n46 1b7423\n63 1b4252\n11 a22e\nf8 eab95\n3f 1d8bb2\n20 197977\ne2 123558\nee 2b6b90\ne7 e9eca\nb6 10868d\na9 2a2636\n3c 1d0612\nf1 f40b1\nf7 27dc89\nbd cf0ba\n4a 1b7549\ne6 27d629\n8e ca548\n84 c9438\n9e caea9\n94 c9d99\n1a d6d\n69 2a502\n6 1cbc58\ndf e6779\n96 d30a0\n74 1f68a8\n29 19fe1b\nee ea022\n49 1f0c65\n29 dbcd\nf7 27c9c5\nc6 27942e\n8c ca85f\nd2 1212e4\nc6 e5fd0\n6b 2a509\n49 1b7297\ne7 27d5d6\nad cea07\nf3 12548c\ne7 ea178\n5e 1d931\n25 1d10bb\na5 ce5f4\n84 264998\nef eb2e3\n84 ca6fa\n77 1ed2f2\nf 1d5120\n3 41ae8\n41 1b7132\na5 ce8a2\n21 1d0d88\na3 267844\n5a 26f0e\n12 4370c\n52 25b67\n5e 1b919f\nc6 128d10\na4 cd5df\n73 22c73\nd6 2bb541\n2b 1d11f8\n14 1cc560\n36 3f7d2\ndb 2bb6d8\n35 19f334\n52 258b9\n5e 1b8ef1\nc6 128a62\ne6 27c057\nac cd488\n50 1af6e0\n2a 1d0ed7\nf 19a6ce\n4b 5ea7e\n50 1e824\nd7 279df0\nd7 280b60\n9d d1f91\n26 1976f3\nd7 281e30\n9d d3261\n33 3f806\n8a ca7c5\nd 19bc97\n1 865f\ne6 27d5d5\nd 144b\nac cea06\nd0 2b2219\nd7 eec82\nf2 12548b\n48 1b7296\ne6 ea177\n1d 1dac\nf6 27df36\nbc cf367\n58 1b7bf7\ncc 2bada0\nc0 127768\n65 63aaa\nc7 28177d\n8d d2bae\nd3 129633\nec 2b6ea9\ne0 123871\n26 1979a1\n82 29cdce\nd6 281e81\nd7 2820de\n9d d350f\n25 1d1069\n42 575ee\ne6 27d319\nac ce74a\n85 d279b\n1a b23\ndc 12849f\n1 9921\n5c 1f1596\n18 1cd948\n1f a3b1\n11 43706\na6 298ea4\n9 8754\n1e 1cd724\n12 3a0ec\nb6 299805\n71 1f6878\n4a 56411\neb e9ff2\na2 d6919\n88 1029e0\n67 1f5cf3\nf8 ebe65\n1e 1d5cc0\n12 42688\n1a 3a243\nbe 29995c\n79 1f69cf\naa d6a70\n63 62812\n6f 1f5e4a\nc4 127739\nef eb2f1\nc4 ee2b7\n89 29d1cb\ncc e5e72\nb8 cf2d6\n4e 1aef10\nce 120ab7\nae 11040f\nd4 eec18\n99 29db2c\n67 1f4a33\ndc e67d3\ncd 28161f\nc1 edfe7\n86 10ae4f\na9 108088\nc9 ee13e\n48 1e266\n96 10b7b0\n91 263ff7\nd9 eea9f\n61 222b7\n86 103e25\nb7 1073bc\na6 29a1ba\n2c 55f1\n99 d3224\ne 1cd071\n2 39a39\n3f 1d1b7a\n33 3e542\n5f 26ca2\n80 d1763\n8c 264d9b\nd2 2bb820\nce e70d9\nc4 e5fc9\nd 1d3e59\n8a 102987\nde e7a3a\n9b 1048b9\nff 2be89c\nab 106b2f\n94 265297\nff ebbe2\n86 102b61\n1e 192ff0\n80 d2777\nc9 e5e50\nbe d7402\n5d 1b8edb\nda e7a09\n51 258a3\n2b 4709a\n9 1d3e28\na7 106d09\n3f 197198\na0 2a0f60\na 40e\n86 103b75\n12 9cc\n1e 194004\nb7 10f6a8\n86 10c111\n55 1b0ce4\n23 3eea3\n86 103e23\n12 c7a\n1e 1942b2\n10 43705\n61 1ed919\n10 1c25\ne3 2843d5\n49 25099\n92 10b7e1\n9e 29ee19\n1c 19c359\n10 8d21\n29 c8fb\nd9 e7cbf\n8a 103ca9\ne4 e9ec2\n4 19221f\n6b 1edadb\ne4 f245e\nd7 279ad2\na9 2a1372\nbb 10f7dc\n8a 10c245\nec ea019\n92 10ca4f\n9a 10460a\ncb ee3e3\n6e 1b30b7\nac 298da8\nf4 ea823\nee 124c5e\n14 192b80\n7b 1ee43c\nf4 f2dbf\nb9 2a1cd3\n9a 10cba6\nfc ea97a\n4a 1e866f\ncf 128e06\nf5 284d60\nc4 2817c9\na6 106a5a\n9c 104882\nad d5a23\nb6 1073bb\n80 d1455\n8c 264a8d\n2e 1d0f78\n22 3d940\nc1 2817a5\n54 1af781\n3b 3e38b\n41 2645e\n8a 103f57\ne4 ea170\n4 1924cd\n6b 1edd89\n9a 1048b8\n6e 1b3365\na7 299eab\n14 192e2e\n7b 1ee6ea\ne1 ea0e0\ned 27d718\n18 1d4788\nb6 107669\n41 26204\n49 2635b\na4 d568f\nac d57e6\nfe 2bf8a3\nf2 12c26b\n8 1cbad9\n50 1d554\n5c 1b0b8c\n2a 3ed4b\n52 1b8cf7\n24 197998\nc5 ef32c\n20 3eb8b\n5 8382\na4 d593d\n0 1cbc30\n54 1b0ce3\n22 3eea2\n4c 1cd29\n4 192281\n4 19a81d\n77 1bd18c\nc 1923d8\n14 19b17e\n89 264d5d\n1c 192d39\naa 2a10cc\n46 575af\n2d 196580\n67 5b757\n10 9d1\n1c 194009\nc3 28179e\n56 1af77a\ncf 128b4c\nb6 2a18f9\n4c 1cfd7\n2 7\ne 19363f\n23 3d8d1\n2f 1d0f09\n55 1af712\nf4 27cccd\n4 19252f\n5c 1d938\n12 968\n1e 193fa0\n83 103b53\n14 3b386\ne7 2bdb36\n14 192e90\n24 daa6\n6d 2117f\n25 1966d7\n35 197038\n92 10cab1\nfd 124049\ncc 120ab2\n56 1b7ad6\n0 62\nc 19369a\nba d635b\n25 1976eb\n46 1b00db\n25 196675\n42 1cbfa\n4e 1b0232\nc3 ef312\nb 1d532f\nca e7356\n41 251f0\n4d 1b8828\n69 221c4\n26 1cfb5f\n7d 21830\n4c 1e299\n7a 29bb7\n25 197999\n5 9652\n1b 1d5c9c\n58 1d649\n67 1b4531\n24 19e9c4\n2c 19657f\n42 1ceaa\n7f 1b3a79\n4e 1b04e2\n34 19f325\n8a c9255\nd 19a727\n9a c9bb6\n1d 19b088\n20 4217\n2c 19784f\n57 1b7acb\n72 1ee2d4\n3a 1d05e6\n85 103b7d\nd6 1212b3\n39 5f22\n1c 90f3\n6e 1b3117\n8d 103cd4\nde 12140a\n16 1d592b\n71 1b3b44\n11 8d20\n1d 19c358\n70 1ed01b\n5e 1afb7f\n33 5e36\n77 1b38d0\nf5 12d7b2\n9b 104857\n6c 2117e\n22 41ae\n2e 1977e6\n9 4319c\n24 1966d6\n32 4b0f\n3e 198147\n19 43afd\n7c 21adf\nd5 279b3b\na3 107cfa\n34 197037\n28 3da82\n62 1ec651\nda eeaf7\nbd 268572\n82 c93ac\n8e 25c9e4\n5 19a87e\n13 8cb7\n1f 19c2ef\n8c 25b46d\nd0 281e59\nb7 29aab8\n4 838f\n1a 1d49d9\n31 6079\n66 1b326e\n2c 19e8dd\n72 1f5362\n3a 1d0894\n85 103e2b\n14 8cf0\n1 866d\nd 19bca5\n53 1f272a\nea e9ff1\n70 1f6877\n11 8fce\n1d 19c606\n70 1ed2c9\n57 1b7d77\n74 1bbec4\n8a ca517\n1 83b1\nd 19b9e9\n6c 22440\n61 5b78f\nb1 d61fc\nda efdb9\nbd 269834\n82 ca66e\n5 19bb40\n44 1aee12\n3a 1983c4\n22 3d932\n2e 1d0f6a\nc1 281797\n54 1af773\ne3 ea157\n42 56576\nef 27d78f\n4e 1e9bae\ne2 2bdb06\n2e 1d9506\n22 45ece\n54 1b7d0f\nbe 25ffe0\nea 2bdc5d\ncb e70a9\n2e 1d1218\n22 3dbe0\n54 1afa21\nf1 27df53\nf 1d5360\n3 41d28\n65 1b3268\n6a 1b4354\n1b 1cd9b0\n3f 1d05b8\n56 1ea9c\n75 1b3bc9\n7a 1b4cb5\n81 10be2c\nd4 2bb54a\n71 22c7c\n89 264abd\nfa f2eec\n65 1b427c\nf5 eaae2\n54 56f01\n43 1af043\n1a 1d81\n1c 1942a9\n10 c71\n6 1ccc6c\n52 1af997\n18 dc8\n17 426b8\nb1 299830\n2 3978b\ne 1ccdc3\n5b 1f12af\nc7 277f1f\nd0 2820f7\n4b 5ea72\nee eb2e4\n49 1f1f27\n3b 61d7\n31 1a0309\n7f 29e25\n78 1ed3bc\nb9 cddbb\n18 3a1da\n9f 2957a6\n64 1b2fb9\n1a 1cd701\n74 1bbeb6\ne3 284683\n10 1ed3\ne9 285a95\n7c 1b3a71\n14 1d5924\nca e5de6\n4d 1b72b8\n2b 45dd8\nda e6747\n5d 1b7c19\na7 25f527\n69 221b6\n3a 4c66\n26 1cfb51\n10 3a341\n1c 1cd979\n9e 264435\n26 1d80ed\nc1 2804d5\n2e 1cfca8\n1b 8e0e\n91 1031dd\n9d 296815\n62 20d3f\n6e 1b4377\n64 1b3267\n50 26b02\n8 1d5087\n6d 1b43e3\n61 20dab\nc2 e5f3d\nce 279575\n45 1b740f\nb9 10fad7\na 19265e\n5a 1b8e5c\n69 22464\n94 25bce7\n3a 4f14\n70 22c7b\n88 264abc\n26 1cfdff\n1b 90bc\n46 1b007b\n8e 294b96\nd2 2bb582\n17 1d4908\n34 1d8a55\n41 24f42\nca e70a8\n4d 1b857a\n18 192cb6\n3a 5f28\n3c 198450\n30 4e18\nfb 284e8f\n91 cad29\n5a 1f12ae\nc6 277f1e\n1d 3a4c8\n26 1d0e13\n92 d20bf\n9e 2656f7\n53 1b7ce6\n19 9117\n61 2206d\nc2 e71ff\n45 1b86d1\n18 192f64\n3a 61d6\n26 1d10c1\n63 2a3b2\n41 1b7140\n6a 5a8ca\n39 47c40\na5 ce8b0\neb e9f90\na2 d68b7\n88 10297e\n67 1f5c91\nc2 2b994f\ne4 2b6cee\n48 1e88b6\n6a 5bb28\n4f 2531f\n50 1af6e2\n2a 1d0ed9\n20 1cfdc9\nf9 ea8ea\nb0 d7211\n80 10be39\n74 1b3bd6\ne3 eb169\n51 1af98f\n2b 1d1186\n85 d2a59\n70 1b3b99\n7b 5b1d6\n56 1e8e9c\nc3 2baec0\n54 1e8ea3\nc1 2baec7\n55 1e90f0\n86 c9191\n9e c9c49\ne3 eb40b\n42 5782a\ne1 eb0f4\nc4 ee2c5\ncc e5e80\nf1 eba55\nae 11041d\nd4 eec26\ned 12cfa8\n6f 1b30c6\n78 1bd29e\nc4 e729b\nf5 ea832\ne4 27d630\n11 3b656\ndd 280f5c\nab 10f11b\n55 1ea3b2\n86 ca453\nb7 d5f86\n86 d29ef\n5d 1ea509\n51 56ed1\n8e ca5aa\ne9 e9feb\na0 d6912\nf5 12c552\n43 1f0ab3\n56 1afa28\n88 294e6e\na8 d6a69\n8 192345\n88 d1600\ne5 eb441\n38 19810f\n4b 1f0c60\n49 250a7\n92 10b7ef\n9e 29ee27\n75 1ed297\na6 cd338\n9c cb160\nfc ebc3c\na8 106b89\n25 197945\n42 1deca\nbc 2996f9\nbd 26123a\nb1 cdc02\n6b 62907\nb8 1074ea\ne4 e9ed0\ne6 2bdd75\nec 12cfa7\n6e 1b30c5\nf4 ea831\n3c 1d084e\naf 1101d0\nd5 ee9d9\n1d 1d49f6\nf5 284d6e\nc4 2817d7\n4b 2630e\n4d 1b8836\nca e7364\n41 251fe\n5b 26c6f\n80 d14a9\n8c 264ae1\nee eb28e\n45 1cbc3\ne4 ea17e\n2 1934b9\nb1 110932\nfe ebbef\n6e 1b3373\n63 1ec6c2\ndf efd97\n6 1d5276\n4f 1e894f\n13 1d48d9\n41 26212\na8 107e4b\n9c 10cbd0\nee 2b6bf4\ne2 1235bc\n41 264c0\n3d 1d88fd\nc 1d5366\n54 26de1\n0 41d2e\n42 1e84b8\n8 398e9\n8f 294eb5\n52 1e8e19\n18 3a24a\n22 c80e\n2e 19fe46\n9f 295816\n40 1cb91\n4c 1b01c9\n43 1b7147\n5f 1e952\n1a a0d1\n61 1b31d5\n4b 1b729e\n17 19c446\n2 41adb\ne 1d5113\n5b 1b7bff\neb eb250\nce ee421\na 3ab50\nb4 25fe2e\n97 25bce1\nc 1cd078\n0 39a40\n2d 1d1220\n21 3dbe8\nc0 2baec6\n44 1b0320\n3d 1d1b81\n31 3e549\nd0 2bb827\n53 1b7d56\n9e 1045d9\n94 1034c9\n5a 582e2\nfb ebec3\n63 1ed922\n29 3ed53\n58 1e9217\n5f 25c80\n7a 5c489\n51 1b8cff\n8 415\n43 1b8409\n10 9d3\n1c 19400b\n4b 1b8560\na5 107d24\nce 1207fd\n31 4b7b\n3d 1981b3\n21 3eeaa\n43 1b86b7\nde 11fe9a\n10 c81\n1c 1942b9\n79 1bd29f\n44 1b032e\nad cd437\nff 2b74f4\nf3 123ebc\n62 1ec65f\n28 3da90\n50 1b7a3c\naf 29905c\nbf 2999bd\nb8 ce068\n43 1f0807\n9 41c38\nc4 ee2c3\ne1 eb0f2\ndc efdf3\n5c 1b0b38\n50 1d500\n2a 3ecf7\n86 25c8f1\nb7 25fe88\n2c 1d121f\n20 3dbe7\nac cd428\n3c 1d1b80\n30 3e548\nd 1d53c7\n8a 103ef5\n1 41d8f\na8 107dd9\n8d d15d0\n40 1de63\n1d 1d5d28\n9a 104856\n11 426f0\nd4 e6628\nae 107e1f\nbe 108780\n76 2197f\n2e 1cff04\n7b 21b16\nde 2ba3e4\n15 3a0b5\nb4 107670\n1 42da3\nc 9a54\nf3 f40ba\n52 604d9\n1a 3a253\nc7 2babf1\n8d 10c022\n5 19baee\nde 129758\ne6 f21c5\n47 575bc\n20 3eea9\n1 43051\n5 3aa16\na4 107fd1\nfe 124041\n30 4e28\n3c 198460\n84 10c16a\n6 192288\n2f 1d0f0b\nf4 27cccf\n23 3d8d3\n42 1cba6\n4e 1b01de\n20 19e9f5\n5d 1b7e73\n2b 46032\n63 20d4e\n6f 1b4386\nca 278044\n65 1b3276\n4e 1dff2\n27 1976f2\na6 268de6\nef 27c4bf\n4e 1e88de\n40 1cc01\n4c 1b0239\n18 ab8\n9f 25c084\n27 1979a0\nee 2b58d0\n26 19e9cb\n36 19f32c\ne9 f22d5\n7c 1b4d33\n70 216fb\n28 1cfc80\nad 110417\nc8 ef69f\nf9 f2c36\n38 1d05e1\nbd 110d78\n64 1b2fc7\nca e5df4\n4d 1b72c6\n73 2acb3\n8d ca5a4\n6e 21185\n26 1966dd\n7e 21ae6\n36 19703e\n95 d1e4a\nb0 108653\n30 1d0738\n17 19b1e6\n34 19f333\n53 1b7a44\nca 2bad76\n6e 22199\n1d da6\n26 1976f1\n86 294ced\ne9 f3597\n70 229bd\n28 1d0f42\n1c 1d5cc7\n10 4268f\n62 1ec6b3\nbd 2685d4\ne7 12ce4a\n41 24f50\nca e70b6\n4d 1b8588\n6e 22447\n63 5b796\n26 19799f\ncd ee1c3\n18 ac6\n6a 1ecac4\n23 19fa11\nb9 2685a3\n2b 4368\n76 5adf5\ne3 12ce19\n53 1b8d68\n23 19fcbf\n2 39737\ne 1ccd6f\ndd e6780\n94 d30a7\n88 10ad20\n6b 1b4659\nec ea029\n39 4f1c\n55 1af720\n23 3d8df\n2f 1d0f17\n8a 294bd5\n25 1cfe07\nb5 d724f\ncd ef485\n18 1d88\n91 265567\n5a 1d650\n99 2656be\n0 39792\nc 1ccdca\nec eb2eb\nc0 e6f58\na1 2a24dd\n34 1d04b9\nc8 e70af\na9 2a2634\n3c 1d0610\n8a 102985\nd 1d3e57\nff 28516e\n95 cb008\n5e 1f158d\n33 47844\n54 1af71f\n22 3d8de\n2e 1d0f16\n24 1cfe06\n6b 1b30e7\n19 90c3\n15 43983\n67 1ed9a7\nb1 26970e\n24 1d0e1a\n90 d20c6\n9c 2656fe\n5b 2599f\n1 41ae1\n8a 103c47\nd 1d5119\nff 286430\n52 5f217\nf3 f2df8\n5e 1f284f\na7 268de7\n6 1d5206\n4f 1e88df\n24 1d10c8\n35 1d0708\n5e 1ea2c5\n52 56c8d\n20 1cfd67\n2f 1977e9\n23 41b1\n53 1e8e0a\nf2 2b63c5\n29 4301\n30 1d06c8\n27 1cfb54\n3f 4c9a\nd2 ee940\nde 281f78\n80 10be8b\n77 1bbf2c\n84 29cd86\n21 44b8\n2d 197af0\n32 1d19f3\n90 d1e16\n9c 26544e\na8 d5a63\n9 84a8\n3b 198427\n18 1cc42a\n1f 192cdf\n9d 10cbc1\n10 1d486f\nf3 123f1c\nff 2b7554\nc2 120985\nb4 d629e\n30 3f4ec\n15 8ce3\n8c 10c015\n8e 25b722\nd2 28210e\n7 1d3cfb\n1b 8e10\n47 1b7178\n62 1ed981\nda 282265\n1 85fd\nd 19bc35\n6f 1b437a\n63 20d42\n67 20d73\n16 1d5b69\ned 12ba36\n22 d7c0\n6b 20e99\n26 d7f1\n6f 20eca\n25 1d0e1d\n2b 197ac6\n76 1ee553\n11 8f5e\n1d 19c596\ne8 f25f4\n49 25039\n20 1cfb1b\n27 1963d0\na5 1102b2\n71 219aa\n29 1cff2f\n7d 1b4fe2\n30 1d047c\n37 196d31\nb5 110c13\n8c 263acb\n33 3f502\nbf 261241\nb3 cdc09\nb5 cdc33\n6f 62938\nbb cdd60\n62 1bb2ed\n13 436a9\n98 295521\n5e 1b0b31\n52 1d4f9\na 1cba7e\ne5 e9e63\nff 27ddd2\nf3 ea79a\nf5 ea7c4\ne8 f3598\n2 83b9\ne 19b9f1\na3 ce87a\n2 3ac99\na1 ce563\n84 d1734\ndf 281f79\nd3 ee941\nc2 ef301\na5 268d7c\nf2 12517b\nd7 ee972\n15 19b17d\n80 10ae77\n8c 29e4af\n73 1ee583\n20 3eb99\n5 8390\n1b 1d49da\n32 1d1a01\n6b 1b3395\n9 84b6\n21 1d9376\n36 1d1a32\n28 3ecf0\nd 84e7\n80 29d065\n10 3a0f3\n1c 1cd72b\n4d 1b7504\n21 1963f8\n20 d7c7\n69 20ea0\n1a 3b507\n14 3b384\nb5 cef65\n7e 1f54ea\n25 196429\n24 d7f8\n6d 20ed1\n7 1d4fbd\n2f 1d9505\n23 45ecd\n55 1b7d0e\n82 c9410\n8e 25ca48\nbf 25ffdf\n75 216db\nec 124a0d\n1 19a59f\n49 25047\n82 c90fe\n8e 25c736\n5 19a5d0\n11 19af00\n59 259a8\nf9 eab98\nb0 d74bf\n95 264274\ndf 1294ad\n2b 1d7fc6\n22 3ee4e\n54 1b0c8f\n89 d15ad\na8 107de7\n8d d15de\nc2 128a23\na5 2a249e\ndf 2bb69b\nd3 128063\nb8 108748\n9d d1f3f\nd2 129384\nb5 2a2dff\n92 1044a7\nf6 2be48a\nfd 27ddd9\nf1 ea7a1\n86 103b77\ncf ee1d6\ne8 124978\ncd ee16f\n60 5b9d8\n45 251cf\nb8 d7678\nef 27c4cb\n7 19b837\n8 8509\n7b 2ae78\nd0 278ae7\naa 29a2de\n8f 263ad5\n47 1b83c8\n48 2509a\nea 2b6e6f\ncf 280666\n3b 4ccb\nda 281fa9\nfd 12c647\na 1d5080\nf0 2be70e\n0 1d3f70\n73 1f68df\nf 19b9f2\n50 1b0a04\n3 83ba\n9 850a\n20 d81b\n69 20ef4\n4e 1b04ee\n42 1ceb6\n16 1d5bcb\ned 12ba98\n65 1bb564\n22 d822\n6b 20efb\n49 2509b\n4b 250a2\n32 3f811\nae ce6f1\na4 cd5e1\n62 5a46f\n6e 1edaa7\n5b 25a03\n21 c806\naa ce96c\n2d 19fe3e\nbe cf052\nde 11fe9c\n19 1d5c95\nde 2b20ea\n2 1924a3\nb4 cdf42\nb0 299821\nbf 2612a3\n1e 1cd6c2\n12 3a08a\nb3 cdc6b\n52 1b0cb9\n80 29d067\nf3 2bf9d6\n65 1f49d8\n6e 5a5e9\ndb 12821c\ncb ee199\nfb 2b7533\n94 25bc77\nb 1d40cf\n18 8e78\n27 1cfb60\n2a 470a9\n5c 1b8eea\n50 258b2\nf2 2b7687\n53 1ea0cc\nd7 280e7e\nfd 27de3b\nf 41c10\n50 56c22\n5c 1ea25a\nf1 ea803\n6b 29493\n3a 3f6ba\nac cd48a\n7b 29df4\n0 1937d0\nc2 11f3b1\n31 196d67\nce 2b29e9\ne2 124ac8\nc7 ee2bf\n9d caf13\n62 1b41ef\na 1cba7c\nbc 10751b\n8d ca860\nd3 1212e5\nf0 125432\nac cea08\nf2 12548d\ne6 ea179\n8d 29cf40\n3a 196eb6\n85 ca44d\nac ce74c\n6 1d5214\n4f 1e88ed\nb5 cf1af\n76 21981\n8e 2637c2\na6 267876\nb8 107734\na7 106a69\n3a 197164\n85 ca6fb\na4 ce8a3\ne6 eb43b\n81 10abca\n8d 29e202\n84 d14da\nf7 f3e49\n33 4682e\n3f 1d9e66\n9a 29db24\na4 299eb1\n87 295d64\n8c d1631\nff f3fa0\n1d 1942aa\n11 c72\ned 27c1a6\n9c d1f92\n4f 1dff1\n3d 19f22f\nc 19bc98\nba cdd5d\n0 8660\n73 2afcf\n5e 1d93d\n10 193e21\n8c d28f3\n4f 1b883d\n43 25205\nd4 ee968\n29 461f\n8c 29ceed\n5f 1f15f2\nd1 2820fa\n3e 1d18cd\n32 3e295\naf 107e74\nd5 e667d\ncf 120ab8\nf5 27ca12\nc4 27947b\ncb ee3e5\n42 1aed86\nf4 ea825\nee 124c60\n87 10ae50\n41 1e110\n56 1f275a\nb6 1073bd\n20 197967\nbe 107514\ndf e7a3b\nee eb282\ne4 ea172\nfe ebbe3\n8a d28c7\n59 1f2826\n42 1af034\nf4 eaad3\n19 192d17\na6 106d0a\nd4 efc2a\n29 58e1\n80 10ab77\n8c 29e1af\n3 42fea\n22 dad0\n6b 211a9\nce 2b9a77\nf5 125464\n6b 29255\nf6 27c9b6\n54 1b0a37\n22 3ebf6\n94 1031ab\n1 3a9e5\na0 107fa0\n85 d1797\n19 1d5a4b\nf6 27cc64\ne4 eb434\nc4 ee06b\nba d761d\nda 2ba6b5\nae 1101c3\nd4 ee9cc\n5c 1af868\n2a 3da27\n8e 10ad5a\n29 197a5f\n49 1af1e7\n0 19bb0e\nff 12d662\n79 29b4d\n48 265b6\n21 1966b6\nef 2b58cf\n83 29d06d\n48 2635c\nfd f2c77\nc3 ee2f0\ncf 281928\ncc ef484\ne9 ea235\n5 192282\ned f2306\n89 10afdd\n5e 1b0ba1\n52 1d569\n35 196fe4\n7f 5c21d\n99 d34de\na2 298ed5\nf6 27df88\n73 1f65d1\n39 47a02\n89 ca82f\nd4 1212bc\n3 8\nf 193640\nf5 27ccce\n5 192530\n15 192e91\na 87bc\nb4 cef54\n1e af0\n8b d2b14\n22 41b0\n2e 1977e8\n6a 5bb98\n9b d3475\na3 107cfc\n1 31f\nd 193957\n53 1ea3dc\nf2 284fe5\n77 2af92\n70 1ee529\nd4 27887a\nae 29a071\na2 106a39\nf6 ebaec\n20 44c7\nee 1236e0\n2c 197aff\n72 1ee584\nd0 ee9a7\ndc 281fdf\n63 1f5f1e\nd8 e7cbe\n29 4734f\n76 1b3bd1\n73 1f687f\n39 47cb0\n9a d21b4\n5 193544\ned f35c8\n52 1e82b\n35 1982a6\n90 25bf64\nbd 29ac18\nb1 1075e0\nda 12119d\n56 1e8e4a\n89 10c29f\nc6 2781c0\n7a 217f9\ncf 2bb048\nc3 127a10\n5b 1b7e9f\nec 2bf195\ne0 12bb5d\n47 1b8678\ncf 128bac\nec 12ccf9\n14 a260\n66 1b4284\n3d 19f48b\n5a 25a10\n5 1937f2\n66 1b4532\nc 19a728\n73 29a5f\n42 264c8\n7f 1bd097\n1c 19b089\n67 1b4533\n84 10abfa\n0 83c0\nc 19b9f8\n52 1f247d\n56 1b7aca\n2 8357\ne 19b98f\n90 cb03a\n55 1ea414\n4a 5fd3f\n55 1b7a62\n2f 1d9259\n23 45c21\nf4 28501d\n0 83b2\nc 19b9ea\n45 1aee13\n3b 1983c5\n90 10caaa\ne1 285940\n74 1b391c\n7 1d3f47\n97 10c7c3\n45 1b8425\nc2 e6f53\na3 2a24d8\n36 1d04b4\n41 24f44\n4d 1b857c\nca e70aa\nab 2a262f\nd1 280e38\n3e 1d060b\n45 1af0c1\nfc ebc4c\n4a 1b01ad\nfa f2f4e\n78 5c492\n5b 58345\n95 d1e48\n5e 1d681\ncb ef6a5\n62 20d41\n6e 1b4379\n21 1d0d8a\n6f 5a8a6\nb9 2695c5\ndb f0006\n74 1b3bca\n31 1d16eb\n7f 5b207\n94 29540b\nb6 10867d\n3a 4f16\n88 264abe\n64 1b427d\n6f 5b8ba\n7 1d5209\n19 dc9\n6f 1bb474\n3a 5f2a\n5a 1e8fc2\ndf 129759\n3c 198452\n30 4e1a\nda 129735\n5a 1f12b0\nc6 277f20\n26 1d0e15\n38 4f71\nce 278077\nb5 25fe83\n14 1cc2a2\n64 1b452b\n83 d2773\nca 11f826\n6f 5bb68\n20 1cfb19\n8e 25b476\n3a 61d8\n5a 1e9270\n44 1b7162\n4f 5e79f\n3a 1a0714\n4c 1b72b9\n85 102b07\n2a 45dd9\n5c 1b7c1a\na6 25f528\n77 22c50\n1a 8e0f\n17 19b184\n82 10ae7e\n8e 29e4b6\ne 1d3e51\n42 24ee8\n4e 1b8520\n1f 4256f\nd0 e7bcb\nfe 27cb0d\n44 1b7410\n22 45f30\n2e 1d9568\n54 1b7d71\n11 1d5892\n5f 5f3ae\n16 1d4909\n39 1d1b42\n9b 295529\n44 1b8424\n4f 5fa61\n40 24f43\n4c 1b857b\n4f 5fd0f\n23 1d80bd\n98 c9e5d\n2a 45de7\n5c 1b7c28\nc4 127799\naf 107e82\nd5 e668b\n1d 1cc6a8\nee eb290\ne4 ea180\ne6 2bdb29\nb1 110934\nfe ebbf1\n23 1d937f\n98 cb11f\n5 19bae0\nd3 28210d\n1 63\nd 19369b\n57 1b7ad7\nb 41ee1\ne4 eb442\n3d 3e661\nd 1ccd5b\na0 106cde\nac 29a316\n1 39723\nef 27c1ad\nc4 ee079\nc 1d4096\nda 2ba6c3\ncc ee1d0\n4a 2630f\n7d 1bbdce\nfa ea8fc\n4c 1b8837\n40 251ff\n5a 26c70\ne1 eb156\nc4 ee327\n51 5f221\n5d 1f2859\n8e d28fa\ncc ef492\nf5 f2b80\nc4 ef5e9\n1d 42808\n9 398ea\n1d 1d5cc8\n63 1ec6b4\n11 42690\n19 3a24b\n50 1eac4\n39 1d05e0\n8 1cd049\n65 20d6a\nd 1cd079\n1 39a41\n1b 3b4b2\n10 193e75\n1d 1cd9da\n11 3a3a2\na4 106d11\n1 42ff1\n1 1d523f\nc6 11f446\nf4 27c9bd\n6b 1f4e15\n20 3ebfd\na7 29a1c9\na4 107d25\n2b 1d0f48\n51 1af751\n61 1ec657\n1c 1d5a7b\n10 42443\nf5 ebd34\n97 29da0f\n8c 10ad61\nf4 f3e35\n42 1b8396\ned 2b58d6\n9c 10b6c2\n8e 10bfc8\n0 42da4\n87 29e370\n8 42efb\n83 10ae8f\nd7 eff42\nd0 2b34d9\n8f 29e4c7\ned 27d468\ne1 e9e30\ne9 e9f87\n45 1aee21\n67 2a62f\n60 1edbc6\n45 1b73bd\ncd 1278f1\nb8 269866\n65 1b4538\n8 19a6f7\n7b 1bd066\ndb efd66\n2 1d5245\n4b 1e891e\n8c 102c5d\n55 1b09d4\n23 3eb93\n6 41d64\nf4 27df8f\nd9 eedad\ne4 1235f2\ned 27d716\ne1 ea0de\n86 10bec5\n26 1966df\nf5 27dce4\n7 41ab9\n54 1ea103\nd6 280bbf\n36 197040\na 19b95e\n51 1ea3e3\n43 1cba7\n4f 1b01df\n45 1af0cf\ndb f0014\n72 216b0\n7e 1b4ce8\ne6 124859\n74 1b3bd8\nc1 ef5b7\n64 1b428b\n9d 10b8ff\n64 1b4539\ne8 f22d6\ne 19a72f\nf8 f2c37\n44 1b7170\n4c 1b72c7\nd2 121044\nb5 29aabf\n2e 1d92c8\n22 45c90\n54 1b7ad1\ne0 f3441\n2a 3efa5\n50 1d7ae\n5c 1b0de6\n51 1ea135\nf0 284d3e\n44 1b8432\n2b 3ed4e\n19 a12b\nf2 2862b5\nae 25f8c9\n53 1b8cfa\n31 1d0419\n0 1cce82\n45 1b7411\nce 279577\nc2 e5f3f\nff 27cb0e\n27 471c0\n5 1d3f4e\nd 1cbb09\nff 27ce20\nb6 269747\n5e 1e923f\n16 426c7\n33 3f4f6\ne5 f245d\nb4 108684\n54 1af721\n22 3d8e0\n76 22993\n2e 1d0f18\n69 1bc93c\n9a 10b8e4\n5 1ccc74\nf7 27df8b\n56 1ea3aa\na4 26787d\n7b 1b39e6\n4a 1b044f\n1 39793\nd 1ccdcb\nff 27e0e2\n52 56ec9\nf3 eaaaa\n5e 1ea501\nac 2679d4\nf2 2be459\n24 1d10ca\nd4 2bb5aa\nc5 e728c\n7a 1b3cf5\n4 1d3d01\n15 19b18b\n80 10ae85\n8c 29e4bd\nb4 ceef2\n5f 603c4\nc 1d3e58\n7a 1b4fb7\n5e 1b8e81\n52 25849\n4 1d4fc3\n80 d2779\nc2 120681\n45 1f1b53\n35 4dea\nac 10811c\na2 110287\neb 123960\nd4 2820c8\n5b 1b8eb1\n78 1bcffe\n0 1cbbce\n54 1b0c81\n22 3ee40\n7 8637\n55 1b0c82\n23 3ee41\n20 1966b5\nee 2b58ce\n67 21021\n33 3f7b0\nce e713d\na5 cd580\nbf 2614ef\nb3 cdeb7\nb7 cdee8\n28 470a2\n21 1d93d8\n8f 264d35\n83 d16fd\nad ce9a7\nf3 12542c\ne7 ea118\nff 27e080\nf3 eaa48\naf 110410\nd5 eec19\n27 1d0dc2\n59 1b8eb8\n20 3ee47\n5 863e\nca 11f508\n39 196ebe\n8 193927\n14 1cd882\n12 3b65e\n76 1f5641\n7d 1b4f90\n71 21958\ne8 124c8a\n0 9680\n49 1cd59\nce e714b\na5 cd58e\n25 197947\n42 1decc\nbc 2996fb\n23 1d811d\n99 c9c20\ned 27d726\ne1 ea0ee\n92 104755\nf6 2be738\nfd 27e087\nf1 eaa4f\n3f 19845a\n96 29d6f0\n33 4e22\nd2 282100\n1f 19c601\n13 8fc9\n5f 1b9192\n53 25b5a\n27 1d0e24\n13 8f65\n1f 19c59d\n8c 25b71b\nd0 282107\nb5 108617\ndf 1281e9\n1d 19c608\n11 8fd0\n6b 22169\n43 25195\n4f 1b87cd\n23 1976c1\ne5 2bf03d\n6d 1b4691\nc4 2b9927\n61 21059\n33 198022\nc 193956\n0 31e\nf5 2bf99e\n4b 26310\n3 19b868\n13 19c1c9\n79 1b39ef\n30 1a0316\n34 3e517\n6f 1bb406\nee f262c\n6a 5b87a\n4f 25071\n48 1e8608\n8c 102c5f\n6 41d66\n23 3eb95\nf4 27df91\n4e 1b87dc\n7f 1bbd73\n42 251a4\nd5 11fdad\n2d 1967da\n4a 1cd5f\n5e 1b913d\n52 25b05\n3d 19713b\n5a 1d6c0\n6b 20f07\nd6 27881f\n27 1d7eb0\n9c c9c50\nf6 2b60d8\n38 1d9e2f\na4 260a9f\nce e5e87\nf8 eaba3\nef ea02f\nad ce999\ne2 ea156\nee 27d78e\nb5 d5f8d\n84 d29f6\nc6 279180\n8c ca5b1\nb5 29aab1\nd2 121036\n16 192e97\nf7 f2b25\nc6 ef58e\na5 cd58c\nce e7149\naa 2a10be\n5a 60630\nfb f4211\nc6 e72a0\na2 2a1215\n6e 1bc983\n62 2934b\nc4 1209bd\nf5 123f54\ne4 2b6d52\n6a 20f06\n25 196677\n42 1cbfc\n4e 1b0234\na4 cd33f\nb4 cdca0\n3 42fe8\ne6 f2473\nd8 279c54\n47 5786a\nee ea02e\nf6 f2dd4\n57 581cb\nac ce998\n84 10aeb8\na1 107ce7\nfe ea98f\nd8 eed4a\ndf eeb37\n9 172a\nea ea23b\n6d 1bb70d\n5f 1b919e\n53 25b66\nc0 ef29a\n47 1cbca\ne6 ea185\nb3 110939\nd0 efbfb\n57 1d52b\nf6 eaae6\n62 2a60d\nf5 125216\n6a 221c8\nd8 eed3c\ncf ef498\n43 264c7\nbd 2a1cf6\nc0 280228\ncb 127865\n65 1bc56a\ne 1ccdd1\n2 39799\n9 8762\n7f 22d99\n14 19c44e\n2b 3dce6\ndb 1281c6\n75 1bcecb\n1e 1cd732\n12 3a0fa\n42 1cb98\n4e 1b01d0\n44 1af0c0\n2c 55ff\n21 1cfab8\n99 d3232\nc0 2804d6\ncb 127b13\n65 1bc818\ne 1cd07f\n2 39a47\n46 1b0327\n2 3aa5b\n55 1b0a44\n23 3ec03\nb7 10f6b6\n86 10c11f\n8e 103cda\na7 107d2b\n44 1aedc0\n66 22032\nd3 efc65\n2 3ad09\nbb 110da2\n86 103e31\nba ce06f\n7f 1ed449\n96 2953a0\nb 41c3f\n29 4611\n8c 29cedf\n4c 1b023b\n40 1cc03\nb6 10f965\n94 10478d\nbe 107520\n43 1b7391\nf5 f2e30\n54 5f24f\nc4 e5d2b\n73 5b07f\n7f 1ee6b7\n93 2652c2\nda 2b2375\nc 1cbd48\n9f 10b6c8\nb5 d623d\n66 1ec680\nde efdfa\nae cd42f\n9 1d4072\n1 19249d\n7 3975b\na6 106d16\n11 192dfe\n88 296130\n17 3a0bc\nb6 107677\n5c 1b0b9a\n50 1d562\n2a 3ed59\n52 1b8d05\nb2 cf1da\n77 1ee5b4\n3 42daa\nd4 e668a\nae 107e81\nd6 281e2d\n8f 10c029\n79 1b4f5d\n25 1966e5\n22 19e9fc\nf1 286001\nad 25f615\n50 1f2420\n67 1b327d\nb0 cf1df\n11 1c24\n46 1aee19\n0 70\nf5 2bf6f0\nc 1936a8\n52 1ea12d\n7b 1bd05a\n46 1b00e9\n25 196683\n42 1cc08\n4e 1b0240\n19 90c5\n6b 1b30e9\n22 19fa10\n93 2953e0\n67 1b4291\n7b 1bd308\n46 1b0397\n24 196436\na1 268dbb\n34 196d97\nf4 2bf99f\na9 268f12\n3c 196eee\n66 5b764\n31 5dd9\n66 1b2fce\n76 1bbecb\n85 10abfd\n6 1ccc6e\n4f 1b72cd\n24 1966e4\n40 1b8391\n31 6087\n66 1b327c\n33 1d9a30\n47 1b7424\n1 83bf\nd 19b9f7\n53 1f247c\n70 1f65c9\n43 24f57\n4f 1b858f\n83 29e33d\n16 1cc319\n1c 1942b7\n10 c7f\nd0 282105\n45 1b8431\nc2 e6f5f\n36 1d8a5c\nab 2a263b\nd1 280e44\n3e 1d0617\ncc 2bad40\nc0 127708\n69 22472\n26 1cfe0d\n45 1b86df\nc2 e720d\n36 1d076e\n3c 19845e\n30 4e26\n26 1d0e21\n26 1d10cf\nc 8794\n29 55c3\n1a 3a501\nca e6106\n6f 22448\nda e6a67\n7f 22da9\nde e7d4a\n8a 102c97\n2f 3efd9\ndf 1294b9\n9a 1035f8\n3f 3f93a\n6a 1b30e6\n18 90c2\n14 43982\n66 1ed9a6\n29 c909\n2d c93a\n8e 263826\n7f 1f69f7\n73 633bf\n39 d26a\n3d d29b\n21 1d93e8\n5a 25c5a\n10 19c16f\n7b 22aba\n5e 25c8b\n6b 294a1\ne1 123870\ned 2b6ea8\n21 19f9b6\n6f 294d2\n7b 29e02\nf1 1241d1\nfd 2b7809\n31 1a0317\n7f 29e33\nd8 e7d20\n63 1f5f80\n88 d18ba\n2d dbfc\n82 d14b0\n51 1f140f\n8e 264ae8\n73 64681\n98 d221b\n3d e55d\nad d5a31\nf4 12c4ef\na9 d5a62\nbd d6392\nb9 d63c3\n8d 10ad52\nc7 2b9921\nca ee452\n6f 2a794\na3 106ce6\naf 29a31e\nd5 278b27\neb f25fa\nfb f2f5b\n2d c92e\n8 9a23\nd8 2b3384\ndf efded\n4e 265ec\n43 5f93b\n88 d2b7c\nf4 12d7b1\n76 1b38cf\na9 d6d24\nce ef6e3\nc3 128a32\n5b 1b8ec1\nca ef714\na3 107fa8\nd5 279de9\neb f38bc\n1a 427eb\n3b 3f64b\n1e 4281c\nbb 108742\n1 43045\n9e 10b913\n9a 10b944\n3f 47c86\naf 10f15a\n5 1ccf14\na4 267b1d\ndd 280fcc\nab 10f18b\nbf 10fabb\na 431a0\n15 1cd875\nb4 26847e\nbb 10faec\nf0 ebd70\n51 1e7b5\ne 4317d\n8a 10c2a5\n95 29697a\ndd 28228e\nd1 eec56\nab 11044d\n88 d1858\nea 27c1dd\n98 d21b9\n94 10ca79\ne6 2b6a9d\na9 d5a00\nb9 d6361\nda eed51\nac 2a2666\na0 10f02e\nd 1d50ab\n1 41a73\n8 19a9b1\n18 19b312\n4a 250ad\n2d 19eb28\n73 1f55ad\n74 1f55e6\n29 19eb59\n5a 25a0e\n3d 19f489\n63 63d30\n6b 1b4347\n4e 1b7518\n43 1f0867\n53 1f11c8\n5a 1b7eaa\n86 26366b\n6f 1bb6c0\n6b 1bb6f1\n88 10bf80\n7f 1bc021\n7b 1bc052\na7 267813\n88 d2b1a\n3c 4c9e\na9 d6cc2\nca ef6b2\na3 107f46\nd5 279d87\n7e 21836\neb f385a\n8 19bc73\n42 251a2\n4e 1b87da\n43 1f1b29\n4a 1b880b\n63 2934a\n6f 1bc982\ndd eeb24\nca 280640\n6b 1bc9b3\n9a 10b8e2\nbb 10fa8a\n11 1cd844\nb0 26844d\nf0 27cc9c\n51 1af6e1\n2b 1d0ed8\ne 1d40a9\nd9 f0001\na 1d539c\n3b 3e389\na 3adf2\na5 ce850\n6e 1f4dd5\n3f 3e3ba\ne 3ae23\nee 2bdecc\nbf 1074b1\n8e 103f1a\n3f 3e3c8\ne 3ae31\n4d 1b8838\nfb ea8fd\n41 25200\nca e7366\n60 5ba3a\nff ea92e\nce e7397\n3b 3e3eb\na 3ae54\nbf 10776f\n73 1ecfc1\n39 3e3f2\n8 3ae5b\n6c 1f4e3e\nbb 1074e2\n8a 103f4b\nb9 1074e9\n88 103f52\ne 4316f\na8 29a2e7\nf0 ebd62\naf d6d50\n39 196e5e\nec 2bdf35\nd3 278841\n2c c93b\n3c d29c\n6e 294d3\n7a 29e03\nf0 1241d2\nfc 2b780a\n7e 29e34\n4b 25040\nea f25fb\n4b 579f2\n54 1e90ef\nf5 27ccd0\nfe f2f2b\n22 3eea4\n54 1b0ce5\n5f 58322\nff 2bf8b2\nf3 12c27a\nd3 279b03\n2c dbfd\n82 d2710\n4c 1f0c95\n72 64682\nd0 e78bb\n21 46f4c\n6a 2a764\nfd 12536d\n75 1b4e39\naa 29a2ee\nf2 ebd69\nd0 278af7\n6e 2a795\n63 63ae4\nf6 ebd9a\nae 29a31f\na2 106ce7\nd4 278b28\n9 976a\na8 d6d25\nee f388c\ne3 12cbdb\n4b 26302\nea f38bd\n3a 46994\n3e 469c5\nfd 27ddcb\nf1 ea793\nae 10f15b\nb5 299861\nbe 10fabc\n14 1cd876\n1b 42532\nba 10faed\n71 2295e\n54 25b2f\n2e 47326\nb6 10892b\n94 2956b9\n65 1b428a\nfa f2efa\n50 1b0cb4\n5b 582f1\n28 19eb5a\n38 19f4bb\n6e 1bb6c1\nd8 e67b0\n63 1f4a10\n6a 1bb6f2\n7e 1bc022\n73 1f5371\n25 1d0e1b\nba 10fa8b\n10 1cd845\n2a 1d8283\n3e 1d8bb3\n85 10c17b\n3a 1d8be4\n7b 21868\n4a 1e2d1\n3b 3e3f9\na 3ae62\nfb ea95f\nca e73c8\nbb 1074f0\n8a 103f59\n42 1b7384\n8 87b5\n52 1b7ce5\n18 9116\n63 1bb52c\n29 c95d\n39 19811e\n73 1bbe8d\n39 d2be\ncf 2803c4\n5a 25cae\nc6 2804ac\n8c d18dd\n90 c9a66\n9c 25d09e\nd6 280e0d\n9c d223e\ne7 284654\nad d5a85\n80 ca677\nb1 cdc0e\nbd 261246\nf7 284fb5\nbd d63e6\n6e 1edafd\n1c 43ad9\n62 5a4c5\n80 264959\nce ee475\ned eb59a\n4c 579b9\n2f 1d92c7\n23 45c8f\nd2 e65fe\n55 1b7ad0\nde 279c36\n90 2652ba\nde eedd6\na1 268b01\n0 1d4f20\ne8 2b5bb4\nef f261d\n81 d276c\nc8 11f81f\n6d 5bb61\n45 1b86e1\nc2 e720f\nf3 ea7a6\nff 27ddde\n8c 25b46f\n91 d30cd\nd8 120180\n8c c92f1\n7d 5c4c2\n9c 25bdd0\n63 1bc7ee\n29 dc1f\nf7 284d05\nc6 28176e\n8c d2b9f\ne6 123599\ne7 285916\nad d6d47\nce ef737\ne0 12cbd3\n48 25346\n58 1b0b07\n79 29e4f\na 41ede\n15 1cc5b3\ncf 2bad9c\nc3 127764\n1a 1cd69f\n1a 4283f\nda e69f5\n5d 1b7ec7\n2b 46086\n3b 1d1847\n3b 469e7\ncc ee46e\n21 45c88\n2d 1d92c0\nd0 e65f7\naa 107dee\ndc 279c2f\ndc eedcf\n83 10ab71\n8f 29e1a9\ned f2616\nf1 ea79f\nc0 e7208\nfd 27ddd7\nfd f2f77\n8e 10b006\n15 1d4661\n92 10318f\n9e 2967c7\naf 10f1ae\n82 103da0\n5 1d5272\nb3 107337\nbf 29a96f\ncc ef730\ned f38d8\nc 19a9d4\naa cd6aa\n2d 19eb7c\n19 a377\n4e 1b756c\n60 1f4a08\n6a 5a8d6\n23 d823\n5e 1b7ecd\n70 1f5369\n7a 5b237\n33 e184\n0 865e\nc 19bc96\n66 1ec690\n42 251f6\n4e 1b882e\n60 1f5cca\n63 2939e\n6f 1bc9d6\n48 1e9bd8\n5c 1b7ec6\na6 25f7d4\n78 5b230\n31 e17d\ne 1d40fd\nde 278c14\n2f 1d82a5\n98 10c8ef\n3f 1d8c06\nea eb4fd\n61 29397\n6d 1bc9cf\n2 41d87\ne 1d53bf\n1a 911d\n3b d2c5\n48 25354\n69 294fc\ne9 eb2ab\ncc ee47c\nf9 ebc0c\ndc eeddd\nd7 efc32\n83 10ab7f\n8f 29e1b7\ned f2624\nfd f2f85\n48 26616\n69 2a7be\ncc ef73e\ned f38e6\n73 1f55bd\n39 469ee\ne7 2bdd84\nad 10f1b5\nf7 2be6e5\nbd 10fb16\ne7 2bf046\nad 110477\n69 1b43a2\n4c 1b7573\n72 2af60\n21 d82a\n79 1b4d03\n5c 1b7ed4\n31 e18b\n40 251fd\n4c 1b8835\n61 293a5\nea eb50b\n6d 1bc9dd\n29 1d0f33\nc 1d4104\n39 1d1894\n1c 1d4a65\nc9 2b9d5e\neb 12cfd0\ndc 278c1b\naa 106dda\n2d 1d82ac\nba 10773b\n3d 1d8c0d\n0 41d8e\nc 1d53c6\n7a 29e57\nac d5a86\nb0 cdc0f\nbc 261247\n93 c9ac2\n9f 25d0fa\nee f261e\n6c 5bb62\nfe f2f7f\n6a 2a7b8\n81 263696\n75 1b4e8d\nac d6d48\n5a 1b7b9a\n78 29e50\n5c 1b7ec8\n2a 46087\n3a 1d1848\n35 1d075c\n3a 469e8\nec f2617\nf0 ea7a0\nfc 27ddd8\nd3 e6653\ndf 279c8b\n4a 1b74d9\nfc f2f78\nae 10f1af\n68 2a7b1\n5c 1b918a\n50 25b52\n2a 47349\n35 1d1a1e\nec f38d9\n3c 19f4de\nb4 29a810\n14 1f10\n7e 1bc076\n1a 2093\nf6 2b73a8\n20 c807\n2c 19fe3f\n9d 29580f\n62 2939f\n6e 1bc9d7\na6 25f4c6\n58 56ddb\n7c 1bc06f\n18 208c\n60 29398\n6c 1bc9d0\nbd cf308\n3b 3e639\nd1 efc5c\n89 29e1e1\n39 3e640\n3f 3e66a\nd2 128310\nde 2bb948\nd5 efc8d\n81 10abda\n8d 29e212\nf9 27e0aa\nb 41e7f\n73 1bd1b1\n5d 26f39\n9 41e86\n2d 1967cc\n4a 1cd51\n71 1bd1b8\nfd 27e0db\nf1 eaaa3\nf 41eb0\n98 c9ecf\n77 1bd1e2\n50 1ea3e4\nd 41eb7\nf2 ebd17\n75 1bd1e9\n1f 42811\nfd ebe89\na9 106dd6\naf 106e00\nad 106e07\n8a 25c769\nbb 107730\n6 1d3c98\nb9 107737\nbf 107761\nbd 107768\n8b 10af76\ndd f0030\n89 10af7d\n8f 10afa7\n70 2ad0d\n8d 10afae\n9b 10b8d7\n9f 10b908\n6b 21155\n6f 21186\n7b 21ab6\n7f 21ae7\n4b 252fc\n6a 5bb36\n4f 2532d\n5b 25c5d\n2f 3dd17\ndf 1281f7\nff 2be58c\nc2 1279bd\nce 2baff5\n3b 3e647\n3f 3e678\n2f 1967d3\n73 1bd1bf\n77 1bd1f0\n1b 427ee\n1f 4281f\neb ea24c\nef ea27d\nfb eabad\n38 5f2f\ned f362c\n6f 1edaa8\n63 5a470\n4b 1b84ee\nfd f3f8d\n67 5a4a1\naf 106e0e\nbb 10773e\n0 3aca0\na1 ce881\n6a 1f4e06\ndf f0037\n8b 10af84\nc0 2804e2\nad 1101bd\n2c 432f\n70 2ad1b\n9b 10b8e5\n9f 10b916\n39 3e6a2\nd8 2bb980\n9 41ee8\n1b 42842\n19 42849\n94 265599\nab 106e31\nfd ebeeb\na9 106e38\n6 1d3cfa\nb9 107799\n8b 10afd8\ndd f0092\n89 10afdf\n4d 2506a\nec f2625\nd3 e6661\ndf 279c99\n4a 1b74e7\n5d 259cb\nfc f2f86\n68 2a7bf\n4d 2632c\nec f38e7\n38 469ef\n93 1031f2\n9f 29682a\n28 47350\n35 d146\n2e 19eb84\na6 299eb6\n3e 19f4e5\nb6 29a817\n7c 1bc07d\n18 209a\n60 293a6\n6c 1bc9de\n2c 1d82ad\n3c 1d8c0e\n20 45f37\n2c 1d956f\n14 19c4b0\nd6 128091\n2b 3dd48\ndb 128228\nf2 eba5b\n75 1bcf2d\nac 10f1b6\nd 41bfb\n19 9125\n53 1b7cf4\n3b 3e6a9\nda 2bb987\n1b 42850\ncb ee455\n8d 10acf2\nbb 1077a0\n9d 10b653\n42 1b7392\n8 87c3\n52 1b7cf3\n18 9124\n73 1bbe9b\n39 d2cc\ne6 2bdde5\nb1 110bf0\n47 1f082a\n6b 29503\n2a 55c7\n8 192355\nde 2bb9a8\nd2 128370\n86 d14e1\n55 1f1440\n77 646b2\nd6 280e1b\nb9 cf07b\n9c d224c\ne7 284662\nad d5a93\nff 2bfb50\nf3 12c518\nf7 284fc3\nbd d63f4\n6e 1edb0b\n62 5a4d3\neb eb2b2\n80 264967\nce ee483\nfb ebc13\n90 2652c8\nde eede4\nae ce6ef\n8c 25b47d\nbe cf050\n9c 25bdde\n6b 2a7c5\n8 193617\nf7 284d13\nc6 28177c\n8c d2bad\nd2 129632\ne7 285924\nad d6d55\nd1 2ba568\nf3 12d7da\nce ef745\n49 1e98c9\nef f38ed\n80 c9107\n8c 25c73f\n1a 4284d\n5d 1b7ed5\nda e6a03\n2b 46094\n3b 469f5\n79 22b17\nbb 1087a4\n9e 10b975\naf 10f1bc\nbf 10fb1d\n5d 1b9197\nda e7cc5\n51 25b5f\n2b 47356\nd1 e790e\n8e 10c2d6\nd5 eec87\naf 11047e\n29 197811\nc 19a9e2\n7b 21aa8\n32 e3cf\n52 1f1467\n2d 19eb8a\n73 1f560f\nce 2bb057\nff 2be5ee\nc2 127a1f\n67 63d61\n19 a385\n6b 1b43a9\n4e 1b757a\n7b 1b4d0a\n5e 1b7edb\n6f 1bb722\n0 866c\nc 19bca4\n52 1f2729\n42 25204\n4e 1b883c\n63 293ac\n6f 1bc9e4\n2 41d95\n51 1b0a05\ne 1d53cd\n2b 3ef9a\ndb 12947a\n29 3efa1\nd9 129481\nb 43141\n9 43148\n9 1d5396\nce 11f59d\nf1 ebd65\nf 43172\nd 43179\nab 108091\na9 108098\naf 1080c2\nad 1080c9\n1e 3a214\n8b 10c238\n1c 3a21b\n89 10c23f\n2b 19e8a6\n6b 22417\n4b 265be\n4f 265ef\n50 58192\nf1 ebd73\nf 43180\neb eb50e\nef eb53f\n5e 1d691\ncb ef6b5\n2b 3effc\nf8 12d629\n29 3f003\n23 571f\nd9 1294e3\n9 431aa\nab 1080f3\na9 1080fa\n8b 10c29a\n89 10c2a1\n28 c96c\n38 d2cd\nd 84d9\n28 3ece2\nac d5a94\nfe 2bfb51\nf2 12c519\n28 dc2e\n6a 2a7c6\n81 2636a4\n75 1b4e9b\nb4 26848c\n15 19aed1\n30 1d16da\nd 979b\nac d6d56\nf2 12d7db\n4f 26333\n48 1e98ca\nee f38ee\nb4 29a802\n3a 469f6\nfd 27de2d\nf 41c02\nf1 ea7f5\nae 10f1bd\n5c 1b9198\n50 25b60\n2a 47357\n35 1d1a2c\nf 42ec4\nd4 eec88\nf1 ebab7\nae 11047f\n2c 19eb8b\n72 1f5610\n3c 19f4ec\n39 e52e\n6e 1bb723\n7e 1bc084\n20 c815\n2c 19fe4d\n72 1f68d2\n62 293ad\n6e 1bc9e5\n71 1b38ec\n2e 1d82b4\n3e 1d8c15\n22 45f3e\n54 1b7d7f\n71 1b4bae\n2e 1d9576\n4b 26620\nd6 129353\n2b 3f00a\n14 3a372\ndb 1294ea\nac 110478\nd 42ebd\ncb ef717\nab 108101\n8d 10bfb4\n8b 10c2a8\nf8 eac05\n59 1d64a\n10 9f71\n97 26553d\nfc eac36\n5d 1d67b\n14 9fa2\n18 a0c8\n93 d205c\n9f 265694\n1c a0f9\n31 e119\n39 e270\nf0 27dcb2\n2 41a87\ne 1d50bf\n56 26b3a\n5a 26c60\n2b 1d9236\n51 1b7a3f\n73 2acb1\n59 1b7b96\n7b 2ae08\ndd e6772\n94 d3099\n4 1cbc51\nd9 e67a3\n90 d30ca\n9c d31f0\nc 1cbda8\n98 d3221\nb5 d7241\n25 1cfdf9\n50 1af6ee\n2a 1d0ee5\nbd d7398\n2d 1cff50\nab 2a238f\nd1 280b98\nf3 f3e0a\n34 4de9\nd9 280cef\nfb f3f61\n56 6026c\n59 1d8f8\n10 a21f\n5d 1d929\n14 a250\n31 e3c7\nc7 2814cd\ne 1d536d\nf0 27df60\n3f 1d8904\n2 41d35\n56 26de8\n2b 1d94e4\n51 1b7ced\n73 2af5f\ndd e6a20\n94 d3347\nd9 e6a51\n90 d3378\nbb 2a1a2c\n8a 29e495\nd2 eff10\nab 2a263d\n3e 1d0619\nd1 280e46\nf3 f40b8\neb 2857f0\n4a 1f1c0f\n29 1d922f\n71 2acaa\nb7 260138\n16 1cc557\n79 2ae01\na9 2a2388\nf1 f3e03\ndf 11fe9b\n96 10c7c2\n9e 10c919\n18 4283a\n91 29d729\nb3 11099b\n99 29d880\nbb 110af2\n69 1f5e22\n91 29d9d7\nb3 110c49\n98 d31bf\n8 1cbd77\nb9 d7367\n29 1cff1f\nfb f3eff\n6b 1ecab7\nc4 e5f75\nf9 f2ee6\n59 1af89a\n10 19c1c1\n10 8caf\n1c 19c2e7\n18 19c318\n35 1a0338\n90 263ff6\n31 1a0369\n39 1a04c0\n52 1b8d59\n75 633f7\n7d 6354e\n7b 1bd058\nd9 e69ef\n90 d3316\nb1 d74be\n41 2519e\n4d 1b87d6\n3e 1d05b7\nf3 f4056\n5d 1afb17\n2b 3dcd6\n14 19c43e\n59 1afb48\n10 19c46f\n90 2642a4\n31 1a0617\n52 1b9007\n23 1d80cb\n98 c9e6b\n77 1bd17e\nd2 280e3c\nf1 27df61\nf 1d536e\n57 26de9\n3 41d36\n50 1ea380\nf1 f3da1\n96 29564e\nf9 f3ef8\ndb 11fe6a\n92 10c791\nd7 eff52\n9a 10c8e8\n67 1ec92f\ndf f00a9\n50 1b8d52\n79 1bd051\n5b 1e8fc3\n12 1d58ea\n61 1bb283\nbf 2695f1\n12 423d8\nb3 d5fb9\n1e 1d5a10\n1a 1d5a41\n69 1bb3da\n37 1d9a61\n92 29d71f\n33 1d9a92\n33 46580\n2 42fe9\n3f 1d9bb8\n9a 29d876\n3b 1d9be9\nb2 cdc68\nbe 2612a0\nd0 280e35\nb7 269748\n5f 1e9240\n16 1d5b67\n61 1bb531\n37 1d9d0f\n92 29d9cd\n33 1d9d40\nfa eac0c\n5b 1d651\n12 9f78\n1a a0cf\n11 19aeae\n33 e120\n19 19b005\n3b e277\n54 26b41\n58 26c67\nd3 eebfb\ndf 282233\n5c 26c98\n71 2acb8\n85 263915\n16 1cc565\n79 2ae0f\n8d 263a6c\ndb e67aa\n92 d30d1\n9a d3228\nf1 f3e11\n5b 1d8ff\n12 a226\n69 1bc700\n11 19b15c\n33 e3ce\n50 26dbe\nb7 d74f8\nc 1cbafa\n54 26def\n71 2af66\n2d 1cfca2\ndf e6a27\n96 d334e\ndb e6a58\n92 d337f\nd4 efee6\nf1 f40bf\ne9 2857f7\n48 1f1c16\n97 29ec6d\n93 10b78c\n9f 29edc4\n31 47849\ndd 11fea2\n94 10c7c9\n6b 1edd7b\nd9 11fed3\n90 10c7fa\n98 10c951\n7b 22d68\n10 19c41d\nb5 110971\nb1 1109a2\nbd 110ac8\nb9 110af9\nf6 27ca1a\n31 47af7\nf6 123f4c\nd9 120181\n90 10caa8\n47 1f088a\nb1 110c50\n69 1ecabe\nc6 e5f7c\nfb f2eed\n12 8cb6\n1e 19c2ee\n37 1a033f\nb1 261122\nff eac3e\n17 9faa\n10 1cd541\n92 263ffd\n33 1a0370\n33 ce5e\n2 98c7\n3f 1a0496\n42 1f0aa6\n8 41ed7\nb9 261279\n1f a101\n18 1cd698\n9a 264154\n3b 1a04c7\n6b 5a5c7\n54 1b8d2f\n50 1b8d60\n58 1b8eb7\n79 1bd05f\ndb e69f6\n92 d331d\nb3 d74c5\n43 251a5\n4f 1b87dd\nd0 efeb5\n8c 294bf1\nf1 f405d\nad 298d99\n5f 1afb1e\n16 19c445\n5b 1afb4f\n12 19c476\n37 1a05ed\nb1 2613d0\n17 a258\n10 1cd7ef\n92 2642ab\n33 1a061e\n6b 5a875\n54 1b8fdd\n50 1b900e\n75 1bd185\nb 1d507f\nbd 110b1e\nd0 280e43\nb1 110940\nb5 2694a1\n5d 1e8f99\n14 1d58c0\nf9 12c616\n59 1e8fca\n10 1d58f1\nbd 2695f8\n10 423df\nb1 d5fc0\n1c 1d5a17\n18 1d5a48\n35 1d9a68\nca 120ada\n90 29d726\n31 1d9a99\n31 46587\n0 42ff0\n3d 1d9bbf\n98 29d87d\n39 1d9bf0\n74 2acdc\n47 1f0828\nb1 110bee\nb5 26974f\n5d 1e9247\n8e c92e8\n14 1d5b6e\n59 1e9278\n10 1d5b9f\n90 29d9d4\n79 217f3\n30 e11a\n3c e2a2\na6 298c48\n76 2ace3\n3b 1d9bf7\n7a 2ae09\n7e 2ae3a\n23 1d938b\nfd ea91b\nb4 d7242\n24 1cfdfa\nf9 ea94c\nb0 d7273\n53 1f1414\nbc d7399\n2c 1cff51\nf6 f3dda\nbb 2a2cee\nf2 f3e0b\nfe f3f31\nfa f3f62\n79 21aa1\n30 e3c8\n8c d28ff\nc6 2814ce\n7d 21ad2\n34 e3f9\n76 2af91\n3b 1d9ea5\nf6 f4088\n4 42d71\n4d 5644a\na5 d6952\nbb 2a2f9c\nf2 f40b9\n70 2acab\ne6 12358b\n21 1d9384\n3a 4799a\nf4 f3dd3\nb9 2a2ce7\nfc f3f2a\nf8 f3f5b\nba 110af3\n47 5e64a\n74 2af8a\nf4 f4081\n7a 5af27\nb9 2a2f95\nfb 124323\nb2 110c4a\nb8 d7368\n28 1cff20\nfa f3f00\n6a 1ecab8\n79 1b3a43\n30 1a036a\n3b 479a7\n30 ce58\n3c 1a0490\n38 1a04c1\n76 1bced1\n72 299f0\n7e 1bd028\n7a 1bd059\nf2 f4057\n0 42d40\n49 56419\na1 d6921\ne9 27d499\ncc 28066a\n3b 47c55\n76 1bd17f\n25 19fa49\nf0 f3da2\nf8 f3ef9\n63 1b324e\n8c 29cf4d\nba 110a91\n70 299e9\n7c 1bd021\n78 1bd052\n7b 1ed16c\n32 1d9a93\n48 1af192\n3a 1d9bea\nfb 1242c1\nb2 110be8\na9 29a02a\n8c 29d1fb\nb4 cdc30\n5f 5f102\n74 1bd178\nda 128473\n7b 1ed41a\n32 1d9d41\n4c 1af1b7\n4e 1af1be\n4a 1af1ef\n2a 3dcd7\n5c 1afb18\n58 1afb49\nf7 27dc79\n2e 58a6\nc 192634\n6a 22468\n48 1af1f6\ne7 27d326\n8e c9598\n9c c9ef2\n9e c9ef9\ncc e6122\n21 197666\nce e6129\nf7 2be6d7\nbd 10fb08\ne 19268f\n1c 192fe9\n84 102b5a\n4c 1af219\n4e 1af220\n2a 3dd39\n5c 1afb7a\n5e 1afb81\nc6 11f6f2\n3a e278\n91 cad8b\n5a 1f1310\n74 2acea\nb7 2a1b52\n7c 2ae41\nb1 1085f2\ne6 2b57e7\n5a 56d72\nfb ea953\nb2 d727a\n98 103341\n77 1f6654\nf8 f3f69\n74 2af98\nfb eac01\n5a 57020\nb2 d7528\n98 1035ef\n77 1f6902\nac cd6d4\n51 26b05\nf0 f40c0\n77 21980\n2f 1cff05\ndf 2ba3e5\n3c 479d2\n9e d3507\nb8 110afa\n13 19c479\n7d 5b202\n34 47b29\ncc 120b12\n3a 1a04c8\n78 1bd060\n7b 1b3cf8\n32 1a061f\n12 8fc6\n1e 19c5fe\n38 1d9bf1\n8c c959f\nde 2b365c\nd2 120024\n83 10ab6f\n8f 29e1a7\n77 5c366\n9c c9f00\ncc e6130\nf5 2be6de\nc 192696\n52 1e911b\n2a 3dd47\n5c 1afb88\n32 19f0af\nc4 11f6f9\n59 1d658\n10 9f7f\n5d 1d689\n14 9fb0\n18 a0d6\n1c a107\n31 e127\n39 e27e\n52 26b17\n56 26b48\n32 3f565\n81 c93a6\n8d 25c9de\n5a 26c6e\n5e 26c9f\nd9 e67b1\n90 d30d8\n98 d322f\nb1 d7280\nb9 d73d7\nd6 efc3f\n62 1b4251\n59 1d906\n10 a22d\n5d 1d937\n66 1b4282\n14 a25e\n56 26df6\nd9 e6a5f\n90 d3386\n47 1b7168\nb1 d752e\na9 268c66\n8 1d5085\nd6 efeed\n5b 56d81\n12 436a8\n5f 56db2\n16 436d9\n1a 437ff\n1e 43830\n33 47850\ndf 11fea9\n96 10c7d0\ndb 11feda\n92 10c801\n9e 10c927\n9a 10c958\nb3 1109a9\nbf 110acf\nbb 110b00\n5b 5702f\n12 43956\n5f 57060\n16 43987\n33 47afe\ndf 120157\n96 10ca7e\ndb 120188\n92 10caaf\nb3 110c57\nd2 efc0e\n42 1e87c6\n2b 3da36\n5d 1af877\n14 19c19e\nf9 f2ef4\n59 1af8a8\n10 19c1cf\n10 8cbd\n1c 19c2f5\n18 19c326\n35 1a0346\nca e73b8\n90 264004\n31 ce65\n0 98ce\n3d 1a049d\nc2 1289d1\n98 26415b\n56 1b8d36\n52 1b8d67\ne2 27d348\nd9 e69fd\n90 d3324\n47 1b7106\nb1 d74cc\nd2 efebc\n8e 294bf8\n5d 1afb25\n2b 3dce4\n14 19c44c\n59 1afb56\n10 19c47d\n90 2642b2\nc2 128c6f\nf6 ea82c\n31 1a0625\n56 1b8fe4\n52 1b9015\ndb 11fe78\n92 10c79f\n9a 10c8f6\n95 10b80a\nb3 110947\n5f 1e8fa0\n16 1d58c7\nfb 12c61d\n12 423e6\n1e 1d5a1e\n1a 1d5a4f\n33 1d9aa0\ndb 120126\n92 10ca4d\n5f 1e924e\n16 1d5b75\n5b 1e927f\n12 1d5ba6\n33 1d9d4e\nbf cddf1\n8e ca85a\nfd ea97b\ncc e73e4\n0 312\n3d 196ee1\nc 19394a\n40 1cea3\n7d 1b3a72\n4c 1b04db\neb 12ba70\n7d 21832\n34 e159\n54 1f11f1\n3c e2b0\n5c 1f1348\na6 298c56\n72 2acc0\n76 2acf1\nca e710c\na1 cd54f\nad 260b87\n7a 2ae17\n91 cb02b\n7e 2ae48\n58 56d79\nf9 ea95a\nb0 d7281\nd7 ee980\nf2 125189\n75 1f665b\nb8 d73d8\nf6 f3de8\n31 3f7ab\n66 1ec9a0\nae cd42d\nf2 f3e19\n3a 1d9e36\nfe f3f3f\nfa f3f70\n7d 21ae0\n34 e407\n54 1f149f\n76 2af9f\nf9 eac08\n58 57027\n46 1b7169\n11 9f74\nb0 d752f\n9f 29d854\nf6 f4096\n3a 479a8\n3e 479d9\nff 124052\nb6 110979\nbe 110ad0\nba 110b01\nf2 f3db7\n62 1ec96f\nfa f3f0e\n6a 1ecac6\n43 1b7383\nf5 f2e22\n79 1b3a51\n30 1a0378\n38 1a04cf\n76 1bcedf\n7a 1bd067\nf2 f4065\nae 298da1\n79 1b3cff\n30 1a0626\n76 1bd18d\nba 110a9f\n3 1d3f14\nb5 10f9b3\n32 4658f\n3e 1d9bc7\n3a 1d9bf8\nfb 1242cf\nb2 110bf6\nf7 27c9c7\nbd cddf8\n8c ca861\nd2 1212e6\nfd ea989\ncc e73f2\nf5 2bf9a0\n0 320\n3d 196eef\nce 11f539\nc 193958\n52 1ea3dd\n59 1d6ac\n10 9fd3\n97 26559f\nfd 27cdc5\nb4 2696ec\n52 1b8cf9\n18 a12a\n93 d20be\n9f 2656f6\nb0 d620b\nbc 269843\n31 e17b\n91 296949\n73 1bcea1\n39 e2d2\n99 296aa0\na 1d50f0\n35 1a05e6\n52 26b6b\n1d a0fa\nf6 286284\n31 d105\n3d 1a073d\n5a 26cc2\nf2 f2da3\nfe 2863db\nd3 2b34e1\n59 1b7bf8\n7b 2ae6a\ndb 2b3638\ndd e67d4\n94 d30fb\nd6 281e21\n9c d3252\nf7 285fc9\nbd d73fa\n8a 10ad37\nb5 d622d\nde efdea\n8f 10c02b\nd0 12103d\nd8 121194\n50 1b0c60\n59 1d95a\n10 a281\n31 e429\nc7 28152f\na 1d539e\n3b 1d8935\n52 26e19\n5e 1d92f\n10 193e13\ndd e6a82\n94 d33a9\n8 1d50e9\n50 26b64\n83 29d07d\nd7 282130\nf4 28627d\nd1 2b34da\n16 1cc5b9\n79 2ae63\nd9 2b3631\n11 1d4632\n33 478a4\n19 1d4789\n3b 479fb\n8b ca828\nd6 1212b5\n8c 29e211\n80 10abd9\nd4 efc8c\n88 10ad30\ndc efde3\n95 29d75a\nb7 1109cc\n39 1d892e\n8 1d5397\n50 26e12\n11 1d48e0\n33 47b52\nad 2a2667\na1 10f02f\nf5 f40e2\n4f 1b757d\n6a 1edd86\n5d 1af8cb\n2b 3da8a\n14 19c1f2\nc4 e5fd7\nf9 f2f48\n10 8d11\n1c 19c349\nb2 ceec8\n35 1a039a\n9d 10c977\n8a 29e493\n2 1d3cd7\n56 1b8d8a\n35 19f324\n52 258a9\na 1d3e2e\n5e 1b8ee1\nb 41c41\n58 1ea28b\n5d 1afb79\n2b 3dd38\n14 19c4a0\nb2 cf176\n35 1a0648\n2 1d3f85\n56 1b9038\n50 258a2\n8 1d3e27\n5c 1b8eda\n9d c9e9d\na6 2607e8\n5b 1b7e49\n37 1d9ac3\n33 465e2\n2 4304b\n3f 1d9c1a\n0 1d3f7e\n6b 5a8c9\n54 1b9031\n37 1d9d71\n5b 1d6b3\n12 9fda\n1a a131\n11 19af10\n33 e182\n64 1b3277\n93 296950\n19 19b067\n3b e2d9\nbf 2996af\nd6 e7b93\n6c 1b33ce\n9b 296aa7\n50 26b72\nd7 28213e\n58 26cc9\nd3 eec5d\ndf 282295\n71 2ad1a\na0 29a180\n85 263977\n16 1cc5c7\n79 2ae71\na8 29a2d7\n8d 263ace\ndf e67db\n96 d3102\n9e d3259\nd4 efc9a\ndc efdf1\n8a 29e1d7\n72 5c396\nf5 f3e42\n98 29db1d\nfd f3f99\n5b 1d961\n12 a288\n11 19b1be\ndf 2ba3d7\n33 e430\n50 26e20\n71 2afc8\ndf e6a89\n96 d33b0\nd4 eff48\nf5 f40f0\n97 29eccf\n93 10b7ee\n9f 29ee26\n31 478ab\ndd 11ff04\n94 10c82b\nb5 1109d3\nf7 2bf6f9\nbd 110b2a\nfb eabff\n90 2642b4\n52 26b7b\ne 18f\n35 1a05f6\n59 5708a\na7 25f775\n10 439b1\n31 47b59\nb5 110c81\n5f 1af8d2\n16 19c1f9\nc6 e5fde\n5a 5f36e\nfb f2f4f\n79 5c493\n12 8d18\n1e 19c350\n50 258b0\n5c 1b8ee8\n71 29a58\nfa ebbbe\n40 264c1\n7d 1bd090\n5f 1afb80\n16 19c4a7\n6b 5a8d7\n54 1b903f\n5d 1e8ffb\n14 1d5922\nf9 12c678\n10 42441\n1c 1d5a79\nb2 1085f8\n35 1d9aca\n31 465e9\n0 43052\nba 10874f\n3d 1d9c21\n5d 1e92a9\n14 1d5bd0\nb2 1088a6\n35 1d9d78\n79 21855\n30 e17c\n38 e2d3\n72 2ad14\n7a 2ae6b\n57 1f1445\nf6 f3e3c\nfe f3f93\n36 1d04c0\na3 2a24e4\n72 2afc2\nf6 f40ea\n3a 479fc\n7b 5b22c\n32 47b53\n7a 5af89\n72 29a52\n7e 1bd08a\n70 29a4b\n7c 1bd083\nce 2baff3\nc2 1279bb\n5a 1b7e4a\n28 1d117e\nd 19a975\ndb 280fa2\n2a 1d1185\n50 1af98e\nf 19a97c\n9 19a9a6\nb 19a9ad\n24 19fc86\n6d 1b335f\n26 19fc8d\n6f 1b3366\n20 19fcb7\n69 1b3390\n34 1a05e7\n7d 1b3cc0\n30 1a0618\n79 1b3cf1\n4d 1b7506\n49 1b7537\n4b 1b753e\n59 1b7e98\n2 1cbbc5\nb4 107664\na3 25f7a6\n24 19fc94\n6d 1b336d\n12 3a39c\n1e 1cd9d4\n20 19fcc5\n69 1b339e\n6b 2a7b7\n49 1b7545\n7b 2185c\n32 e183\n3a e2da\n4a 1b84fb\nfc f3f9a\n7b 21b0a\nde 2ba3d8\n32 e431\n2c 45dd\n70 2afc9\n5f 1f12ee\n38 47a03\n17 19c4aa\ndd 121164\n1e 3a4c0\nbf ce0a1\ndd 120150\n94 10ca77\ne6 2b6a9b\n8 19bc05\n39 19f19c\na8 1080e9\n8d d18e0\nb8 108a4a\n9d d2241\n98 2957df\nba 108a51\n9f d2248\nc6 2bac42\na4 d6bf1\ned ea2ca\nef 2b5941\na6 2a2268\n18 d74\ncd ee471\nc8 2b1a0f\nea 124c81\ncf ee478\ndd eedd2\nd8 2b2370\nfa 1255e2\ndf eedd9\n2d 196830\nab 10eecf\ndd 280d10\n2f 196837\n98 cae81\n51 56c23\n5d 1ea25b\ndf 280d17\nfc 284e64\n2a 1d11e7\n50 1af9f0\nf 19a9de\n87 295d10\n24 19fce8\n6d 1b33c1\n26 19fcef\n6f 1b33c8\n34 1a0649\n7d 1b3d22\n36 1a0650\n7f 1b3d29\ne7 12389a\nc7 2b28a1\n70 29a59\n7c 1bd091\nc 39b66\nad cd747\nff 2b7804\nf3 1241cc\n1c 3a4c7\nbd ce0a8\ne4 2b6aa2\n2d 19683e\nab 10eedd\ndd 280d1e\n73 1ed2c3\n28 1d11ee\nd 19a9e5\n53 1f146a\n59 1d6ba\n10 9fe1\n52 1b8d07\n18 a138\n31 e189\n73 1bceaf\n39 e2e0\nd4 e7b9a\n35 1a05f4\n52 26b79\n6a 291f4\n89 25ca0f\ndd e67e2\n94 d3109\nb5 d72b1\nf7 285fd7\nbd d7408\nd6 efca1\nbf 26989f\n12 42686\nb3 d6267\n1e 1d5cbe\nb5 d623b\nde efdf8\n59 1d968\n10 a28f\ndd 2ba3de\n31 e437\n52 26e27\ndd e6a90\n94 d33b7\nd6 eff4f\n1a 43861\n33 478b2\n3b 47a09\nd6 1212c3\n9e 10c989\nb7 1109da\nbf 110b31\n5b 57091\n12 439b8\n33 47b60\nb7 110c88\n5d 1af8d9\n2b 3da98\n14 19c200\n58 5f375\nf9 f2f56\n10 8d1f\n1c 19c357\nc6 1289f2\n35 1a03a8\na5 2a24ae\nc2 128a33\n56 1b8d98\n35 19f332\n52 258b7\n5e 1b8eef\n2b 3dd46\n5d 1afb87\n14 19c4ae\nc6 128ca0\n35 1a0656\n56 1b9046\nfb 12c67f\n12 42448\n1e 1d5a80\n4a 1e015\n21 4458\n2d 197a90\nab 11012f\nd1 ee938\ndd 281f70\nfb 27e0bd\n88 25b74e\n23 445f\nd9 128223\n2f 197a97\nd3 ee93f\ndf 281f77\n8a 25b755\n29 197ac1\nd9 281fa1\n2b 197ac8\n14 192e30\ndb 281fa8\n1 85ff\nd 19bc37\ndb 282264\n9 19bc68\nb 19bc6f\n39 cfbe\n61 20fe9\n6d 1b4621\nc8 2782df\n8c 10acef\n63 20ff0\n6f 1b4628\nca 2782e6\n69 1b4652\n41 25190\n4d 1b87c8\n43 25197\n4f 1b87cf\n49 1b87f9\n4b 1b8800\n79 29b4f\n4a 1e023\n21 4466\n2d 197a9e\nab 11013d\nd1 ee946\ndd 281f7e\naa ce9ce\n88 25b75c\neb 1236b0\nd4 281e18\n29 197acf\n34 60a9\nd9 281faf\n1 860d\nd 19bc45\ncb 127857\n9 19bc76\n61 20ff7\n6d 1b462f\nea eb55f\nc8 2782ed\n69 1b4660\n49 1b8807\nd9 12972f\n5b 1af84d\ne 3ae21\naf cea02\n4 1d4f53\nec 2b5be7\n8d d2ba2\n88 296140\nd0 e7bbb\nb9 2996d7\n8f d2ba9\n3b 5f29\ncc 2b9d8e\ned eb58c\n18 2036\ncd ef733\nc8 2b2cd1\nf9 2b6268\ncf ef73a\n79 21863\n30 e18a\n38 e2e1\n2e 4336\n72 2ad22\n8 1ccfd7\na9 260bb8\n7a 2ae79\n21 44ba\n84 29cd88\n2d 197af2\nbc d7409\n23 44c1\n86 29cd8f\n2f 197af9\nd3 ee9a1\n8b 29cf26\ndf 281fd9\nf0 f2aee\nfc 286126\nf6 f3e4a\n32 4682f\n3e 1d9e67\nfe f3fa1\n2e 45e4\n72 2afd0\n8a ca7c7\nbb cdd5e\n1 8661\nd 19bc99\n50 1b0cb2\n3 8668\nf 19bca0\n3d cfef\n61 2104b\n6d 1b4683\n63 21052\n8c 10ad51\nc6 2b9920\n6f 1b468a\nca e7358\nfb ea8ef\n41 251f2\n4d 1b882a\n43 251f9\n4f 1b8831\n7d 29b80\n89 d184b\n3a 47a0a\nbe 110b32\na 19bc0c\n3b 19f1a3\n7d 1b3a82\n49 1f1ec5\n34 1a03a9\n30 cec8\n3c 1a0500\n72 29a60\n7e 1bd098\nc 3ae28\nad cea09\nf3 12548e\n21 44c8\nef 1236e1\n84 29cd96\n2d 197b00\nab 11019f\nd1 ee9a8\ndd 281fe0\n73 1ee585\ncf 127888\n1 866f\nd 19bca7\n53 1f272c\n18 a376\n1c a3a7\na6 298e94\n11 436f6\n39 e51e\nc3 edfec\ncf 281624\n53 6028e\n70 643db\n59 1b7e44\n7b 2b0b6\n9c d349e\n91 10c7ed\n98 d34cf\nda f0067\nd9 280f9d\nfb f420f\n56 6051a\n88 10af7c\ndc f002f\nbe 10776c\n21 3ee9e\n9e 10cbc7\n99 29db2e\nbb 110da0\n8 97d7\n42 1b83a6\n98 d346d\nb9 d7615\nfb f41ad\n10 8f5d\n1c 19c595\n11 1d58e4\n18 19c5c6\n38 3e3f1\n72 1ecfc0\n39 1a076e\n5a 1b915e\n2b 1d8222\n73 29c9d\n7f 1bd2d5\nda 280f93\nf9 27e0b8\n5f 26f40\nb 41e8d\n58 1ea4d7\n7b 1bd306\n9a 10cb96\n77 1ee552\n86 25b381\n29 1d821b\n71 29c96\n7d 1bd2ce\nd8 280f8c\n1a 1d5cef\n69 1bb688\n3b 1d9e97\n1a a37d\n19 19b2b3\n3b e525\n58 26f15\n5c 26f46\ne6 2b5a33\n51 60295\n79 2b0bd\n9a d34d6\nd8 f006e\nf9 f4216\ne2 1235ba\nee 2b6bf2\n9c 10cbce\n98 10cbff\n4f 1f09e1\nb9 110da7\nf6 27ccc8\n9a d3474\nd8 f000c\nf9 f41b4\n1a 19c5cd\n33 d10c\n3f 1a0744\nb9 261527\n1f a3af\n18 1cd946\n9a 264402\n3b 1a0775\n50 25afc\n5c 1b9134\n51 1f2483\n58 1b9165\ncd 277fff\n84 264926\n71 29ca4\n7d 1bd2dc\nd8 280f9a\n79 1bd30d\na5 268ace\n75 1ee559\n18 1d4718\na6 ce5fa\n84 25b388\nbd 2698a6\n10 4268d\n62 1ec6b1\nb1 d626e\n1c 1d5cc5\n18 1d5cf6\n98 29db2b\n3c e550\na6 298ef6\n31 4789f\n7a 2b0b7\n29 d981\n7e 2b0e8\n73 64437\nbc d7647\nb1 110996\nfe f41df\nc 42ec8\nad d6aa9\nf3 12d52e\nfa f4210\n7c 2b0e1\nb1 108892\ne6 2b5a87\n3a 47c48\n3e 47c79\nfc f41d8\nbe 110d70\n80 10c0e5\nb4 cdca2\nc9 11f7be\nba 110da1\n47 5e8f8\nb8 d7616\nfa f41ae\n8 42e97\na9 d6a78\n30 d106\n3c 1a073e\n31 1d9a8d\n38 1a076f\n72 29c9e\n7e 1bd2d6\n21 c568\n2d 19fba0\n73 1f6625\n7a 1bd307\nba 110d3f\n70 29c97\n7c 1bd2cf\n3a 1d9e98\n2e 3efc8\n54 1d7d1\nc 1cbd56\n3e 3f929\n1c 1cc6b7\neb 124c22\n48 1e01a\n1a 1cc6e1\n5e 1afb2d\nfb 12c60f\nc6 11f69e\n5a 1afb5e\nce 2b2d07\nc2 11f6cf\n86 25b31f\n56 1d7d8\ne 1cbd5d\n1e 1cc6be\n1a 1cc6ef\ne4 2b57e0\n3a e526\n91 cb039\n5a 1f15be\n7c 2b0ef\n47 1e84da\nb1 1088a0\ne6 2b5a95\n2d 3da52\n71 6443e\n59 26c5c\nf8 f4217\n3c 47c80\n32 d10d\n3e 1a0745\n33 1d9a94\n3a 1a0776\n78 1bd30e\n38 1d9e9f\n8e 102cba\n8c 102cc1\n9e 10361b\n9c 103622\n1e 1cc712\n1c 1cc719\nb1 2a18d0\neb 124c84\nce e6137\n9b 10c8eb\nde e6a98\n8e 102cc8\n9e 103629\n5e 1afb8f\nfb 12c671\nc6 11f700\ne 1cbdbf\n1e 1cc720\n6a 1b43a8\n18 a384\n5a 26f1c\n5e 26f4d\n53 6029c\n98 d34dd\n4f 1b72bf\nb9 d7685\n8d 10c014\nc7 2babe3\n1a 43aad\n1e 43ade\n9e 10cbd5\n9a 10cc06\nbb 110dae\nea 27d49f\n98 d347b\n4f 1b725d\nb9 d7623\nda f0013\na0 1102f0\ne9 1239c9\n1 42d35\n18 19c5d4\n31 d113\n5a 26cd0\n3d 1a074b\nf3 12c216\nff 2bf84e\nc2 128c7f\n98 264409\n52 25b03\n5e 1b913b\n53 1f248a\n5a 1b916c\ncf 278006\n86 26492d\n9a 10cba4\n12 42694\n1e 1d5ccc\n1a 1d5cfd\nbf 107513\n8e 103f7c\nf7 2b60e9\nbd 10751a\n8c 103f83\n3d 1d0611\n0 39a42\nc 1cd07a\n3c e55e\n92 d3071\n5c 1f15f6\n7 1cb949\na6 298f04\n31 478ad\n7a 2b0c5\n63 21050\n6f 1b4688\nc6 2b991e\n7e 2b0f6\n2f 3da59\n73 64445\n19 a0cb\n4e 1b72c0\nb8 d7686\nfe f41ed\naf 106b50\nf3 12d53c\n3a 47c56\n3e 47c87\nbe 110d7e\nfa f41bc\n38 1a077d\n72 29cac\n7e 1bd2e4\n2f 1cfc47\n73 1f6633\n7a 1bd315\nba 110d4d\nc4 12779b\n32 4683d\n3e 1d9e75\n3a 1d9ea6\nff ea990\nce e73f9\nbf 107521\n8e 103f8a\n3f 1d0618\n2 39a49\ne 1cd081\n52 1b8fa7\n18 a3d8\n73 1bd14f\n39 e580\nc3 ee04e\ncf 281686\n5a 26f70\nd6 2820cf\n9c d3500\nf6 123efa\nf7 286277\nbd d76a8\n62 5b787\n8a 10afe5\nde f0098\nac 106b48\nf0 12d534\nd8 121442\n79 2b111\n88 10afde\ndc f0091\n10 8fbf\n1c 19c5f7\na9 110446\n76 1ecff1\n52 25b57\na 1d40dc\n5e 1b918f\n2c 1cfc3f\n70 1f662b\n50 25b50\n8 1d40d5\n5c 1b9188\na6 260a96\n33 46890\n3f 1d9ec8\n1a a3df\n19 19b315\n3b e587\ndc f009f\nfd f4247\nd6 2bb7ff\n9c 10cc30\nf7 2bf9a7\nbd 110dd8\n50 25b5e\n5c 1b9196\nfa ebe6c\n71 29d06\n7d 1bd33e\n10 426ef\n1c 1d5d27\nba 1089fd\n31 46897\n3d 1d9ecf\n38 e581\na5 267aca\nc2 ee04f\nce 281687\n7a 2b119\nfe f4241\n78 2b112\n3a 47caa\n6e 1edd55\n62 5a71d\nbe 110dd2\n80 10c147\nc9 11f820\n30 d168\n3c 1a07a0\n72 29d00\n7e 1bd338\n70 29cf9\n7c 1bd331\n69 221c2\n3b 1d0889\n85 263921\ncf 128b5a\n39 1d0890\n2f 47317\n55 25b20\nd 1d40a5\nda e7cb7\n51 25b51\n5d 1b9189\n2b 47348\n9 1d40d6\n3f 47c78\n1d 1d4a06\n49 26369\n1b 1d4a30\n3b 47ca9\n19 1d4a37\n6f 1b3374\n88 103c34\n7f 1b3cd5\ne7 123846\n4b 1b754c\n7a 1ee685\n5f 1b7e7c\nc7 1279ed\n5b 1b7ead\n87 26366e\n73 219b1\nd6 2ba27f\n7f 1b4fe9\n2b 1cff36\n36 3e510\ndb 2ba416\n3f 1d0866\n3b 1d0897\n1b 1d4a3e\n79 22d63\nad 106e69\naf 2a260c\nf7 f4087\na3 10efd4\nd5 280e15\nbf 1077c3\n8f 10b009\n8d 10b010\n9f 10b96a\n9d 10b971\n2f 1cff59\n98 1045a3\ndf 2ba439\nbf d73a1\nfc 2be586\n2d 1cff60\n27 19667c\ndd 2ba440\nbd d73a8\n3f 1d08ba\n3d 1d08c1\nf 1d4100\n8a 102c35\nde e7ce8\nd 1d4107\n1f 1d4a61\n9a 103596\n1d 1d4a68\n3a e588\n38 47cb1\n70 29d07\n7c 1bd33f\n30 46898\n3c 1d9ed0\nbf 1077d1\nd0 120029\ndc 2b3661\n8f 10b017\n9f 10b978\n2f 1cff67\ndf 2ba447\n3f 1d08c8\n52 1b8fb5\n18 a3e6\n73 1bd15d\n39 e58e\nd6 2820dd\n9c d350e\nf7 286285\nbd d76b6\n62 5b795\nde f00a6\n1a 43b0f\n3b 47cb7\n9e 10cc37\nbf 110ddf\n10 8fcd\n1c 19c605\n52 25b65\n5e 1b919d\n12 426f6\n1e 1d5d2e\n2b 1d11ea\n71 22c6c\n29 1d11f1\n55 26de2\n1 41d2f\nd 1d5367\nb 1d5391\n39 466e0\n94 25cf39\n51 26e13\n9 1d5398\n8c 10acfd\n63 20ffe\n6f 1b4636\nca 2782f4\n4b 1b880e\nad 10812b\n8f 10c2cb\n8d 10c2d2\n23 3dbe3\n2f 1d121b\nd3 1280c3\ndf 2bb6fb\nf0 12c210\nfc 2bf848\n21 3dbea\n2d 1d1222\nd1 1280ca\n27 19793e\ndd 2bb702\n3 41d8a\nf 1d53c2\n3d 46711\n1 41d91\n8a 103ef7\nbb 10748e\nd 1d53c9\n38 e58f\n84 29cde8\n1d a0fc\nbc d76b7\n3a 47cb8\n30 d176\n3c 1a07ae\n72 29d0e\n7e 1bd346\n32 4689f\n3e 1d9ed7\nd0 1212eb\n8f 10c2d9\n23 3dbf1\n2f 1d1229\nd3 1280d1\ndf 2bb709\n0 1921fc\n3b 3f8fb\n20 1963a3\n1 1921fd\n21 1963a4\n8 192353\n28 1964fa\n9 192354\n29 1964fb\ne5 284401\n30 196d04\ned 284558\n38 196e5b\ncc 2b9d8c\n19 192cb5\n8 1938c5\n39 196e5c\n0 19a54a\n6 3acd8\n3b 47c49\n40 1b70db\n8 19a6a1\n48 1b7232\n9 19a6a2\n4a 1e25f\n10 19aeab\n58 1b7b93\ne7 1235ee\n19 19b003\na1 26784d\n0 1d3c6c\nb1 2681ae\neb eb562\n4a 57981\n10 1d45cd\nb9 268305\n18 1d4724\n0 1934be\n90 1046f8\nfa 2be85e\n20 197665\n1 1934bf\n8 193615\n28 1977bc\n9 193616\n29 1977bd\n49 1aeee5\n0 19b80c\n40 1b839d\n8 19b963\n48 1b84f4\n9 19b964\na1 268b0f\n49 1e8607\nef f262b\n0 1d4f2e\ne7 284406\n14 1c56\nef 28455d\n1c 1dad\n1e 1db4\n50 1e7b6\n52 1e7bd\n58 1e90d\n5a 1e914\n5e 1e945\n90 cad1c\neb f3602\n4a 5fa21\nf4 284cff\nf6 284d06\n94 cad4d\n96 cad54\n98 cae73\nfc 284e56\nfe 284e5d\n0 19a85a\n9c caea4\n21 46f3e\nd0 e78ad\n17 1d58ca\nd4 e78de\n99 2967f2\n13 423e9\n1f 1d5a21\n50 1e7c4\n54 1e7f5\n58 1e91b\n97 29565d\n5c 1e94c\n94 cad5b\n9c caeb2\nd4 e78ec\n50 1e818\n35 19829a\n52 1e81f\n93 295680\n58 1e96f\n3d 1983f1\n31 4db9\n5a 1e976\n90 cad7e\nce 128e07\nff 12c39e\nf4 284d61\ne2 12bb64\nee 2bf19c\n92 cad85\n29 19781f\n74 1ee2ac\n57 1ea15f\nf6 284d68\n98 caed5\nfc 284eb8\n9a caedc\n70 5adcb\n7c 1ee403\n53 56c7e\n5f 1ea2b6\n94 cad59\nfe 284ebf\nd0 e790f\n17 1d592c\nb5 261391\nd2 e7916\nd8 e7a66\n13 4244b\n1f 1d5a83\nbd 2614e8\nb1 cdeb0\nda e7a6d\n85 29e367\nef ea031\n4e 56450\n81 10ae86\n8d 29e4be\nff 27de42\n5e 1ea261\nf3 ea80a\n52 56c29\n91 29ec97\nea 27d74d\n95 29ecc8\nff ea992\ncb 128dd5\n5e 56db1\n91 10b7e7\n9d 29ee1f\n7f 1ee409\n42 5783a\n73 5add1\n28 3f000\n62 1edbcf\n46 5786b\n77 5ae02\n66 1edc00\nce 11f547\nd6 11fd51\nde 11fea8\nef 1236ef\ne4 27c0b2\nd4 281e88\neb 123720\nc6 120962\nf7 123ef9\n25 1d1059\n42 575de\nd 3ab6d\nac 108128\ne6 2b6cf7\nce 120ab9\nff 124050\n21 3db78\n2d 1d11b0\n4a 57735\nf4 27ca13\nea 2b6e7f\n2d 3da54\nd9 27899f\n90 2652c6\nf7 eaae9\n56 56f08\n6f 1edd56\n63 5a71e\nf8 27cb39\n67 5a74f\nb1 26946e\n77 5b0b0\nd6 11ffff\n1a 42531\n77 5c372\ncb 128b7b\n4 39a03\n96 25bc80\nf7 1241a7\n42 5788c\n25 3dbab\nb7 25fe28\ne3 eb16b\n42 5758a\nef eb2f3\n4e 57712\n63 5b732\ndb f0016\n52 1d509\n5e 1b0b41\na5 29a15e\nc2 1206e3\n31 196fb5\n5a 1b0b72\nce 120809\ne7 12485a\n73 216b1\n7f 1b4ce9\n48 5672a\nda 2789a7\nef 1249b1\ne4 27d374\neb 1249e2\ne3 eb419\n42 57838\ne7 eb44a\n7b 647da\n46 57869\n63 5b9e0\n52 1d7b7\n5e 1b0def\nc2 120991\n5a 1b0e20\n86 25c5e1\n8a 10acc7\ne7 124b08\n9c 10b65e\n73 2195f\n7f 1b4f97\nda 278c55\ne3 124b39\n98 10b68f\n7b 1b4fc8\n59 5809e\na7 260789\n6e 5a5f7\na1 10f02d\nad 2a2665\ndb 12822a\n7e 1ee408\n72 5add0\nb1 2a2e3e\nb5 2a2e6f\neb 12cf7c\n7e 5af58\nb1 10f98e\nbd 2a2fc6\n4f 1f1c4f\n43 5e617\n47 5e648\n7b 1b3c96\n32 1a05bd\n5f 1f25b0\n53 5ef78\n57 5efa9\n77 1f533e\n46 1f1da7\n7e 1b3a78\neb 285a9c\nea 12371f\n28 461c\n62 1b31eb\nf6 123ef8\ncf 127896\nc4 280259\ncb 1278c7\ne2 eb0fa\n65 1bc5cc\n54 5f251\n9 87c4\n43 1b7393\n66 5a74e\ndf 2bb9b9\nd3 128381\n76 5b0af\n4f 1f1efd\n43 5e8c5\nd8 280ce0\n47 5e8f6\n57 5f257\nf6 1241a6\nd7 12834e\ndf 2bb9b7\nd3 12837f\n5 41d52\n97 263fcf\n4f 2507f\n6a 5b888\nc6 279482\nf7 27ca19\n6e 5b8b9\n91 2642b3\ndb 1294ec\n43 5f8d9\n47 5f90a\nee 1249b0\n4 39a11\ncb 128b89\n54 60513\n9 9a86\n43 1b8655\n47 251d6\n62 5b9df\n66 5ba10\n8f 102c57\nd3 129643\n43 5fb87\nd8 281fa2\n47 5fbb8\nc7 128caf\n42 1e8518\n53 25b06\n5f 1b913e\nc3 128ce0\n5b 1b916f\nce 2b19e3\n87 264930\nd8 129730\n5a 1af84e\nef 2b6b9f\ne3 123567\n52 1e8e79\nd7 129610\n5a 1e8fd0\ndf 129767\na2 1102e9\neb 1239c2\nd4 28212a\n6f 1ec7e6\n6b 1ec817\n8d 10afa0\nef 2b6e4d\ne3 123815\n7b 1b3ca4\n7b 1f5706\ne7 27c376\n46 1e8795\nf7 27ccd7\n56 1e90f6\n52 1e9127\n77 1ed29e\n41 5e672\n4d 1f1caa\nca 1207d8\ne3 124829\n6b 294f7\n42 1e97da\n6b 1edad9\n45 1f1e01\nc2 12092f\ne3 124ad7\n7b 1f69c8\ne7 27d638\n46 1e9a57\n42 1e9a88\n6d 1f4b8f\nea 1236bd\ncf 2bad46\nc3 12770e\n5b 1b7b9d\n66 1ec68e\nde 280f72\n62 1ec6bf\ne7 12ce56\nda 280fa3\n6e 1ec7e5\n76 1ecfef\n7e 1ed146\n8c 10af9f\nff 12d90e\nf4 2862d1\n47 1f0836\n4b 1f09be\n57 1f1197\n5b 1f131f\n7a 1b3ca3\ncf 2baff4\nc3 1279bc\n5b 1b7e4b\ndf 2bb955\nd3 12831d\n66 1ec93c\n62 1ec96d\n76 1ed29d\n72 1ed2ce\n43 1f0b15\n53 1f1476\nc3 1289d0\n66 1ed950\nd2 eebfc\nde 282234\n47 1f1af8\n4b 1f1c80\n65 1f5fa8\nc7 ee2cd\ne2 124ad6\n7a 1b4f65\nf7 ea83b\n56 56c5a\nc3 128c7e\n66 1edbfe\n47 1f1da6\n43 1f1dd7\n50 1e826\n93 29568e\n58 1e97d\n90 cad8c\n55 1ea166\n21 45ed4\n2d 1d950c\n4a 5fa91\nf4 284d6f\n98 caee3\n51 56c85\n5d 1ea2bd\nfc 284ec6\nd0 e791d\nd8 e7a74\ne7 2846b4\n14 1f04\n16 1f0b\nf8 27cde5\n90 cafca\neb f38b0\n4a 5fccf\nf4 284fad\n57 1ea3ab\nf6 284fb4\n94 caffb\n96 cb002\n17 1d5b78\n50 1ea72\n54 1eaa3\n94 cb009\nca 281902\ndd efde6\n57 56c5b\neb 27d74e\n77 1ecff0\n46 1e9a59\n66 5a4a2\n6a 5a5c8\n6e 5a5f9\ne3 12ce27\n76 5ae03\n7a 5af29\neb 12cf7e\n7e 5af5a\nc7 11f3f1\n86 10be6f\ncf 11f548\nc4 277f0b\nee 2b6c02\ne2 1235ca\nea 123721\nfe 124051\n11 19c472\nfa 124082\n4f 1e9baf\n43 56577\ndd f0094\n5f 1ea510\n53 56ed8\n57 56f09\n6e 1edd57\n62 5a71f\n66 5a750\nc7 11f69f\ne6 123847\nee 2b6eb0\ne2 123878\nf6 1241a8\n6a 5b88a\n97 295403\n20 196651\n56 1e8e3a\nf7 27ca1b\nc7 1206b3\ncf 12080a\nc4 2791cd\ncb 12083b\ne6 12485b\nbc 25ffe5\ne2 12488c\nee 1249b2\na4 25f779\nc7 120961\nc3 120992\ne6 124b09\n1d 192cd8\nbc 260293\ne2 124b3a\n4a 5e76f\n4e 5e7a0\n5a 5f0d0\nce 127897\nd6 1280a1\nf9 1252da\n77 64414\nde 1281f8\n4e 1f1efe\n7f 1f5495\n42 5e8c6\ne3 12481d\nc6 1279ee\ne9 124c27\nf3 12517e\nd6 12834f\ne7 2856ca\n46 1f1ae9\nf9 125588\n77 646c2\n4a 5fa31\n0 19a7f8\ncf 2b29f8\nc3 11f3c0\n47 1e84e8\n2 1d4f97\n4b 1e8670\n57 1e8e49\n12 1d58f8\n5b 1e8fd1\n62 1ec6c1\nbd 2685e2\ne7 12ce58\n6e 1ec7e7\n6a 1ec818\n43 1b70d5\nf5 f2b74\nef 12cfaf\n72 1ed022\nf7 12d7b9\n7e 1ed148\n7a 1ed179\nff 12d910\ncf 2b2ca6\nc3 11f66e\n71 216fe\n7d 1b4d36\n53 1e9128\n66 1ec93e\n76 1ed29f\n72 1ed2d0\ncb 1207d9\nc0 27919c\nb8 25ffb4\n47 1e97aa\ne6 2843b3\n43 562c9\n4f 1e9901\nee 28450a\n4b 1e9932\nea 28453b\n66 1ed952\n62 5a471\n6e 1edaa9\nc3 120930\n19 192ca7\nb8 260262\n43 1e9a89\nca 127866\n7b 22dda\nde 2bb6a8\nd2 128070\nda 1281c7\n42 1f0868\n4e 1f098e\n75 1bbeb5\n4a 1f09bf\n56 1f1198\nca 2818f6\nfb 284e8d\n79 1ee3d1\n52 1f11c9\n5e 1f12ef\n5a 1f1320\nde 2bb956\nd2 12831e\n18 202a\neb 2847da\n69 1edd1e\n42 1f0b16\nfb 28513b\naa ce720\n88 25b4ae\n79 1ee67f\n52 1f1477\nca 128b28\n46 1f1af9\n24 196676\neb 2857ee\n42 5e618\n4e 1f1c50\n21 1d80c4\n75 1bd177\n4a 1f1c81\n39 1d05e2\n50 1eac6\n3b 1d05e9\n52 1eacd\n90 cb02c\nff 12c64c\n4a 5fd31\nf4 28500f\n92 cb033\n29 197acd\n74 1ee55a\n57 1ea40d\nf6 285016\n1f 42561\nb9 2996d9\nd0 e7bbd\n17 1d5bda\nbb 2996e0\nd2 e7bc4\n50 1ead4\nf4 eaa6f\nac 298ff4\n8f 294ea7\n52 1e8e0b\n18 3a23c\nbc 299955\n9f 295808\n73 1ecfb3\n8 3ae4d\n39 3e3e4\n28 1d11e2\n58 1ea4d9\n5f 26f42\n51 60297\nd 398ab\nac 106e66\ne6 2b5a35\n71 1f68da\n2d 1cfeee\n4a 56473\n59 603ee\nee 2b5b8c\n1d 3a20c\nbc 1077c7\nf6 2b6396\n3d 1d084f\n5a 56dd4\nfe 2b64ed\n0 1d3cd0\n54 1b8d83\n6b 5a61b\n6a 1edd7a\n83 25b34f\n94 10320d\ne6 2b6d4b\n5d 26c9b\nc7 2b1641\n4e 1e9c02\n42 565ca\n6f 1eddaa\n63 5a772\n7f 1ee70b\n73 5b0d3\nc0 ee2f4\ncc 28192c\n83 25b5fd\n94 1034bb\nd6 120053\nf7 1241fb\n51 1b8cf1\n0 1d4f92\n6b 5b8dd\n84 103b6e\n10 9c5\n1c 193ffd\nc6 120706\n35 196fd8\n52 1d55d\n5e 1b0b95\ne7 1248ae\n73 21705\n7f 1b4d3d\n21 3ee9c\nb3 261119\n63 5ba34\n84 103e1c\n10 c73\n1c 1942ab\nbb 10f840\na5 107fc4\n94 29d6e9\n31 4e1b\n3d 198453\nc6 1209b4\n52 1d80b\n5e 1b0e43\ne7 124b5c\nd6 2ba281\n8b 2637f4\n73 219b3\n9c 10b6b2\n7f 1b4feb\n3c 1d18c4\n9e c9be9\n30 3e28c\nb7 299858\n72 1ecfb2\n38 3e3e3\nbf 2999af\n6a 5a61a\n7e 1ee45c\n72 5ae24\n4b 5e7c2\n68 6290f\n5b 5f123\n78 63270\na3 25f4f6\nb4 1073b4\n95 10b55c\nd7 1280f4\na9 d57b4\nf4 12c241\nf7 2be489\nc6 2baef2\n7e 1ee70a\n72 5b0d2\n4f 1f1f51\n43 5e919\n6c 1f609e\n60 62a66\n53 1ea0da\n5f 1f28b2\n53 5f27a\n7c 1f69ff\n70 633c7\na3 25f7a4\nb4 107662\nf6 1241fa\n62 1ed913\n28 3ed44\nd5 278b19\na3 106cd8\naf 29a310\n50 1b8cf0\n4b 5fa84\n68 63bd1\n85 10bebd\n11 8d14\n9a cae7a\n1d 19c34c\nc7 128a55\n53 258ac\n5f 1b8ee4\ne4 12cba2\n8e ca7f8\nbf cdd8f\n20 3ee9b\nb2 261118\n1 43043\n93 2652c0\n40 1e87c1\n62 5ba33\n43 5fbdb\n60 63d28\n7 192289\n85 10c16b\n4 1cb9a3\n81 29e328\n14 1cc304\n89 29e47f\n1c 1cc45b\n25 1cfb4b\nb8 29ac38\n9d 26442f\n4 1ccf15\n35 1d04ac\n14 1cc5b2\n35 1d075a\nef 2bef43\ne3 12b90b\n3a 1d1846\n4 1ccc65\n0 39784\nc 1ccdbc\n21 3d92c\n2d 1d0f64\nbd 1077ca\nb 1cbd2b\n4 1ccf13\n52 26e1b\n3b 1d8937\n24 1cfb4a\n9c 26442e\n2c 1cfca1\na1 2a24cf\n34 1d04ab\na9 2a2626\n3c 1d0602\n82 102820\n8e 295e58\n5 1d3cf2\n8a 102977\nd 1d3e49\n9a 1032d8\n1d 1d47aa\n14 426be\n31 3f4ed\nb5 d629f\n66 1ec6e2\nde 280fc6\ne3 2bf067\n76 1ed043\na7 d6bf7\n1 1ccbd3\na0 29a18e\n85 263985\n24 1cfdf8\n34 1d0759\nbc 110b2b\na 1d508c\n1a 1d59ed\n31 3f79b\n66 1ec990\n76 1ed2f1\n86 c9131\n24 1d0e0c\nb4 107672\n2 1cbbd3\n90 d20b8\n9c 2656f0\n82 103ae2\n5 1d4fb4\n1 41ad3\n8a 103c39\nd 1d510b\nd2 eec50\nde 282288\n86 c93df\n24 1d10ba\n82 103d90\n5 1d5262\n66 1edc52\n5d 26f49\nc7 2b18ef\n48 5647a\n69 5a622\n68 1edd81\n40 575e5\nc7 2b2bb1\n48 5773c\nc3 11f6d0\ncf 2b2d08\n69 5b8e4\nc4 12070d\n50 1d564\n2a 3ed5b\n5c 1b0b9c\ne5 1248b5\n71 2170c\n7d 1b4d44\n7b 1b3a4a\n32 1a0371\nc4 1209bb\n2a 3f009\n50 1d812\n5c 1b0e4a\n88 10ad22\ne5 124b63\nbc 2685e3\n1d 19b028\n38 1d1831\n68 5a621\n7 1d4f59\nef 2b5bed\nf7 2b63f7\n78 5af82\n17 1d58ba\nff 2b654e\n49 5e7c9\nd5 1280fb\nf5 2be490\n5f 1d93e\nc4 2baef9\n68 5b8e3\ne3 123877\nef 2b6eaf\n49 5fa8b\nc5 128a5c\n44 1e9804\n40 56323\n4c 1e995b\n44 1e9ab2\n64 1ec6e9\nf9 27ddfc\nb 41bd1\naa 10f18c\ndc 280fcd\n6c 1ec840\n64 1ec997\n74 1ed2f8\nc6 e5cd0\n64 1ed9ab\nb 42e93\nd0 eec57\naa 11044e\ndc 28228f\nc6 e5f7e\n64 1edc59\n8e 294ba4\nca 11f57a\nf5 eaa70\nad 298ff5\n39 1d05d2\n8 1cd03b\nda 11fedb\nbd 299956\n2c 1d0f65\n20 3d92d\na7 298ef9\ne5 eb193\nb7 29985a\n38 3e3e5\ned eb2ea\nbf 2999b1\n6a 5a61c\n85 1028ad\n8d 102a04\nd3 278afd\na4 106a55\na3 25f4f8\n2 1cb917\nb4 1073b6\n21 1d93e6\ne6 1235ed\n21 47198\n29 1d953d\nee 123744\n29 472ef\n31 1d9d47\nf6 123f4e\n66 1f4cee\n31 47af9\n39 1d9e9e\nfe 1240a5\n6e 1f4e45\n39 47c50\na4 106d03\ne6 12389b\nf6 1241fc\n20 3ebef\na7 29a1bb\n28 3ed46\n50 1b8cf2\na3 106cda\naf 29a312\n3a 1d05d8\n85 103b6f\n8d 103cc6\nd3 279dbf\ne6 1248af\nbf cdd91\n20 3ee9d\nb2 26111a\n3a 1d0886\n85 103e1d\na4 107fc5\ne6 124b5d\n73 64443\n87 29d0a0\n8 41c2b\n7b 6459a\n8f 29d1f7\nc5 ef33a\n97 29da01\n18 4258c\ncd ef491\n9f 29db58\n84 10abfc\nf7 12d56b\n8c 10ad53\nff 12d6c2\nd2 280e4c\n77 1bd18e\nc6 127794\n61 1b4499\nce 1278eb\n69 1b45f0\nd6 1280f5\nde 12824c\n84 10aeaa\nf7 12d819\nc6 127a42\nd6 1283a3\n0 42d96\nb5 10f6b1\n87 29e362\n8 42eed\nbd 10f808\n83 10ae81\n8f 29e4b9\n25 1d93a9\n42 5f92e\nf7 12c249\n84 10bebe\nc6 128a56\n92 2652c1\nf7 12c4f7\n5 1cb9a4\n1d 1cc45c\n24 1cfb4c\na1 2a24d1\n34 1d04ad\na9 2a2628\n3c 1d0604\ne3 2bf069\n76 1ed045\neb 2bf1c0\n7e 1ed19c\n34 1d075b\n76 1ed2f3\n1 39785\nd 1ccdbd\nac 2679c6\n24 1d0e0e\n56 1b0c88\n2 1cbbd5\n24 1d10bc\n66 1edc54\n4 1d3cf3\n77 1f6662\nc 1d3e4a\n14 1d4654\n1c 1d47ab\n11 43696\nf9 12432a\nb0 110c51\n46 1f088b\n56 1f11ec\n5e 1f1343\nbe 29969e\n4 1d3fa1\n77 1f6910\n14 1d4902\n56 1f149a\n4 1d4fb5\n0 41ad4\nc 1d510c\n77 1f5392\n46 1f1dfb\n63 1bb28c\n29 c6bd\n74 6314a\n0 42da2\n49 5647b\na7 25f4c7\n10 43703\n59 56ddc\n79 1ed171\n48 1e9bda\n6c 1edb04\n60 5a4cc\n68 5a623\n78 5af84\n84 10beca\ncd 11f5a3\nb8 261518\ne4 1235f4\nec 12374b\n4d 1e9c0a\n41 565d2\nd3 27884f\nf2 27c9f7\ne4 1238a2\n60 5b78e\n68 5b8e5\n7a 1ed177\nc5 12070e\nac 2a1334\ncd 120865\ne4 1248b6\nb9 26126b\n9c 26443c\n18 1cd68a\n48 5e7ca\ncc 1278f2\n7d 1f54f0\n4c 1f1f59\nfa 12401e\n40 5e921\nd2 280b9e\n77 1bcee0\n5c 1f28ba\n50 5f282\nc4 127a49\n40 5f935\n48 5fa8c\nc4 128a5d\n8e 25b474\nd2 281e60\n64 1ec6eb\n6c 1ec842\ne1 2bf070\n74 1ed04c\ne9 2bf1c7\n7c 1ed1a3\n74 1ed2fa\n64 1ed9ad\n64 1edc5b\nd6 e6621\n8e 294ba6\n44 1f0892\n3a 1d9e44\n4c 1f09e9\n54 1f11f3\n5c 1f134a\na6 298c58\ne1 28467c\n54 1f14a1\n44 1f1b54\n40 5e673\n4c 1f1cab\n1c 90f5\n6e 1b3119\n39 5f24\nae cd6db\nf2 f40c7\n53 26b0c\nd6 2bb551\n9c 10c982\n73 22c83\n50 1ea0d4\nf6 f40f8\n57 26b3d\nfa f421e\n5b 26c63\n88 264d5c\n58 1ea22b\nfe f424f\n5f 26c94\n80 d1755\n8c 264d8d\nac 106bb8\ne6 2b5787\ne6 27c0b9\n94 d2095\nb1 ceec4\n21 19ea02\nbd cf04c\n4a 1b74db\nd8 12011e\nc6 280260\n91 d306b\nda 120125\n5d 1f15f7\n93 d3072\n52 1e7bb\na 1ccd40\n9b d31c9\n9d d31f3\nd6 eec2d\nf3 eba5c\n84 264996\ncd 27806f\nf5 eba86\n97 29d761\nf7 eba8d\n61 1bb593\nfd ebbdd\n9f 29d8b8\n63 1bb59a\nff ebbe4\nd3 efc03\nd5 efc2d\nd7 efc34\ndb efd5a\ndd efd84\ndf efd8b\n46 1b7177\n11 9f82\n15 9fb3\n4e 1b72ce\n19 a0d9\n83 294a7f\n1d a10a\n87 294ab0\n90 29ec88\n71 2296c\n79 22ac3\nac cd6e2\n51 26b13\nc6 27816a\n55 26b44\n59 26c6a\nce 2782c1\nc3 2b1610\nc6 28026e\n91 d3079\n95 d30aa\nce 2803c5\n99 d31d0\n9d d3201\nf1 eba63\nf5 eba94\nf9 ebbba\nfd ebbeb\nd1 efc0a\nd5 efc3b\nd9 efd61\n5f 26f50\n54 1b8d91\n6b 5a629\n30 6086\ne5 f3783\n6a 1edd88\n43 1b8645\nf5 f40e4\nd6 11fdb3\nc6 1209c4\nf7 123f5b\ne6 2b6d59\nd6 120061\nf7 124209\n25 1d1067\n42 575ec\n2d 1d11be\n21 3db86\n4a 57743\nf4 27ca21\n6b 5b8eb\n35 196fe6\n52 1d56b\n5e 1b0ba3\ne7 1248bc\n73 21713\n7f 1b4d4b\nfb 12d933\nc6 1209c2\n52 1d819\n5e 1b0e51\n8a 10ad29\ne7 124b6a\n1f 19b02f\n3a 1d1838\n4b 5e7d0\n5b 5f131\nf6 123f5a\nd7 128102\nf7 2be497\nc6 2baf00\n39 5f86\n58 57089\na6 25f774\n11 9fd6\n86 25b62d\n57 56c4d\nf6 124208\nd7 1283b0\n6a 5b8ea\n4b 5fa92\nc7 128a63\n53 258ba\n5f 1b8ef2\n73 229c7\n7b 22b1e\ne6 27c305\nac cd736\n51 26b67\n91 cafc9\nc6 2781be\nc7 128d11\n53 25b68\n5f 1b91a0\nbb 2695c0\n1a 1d59df\nae cd73d\n53 26b6e\n5b 26cc5\ne2 2b57b8\naa 106e2e\ndc 278c6f\nd6 eec8f\n52 57edd\nf3 ebabe\n19 3b4ab\n4e 1e86a0\n56 1e9158\n25 1cfda5\n42 5632a\n4e 1e9962\n31 3f4fb\n66 1ec6f0\n39 3f652\n6e 1ec847\n47 1f0898\n4f 1f09ef\n57 1f11f9\n5f 1f1350\n31 3f7a9\n66 1ec99e\n76 1ed2ff\n47 1f0b46\n57 1f14a7\n66 1ed9b2\nd2 eec5e\nde 282296\n47 1f1b5a\n43 5e679\n4f 1f1cb1\n66 1edc60\n47 1f1e08\n39 5f94\n11 9fe4\n19 a13b\n71 229ce\n79 22b25\nac cd744\nd 189\ne6 27c313\n51 26b75\nfb 28513d\n91 cafd7\n5a 1f155c\nc6 2781cc\nee 27c46a\n59 26ccc\n99 cb12e\nce 278323\n6b 63c39\nb 39881\naa 106e3c\ndc 278c7d\n18 3b4aa\nb9 cf08b\nf 42ed2\n50 57ee4\nf1 ebac5\n58 5803b\nf9 ebc1c\n66 1b3270\n31 607b\n15 a253\nbc 299709\n53 26dba\nfa 2b6270\n55 26de4\n1e 3b4d4\nbf cf0b5\nfc 2b629a\n50 1ea382\n57 26deb\nfe 2b62a1\ne6 27c367\nb1 cf172\n49 1f0c05\n6b 63e77\nb5 cf1a3\nca 127b66\nc6 28050e\n91 d3319\n93 d3320\nf3 ebd0a\nd3 efeb1\n67 1b4285\n15 a261\n51 26dc1\n55 26df2\nf1 ebd11\n3e 3f68b\nd1 efeb8\n11 a284\n89 264aaf\n71 22c6e\n9c 10c974\nd6 2bb543\n8b 264ab6\n73 22c75\n51 26e15\n53 26e1c\n52 5818b\nf3 ebd6c\n4a 1e98c3\n12 4370a\n5b 56de3\nd5 eff3d\n7b 1ed178\n4a 1e9be1\n6a 5a62a\n80 d16f3\n8c 264d2b\n7a 5af8b\nc7 11f453\nb2 2613c8\n86 10bed1\ncf 11f5aa\nba 26151f\nd7 11fdb4\n96 10c832\ndf 11ff0b\ne6 1235fb\nee 123752\nf6 123f5c\nfe 1240b3\n82 102884\n8e 295ebc\nd7 120062\ne6 1238a9\nf6 12420a\nc7 120715\nae 2a133b\ncf 12086c\ne6 1248bd\n62 5ba43\nc7 1209c3\nbb 261272\n1a 1cd691\nce 1278f9\n69 1b45fe\nd6 128103\nf9 12533c\nc 19a982\n29 1977b1\ne3 12487f\nc6 127a50\ne9 124c89\na1 cd2a1\nad 2608d9\nf3 1251e0\nd6 1283b1\n46 1f1b4b\nf9 1255ea\n25 1d93b7\n42 5f93c\nc6 128a64\n47 1e854a\n57 1e8eab\n16 1d5929\n5f 1e9002\n97 2642df\nb2 29aae8\n31 3f4fd\n66 1ec6f2\ne3 2bf077\n76 1ed053\neb 2bf1ce\n84 263912\n7e 1ed1aa\n57 1e9159\n76 1ed301\n47 1e980c\nb1 d7220\ne6 284415\n43 5632b\n4f 1e9963\nb9 d7377\nee 28456c\n66 1ed9b4\n47 1e9aba\n47 1b7108\n62 1ed911\nb1 d74ce\ne6 2846c3\n66 1edc62\n56 1f11fa\n79 1ee433\n69 1edd80\n46 1f1b5b\n77 1f53a0\n46 1f1e09\ndf 1294ab\n11 a292\nd 437\n51 26e23\nb9 ce069\n18 3a488\n29 3dccf\neb ea2a0\na2 d6bc7\n4a 566bf\na6 d6bf8\n0 1ccbd4\nef ea2d1\n4e 566f0\n98 26540f\n0 1d3f1c\n54 1b8fcf\n6b 5a867\n31 1d16dd\n14 1d48ae\n7f 5b1f9\nc1 127769\ncd 2bada1\n6 41d56\na7 d5937\na0 298ece\nf4 27df81\n88 102c80\n2d 3efc2\n5 19bb42\n82 ca670\nb3 cdc07\nbf 26123f\n9c 1035b0\n98 1035e1\n3d 3f923\na9 106e28\n96 2953b0\nbd 107758\nb9 107789\n62 22001\n40 1aed8f\n3d 19719d\nce 11f7e7\nca 11f818\n35 1d87a6\n4 1d520f\n6f 5bb5a\nda 120179\n8e c92ea\n14 1d5b70\n7f 5c4bb\n84 29d044\nef 12398f\n94 29d9a5\nff 1242f0\n90 29d9d6\nfb 124321\na9 ce9ca\n8 3ade9\nad ce9fb\nc 3ae1a\n15 19af31\n92 c9a5f\n9e 25d097\nb5 2681df\n14 1d45fe\nef eb593\n4e 579b2\n73 1b4bc3\nad 1080b9\n28 3dcce\n38 3e62f\nce 2b1735\n4b 5ea0e\nc7 e5fdf\n5b 5f36f\n39 1d05d4\na8 106e27\nbc 107757\nb8 107788\n53 1b7aa8\n8d 10af9e\na1 2a121d\nf5 2862d0\n89 10afcf\n99 10b930\ncb 127b67\ne8 12bcb4\ndb 1284c8\nf8 12c615\nd 8787\n28 3ef90\n2c 3efc1\n35 19f0d8\n4 19bb41\nb2 cdc06\nbe 26123e\n99 10cbf4\n9 43138\nd 43169\n93 d1dae\n9f 2653e6\n4b 5fcd0\ne9 2b6e79\n4f 5fd01\n72 1b4bc2\nac 1080b8\n1c 1d4a67\n39 1d1896\ncb 128e29\n88 102c1e\naf 268f3e\ne 1d535d\n2 41d25\na3 d5906\nf0 27df50\n98 10357f\na9 106dc6\nb9 107727\n39 19716c\nca 11f7b6\nd4 2820c6\neb 12395e\nfb 1242bf\n18 1cc6d8\n7b 1ed418\n88 103ee0\n8 1cd039\nda 11fed9\nbd 299954\n42 56568\nef 27d781\ne3 ea149\n4e 1e9ba0\ne2 2bdaf8\n6b 1edd79\na8 106dc5\nb8 107726\n89 10af6d\nb6 29a87b\nf1 28629f\nea 12395d\nb6 d5fe9\ncb 127b05\ndb 128466\n28 1cff1e\n38 1d087f\ncc 128b50\n9 1d40c6\n19 1d4a27\n6e 1eca85\n6a 1ecab6\n7a 1ed417\n4b 1f0c5e\n2b dbc6\n68 1f4dab\n5b 1f15bf\n67 22033\n78 1f570c\n8d d187e\na8 108087\nb 19234d\n1c 3a20b\nbd cddec\n89 10c22f\nff ea984\n5e 56da3\nb6 d72ab\ncb 128dc7\nd 19a9d7\n8a c9505\n28 1d11e0\n9 1d5388\n62 5a70f\n6e 1edd47\n4f 1b756f\n6a 1edd78\n4b 1f1f20\n68 1f606d\n5 9644\na4 d6bff\ned ea2d8\n4c 566f7\n2f 1d8005\nde 278974\nfd eac39\n4b 1af19a\nb4 d7560\n15 9fa5\n5c 57058\ndc e7d43\n88 102c90\n67 1f5fa3\n69 5a86e\n95 d309c\ndc 12014f\n1 42d33\ne9 1239c7\nfd 1242f7\nda 279c59\nbd 25ff78\ned 124c58\n21 d7ba\n68 5a86d\n25 d7eb\n6c 5a89e\n61 1b424d\n44 1b741e\nfe 27cb1b\nd9 1284d1\n66 1bb310\n31 e11b\n78 5b1ce\n49 5ea15\n4d 5ea46\n30 1d9a9a\n79 1ed173\nc9 127b6e\n8d 2637be\nd9 1284cf\n9d 26411f\n4d 25326\n68 5bb2f\nf5 27ccc0\n6c 5bb60\nf2 ea7a5\n75 1bbc77\n44 1b86e0\nfe 27dddd\nd9 129793\n49 5fcd7\n4d 5fd08\nd3 ee94d\ndf 281f85\nec 124c57\n79 1ee435\n4b 1aef4e\nc9 128e30\n81 d1448\n8d 264a80\ne9 123965\n4b 56412\n2 42d39\nea 1239cd\na3 d691a\n99 d346e\nce 280663\n4a 1cfad\ne0 2bdaff\nb4 d5ff0\nc9 127b0c\nd9 12846d\n4d 1b7576\nca e60a4\n68 1edd7f\n28 3dcd0\n38 3e631\n6 43018\na0 29a190\na7 d6bf9\n4f 566f1\n99 265410\nb3 d7529\n12 43948\n5b 57021\n6a 5a868\n63 2a350\n41 1b70de\nd1 12963c\n8d 102c50\na1 298ecf\n7 41d57\nf5 27df82\n89 102c81\n9d 1035b1\n99 1035e2\nac 106df8\na8 106e29\nbc 107759\nb8 10778a\n86 10c10f\ncf 11f7e8\n82 10c140\ncb 11f819\ne8 123966\n96 10ca70\ndf 120149\n92 10caa1\ndb 12017a\nf8 1242c7\nee 123990\ne7 f3478\nc5 280206\nea 1239c1\ne3 f34a9\nc1 280237\n28 3ef92\n2c 3efc3\n5b 58035\ndf eede7\nfa 1255f0\n35 19f0da\nb2 cdc08\nbe 261240\n30 1d8776\n6a 5bb2a\n41 1b83a0\n8d 103f12\n89 103f43\nac 1080ba\n95 29d6f6\ncf 120aaa\n91 29d727\ncb 120adb\ne8 124c28\nb2 107338\nbe 29a970\n35 1d880a\nc5 2814c8\n4a 5ea0f\n4e 5ea40\n5e 5f3a1\n88 10afd0\nfb 12d93f\n2d 47312\n9c 10b900\n98 10b931\n3d 47c73\n3d 19f4ed\nce 127b37\nca 127b68\n6f 63eaa\n3d 46701\nc 4316a\nda 129797\n92 d1daf\n9e 2653e7\n5f 1b7c30\n7a 1ee439\n89 102c1f\nf 1d535e\n3 41d26\nf1 27df51\n99 103580\na8 106dc7\nb8 107728\n82 10c0de\nb6 cdc9b\ncb 11f7b7\n92 10ca3f\ndb 120118\nea 12395f\n19 1cc6d9\n12 1d5b98\n5b 1e9271\n3b 61d9\n6e 1eca87\n7a 1ed419\nc4 2804b1\n89 103ee1\na8 108089\nb6 cef5d\ncb 120a79\nea 124c21\n37 1d8803\n9 1cd03a\na8 267c43\n88 10af6e\nfb 12d8dd\nf0 2862a0\n7b 1f5458\n46 1e84e7\ne7 27c0c8\n98 10b8cf\n56 1e8e48\nf7 27ca29\n39 19f4bc\nca 127b06\n8 1d40c7\n7b 1f6a36\na7 2a21f7\n18 1d4a28\nb7 2a2b58\n4e 1f0c2e\n4a 1f0c5f\n5a 1f15c0\n7b 1f671a\n46 1e97a9\ne7 27d38a\n40 1e9a81\ndf 278975\n47 1b716a\n62 1ed973\nb1 d7530\n10 4394f\n59 57028\nb5 d7561\n14 43980\n66 1ed9a4\n5d 57059\n68 5a86f\n2 43049\n4b 56722\n6c 5a8a0\nfe 27cb1d\n78 5b1d0\n12 439aa\n5b 57083\n3b 19f1a5\n84 10c116\ncd 11f7ef\nec 123997\n86 10c171\ncf 11f84a\n0 42d34\ne8 1239c8\nfc 1242f8\n96 10cad2\ndf 1201ab\nc9 120ae2\nec 124c59\n95 29d758\ncf 120b0c\n48 5ea16\n4c 5ea47\nde 280cc4\nc8 127b6f\n6d 63eb1\nd8 1284d0\n7d 64812\n79 6326f\n48 5fcd8\naf 2a2660\na3 10f028\nd5 280e69\n49 1e9bd9\nc8 127b0d\nd8 12846e\n12 3b34e\n2b 19e8a4\n76 1f5331\n16 3b37f\n1a 3b4a5\nb5 cef03\n7e 1f5488\n1e 3b4d6\nab 26799b\nf6 2be428\n96 104476\n94 10447d\n6b 1bc943\nfe 2be57f\n2 1d3f83\n9e 1045cd\n9c 1045d4\n52 1e7cb\n56 1e7fc\n5a 1e922\n5e 1e953\n12 3b35c\n76 1f533f\n1a 3b4b3\n91 295679\n7e 1f5496\nd6 e78f3\n15 1d5925\n92 104453\nf6 2be436\n11 42444\n1d 1d5a7c\n9a 1045aa\nfe 2be58d\n10 3b3b7\n74 1f539a\n18 3b50e\n7c 1f54f1\n90 1044ae\n5e 1d93f\nf4 2be491\n98 104605\nfc 2be5e8\neb ea2ae\n3 961a\n4a 566cd\nef ea2df\n7 964b\n0 1ccbe2\n4e 566fe\n98 26541d\n13 9f7b\nfb eac0f\n5a 5702e\n10 1cd543\nff eac40\n17 9fac\n5e 5705f\n87 d2742\nce 11f7f5\n97 d30a3\nde 120156\n62 63ad5\nef 12399d\ne4 27c360\nff 1242fe\n10 1d462f\n4a 579e3\nf4 27ccc1\n13 4369b\nfb 12432f\n20 1d108b\nbf 25ff7f\n2d 3dd02\nef eb5a1\n4e 579c0\neb 124c90\na3 cd2a8\naf 2608e0\n23 d7c1\n6a 5a874\n27 d7f2\n20 1d0d89\n6e 5a8a5\ndb 1284d8\n33 e122\n7a 5b1d5\n30 1d16ea\n37 e153\n7e 5b206\na7 d68e9\nee 12399c\n32 1d9aa1\n7b 1ed17a\nb7 d724a\nfe 1242fd\ncf 127b44\nc4 280507\ncb 127b75\ne2 eb3a8\n65 1bc87a\ndf 1284a5\na2 10f027\nae 2a265f\nd4 280e68\ndb 1284d6\nf2 ebd09\n75 1bd1db\nd 41ea9\n6e 5bb67\ndb 12979a\ncb 128e37\n21 1d912a\n83 d144f\n8f 264a87\n33 3f504\n6f 1eca94\n7f 1ed3f5\n6d 1f4e3d\na3 d68b8\nea 12396b\nb3 d7219\n7d 1f579e\nfa 1242cc\n27 19f9e0\n6e 1eca93\n37 1a0341\n7e 1ed3f4\n4b 1f0c6c\n4b 1f1f2e\n35 1982a8\n52 1e82d\n12 3b3be\n76 1f53a1\n1a 3b515\n7e 1f54f8\nb5 26139f\nd2 e7924\nbd 2614f6\nb1 cdebe\nda e7a7b\n92 1044b5\nf6 2be498\n9a 10460c\nfe 2be5ef\n12 3b5fc\n2b 19eb52\n76 1f55df\n16 3b62d\nab 267c49\nf6 2be6d6\n96 104724\na4 298bdf\n52 1ea79\n56 1eaaa\n12 3b60a\n76 1f55ed\nd6 e7ba1\n15 1d5bd3\n92 104701\n1d 4255a\nf6 2be6e4\n96 104732\n10 3b665\n74 1f5648\n6a 5a876\n6e 5a8a7\nb8 2695c6\n7a 5b1d7\n7e 5b208\n86 10c11d\ncf 11f7f6\nc4 2781b9\nee 12399e\n2 42d3b\nea 1239cf\nfe 1242ff\n12 4369c\nfa 124330\n6e 5bb69\ncb 120ae9\n4a 5ea1d\n6b 5b87d\n4e 5ea4e\n5a 5f37e\n7b 5c1de\n5e 5f3af\neb 124974\nce 127b45\nca 127b76\n6f 63eb8\nda 1284d7\n7f 64819\n7f 632a7\n4e 5fd10\n6e 1eca95\n7e 1ed3f6\n4b 1e9be0\nca 127b14\n75 29a1b\n45 1e9805\nda 128475\n4a 1f0c6d\n7b 1ee3cc\n5e 1f159d\n12 3b66c\n76 1f564f\nd2 e7bd2\n92 104763\n52 1e90b9\n18 3a4ea\n73 1ed261\n39 3e692\n86 29e0bf\ncf 2b1798\n4a 56721\n5a 57082\n10 1d48df\n7b 5b22a\nc6 2b1880\n8c 102cb1\n80 295d2d\nce 11f849\n90 29668e\nde 1201aa\n63 1edbc2\n29 3eff3\n16 1cd57b\n51 1b8f9f\nbb 261270\n31 1d87d7\n0 1d5240\n6b 5bb8b\na3 110286\n36 3e262\nd5 2820c7\n72 1ed260\n38 3e691\n99 1045a2\nce 2b1797\n53 1f1408\n19 42839\n4b 5ea70\n68 62bbd\n5b 1ea231\n5b 5f3d1\n78 6351e\ne6 2b5a27\nac 106e58\nd4 280e04\nab 25f8fb\nf6 2b6388\nbc 1077b9\n91 103189\n9d 2967c1\na0 299ed4\nee 1239f0\nc5 280266\n9b 265417\n4b 5fd32\n68 63e7f\n1c 1cc709\n3d 1d08b1\n0 39a32\nc 1cd06a\n21 3dbda\n2d 1d1212\n2c 1cff4f\n3c 1d08b0\n8a 102c25\nd 1d40f7\n9a 103586\n1d 1d4a58\n39 3f8f2\n6e 1ecae7\n7e 1ed448\n20 3dbd9\n8e c9536\n2c 1d1211\n3d 1d0851\n5a 56dd6\n8a 103ee7\n1 41d81\nd 1d53b9\n62 5a771\n6e 1edda9\n85 d279d\n3a 19f206\ncc 11f850\n5 42d64\ned 1239f8\n15 436c5\nfd 124359\ned 124cba\neb 27c49a\nb5 d72a5\n14 436c4\n3 19b806\nfc 124358\n40 565d1\n4c 1e9c09\na2 ce569\n25 19fa3b\n6c 1ecaee\nb2 ceeca\n35 1a039c\n7c 1ed44f\nce e60d5\n60 5a778\n6c 1eddb0\n19 3a4eb\n38 3e693\n8d 102cb2\n9d 103613\nee 1239f2\ne7 f34da\ne0 2b6a71\nc5 280268\nfe 124353\naf 2a23c0\nf0 2b73d2\na3 10ed88\nf7 f3e3b\nd5 280bc9\n28 3eff4\n50 1b8fa0\nba 261271\n8d 103f74\n4a 5ea71\n5a 5f3d2\n8c 10b001\nff 12d970\nce 127b99\n23 1d90dd\nde 1284fa\n33 1d9a3e\n9a 265418\nbd 10fab6\nff 12c64e\n1d 1cc70a\n3c 1d08b2\n7e 1ed44a\n1 39a33\nd 1cd06b\nac 267c74\n20 3dbdb\n2c 1d1213\n62 5a773\n6e 1eddab\nc 1d40f8\n1c 1d4a59\n5e 1f15f1\n7f 1f54e9\n42 5e91a\n4e 1f1f52\n6b 1bb3e3\n7c 632a1\n0 43050\n49 56729\ndb 2789a6\n68 5a8d1\nfa 27cb4e\n84 10c178\n3b 19f207\ncd 11f851\n94 10cad9\ndd 1201b2\n4 42d65\nec 1239f9\n68 5bb93\nfa 27de10\n3b 1a04c9\ncd 120b13\n48 5ea78\ne7 12cba8\nda 280cf5\ncc 127ba0\nda 281fb7\n7c 1ed451\n60 5a77a\n6c 1eddb2\n4c 1f0c97\n5c 1f15f8\na6 298f06\n1e 4281e\n3b 3f64d\n6e 1ec849\n39 3f654\n1a 4252f\nbb d6110\nfb 124331\nb2 110c58\n13 4369d\n46 1f0899\n11 436a4\ne9 f35a5\n86 294cfb\nba 110daf\n1b 437f4\n4e 1f09f0\n19 437fb\n8e 294e52\nbe 110de0\n1f 43825\n1d 4382c\n96 10b7be\nb3 1085ed\n8d 294c00\nde 2b2336\nb7 10861e\nb5 108625\n8 41ee5\n42 1f0ab4\n9e 10b915\nbb 108744\nee 2b5940\nb9 10874b\n23 1d812b\nbf 108775\nbd 10877c\n4a 1f0c0b\nc6 2b9990\n91 10c79b\n97 10c7c5\n9f 10c91c\n9d 10c923\n73 22973\n1 1934b1\na0 260a6c\n7b 22aca\n9 193608\na8 260bc3\n53 26b1a\n57 26b4b\ncd 27831d\n84 264c44\n5b 26c71\n88 264d6a\n3b 3f65b\n13 436ab\n17 436dc\nc8 2bb01f\nf9 2be5b6\n1b 43802\n1f 43833\nf3 eba6a\nf7 eba9b\nfb ebbc1\nff ebbf2\nd3 efc11\nd7 efc42\ndb efd68\nb3 1085fb\n59 5f378\n62 1f5cc3\n97 265291\nde 2b2344\nb7 10862c\n5d 5f3a9\n66 1f5cf4\nbb 108752\n6a 1f5e1a\nbf 108783\n93 10c7a2\n97 10c7d3\n9b 10c8f9\n9f 10c92a\n39 3f6b6\n1b 43856\nf6 eaa76\nae 298ffb\n19 4385d\n8e 294eb4\n9e 10b977\nbb 1087a6\nb9 1087ad\n87 d27a4\n80 295d3b\nce 11f857\na1 299ee3\n7 42d6b\nef 1239ff\nb1 29a844\n17 436cc\nff 124360\nef 124cc1\na7 d694b\n4f 56443\n6 42d6a\na0 299ee2\nee 1239fe\n81 29e08a\ncf 127ba6\n91 29e9eb\ndf 128507\ncf 128e68\n33 3f566\n42 565d8\n4e 1e9c10\n39 3f900\n27 19fa42\n6e 1ecaf5\n4f 1f0c9d\n62 5a77f\n6e 1eddb7\n43 5e927\n4f 1f1f5f\n73 229d5\n7b 22b2c\nf 190\n53 26b7c\n5b 26cd3\n3b 3f6bd\n13 4370d\n1b 43864\nb3 10865d\n97 2652f3\nde 2b23a6\nbb 1087b4\n51 604e3\n13 4394b\n63 1ed976\n46 1f0b47\n11 43952\n11 1d5ba0\nd6 11fda7\n17 4397c\nb3 10889b\n8d 294eae\nb7 1088cc\n93 10ca42\nc5 11f6fa\n33 19f0b0\n9c 10c920\n73 22c21\n57 26df9\n33 3f7b2\n13 43959\n17 4398a\nf9 2be864\nf3 ebd18\nd3 efebf\nb3 1088a9\n62 1f5f71\n7f 1f67ab\n42 5fbdc\n73 63173\nb7 1088da\n66 1f5fa2\n93 10ca50\n13 439ad\n11 439b4\nb3 1088fd\n2 43057\n4b 56730\n6a 5a8d8\n86 10c17f\ncf 11f858\n96 10cae0\ndf 1201b9\n6 42d6c\nee 123a00\n16 436cd\nfe 124361\n6a 5bb9a\ncf 120b1a\n4a 5ea7f\neb 1249d6\nce 127ba7\nfb 125337\nde 128508\n16 1d5bd7\n5f 1e92b0\n7e 1ed458\n43 565d9\n4f 1e9c11\n62 5a781\n6e 1eddb9\n7f 1f54f7\n42 5e928\n4e 1f1f60\n33 3f814\n13 439bb\nb3 10890b\n95 10c7be\n94 d20f7\nb1 cef26\n10 3b345\n97 296911\nb5 cef57\n3 1934b8\n14 3b376\n9c d224e\nb9 cf07d\n18 3b49c\n93 103430\n9f 296a68\nbd cf0ae\nb 19360f\n1c 3b4cd\nf7 ebaef\n56 57f0e\nfb ebc15\nde eede6\n5a 58034\nff ebc46\n5e 58065\n94 10446d\n90 10449e\n9c 1045c4\n98 1045f5\n10 1940c1\nb5 108615\ne3 284685\n42 1f0aa4\nbd 10876c\neb 2847dc\n81 ca676\n4a 1f0bfb\nd6 121005\n10 1f43\nde 12115c\n18 208a\n52 1b0c59\nf7 1251ad\n31 60eb\n94 29e9b9\nff 125304\nb1 cf1d4\n10 3b5f3\nb5 cf205\n3 193766\n14 3b624\n94 10471b\n90 10474c\nb5 1088c3\nb1 1088f4\nf7 12545b\n23 19765f\n34 3f51d\n1d 8e3a\nbc d63f5\n38 3f643\nb3 1075d7\nbf 29ac0f\n2b 1977b6\n3c 3f674\n46 1f0889\nf9 124328\n11 43694\n4e 1f09e0\n85 ca45b\n19 437eb\n9 1923b6\n54 1e8e43\n76 5c0b5\n25 1cfae9\n9d d3263\nc7 e6ff3\n5b 60383\nb4 108614\n95 d1e3c\nb0 108645\nbc 10876b\n99 10c944\n23 19790d\nd9 2bb6d1\n34 3f7cb\n46 1f0b37\n11 43942\n15 43973\n25 1cfd97\n42 5631c\n4e 1e9954\n9d d3511\nf1 2b7683\n57 6050b\nb4 1088c2\n95 d20ea\nb0 1088f3\n13 192bb9\n91 10ca9b\n90 10443c\n98 104593\n94 10b7b5\ne6 2b57d9\nb1 1085e4\nea 2b6bc1\n98 10cb9d\ncf 2803b8\nda 12112b\nb5 261145\n14 1cd564\n10 1cd595\nbd 26129c\n10 3a083\nb1 cdc64\n1c 1cd6bb\n18 1cd6ec\nf7 27dcdd\n56 1ea0fc\nff 27de34\n52 56c1b\nf3 ea7fc\n5e 1ea253\n31 1d06c7\n5a 1ea284\n90 1046ea\nb5 2613f3\n14 1cd812\n10 1cd843\n31 1d19eb\n95 d1dda\nb0 1085e3\n99 d31c2\nce 2803b7\n9d d1f31\nb8 10873a\ndf eeac9\nfa 1252d2\n6f 21184\n21 197668\n34 1d170b\n9e 25d099\n15 19af33\n92 c9a61\n30 1d173c\n30 3e22a\n3c 1d1862\n76 1ee2a3\n72 5adc2\n7e 1ee3fa\n85 264be5\n5b 1f25d3\n78 1f6720\nc6 2b9c2e\n13 192b57\n91 10ca39\n15 1d5b61\n11 1d5b92\nf5 1251b4\nfd 12530b\n98 10b621\nf5 125462\n6b 211a7\n0 19a85c\nce 2b9a75\n55 25882\n2f 47079\n70 5c08b\nf7 2b7657\n84 294ce8\n5d 259d9\n78 5c1e2\nf3 124176\nff 2b77ae\n51 60233\n70 1b4e59\n2f 19fe47\n23 c80f\nf4 1251b3\n2f 47327\n55 25b30\n70 5c339\nf4 125461\nb4 2610e2\n53 1af758\nd1 12963a\nf1 125183\n79 29e51\nf1 125431\nf5 27df92\n7 41d67\n54 1ea3b1\nd6 280e6d\n71 1ee58a\n2a 19fb05\ndd eead0\nf8 1252d9\n53 1b0c58\n59 1f25da\na3 299ee8\nd5 eec27\naf 11041e\nf0 125430\n30 3f4ee\n38 3f645\n2b 562a\n76 5c0b7\n95 10446e\n9d 1045c5\n99 1045f6\nb4 108616\nb0 108647\nbc 10876d\nb8 10879e\nd7 121006\ndf 12115d\ndb 12118e\nf8 1252db\nab ce721\nf6 1251ae\n30 3f79c\nd9 2bb6d3\n23 19790f\n34 3f7cd\n95 10471c\nb4 1088c4\nb0 1088f5\nd7 1212b4\nf8 124329\n10 43695\n84 ca45c\n18 437ec\nb 97d1\n56 6025e\nc6 e6ff4\n5a 60384\n10 19b14b\n90 10c7ee\n98 10c945\n8b d28c8\nd6 129355\nde 1294ac\nfb 2bfb2f\n94 264273\n10 43943\n3 19bab6\n14 43974\nb 9a7f\n56 6050c\n16 192b89\n94 10ca6b\n12 192bba\n90 10ca9c\nc6 2b1632\n91 10443d\nce 2b1789\n99 104594\nb0 1085e5\nb8 10873c\ndb 12112c\nfa 1252d4\nbc 2682c5\n19 1cd6ed\n27 45f0c\nb8 2682f6\n34 1d170d\n30 1d173e\n30 3e22c\n3c 1d1864\n38 1d1895\n5b 1ea285\n90 cad28\nfa 284e8e\n7f 2ae3b\n78 1ee3d2\n2b 197818\n76 1ee2a5\n72 5adc4\n7e 1ee3fc\nc6 2b18e0\n91 1046eb\nb0 108893\n34 1d19bb\n30 1d19ec\n90 10c78c\n98 10c8e3\nda 12947b\n10 1d58e5\n18 1d5a3c\nb 19b9bf\n56 1f244c\n52 5ef6b\n5e 1f25a3\n12 192b58\n90 10ca3a\n14 1d5b62\n10 1d5b93\nd9 121195\na9 ce728\nf4 1251b5\n60 222b6\nd7 121068\nfc 12530c\n68 2240d\ndf 1211bf\nf8 12533d\na9 ce9d6\nf4 125463\nd7 121316\nf0 125494\nad cd429\n50 60234\n89 d28cf\nd4 12935c\n8c 1029a1\nd0 12938d\ndc 1294b3\n22 5720\nd8 1294e4\n52 1af759\n8c 102c4f\nd0 12963b\n59 1ea28c\nf8 284e95\n78 1ee434\n70 1ee58b\n2f 46057\nd0 12932b\ndc e6a81\n67 1f4ce1\nd8 129482\n5 3acd2\n6f 1f4e38\n9 19b9c6\n54 1f2453\nf9 286148\n50 5ef72\n5c 1f25aa\na6 299eb8\n58 1f25db\na2 299ee9\nce e5e7b\n9 19bc74\n54 1f2701\nb2 10739a\nbe 29a9d2\nf9 2863f6\n14 193e44\n50 581f4\n16 193e4b\n52 581fb\n22 3eb94\n54 1b09d5\n52 1b0a0d\n8d 29ceec\n75 5b0ab\n2a 3eceb\n50 1d4f4\n5c 1b0b2c\n58 1b0b5d\n14 193e52\n10 193e83\n22 3eba2\n54 1b09e3\n94 cadaf\n96 cadb6\n9c caf06\n9e caf0d\nd4 e7940\nd6 e7947\n16 193ead\n56 1b0a3e\n50 1d556\n2a 3ed4d\n5c 1b0b8e\nf3 ebacc\n52 57eeb\nfb ebc23\n5a 58042\nd6 121013\nde 12116a\nfb 125343\n88 1029d4\n40 1af03b\n2d 3ed16\nf3 ebd7a\n52 58199\nf7 ebdab\n56 581ca\n73 5c341\nc 1cd00a\n3d 1d05a1\n0 399d2\nd6 1212c1\nd2 1212f2\n35 3e25c\n4 3acc5\n9a 10b628\nf7 125469\n84 102afa\nf3 12549a\n8c 296163\nbd 2996fa\n80 102b2b\n25 3ee6d\n57 25889\n72 5c092\n76 5c0c3\n5f 259e0\n7a 5c1e9\n8e 294e46\n7e 5c21a\n53 6023a\nf6 1251ba\nfe 125311\n57 25b37\n72 5c340\n76 5c371\n53 604e8\n57 60519\nf6 125468\nd3 129641\n5 43014\n51 5efd3\n5d 1f260b\nda 121139\nf7 27dceb\n56 1ea10a\n52 1ea13b\n31 1d06d5\n5a 1ea292\n7b 1ee43a\n8 1cbacb\n8d 10c262\n55 1f2762\nd2 121290\nf3 125438\n8c 296101\nbd 299698\n80 102ac9\nf7 27df99\n56 1ea3b8\n52 1ea3e9\n1a 1d471f\n77 1ee560\nd2 2b221e\ncb 2bad69\n4 1cbbf1\n76 1ee2b1\n5b 1f25e1\nd7 eec2e\n75 1f6909\nf2 125437\nd3 1295df\n76 1ee55f\n57 1b7d87\n72 1ee590\n57 1f2707\n53 1f2738\n94 cadbd\n9c caf14\nd4 e794e\n14 193eb4\n22 3ec04\n54 1b0a45\n14 1940f2\nd6 2b31f5\n8b 25c768\n9c 104626\nbf 2a2cbf\nb3 10f687\n22 3ee42\n54 1b0c83\nff 2bf850\nf3 12c218\n56 1b0c8a\nb5 107601\nde 1211be\n52 1b0cbb\n22 3ee50\n54 1b0c91\ncb 279307\ndc 1211c5\n94 cb05d\n96 cb064\nbd 29970a\nd4 e7bee\nbf 299711\nd6 e7bf5\n56 1b0cec\n72 5c094\n76 5c0c5\n7a 5c1eb\ne5 2846af\n30 196fb2\nd7 121014\ndf 12116b\ndb 12119c\nf6 1251bc\nf2 1251ed\nfe 125313\nb4 2600da\nfa 125344\n11 192b50\nb0 26010b\n72 5c342\nd7 1212c2\nd3 1212f3\nf6 12546a\nf2 12549b\naf cd430\n52 6023b\n5a 60392\n10 19b159\nd6 129363\nb5 2a2e0f\n8e 1029a8\nd2 129394\nde 1294ba\n94 264281\naf cd6de\n52 604e9\nd6 129611\n8e 102c56\nd2 129642\ndb 12113a\n57 1ea10b\n64 62a97\nf6 284d14\n53 56c2a\n5f 1ea262\n6c 62bee\nfe 284e6b\n5b 1ea293\n68 62c1f\nfa 284e9c\n76 1ee2b3\n72 1ee2e4\n72 5add2\n7e 1ee40a\n7a 1ee43b\nd3 121291\n53 1ea3ea\nac 10eefa\n19 8e69\n53 1b7a38\nf2 284ff3\n76 1ee561\n72 1ee592\nd2 129332\nda 129489\n56 1f245a\n34 196fd7\nfb 28614f\ne 1cba9f\naf 25f680\n52 1f248b\n52 5ef79\n5e 1f25b1\n31 1d8a25\n5a 1f25e2\nd2 1295e0\n56 1f2708\nfb 2863fd\ne 1cbd4d\naf 25f92e\n52 1f2739\n94 cb06b\nd4 e7bfc\nd6 11fd43\n14 194162\n22 3eeb2\n54 1b0cf3\n10 3b3a7\nb4 29aac0\n97 296973\n52 1ea0cd\n18 3b4fe\nb0 1075df\nbc 29ac17\n93 103492\n9f 296aca\n73 1ee275\n56 1f1446\n39 3f6a6\n35 1d19ba\n52 57f3f\n1d 3b4ce\nbc 108a89\nf6 2b7658\n3d 1d1b11\n31 3e4d9\n5a 58096\n10 1d58f3\n7b 5c23e\n83 25c611\n94 1044cf\nd6 121067\nf7 12520f\n94 29ea1b\nff 125366\n94 10b4f9\n77 1b4e32\nd2 278af0\n10 3b655\n31 3f7fd\nc7 2b2903\n52 581ed\n73 5c395\n83 25c8bf\n94 10477d\nb5 108925\nd6 121315\nf7 1254bd\n9e caeab\n30 3f54e\nb7 29ab1a\n72 1ee274\n38 3f6a5\nb3 107639\nbf 29ac71\n5b 603e5\n78 64532\n95 10c81e\nd7 2bb544\n9d 10c975\n15 19c441\nd4 2b1f9c\nf6 12520e\nd7 1293b6\na9 d6a76\nf4 12d503\ndf 12950d\n57 1b8fd9\nfc 12d65a\nfb 2bf873\nc6 2b2902\n11 439a4\n53 6053c\n70 64689\na3 260a66\nb4 108924\n17 192bea\n95 10cacc\nd4 2b224a\nf6 1254bc\n14 1cd5c6\n10 3a0e5\n1c 1cd71d\n35 1d176e\n13 1cc535\n0 3acf6\n31 3e28d\n3d 1d18c5\n1b 1cc68c\n56 1ea15e\n35 1d06f8\n52 56c7d\n5e 1ea2b5\n14 1cd874\n35 1d1a1c\n56 1ea40c\n96 c9a92\n34 1d176d\nc7 2b9c31\n12 1cc534\n92 104443\n15 1d5915\n9a 10459a\n11 42434\n1d 1d5a6c\n76 1ee305\n96 c9d40\n34 1d1a1b\n92 1046f1\n15 1d5bc3\n76 1ee5b3\n50 57f46\nd7 2b3512\na6 260788\n58 5809d\nd3 120031\ndf 2b3669\n79 5c245\nde e7a4a\n70 5c0ed\n9a 29d878\nf7 2b76b9\n78 5c244\nf3 1241d8\nff 2b7810\nd5 1293bd\n27 5750\ndd 129514\n51 60543\ne3 27d605\nf4 1254c3\n54 1ea165\n9 1925f4\n50 56c84\n5c 1ea2bc\n54 1ea413\nd6 e6631\n74 1ee30c\n29 19679b\nde e6788\n70 5ae2b\n7c 1ee463\nd6 e68df\n74 1ee5ba\n9e 295505\n30 3f550\n38 3f6a7\n72 5c0e8\nf6 125210\nfe 125367\n5f 1f1600\nb4 26012e\n30 3f7fe\n95 10477e\nf6 1254be\n10 436f7\n18 4384e\n35 1d9d0a\n52 6028f\nd6 1293b7\n94 2642d5\n10 439a5\n52 6053d\nd6 129665\n11 3a0e6\n1d 1cd71e\nbc 268327\n34 1d176f\n12 1cc536\n30 3e28e\n3c 1d18c6\n1a 1cc68d\n76 1ee307\n72 5ae26\n7e 1ee45e\n34 1d1a1d\n7c 5c213\n6b 1b4355\n82 29cdbe\n76 1ee5b5\n14 1d5916\n10 42435\n1c 1d5a6d\n56 1f24ae\n14 1d5bc4\n56 1f275c\n70 5c0ef\n78 5c246\nc 398aa\nad cd48b\n50 60296\nc 39b58\nad cd739\n50 60544\n74 1ee30e\n29 19679d\n70 5ae2d\n7c 1ee465\n80 29cdc5\n74 1ee5bc\n9e 295507\n54 1f24b5\n9 19a944\n50 5efd4\n5c 1f260c\na6 299f1a\n74 1b391a\ne1 28593e\n54 1f2763\n37 197ff3\n73 5c3a3\n92 25bcb1\nff 27ce2e\n5e 1e924d\n17 19c19a\n53 6054a\n77 1b4b84\nd2 278842\n71 2169c\n2e 46064\n7d 1b4cd4\n47 565a8\nd8 278992\nf6 2862e6\n57 1b8d2b\n35 197ffa\n4a 1f09bd\nb2 cef2a\n90 25bcb8\n8a 10acc5\n7e 5c4bc\n16 3b37d\nb7 cef5e\nf4 2b6143\n90 29669c\nde 1201b8\n97 d3105\nd4 2ba2ea\n98 2967f3\n9f d325c\n26 19667d\ndc 2ba441\n43 1b0049\ne2 27d604\nf5 ebae8\n4b 1b01a0\nea 27d75b\nfd ebc3f\nc2 2817ab\nd5 efc8f\n37 198055\n5c 1e92a8\n92 cad23\n15 19c1f5\n5e 1e92af\n17 19c1fc\n77 1b4be6\n23 46f45\nd2 e78b4\n55 1b8d86\n57 1b8d8d\n6 1ccc0a\na7 2607eb\n35 1d19c8\n52 57f4d\n3d 1d1b1f\n31 3e4e7\n5a 580a4\n7b 5c24c\n8 398dd\nb5 10760f\nde 1211cc\n56 1b0c98\nf7 12521d\n84 1028ae\nff 125374\n94 10b507\n77 1b4e40\n8c 102a05\nd2 278afe\nd6 121323\n9a 10b68a\nf7 1254cb\n84 102b5c\n72 5c0f4\n7a 5c24b\n8e 294ea8\n5b 603f3\nd7 1293c4\ndf 12951b\n57 1b8fe7\n57 57f0f\nf6 1254ca\nd7 129672\n56 1ea16c\nbd ce09a\n1c 3a4b9\nb 1925fb\n35 1d0706\n52 56c8b\n5e 1ea2c3\n77 1ee314\n4 1cb9a5\n56 1ea41a\n76 1ee313\n3c 3e660\n2b 1967a2\n72 5ae32\n7e 1ee46a\n57 1f24bb\n53 5efda\n5f 1f2612\n76 1ee5c1\n57 1f2769\n95 d310c\n54 57f15\n43 1b0057\nf5 ebaf6\n5c 5806c\n4b 1b01ae\nfd ebc4d\nd5 efc9d\ndd efdf4\n15 19c203\n31 1982cb\n33 1982d2\n71 1b4e5c\n51 1b9003\n53 1b900a\nf8 12d68b\ndf 2bb699\nd3 128061\n11 19c480\n71 1b4e6a\nd 192625\n51 1b9011\n90 29694a\n97 d33b3\nd4 2ba598\n43 1b02f7\nf5 ebd96\n56 581bc\nf7 ebd9d\n42 562bc\n4e 1e98f4\nd0 2b34db\nd7 eff44\n37 198303\n92 cafd1\n15 19c4a3\n57 1b903b\neb 2857fe\n4a 1f1c1d\nfc 12d6bc\nd7 121076\nbe 2a1c9c\ndf 1211cd\nfe 125375\n15 192b81\nb4 26013c\nd7 121324\ne 398b1\n35 1d9d18\naf cd492\n52 6029d\nb5 10f95f\nde 12951c\ne 39b5f\naf cd740\n52 6054b\n57 1ea16d\nf6 284d76\n53 56c8c\n5f 1ea2c4\nfe 284ecd\nc5 278156\n76 1ee315\n3c 3e662\n2b 1967a4\n57 1ea41b\n57 1b7a69\n72 1ee272\nf6 285024\n82 29cdcc\n76 1ee5c3\n56 1f24bc\n1c 42809\nb 19a94b\n76 1b3921\ne3 285945\n56 1f276a\n46 1e97fd\n95 d33ba\nd5 eff4b\n90 10c78a\nf7 123eeb\nc6 120954\n35 19830a\nd7 128092\n15 19c4b1\n23 47201\nd2 e7b70\n55 1b9042\nb1 269462\nf8 2b6515\nff f2f7e\n10 1d5881\n9c 104872\n98 1048a3\nbd 108a1a\nda 12143b\n94 29ec67\nff 1255b2\n1d 90e8\n38 3f8f1\n45 1f0891\nc2 11f3bf\nce 2b29f7\n4e 1f0c8e\n85 ca709\n19 43a99\n1d 43aca\nc7 e72a1\n5b 60631\nf9 2b77da\n5f 60662\nbc 108a19\n1b 192d10\n99 10cbf2\ndb 12978a\n7a 1b39f5\nf8 12d8d7\n98 104841\n18 1cd99a\n5a 1ea532\n7b 1ee6da\n9d d21df\nb8 1089e8\nce 2b9d85\n1b 192cae\n99 10cb90\ndb 129728\n19 1d5ce9\n5b 1f2881\n78 1f69ce\nfd 1255b9\n66 1bc5d2\n5d 25c87\n78 5c490\nfc 1255b8\ndd eede0\nf8 1255e9\nb0 cdc01\nbc 261239\n5b 1af8af\nd9 129791\nfd 27e0e9\n50 56ed0\nf1 eaab1\nf 41ebe\n5c 1ea508\nde 280fc4\n5b 56d73\nfa 12432e\n12 4369a\nb3 d727b\n5a 1d90e\nf0 2be460\n38 3f8f3\n9d 104873\n99 1048a4\nbc 108a1b\nb8 108a4c\ndf 12140b\ndb 12143c\nf8 125589\n84 ca70a\nb5 cdca1\n18 43a9a\n77 1b4bf4\n5e 60663\n1e 192ce0\n9c 10cbc2\n1a 192d11\n98 10cbf3\nce 2b1a37\n99 104842\nb8 1089ea\ndb 1213da\n19 1cd99b\nb8 2685a4\n38 1d1b43\n53 56eca\n5f 1ea502\n11 8fc2\n1d 19c5fa\n9a cb128\nfe 28510b\n5b 1ea533\n90 cafd6\nfa 28513c\n7f 2b0e9\n78 1ee680\n1a 192caf\n98 10cb91\n18 1d5cea\nd9 121443\nfc 1255ba\ndf 12146d\nf8 1255eb\n5a 1af8b0\nd8 129792\n70 5b079\n7c 1ee6b1\n53 56f2c\n5f 1ea564\n9a cb18a\n94 cb007\nfe 28516d\n16 1cd56d\n14 1cd574\n96 264030\n9c 26443a\nb9 261269\n12 1cd59e\n61 1b2f37\n10 1cd5a5\n7e 1ed456\n37 1a03a3\n12 3a08c\n1e 1cd6c4\n10 3a093\n1c 1cd6cb\n9e 264187\n1a 1cd6f5\n20 19f9b5\n69 1b308e\n8c 103f11\n52 1b0a1b\n12 1cd5ac\n12 3a09a\n1e 1cd6d2\n59 1b90f6\n1a 1cd703\n9c 104636\n14 1cd5d6\nfb ebed1\n5a 582f0\nff ebf02\n5e 58321\nde 121418\nda 121449\n3d 3e3b3\nc 3ae1c\nff 1255c0\n6 41d58\nf4 27df83\n8c 102c51\nfb 1255f1\n88 102c82\n2d 3efc4\n5f 25c8e\n7a 5c497\n7e 5c4c8\nfe 1255bf\ndb 129798\nd 4316b\n51 5f281\n5d 1f28b9\nda 1213e7\nfb 12558f\ne 1d535f\n3f 1d88f6\n2 41d27\nf0 27df52\n88 102c20\nff 27e0f0\nf3 eaab8\n52 56ed7\n5e 1ea50f\nf2 2be467\n72 5b07e\n7e 1ee6b6\nd6 e7955\n12 3a33a\n1e 1cd972\nb5 cdeef\nde e7aac\n56 1b0a4c\n14 1cd822\n96 2642de\nb9 261517\n10 1cd853\n37 1a0651\n52 1b0cc9\n16 1cd829\nbb 26151e\n12 1cd85a\n14 1cd884\n7a 5c499\n8a 10acd3\n7e 5c4ca\ndf 121419\ndb 12144a\nfe 1255c1\nfa 1255f2\n5a 60640\n5e 60671\nde 129768\nda 129799\ndb 1213e8\n72 5b080\n7e 1ee6b8\nda 129737\n52 5f227\n5e 1f285f\nd6 e7c03\n56 1b0cfa\n16 1cd88b\n52 1ea37b\n18 3b7ac\n73 1ee523\n39 3f954\nc3 11f422\ncf 2b2a5a\n5a 58344\n10 1d5ba1\n7b 5c4ec\nd6 2b34a3\n8b 25ca16\n9c 1048d4\nbf 2a2f6d\nb3 10f935\nde 12146c\n94 29ecc9\nff 125614\n53 1f26ca\n19 43afb\n5b 60693\n78 647e0\nd7 2bb7f2\n1f 192d41\n9d 10cc23\n10 3a393\n1c 1cd9cb\n31 3e53b\n3d 1d1b73\n52 56f2b\n5e 1ea563\n9a 104848\n11 426e2\n1d 1d5d1a\nfd 12561b\neb 27d75c\nfc 12561a\n50 56f32\n5c 1ea56a\nde e6a36\n70 5b0d9\n7c 1ee711\n38 3f955\n9d 1048d5\nfe 125615\n18 43afc\n5a 60694\nde 1297bc\n11 3a394\n1d 1cd9cc\nbc 2685d5\n30 3e53c\n3c 1d1b74\n72 5b0d4\n8a 29cf15\n7e 1ee70c\n10 426e3\n1c 1d5d1b\n52 5f27b\n5e 1f28b3\ndd 121474\n51 56f33\n5d 1ea56b\n98 cb191\n2b 45d78\n5d 1b7bb9\nfc 285174\n88 29cf1c\n70 5b0db\n7c 1ee713\n37 1d1715\n92 2953d3\n33 3e234\n3f 1d186c\n9a 29552a\n31 3e23b\n3d 1d1873\n9f d1f9a\nba 1087a3\n98 295531\nff 2b6550\nb6 2a2e77\n17 1d58bc\n77 1b4b92\n7d 1ed1a2\n4c 1e9c0b\n40 565d3\nd2 278850\n57 1b8d39\n37 1d1723\n92 2953e1\n33 3e242\n3f 1d187a\n9a 295538\nb7 108680\nd2 278afc\nbf 1087d7\n69 2114e\n20 da75\n97 10c827\n28 dbcc\n9f 10c97e\n9d 10c985\n37 1d1777\n33 3e296\n3f 1d18ce\n31 3e29d\n3d 1d18d5\n17 1d591e\n13 4243d\n1f 1d5a75\nde 12147a\nff 125622\n8c 102cb3\n5f 58066\nfe 125621\ndf 1297c9\n52 56f39\n5e 1ea571\n72 5b0e0\n7e 1ee718\n56 57f1c\nf7 ebafd\n33 3e4e2\n3f 1d1b1a\nc8 e7111\n9a 2957d8\n5e 58073\nff ebc54\nd7 efca4\n13 42689\n1f 1d5cc1\n67 1ec681\ndf efdfb\nb7 10868e\nbf 1087e5\n55 60514\n97 10c835\n9f 10c98c\n57 1b8d9b\n37 1d1785\n33 3e2a4\n3f 1d18dc\n37 1d19c3\nc0 e6fba\n92 295681\n33 1d19f4\n91 d1e17\n9d 26544f\n31 1d19fb\n17 1d5b6a\n13 1d5b9b\n11 1d5ba2\nf 19262c\n53 1b9018\n33 1d1a02\n13 1d5ba9\nb7 10892e\n3 1cce96\nb5 108935\n8e 29cf48\n97 10cad5\n37 1d1a25\n17 1d5bcc\ndf 12147b\nfe 125623\nde 1297ca\n53 56f3a\n5f 1ea572\n8a 29cf23\n72 5b0e2\n7e 1ee71a\n52 5f289\n5e 1f28c1\nb7 10893c\n97 10cae3\n94 10b569\n77 1b4ea2\n17 19aed8\n32 1d16e1\n57 1b9049\nb3 269469\nfa 2b651c\n5b 1e8f61\n12 1d5888\n37 1d1a33\n25 45c49\n29 45d6f\n2d 45da0\n73 1bbe99\n20 1d9377\n4 43013\n35 465aa\n24 1d93a8\nef 285821\ne3 f21e9\n4e 1f1c40\n42 5e608\n6f 1f5de8\n63 627b0\n67 627e1\n46 5fbab\n77 63142\n66 1f5f40\ne7 e9ed8\n4a 5fcd1\n7b 63268\n6a 1f6066\n4e 5fd02\n7f 63299\n35 4786c\na5 10ed40\n79 6357f\n3d 19f1cf\n4 19228f\n39 19f200\nad 10ee97\na9 10eec8\n6 192228\n84 10c10a\nb5 10f6a1\na4 2a249f\n2 192259\nbd 2a2d0a\n80 10c13b\nb1 10f6d2\n3b 4f15\na0 2a24d0\ne 19237f\n8c 10c261\nbd 10f7f8\na 1923b0\n88 10c292\nb9 10f829\na8 2a2627\n3d 19f23d\n0 866e\nc 19bca6\nce 127887\n25 19fcf7\ne7 12b8d8\n7f 1bbd67\n7a 5c1db\n5f 259d2\nfe f2f8d\n58 1e8f69\nef 2bef41\ne3 12b909\n11 1c32\nc4 128d09\n46 1aee27\n7b 1bbd98\nd9 129731\neb 12ba60\nc6 128ca2\n35 1a0658\nf7 12c239\n2e 196586\ne6 2bf037\nca 128e2a\nfb 12c3c1\nea 2bf1bf\n96 263fcc\nbd 269898\nb1 d6260\n1c 1d5cb7\n10 4267f\nb5 d6291\n3 19a7f2\n14 426b0\n2d 1d94fe\n4a 5fa83\n21 45ec6\n25 45ef7\n86 264bdd\nb7 268174\nc7 2b992d\n35 46858\nf0 2b63c0\nf7 f2e29\n56 5f248\n6f 1f6096\n63 62a5e\n67 62a8f\n77 633f0\n94 10b7a7\n35 47b1a\nce 128bab\na5 10efee\n5a 25a02\n3d 19f47d\nad 2a2657\na1 10f01f\n4 19253d\n39 19f4ae\nde 12950c\n6 1924d6\nb5 10f94f\n2 192507\nbd 2a2fb8\nb1 10f980\nca 11f7c6\n4d 1f0c98\ne7 12bb86\n7f 1bc015\nef 2bf1ef\ne3 12bbb7\n11 1ee0\n46 1af0d5\n7b 1bc046\nf7 12c4e7\n21 46eda\n29 47031\ne3 f34ab\n42 5f8ca\na1 110033\n4 193551\n39 1a04c2\nad 110159\na9 11018a\n31 cec7\n0 9930\n3d 1a04ff\nce 128b49\na5 d6bf2\n4 43011\ndf 278967\n96 26528e\n21 47188\ne3 f3759\n42 5fb78\n23 1963ff\na1 1102e1\n4 1937ff\n39 1a0770\na9 10ee66\n6f 63bfa\n4d 1f0988\na 19234e\n88 10c230\nb9 10f7c7\n39 19f20c\n8 19bc75\nca 127856\n39 1a077e\nca 128dc8\nfb 12c35f\n0 1d3cc0\nc7 e5f7d\n8 1d3e17\n10 1d4621\nd7 e68de\n18 1d4778\n25 1d7e37\n8f 2637c5\n21 1d7e68\n2d 1d7f8e\n29 1d7fbf\n4 1d5201\n35 1d8798\n0 1d5232\n9f 264126\n31 1d87c9\nc 1d5358\n0 41d20\n3d 1d88ef\ne4 2856d0\n8 1d5389\n39 1d8920\ne7 284408\nf9 1242c6\n46 1f0827\nf7 284d69\n56 1f1188\nff 284ec0\n95 cad5a\n5e 1f12df\n6b 1f4b57\n10 1cc271\nb1 25fe52\n4a 1f1f21\n7b 1f54b8\n18 19b2a4\n9c 29edae\n90 10b776\n6f 63ea8\n4d 1f0c36\na5 267b1e\n4 1d3f3d\n0 1d3f6e\nb5 26847f\n14 1d489e\n10 1d48cf\n42 5e66a\n4e 1f1ca2\n25 1d80e5\n8f 263a73\n21 1d8116\n5e 1f2603\n52 5efcb\n35 1d8a46\n9f 2643d4\n31 1d8a77\ne7 2846b6\n46 1f0ad5\n63 1f4cae\na1 10ffd1\na9 110128\n31 1a0377\nc2 1289c1\n5a 1b8e50\n39 1a04ce\nca 128b18\n0 1d4f82\n84 29e306\nef 124c51\nb5 2a189d\nc7 e723f\n8 1d50d9\n80 10ae25\n8c 29e45d\nd4 efed8\nbd 2a19f4\n25 1d90f9\n80 29cdb7\n21 45c18\n2d 1d9250\n88 29cf0e\n8b d15a6\n29 1d9281\n63 1f5cc2\n6b 1f5e19\nb4 cdc94\n2 1921f5\n80 10c0d7\n18 19c566\n34 3e25b\n23 19639d\na1 11027f\na5 268de0\n4 1d51ff\n9f 264124\n0 1d5230\nd2 1280d0\nde 2bb708\nb5 2a1b4b\ne7 285978\n46 1f1d97\n6a 22158\n48 1aeee6\ne2 f21e8\nee 285820\n6b 20e97\n0 19a54c\n8 19a6a3\n4a 1e261\n7b 217f8\n10 19aead\ned 285828\ne1 f21f0\n4c 1f1c47\n40 5e60f\nc7 2b9bdb\ncf 2b9d32\nfd 286189\nf1 f2b51\n5c 1f25a8\n50 5ef70\nd7 2ba53c\n6d 1f5def\n61 627b7\n65 627e8\n69 6290e\n6d 6293f\ncc 12788e\ne5 12b8df\n7d 1bbd6e\n79 1bbd9f\n7b 21ab4\ne0 2bf06f\n4a 1aef4f\nc8 128e31\nf9 12c3c8\ne8 2bf1c6\n6d 1f609d\n61 62a65\n7d 1f69fe\n8c 26382d\n71 633c6\n79 1bc04d\nc7 2bae9d\n61 63a79\na7 298f07\n69 63bd0\ne1 12cbd2\n79 1bd061\ned 12ccf8\ne9 12cd29\n61 63d27\n63 1b2f9e\ne1 12ce80\n79 1bd30f\nc8 12785d\ne9 12ba05\n12 1d5b38\n5e 1e8ff3\ncb 2bb017\n69 1f4b5e\n48 1f1f28\n79 1f54bf\ncc 2bafec\nc0 1279b4\n58 1b7e43\ndc 2bb94d\nd0 128315\ned 2bf194\ne1 12bb5c\n8 475\nfd 2bfaf5\n42 1af044\nf1 12c4bd\ncf 280612\n61 1f4cb5\ndf 280f73\n71 1f5616\nc0 1289c8\n58 1b8e57\nc8 128b1f\n4 1cbbef\na5 25f7d0\ne1 12cb70\ne9 12ccc7\n48 1f1c78\ncf 281626\nc3 edfee\n61 1f5cc9\ncb ee145\n69 1f5e20\n58 1b9105\ne5 28597f\n44 1f1d9e\ndf 280cc3\n40 1f1dcf\ncf 2818d4\nc3 ee29c\n61 1f5f77\n22 3eb92\n54 1b09d3\n7 8389\n0 1cb920\n27 19e71e\n32 3f4f3\n17 8cea\n10 1cc281\n7f 1f6a67\n73 6342f\n37 19f07f\na2 10ed79\nd4 280bba\nae 2a23b1\n24 45c4a\n28 45d70\n2c 45da1\n72 1bbe9a\n23 19e6ed\n34 465ab\na4 cd341\n38 466d1\n2b 19e844\n3c 46702\n6e 1f5de9\n62 627b1\n66 627e2\n6a 62908\n6e 62939\n2b c6b6\n76 63143\ne6 e9ed9\n7a 63269\n7e 6329a\na4 10ed41\n78 63580\nac 10ee98\n53 1ea388\na8 10eec9\nb4 10f6a2\nbc 10f7f9\nb8 10f82a\ne6 12b8d9\nee 12ba30\nea 12ba61\nab d57ad\nf6 12c23a\nfe 12c391\nfa 12c3c2\n2c 1d94ff\n20 45ec7\n3c 1d9e60\n30 46828\n8c 10ad5f\n63 21060\n6f 1b4698\nc6 2b992e\n6e 1f6097\n62 62a5f\n66 62a90\n2b c964\n76 633f1\na4 10efef\n97 296663\n64 1b2fbb\n93 296694\n92 c9d0d\n15 19b1df\n9e 25d345\ne6 12bb87\nab d5a5b\nf6 12c4e8\na5 10edb2\n20 46edb\n24 46f0c\n28 47032\n2c 47063\n72 1bd15c\n62 63a73\n66 63aa4\n6a 63bca\n6e 63bfb\na4 110003\n78 64842\na0 110034\nac 11015a\na8 11018b\n3d 1a0491\n31 ce59\ne6 12cb9b\ne2 12cbcc\nee 12ccf2\nea 12cd23\n7f 1bd029\n73 299f1\n20 47189\n24 471ba\n62 63d21\n66 63d52\n26 1963cf\na4 1102b1\n22 196400\na0 1102e2\ne6 12ce49\ne2 12ce7a\nac 2a2348\na0 10ed10\na8 10ee67\nb8 10f7c8\nea 12b9ff\nfa 12c360\n24 1d7e38\n20 1d7e69\n2c 1d7f8f\ne7 ea126\n28 1d7fc0\n34 1d8799\n30 1d87ca\n3c 1d88f0\nf7 eaa87\n38 1d8921\n10 3b353\nb1 cef34\n7a 1f54b9\nac 2a25f6\na0 10efbe\nee 2bf18e\ne2 12bb56\n24 1d80e6\n20 1d8117\n34 1d8a47\n30 1d8a78\na0 10ffd2\na8 110129\ne2 12cb6a\nea 12ccc1\n24 1d90fa\n20 1d912b\n9c 2957ac\n20 45c19\n2c 1d9251\ne7 eb3e8\n28 1d9282\n66 1f5c92\n22 19639e\na0 110280\n4d 5e79a\n2a 4367\ne2 12ce18\nbf 2682cd\n20 1d93d9\nba 108741\n9f d1f38\n3d 1d9c13\n0 43044\n31 465db\n6b 22159\n4e 2532a\n0 19b80e\n8 19b965\n64 627e9\n6c 62940\n63 1bb53a\n29 c96b\n74 633f8\n23 45f3d\n2f 1d9575\nd2 e68ac\n55 1b7d7e\nde 279ee4\nd3 2b3233\n64 63aab\n6c 63c02\n64 63d59\nf6 285fd6\n62 1b2f9f\ne0 12ce81\n68 1f4b5f\n2b d97a\n78 1f54c0\n3b e2db\n60 1f4cb6\n70 1f5617\n68 1f5e21\nff 284e6c\n60 1f5f78\ndf eead7\n7d 1f67b2\n40 5fbe3\nfa 1252e0\n71 6317a\nef 28582f\ne3 f21f7\n4e 1f1c4e\n42 5e616\nff 286190\nf3 f2b58\n5e 1f25af\n52 5ef77\nd1 2bb82a\n8d 294e3e\nfb f2caf\n5a 5f0ce\n67 627ef\n7f 1f6757\n42 5fb88\n73 6311f\n62 1f5f1d\n46 5fbb9\n77 63150\n66 1f5f4e\nce 127895\ne7 12b8e6\n7f 1bbd75\nc6 128cb0\nf7 12c247\n25 1d93a7\n42 5f92c\nd 42ebb\ne6 2bf045\n73 5c085\nf7 f2e37\n56 5f256\n6f 1f60a4\n63 62a6c\n7f 1f6a05\n73 633cd\na2 10ed17\nd4 280b58\nae 2a234f\n77 633fe\nf3 12517c\nd6 12834d\ne7 12bb94\n88 10bf82\n7f 1bc023\n7b 1bc054\nf7 12c4f5\n42 5fbda\n25 45ef9\ne3 12cbd9\n7b 1bd068\nef 12ccff\ne4 2856c2\n24 3dbb8\neb 12cd30\n63 63d2e\ne3 12ce87\n7b 1bd316\n6b 22477\nce 2bad45\nc2 12770d\n5a 1b7b9c\ne7 284416\n46 1f0835\n42 1f0866\nf7 284d77\n56 1f1196\n52 1f11c7\nff 284ece\n5e 1f12ed\n5a 1f131e\n63 1f4a0e\n6b 1f4b65\n10 1cc27f\n17 8ce8\nb1 25fe60\n42 1f1dd8\n73 1f536f\n4a 1f1f2f\n7b 1f54c6\nde 2bb954\nd2 12831c\nef 2bf19b\ne3 12bb63\ne7 2846c4\n63 1ed912\n46 1f0ae3\nf7 285025\n73 1ee273\n56 1f1444\n52 1f1475\nff 125306\n63 1f4cbc\n73 1f561d\ne3 12cb77\neb 12ccce\ne0 285691\ne7 2856d8\n46 1f1af7\n42 1f1b28\n21 1d80c2\n4a 1f1c7f\n67 1f5c9f\nc2 2b995d\n63 1f5cd0\n63 627be\n6f 1f5df6\n4 41d4f\na5 d5930\nca 2b9ab4\n6b 1f5e27\nf6 ea83a\nc2 128c7d\n5a 1b910c\n76 5ae01\ne3 12ce25\ne7 285986\n46 1f1da5\n42 1f1dd6\n67 1f5f4d\nc2 2b9c0b\n63 1f5f7e\nef 28480b\n1c 205b\n77 1f5394\n58 1ebbb\n5a 1ebc2\nea e9f9d\n6d 1bb46f\n5c 1ebec\n5e 1ebf3\n98 cb121\nfc 285104\n9c cb152\n30 3f7fc\n9e cb159\n13 42697\n1f 1d5ccf\n2b 4734a\n51 25b53\n5d 1b918b\nda e7cb9\n5c 1ebfa\n58 1ec1d\n5a 1ec24\n98 cb183\nfc 285166\nd8 e7d14\n13 426f9\n1f 1d5d31\nda e7d1b\n85 29600b\n66 627f0\n75 1ecfeb\n8d 296162\n81 102b2a\n6e 62947\n95 29696c\n76 63151\n91 10348b\n9d 296ac3\n7e 632a8\ne6 12b8e7\nee 12ba3e\nea 12ba6f\nf6 12c248\nfe 12c39f\nfa 12c3d0\n6e 1f60a5\n62 62a6d\n66 62a9e\n76 633ff\ne6 12bb95\nf6 12c4f6\n62 63a81\n66 63ab2\n6a 63bd8\nd7 ee9d4\nf2 1251dd\nd0 2b1f6b\n75 1ee2ad\n81 103dec\n6e 63c09\ne6 12cba9\ne2 12cbda\nee 12cd00\nea 12cd31\n7f 1bd037\n73 299ff\n62 63d2f\n66 63d60\ne6 12ce57\n43 5f8cd\ne2 12ce88\nfa 12c36e\n62 1f4a0f\n6a 1f4b66\n72 1f5370\n62 1f4cbd\n72 1f561e\n66 1f5ca0\n62 1f5cd1\n81 295fda\n62 627bf\n6e 1f5df7\n6a 1f5e28\n62 1f5f7f\n7f 1f67b9\n42 5fbea\n73 63181\nd8 e7d22\n87 29d09e\na4 2a11eb\n42 1f07f8\n8 41c29\n8f 29d1f5\nac 2a1342\n97 29d9ff\nb4 2a1b4c\n52 1f1159\n18 4258a\n9f 29db56\nbc 2a1ca3\n63 1f49a0\nd8 e6740\n29 45dd1\n73 1f5301\n8 4319b\n39 46732\n28 1d9530\n3d 1d8b9d\n5a 5f122\n11 426f2\n1d 1d5d2a\n9a 104858\nfe 2be83b\n4a 5fd33\n7b 632ca\n6a 1f60c8\nbb 108a00\n83 26369d\n77 1b4e94\n94 10b55b\na5 10eda2\nba cdd5f\n3d 19f231\ne7 2bdac8\nad 10eef9\n42 24f4a\n4e 1b8582\n25 19e9c5\nf3 284ff2\n6 19228a\n84 10c16c\nb5 10f703\n3f 4f46\na4 2a2501\nf7 2be429\n8c 10c2c3\ne 1923e1\nbd 10f85a\n52 258ab\n5e 1b8ee3\n35 19f326\na0 10f020\nac 2a2658\nd6 1280f3\n71 1b4df8\ne7 12b93a\n15 1c63\n7f 1bbdc9\n7a 5c23d\n58 1e8fcb\ndd 129762\nef 12ba91\n67 1bb55d\n62 5b9d1\n47 251c8\n40 1e875f\nc6 128d04\nf7 12c29b\nce 128e5b\nff 12c3f2\n77 1bbebe\n3b 198119\ne2 12bbb8\nee 2bf1f0\n57 25b29\n72 5c332\n50 1e90c0\n31 1d16e9\n82 264c0e\nb3 2681a5\n9f d21e6\nba 1089ef\n3d 1d9ec1\n31 46889\nc7 2b998f\n4e 1f1f50\n42 5e918\nf3 27dcba\n52 1ea0d9\n5e 1f28b1\n52 5f279\n83 26394b\n94 10b809\na5 10f050\nb5 29a811\nba ce00d\n3d 19f4df\n6 192538\nb5 10f9b1\nd6 1283a1\ne7 12bbe8\n15 1f11\n7f 1bc077\nf7 2b73a9\n84 294a3a\n7a 5c4eb\n58 1e9279\nf7 12c549\n94 29539b\nd0 e78ab\n8f d2899\n21 46f3c\n63 1f5c62\nd8 e7a02\n29 47093\n41 1f0862\n63 63ad4\na2 106cd7\nae 29a30f\nd4 278b18\nc5 2b998a\ne7 12cbfc\n58 1ea28d\n8f d2b47\nd0 e7b59\n21 471ea\nb3 269467\n27 196430\na5 110312\nc5 2b9c38\ne7 12ceaa\n84 295cfc\n58 1ea53b\n4 1d3cf1\nc 1d3e48\n14 1d4652\n1c 1d47a9\na2 1069c7\nae 299fff\nd4 278808\n25 1d7e99\naa 106b1e\ndc 27895f\n2d 1d7ff0\nba 10747f\n0 41d82\nc 1d53ba\n3d 1d8951\n4 1d3f9f\n14 1d4900\n4 1d4fb3\n0 41ad2\nc 1d510a\n4 1d5261\nc7 2b9c3d\n48 5e7c8\ncf 2b9d94\nd7 2ba59e\n6d 1f5e51\ncf ee176\nea 12497f\n61 62819\n69 62970\n48 5fd3a\n79 632d1\n68 1f60cf\ne5 12b941\nfa ea8fe\n7d 1bbdd0\n46 1aee29\nc4 128d0b\nf5 12c2a2\n4e 1aef80\ncc 128e62\nfd 12c3f9\n75 1bbec5\n39 198120\ne0 12bbbf\nec 2bf1f7\nea 124c2d\n6d 1f60ff\ncf ee424\n61 62ac7\n25 19e717\nc2 2817ad\nf3 284d44\ndf eed85\n7d 1f6a60\nfa 12558e\n71 63428\n35 19f078\na0 10ed72\nac 2a23aa\ne5 12bbef\nfa eabac\n7d 1bc07e\n40 5f933\nc7 2baeff\n48 5fa8a\nc3 127a1e\ncf 2bb056\ncf ef438\n61 63adb\n69 63c32\ne5 12cc03\n67 1b2fcf\ne5 12ceb1\n44 1f1b52\n40 5e671\n4c 1f1ca9\n44 1f1e00\n28 45dd2\n38 46733\n6e 1f5e4b\n62 62813\n6a 6296a\n7e 1f67ac\n72 63174\n7a 632cb\na4 10eda3\na3 267846\n2 1d3c65\nb4 10f704\nab 26799d\na 1d3dbc\nbc 10f85b\ne6 12b93b\nee 12ba92\nf6 12c29c\nfe 12c3f3\n38 3f6b3\n72 1ee282\n7e 1f6a5a\n72 63422\na4 10f051\nb4 29a812\n33 4b12\n3f 19814a\ne6 12bbe9\nbc 1087db\nf6 2b73aa\nf6 12c54a\n28 47094\n6a 63c2c\ne6 12cbfd\nee 12cd54\n70 1ecfb9\n77 29a22\nbf d60df\n20 471eb\n62 63d83\n26 196431\na4 110313\nf3 284d46\n52 1f1165\n33 5dd4\ne6 12ceab\n24 1d7e9a\n2c 1d7ff1\n34 1d87fb\n3c 1d8952\n24 1d8148\n34 1d8aa9\n6b 1bb3e1\n24 1d915c\n56 1b8fd6\n2 1d3f23\n20 45c7b\n2c 1d92b3\n52 25af5\n5e 1b912d\na 1d407a\n24 1d940a\n13 1940c7\n68 62971\n78 632d2\ne4 12b942\n3d 198151\n31 4b19\ne4 12bbf0\nd7 2b3264\n60 63adc\n68 63c33\ne4 12cc04\n31 5ddb\n66 1b2fd0\ne4 12ceb2\n69 1bb3cc\n64 1f5cfb\n60 6281a\n6c 1f5e52\n64 1f5fa9\nd6 ee96f\n8e 29cef4\n89 294e6f\n3d 1d8bab\n5a 5f130\n4a 5fd41\n7b 632d8\n6a 1f60d6\nd6 128101\n71 1b4e06\ne7 12b948\n7f 1bbdd7\nef 12ba9f\n67 1bb56b\nc6 128d12\nf7 12c2a9\n47 1f1aec\ne6 2bf0a7\nce 128e69\nff 12c400\n80 10be2b\n77 1bbecc\nf3 1251de\nd6 1283af\n80 ca669\nb1 cdc00\nbd 261238\ne7 12bbf6\n88 10bfe4\n7f 1bc085\na6 107cba\n84 294a48\nf7 12c557\nb6 10861b\n94 2953a9\n25 1d93b5\n42 5f93a\n63 63ae2\ne7 12cc0a\nef 12cd61\nc2 2804eb\n67 1bc82d\ne7 12ceb8\n84 295d0a\n11 436a2\n46 1f0897\n19 437f9\n4e 1f09ee\nb6 cef5b\n94 25bce9\n56 1f11f8\n11 43950\n63 1ed974\n46 1f0b45\n73 1ee2d5\n56 1f14a6\n46 1f1b59\n25 1d80f3\n42 5e678\n4e 1f1cb0\n67 1f5d01\n63 62820\n6f 1f5e58\n46 1f1e07\n67 1f5faf\n6e 1b33c7\n39 61d2\nc5 2b163a\n1b a380\n47 1de8c\ne6 eb447\n58 1f1565\n5b 26f11\nbb cf2d0\nbd cf2fa\nbf cf301\nfc 2b64e6\n96 29ecc0\ndf 2b2399\n79 22d71\n46 1b83c9\nf9 ebe68\nfd ebe99\n79 22dc5\nde 2bb69a\nd2 128062\n7b 22dcc\n5b 26f73\n9 398dc\ne2 2b5a66\n18 3b74a\nb9 cf32b\n1a 3b751\nbb cf332\n6a 62978\n75 1ed04d\n7a 632d9\ne6 12b949\nee 12baa0\nf6 12c2aa\nfe 12c401\n62 63ae3\n6a 63c3a\n75 1ee30f\ne6 12cc0b\nee 12cd62\n77 29a30\n62 63d91\n47 5f8fe\ne6 12ceb9\n6b 1bb3d3\n66 1f5d02\n62 62821\n6e 1f5e59\n66 1f5fb0\n85 294a9b\n19 a3e9\n18 3b758\n6 19b89a\nb9 cf339\n58 582e9\n46 1b842b\nf9 ebeca\n9e 264123\n29 4601d\n2d 4604e\n82 d16fc\n8e 264d34\nbf 2682cb\ne7 ea186\n7b 63516\n31 1d9a2b\n7f 63547\n52 1b7aa7\n8c 10af9d\n19 1d477b\n88 10afce\n2d 47310\nb3 d5f55\n82 d29be\nbf 26958d\n9c 10b8fe\n98 10b92f\n3d 47c71\na9 10f176\n96 29d6fe\ne 19262d\nbd 10faa6\n3d 19f4eb\nce 127b35\n5b 1f1313\nef 12bcdd\neb 12bd0e\nff 12c63e\nfb 12c66f\n92 d1dad\n9e 2653e5\n29 472df\n52 1b8d69\ne 19237d\n8c 10c25f\n19 1d5a3d\n76 1ecfe3\n2b 196556\na9 110438\n96 29e9c0\n31 d175\n3d 1a07ad\nce 128df7\n5b 1f25d5\n88 10af6c\n98 10b8cd\nc6 279490\n5a 1f2820\nf7 27ca27\na9 10f114\n39 19f4ba\nca 127b04\neb 12bcac\nfb 12c60d\n8 1d40c5\n18 1d4a26\n29 1d826d\n39 1d8bce\nef 28480d\n85 ca6a7\n4e 1f0c2c\nf4 2862df\n55 1b8d24\n23 46ee3\n94 25bf27\n6b 1f4e05\n10 1cc51f\nb1 260100\n7b 1f5766\na 19234c\nbc cddeb\n88 10c22e\n2b 1964f4\n3c 3e3b2\na9 1103d6\nfe ea983\n39 1a077c\nca 128dc6\n8 1d5387\nda 128227\nbd 2a1ca2\n8b d1854\n29 1d952f\n6b 1f60c7\ne9 f25f5\n48 5ea14\n69 62bbc\n79 6351d\ne9 12bd15\n4a 1af1fd\nf9 12c676\na3 10f026\naf 2a265e\nd5 280e67\n69 63e7e\n6b 1b30f5\ne9 12cfd7\nc8 127b0b\nd8 12846c\ne9 12bcb3\n4a 1af19b\nf9 12c614\n69 1f4e0c\n79 1f576d\n53 26dc8\na5 2a0f2e\n7c 5af51\n6b 1b3093\ne9 12cf75\n48 1f1f26\ncb ee3f3\n69 1f60ce\n28 4601e\nba 29ac4d\n1b 1cd692\na4 cd5ef\n38 4697f\n6b 211b7\nce 2b9a85\n2b 19eaf2\n3c 469b0\n6a 62bb6\n6e 62be7\ne6 ea187\n7a 63517\n84 25c63a\nd8 e6750\n29 45de1\n7e 63548\nac 10f146\n93 103182\n9f 2967ba\na8 10f177\n6c 1b3112\n9b 2967eb\nb8 10fad8\nee 12bcde\nea 12bd0f\nfe 12c63f\nad 10ef09\n28 472e0\n2c 47311\nb2 d5f56\nbe 26958e\n6a 63e78\n6e 63ea9\n2e 196526\nac 110408\n2a 196557\na8 110439\n5a 26cc4\n3d 1a073f\n31 d107\n8d d163e\nc7 28020d\nee 12cfa0\nea 12cfd1\n7f 1bd2d7\n73 29c9f\na8 10f115\nb8 10fa76\nea 12bcad\nfa 12c60e\nfd f3f8b\na9 10eed8\n28 1d826e\n38 1d8bcf\nb5 cf1b1\n7e 1f5736\n1a 3b753\n10 3b601\nb1 cf1e2\n7a 1f5767\n2a 1964f5\na8 1103d7\nea 12cf6f\n6b 1bb691\n7c 6354f\ne8 12bd16\ndb 2b338a\nf8 12c677\n6c 63eb0\nf2 f2af5\nfe 28612d\n6a 1b30f6\ne8 12cfd8\n7d 1bd2de\n71 29ca6\n68 1f4e0d\n2b dc28\n78 1f576e\na4 2a0f2f\n3b e589\neb 124972\nce 127b43\nb0 25fe4f\n5b 1f1321\nfb 1252d3\nde 1284a4\neb 12bd1c\nfb 12c67d\n2d 46050\neb 12cfde\neb 12bcba\ne0 28467d\nfb 12c61b\nf0 284fde\nef 28481b\n6b 1eda69\n4e 1f0c3a\n55 1b8d32\n23 46ef1\nb6 cf1a7\n94 25bf35\n33 47852\n5a 1f15cc\n23 44bf\n2f 197af7\n86 29cd8d\n6b 1f4e13\n10 1cc52d\n17 8f96\nb1 26010e\n7b 1f5774\na7 2a0f35\n4a 1f1f2d\n6b 1f60d5\n1e 3b784\nfe 2be82d\n9e 10487b\n1a 3b761\n7e 1f5744\n1e 3b792\n5d 1b9199\n2b 47358\n51 25b61\nda e7cc7\n70 5c39b\nde e7cf8\n9e 104889\n18 3b7bc\n7c 1f579f\n98 1048b3\nfc 2be896\n6a 62bc4\na6 cd33a\n75 1ed299\n6e 62bf5\n7a 63525\nb9 2695b9\n18 1d59d8\n84 25c648\n7e 63556\nee 12bcec\nfe 12c64d\n5b 5f0c3\nfa 12c67e\n6e 63eb7\n4b 5fa24\nea 12cfdf\n7f 1bd2e5\n73 29cad\n65 1ed9ac\nfa 12c61c\n6a 1f4e14\na6 2a0f36\n1a 3b7c3\n7e 1f57a6\nda e7d29\n9a 1048ba\n52 1f1407\n18 42838\n73 1f55af\n39 469e0\ncf 2b9ae6\n5a 5f3d0\nc6 2b9bce\n8c 10afff\n90 103188\n9c 2967c0\ne7 2bdd76\nad 10f1a7\n80 103d99\nb1 107330\nbd 29a968\n80 29e07b\n1b ac0\nce 127b97\n55 1f11f2\nd2 11fd20\nde 2b3358\na1 2a2223\nef 12bd3f\n8c 294b91\nb1 2a2b84\nff 12c6a0\n9c 2954f2\ne7 2bf038\n2f 196587\nad 110469\nc 1d40f6\n1c 1d4a57\naa 106dcc\ndc 278c0d\n2d 1d829e\nba 10772d\n3d 1d8bff\n0 41d80\nc 1d53b8\n5b 5f125\n69 62c1e\n2d 19e86e\nca 281904\nfb 284e9b\n19 ac7\ncc 127b9e\ned 12bd46\n4e 1af22e\nfd 12c6a7\n6f 1b3126\ned 12d008\n40 5e91f\n4c 1f1f57\n38 469e1\n99 10c8f2\nce 2b9ae7\n6a 62c18\n7a 1ee3d9\n7a 63579\nac 10f1a8\nb0 107331\nbc 29a969\n3b 4c69\nee 12bd40\nf2 123ec9\n75 1f539b\nfe 2b7501\n6a 63eda\n2e 196588\nac 11046a\n35 d138\nfb 284e9d\n5a 1f12bc\n3b 5f2b\nee 12d002\n70 1ed267\n77 29cd0\n5c 1ea50a\n50 56ed2\n2c 1d829f\n3c 1d8c00\n20 45f29\n2c 1d9561\n62 62ac1\n6e 1f60f9\nbf 108a31\n39 4c70\nec 12bd47\nd3 11fd83\ndf 2b33bb\neb 2847ea\n4a 1f0c09\nfc 12c6a8\n68 63ee1\nfa 28615e\n6e 1b3127\n39 5f32\nec 12d009\n75 29cd7\n60 62ac8\n6c 1f6100\n80 29e089\neb 1249d4\nce 127ba5\n90 29e9ea\nfb 125335\nde 128506\na1 2a2231\nef 12bd4d\nae 107e11\nd4 e661a\n8c 294b9f\nb1 2a2b92\nff 12c6ae\nbe 108772\n9c 295500\nef 12d00f\n80 102829\nd4 e78dc\n8c 295e61\n19 43aa7\n6b 1edacb\n4e 1f0c9c\nd2 e78c2\n55 1b8d94\n23 46f53\nb6 cf209\n94 25bf97\n7b 1ee42c\n5e 1f15fd\n33 478b4\n42 5e926\n4e 1f1f5e\n63 62ace\n6f 1f6106\n6e 1ecaf7\n39 3f902\n1a 427dd\nbb d63be\n1b 43aa2\n1f 43ad3\nbb 1089f2\nbf 108a23\n84 25b324\nbd 108a2a\n9b 10cb99\n3b 3f909\n1b 43ab0\n1f 43ae1\nfb ebe6f\nff ebea0\n39 3f964\n1b 43b04\n19 43b0b\n6 1d4fbc\nb9 108a5b\n6a 62c26\n75 1ed2fb\n6a 63ee8\n81 29cdc6\n75 1ee5bd\n4f 5fa55\nee 12d010\n77 29cde\n62 62acf\n6e 1f6107\n3b 3f96b\na 1d533a\nbc 110dd9\n1d 4381e\n1b 43b12\nbb 108a62\n9d 10c915\n97 29ec5f\n93 10b77e\n9f 29edb6\n31 4783b\na5 ce602\n39 47992\nc6 e6ff2\nfb f3f63\n5a 60382\ne7 eb19a\n59 1f12b8\n7b 6452a\n90 10c7ec\n98 10c943\n10 19c40f\n6 1934ea\nb5 110963\n2 19351b\nb1 110994\ne 193641\n2 9\nbd 110aba\na 193672\nb9 110aeb\nd9 2ba411\n34 3e50b\n23 19664d\nfb 12d683\nb5 d7553\n3 19bab4\n14 43972\n31 47ae9\nc7 2babef\n16 192b87\n94 10ca69\n2 1937c9\n33 196d60\nb1 110c42\nca 120a88\n41 5e922\nfb 12401f\n4d 1f1f5a\n98 10c8e1\ndd f00a2\nfb 12d621\n10 1d58e3\nd7 e7ba0\n18 1d5a3a\n35 1d9a5a\n90 29d718\n9f 2653e8\n93 d1db0\n31 1d9a8b\n0 42fe2\n31 46579\n3d 1d9bb1\n98 29d86f\nf7 28602b\n56 1f244a\nff 286182\n52 5ef69\nf3 f2b4a\n5e 1f25a1\n33 196cfe\n2 193767\nb1 110be0\n41 5e8c0\n4d 1f1ef8\nb5 269741\n14 1d5b60\n71 643da\n85 29d037\nb7 299868\n79 64531\n42 1b00ba\nf1 12d533\n4a 1b0211\n21 196654\nf9 12d68a\n8c 264aef\n80 d14b7\n71 64688\n52 1af757\nd0 129639\nab 106b21\n94 265289\n42 1b0368\n73 1b38ff\nf1 12d7e1\n42 1b0058\n8 1489\nf1 12d4d1\n58 1f25d9\na2 299ee7\ndf 281f87\nd3 ee94f\n71 1f662a\ndb eeaa6\n79 1f6781\n52 1af6f5\nd0 1295d7\nf5 2862e0\nce e5e79\n54 1f26ff\nb2 107398\nbe 29a9d0\ndf 282235\nd3 eebfd\n71 1f68d8\n30 4783c\n23 19f9af\n6a 1eca62\n34 4786d\na4 ce603\n38 47993\n2b 19fb06\n3c 479c4\n9 19a706\n2b d978\n76 64405\ne6 eb19b\n7a 6452b\n7e 6455c\nb4 110964\nb0 110995\nbc 110abb\nb8 110aec\n45 5e643\nab d6a6f\n89 2637fd\nf6 12d4fc\nfe 12d653\nfa 12d684\n23 19fc5d\n34 47b1b\n2b dc26\n9 19a9b4\n76 646b3\n36 196d30\nb4 110c12\nab d6d1d\n89 263aab\nf6 12d7aa\na5 110074\nb0 110933\nb8 110a8a\nfa 12d622\n34 1d9a5b\n30 1d9a8c\n30 4657a\n3c 1d9bb2\nf7 ebd49\n38 1d9be3\n2b 19fb66\n76 1f65f3\n72 63112\n7e 1f674a\n34 1d9d09\n2b 19fe14\n76 1f68a1\nd4 279ada\na2 107c99\n25 1d916b\n63 1bc54e\n29 d97f\n74 6440c\n6b 1bc6a5\n7c 64563\n63 1bc7fc\n29 dc2d\n74 646ba\n72 1b3900\nac 106df6\nf0 12d7e2\n78 1f6782\n2c 1cfeed\n70 1f68d9\n81 102ac8\n8d 296100\nf7 f40f9\n56 60518\nd6 12960f\nf7 286039\n56 1f2458\naf 25f67e\n52 1f2489\nd2 1295de\nf7 2862e7\n56 1f2706\naf 25f92c\n52 1f2737\n2a 3ef99\n50 1d7a2\n5c 1b0dda\nfb 12c36f\n58 1b0e0b\n9c cb1b4\n9e cb1bb\n2a 3effb\n50 1d804\n5c 1b0e3c\nfb 12c3d1\n72 643e2\n76 64413\n7a 64539\n91 10474d\n7e 6456a\nf6 12d50a\nae 106b4f\nf2 12d53b\nfe 12d661\nfa 12d692\n72 64690\n76 646c1\nf6 12d7b8\nae 106dfd\n53 6022e\nf2 12d7e9\nf2 12d4d9\nfa 12d630\n76 1f6601\n2e 1cfc46\n72 1f6632\n7a 1f6789\nf2 12d787\n76 1f68af\n2e 1cfef4\n72 1f68e0\n9c cb1c2\n10 436f5\n97 29ecc1\nfd 2b64e7\nb4 2a2e0e\n52 1f241b\n18 4384c\n93 10b7e0\n9f 29ee18\nb0 10f92d\nbc 2a2f65\n9f d31fa\n31 4789d\n73 1f65c3\n39 479f4\n35 1d9d08\n52 6028d\n1d 4381c\nf6 2bf9a6\n3d 1d9e5f\n31 46827\n5a 603e4\nf2 12c4c5\nfe 2bfafd\n51 1f11c3\n73 64435\n59 1f131a\n7b 6458c\n83 26495f\n94 10c81d\nd6 1293b5\nd5 2ba2eb\nf7 12d55d\n10 439a3\n9f d34a8\n31 47b4b\nc7 2bac51\n52 6053b\n51 1f1471\n73 646e3\nb6 d7559\nff eac32\n5e 57051\n10 1cd535\n83 264c0d\n16 192be9\n94 10cacb\n37 196d91\n6 1937fa\nb5 110c73\nd6 129663\nd5 2ba599\nf7 12d80b\n14 1d5914\n10 42433\n1c 1d5a6b\nb2 1085ea\n97 d1de1\n35 1d9abc\n13 1d4883\n14 1d5bc2\nb2 108898\n97 d208f\n35 1d9d6a\nd7 2bb860\ndf efd99\n71 6443c\n79 64593\n46 1b00eb\nf5 12d564\n46 1b0399\n77 1b3930\nf5 12d812\n54 1f24b3\n9 19a942\n50 5efd2\n5c 1f260a\na6 299f18\n54 1f2761\n30 4789e\n38 479f5\n72 64436\n7a 6458d\nf6 12d55e\nfe 12d6b5\n30 47b4c\nc6 2bac52\n72 646e4\nf6 12d80c\n34 1d9abd\n12 1d4884\n30 465dc\n3c 1d9c14\n1a 1d49db\n34 1d9d6b\n6b 1bc6a3\n2c 3da51\n70 6443d\n78 64594\na9 d59f4\n48 1b8796\n79 1bbd2d\n74 1f665c\n70 6317b\n7c 1f67b3\n74 1f690a\n9e 29d855\naf cd490\n35 1d9d16\n52 6029b\n6a 62916\n89 296131\n31 46835\n3d 1d9e6d\n5a 603f2\nd6 129671\n56 1f24ba\n35 1d8a54\n52 5efd9\n5e 1f2611\n56 1f2768\n71 2194a\n7d 1b4f82\nd8 278c40\n79 1b4fb3\n59 1b915a\n2 1cce87\nb4 108926\na3 260a68\nc9 ee3dc\n7a 6459b\nf6 12d56c\nab d59fb\nfe 12d6c3\n2e 3dd06\n72 646f2\n4a 1b879d\n7b 1bbd34\nc5 2804a4\n76 1f6663\n76 1f6911\n1e 3b782\nbf cf363\n14 1d58b4\nfc 2b6548\n98 296aa1\n9f d350a\ndc 2ba6ef\n5e 58313\nff ebef4\nd8 2b3632\ndf f009b\n89 2637ed\n71 219ac\n7d 1b4fe4\n1c 3b789\nbd cf36a\n1e 192cde\n9c 10cbc0\nd9 2ba6bf\nfb 12d931\n3b 196e55\na 1938be\nb9 110d37\n4 1cb941\na5 25f522\nfb 12d8cf\nb0 10889f\n46 1e84d9\n95 d2096\ne7 27c0ba\n5a 1f2880\n5a 1af8ae\nd8 129790\n90 d1da8\n9c 2653e0\n4a 1b04bf\n7b 1b3a56\nf9 12d938\n5a 1af84c\nd8 12972e\ne6 eb18b\nc4 277f19\n44 1e84e0\ne5 27c0c1\nfd 286437\nf1 f2dff\n50 5f21e\n5c 1f2856\na6 29a164\n58 1f2887\n3 1ccbda\nb5 108679\na2 29a195\n21 577a\n84 29e048\ndb eed54\n79 1f6a2f\na5 2a21f0\n2b 19fdb4\n3c 47c72\ne6 eb449\n7a 647d9\nd8 e7a12\n29 470a3\n7e 6480a\n3e 196e87\nbc 110d69\nfe 12d901\nad 1101cb\nfa 12d932\nfa 12d8d0\na9 11019a\n72 633c0\n7e 1f69f8\ndc 279c31\nd0 e65f9\naa 107df0\n21 45c8a\n2d 1d92c2\n6b 1bc953\n7c 64811\n7a 1b3a57\nf8 12d939\n78 1f6a30\na4 2a21f1\nde 129766\n5a 1f288e\ncf 2b1728\n23 5781\n86 29e04f\n1a 1cd9a3\n97 29ea15\n20 19fc63\n69 1b333c\n12 3a348\n1e 1cd980\n1a 1cd9b1\n7a 647e7\n7e 64818\nfe 12d90f\n5b 60385\nfa 12d940\nfa 12d8de\n72 633ce\n7e 1f6a06\n7a 1f6a37\n9c 1048e4\n10 3a3a3\n1c 1cd9db\nde e7d5a\n9e 1048eb\n12 3a3aa\n1e 1cd9e2\n52 1f26c9\n18 43afa\n73 1f6871\n39 47ca2\nc3 127770\ncf 2bada8\n5a 60692\n59 1f15c8\n7b 6483a\nbe d76b0\n18 1cd68c\n8b 264d64\n1e 192d40\nd6 2bb7f1\n9c 10cc22\ndd 2ba6f0\nff 12d962\n10 426e1\n1c 1d5d19\n4e 1b04f0\n42 1ceb8\n7f 1b3a87\nfd 12d969\n50 5f280\n5c 1f28b8\n7 1ccc0b\na6 29a1c6\n7a 6483b\nab 268f0d\na 1d532c\n3e 196ee9\nbc 110dcb\nfe 12d963\n30 4688a\n3c 1d9ec2\neb 285aac\n4a 1f1ecb\n7e 1b3a88\nfc 12d96a\n70 63429\n7c 1f6a61\nde 1297c8\n52 5f287\n5e 1f28bf\n3b 1d1b4b\n85 264be3\n39 1d1b52\n1b 1d5cf2\n19 1d5cf9\n5f 603b6\nfe 12d971\n72 63430\n7e 1f6a68\nbf 108a85\nb9 2682f7\n18 1d4716\n84 25b386\nb 1ccfed\nbd 108a8c\n9f 10cc2c\n9d 10cc33\n33 3e544\n3f 1d1b7c\n31 3e54b\n3d 1d1b83\n13 426eb\n1f 1d5d23\nbf 108a93\n9f 10cc3a\n33 3e552\n3f 1d1b8a\nf4 ea833\nc0 128c76\n42 1aed94\nfc ea98a\nc8 128dcd\n4a 1aeeeb\n74 5adfa\ne1 12ce1e\n63 1b2f3c\n8 1737\n42 1b0306\nf1 12d77f\n73 1b389d\ncf 2803c6\nea 2b6bcf\n4a 1b045d\nf9 12d8d6\n7b 1b39f4\n8c 25b47b\n9c 25bddc\na5 25f4cc\na1 25f4fd\nad 25f623\nf4 2b60e1\na9 25f654\n80 25c8c7\nb1 25fe5e\n88 25ca1e\nb9 25ffb5\ne3 12482b\ne7 27c064\nf4 123f53\ne3 27c095\nef 27c1bb\nfc 1240aa\neb 27c1ec\n0 1924aa\n4a 576e3\n5a 58044\n4a 1b020f\n21 196652\n6b 5b88b\n5a 1b0b70\n31 196fb3\n9 1ccd2a\naf d6d4e\n8d 263adc\na8 29a2e5\n7b 5c1ec\n74 5b0a8\n63 1b31ea\n84 25b5d2\nce 12080b\nce 279337\nc2 e5cff\na5 25f77a\nef 1249b3\na1 25f7ab\n84 c9128\neb 1249e4\nb1 26010c\n94 c9a89\nfb 125345\n27 1d815c\nd6 278acb\nf4 ebaf5\n42 1b0056\n74 5c0bc\n63 1b41fe\n84 25c5e6\n80 25c617\n80 c9105\n8c 25c73d\n88 25c76e\na5 26078e\na1 2607bf\na1 cd2ad\nad 2608e5\nc2 2791af\nca 279306\nf4 125215\ne3 27d357\n0 19376c\nf4 ebda3\n42 1b0304\n74 5c36a\n63 1b44ac\n84 25c894\n80 25c8c5\nc2 e6fc1\na5 260a3c\na1 260a6d\nc6 27942c\n62 1b2f3b\n6a 1b3092\n72 1b389c\nc5 e6f8a\n7a 1b39f3\n5b 1b7b9b\nc 43168\nad d6d49\na4 25f4cb\na0 25f4fc\nac 25f622\na8 25f653\nb4 25fe2c\n73 29cff\n7f 1bd337\n2b 1d8284\nb0 25fe5d\nbc 25ff83\n75 21989\n8d 2637ca\n23 1cfd6f\nd4 2ba288\n71 219ba\n7d 1b4ff2\n89 2637fb\n9d 26412b\n61 22009\n33 1d06d0\n99 26415c\nc3 1289d2\ne6 27c063\ncd 2bb051\nc1 127a19\ne2 27c094\nee 27c1ba\nc9 127b70\nea 27c1eb\nd4 1280fa\nc3 28023c\n26 448d\ndc 128251\ncb 280393\n1 19a7f9\na0 ce560\n4b 5fa32\n11 19b15a\n88 29e48c\nb0 ceec1\n5b 60393\n75 216cd\n62 1b31e9\n89 10ad23\n81 263952\ncb 128b8b\n95 264282\ndf 1294bb\n51 26b73\ne6 27c311\n71 2acba\nf5 ea826\n43 1aed87\ne2 27c342\nc4 2801f5\nf6 27cc72\nd4 1283a8\nc3 2804ea\n62 1b41fd\na4 26078d\na0 2607be\na0 cd2ac\nac 2608e4\na8 260915\n75 22c4b\n81 d1454\n8d 264a8c\n23 1d1031\ne6 27d325\nc1 128cdb\ne2 27d356\n65 1bb316\ne2 e9e44\nee 27d47c\nc9 128e32\nea 27d4ad\nd4 1293bc\nc3 2814fe\n4 1924dd\ndc 129513\n26 574f\ncb 281655\n97 2966c5\n20 197913\nc6 e5cc2\n1 19babb\n91 cad1b\nc6 277f10\n75 2298f\n62 1b44ab\n89 10bfe5\n54 60511\nf5 f40f2\n43 1b8653\ne6 27d5d3\n56 1af788\nd4 12966a\nc3 2817ac\nb4 1073c4\n2 1cb925\n90 d1e0a\n9c 265442\nc7 2b9983\n12 1cc286\ncf 2b9ada\n1a 1cc3dd\nab 106dcf\n94 265537\n23 1cfacd\n42 1e108\n2b 1cfc24\n2 1cce97\n33 1d042e\n52 1ea69\na 1ccfee\n3b 1d0585\n86 294a4d\n96 2953ae\n31 1d8777\n6b 5bb2b\nc6 e7230\naf 298d4c\n82 295ff0\nb3 299587\n33 1d06dc\nf9 f3f06\n96 29565c\nb4 108686\n2 1ccbe7\n23 1d0d8f\nce 11f84b\n51 1af6ef\n2b 1d0ee6\n82 295d40\n5 1d3d00\n82 10282e\n8e 295e66\n8a 295e97\nd5 278817\na3 1069d6\naf 29a00e\nb4 108934\n2 1cce95\n86 295fbd\n82 295fee\n22 1cfacc\nb0 d5fb1\nbc 2695e9\ne7 2bdb2a\n32 1d042d\nef 2bdc81\n85 103b1b\n3a 1d0584\n13 1d45d5\n1b 1d472c\nae 298d4b\nb6 299555\nb2 299586\n21 1d0dde\n4 1d3faf\nbe 2996ac\n87 29cd9c\n97 29d6fd\nb0 d625f\nbc 269897\ne7 2bddd8\n32 1d06db\nb6 299803\n13 1cc279\nb2 299834\n31 4e19\n3d 198451\n94 29d6e7\n22 1d0d8e\nbd 110b2c\nb 1d508d\n25 1d7ea7\na2 1069d5\nd4 278816\nae 29a00d\nd0 278847\naa 29a03e\ne1 28594e\n74 1b392a\n40 1f1d6d\n87 29e05e\n83 29e08f\nc1 128c77\nf5 ea834\n54 56c53\n43 1aed95\nc9 128dce\nfd ea98b\n5c 56daa\nb4 d72b2\n4b 1aeeec\n14 1cc5b4\n28 436e\ne0 12ce1f\n62 1b2f3d\ne8 12cf76\n6a 1b3094\n38 4ccf\nf0 12d780\n72 1b389e\n85 25b325\n81 25b356\n8d 25b47c\n95 25bc86\n91 25bcb7\n9d 25bddd\n99 25be0e\na4 25f4cd\n87 25b380\n6 8386\na0 25f4fe\nac 25f624\n8f 25b4d7\ne 84dd\na8 25f655\n16 8ce7\nb0 25fe5f\nbc 25ff85\n9f 25be38\n1e 8e3e\nb8 25ffb6\nd4 11fdac\nc3 277eee\ndc 11ff03\ncb 278045\nac cd496\ne6 27c065\ne2 27c096\nee 27c1bc\nea 27c1ed\n1 1924ab\n11 192e0c\n8f d2ba7\n88 29613e\na3 d5904\naf 268f3c\n85 25b5d3\n81 25b604\n95 25bf34\n91 25bf65\n6 8634\na0 25f7ac\nf5 285fce\n16 8f95\nb0 26010d\nd4 12005a\nc3 27819c\n28 5630\n62 1b41ff\n85 25c5e7\n81 25c618\n81 c9106\n8d 25c73e\nd4 2b31fc\n89 25c76f\na4 26078f\n87 25c642\n6 9648\nee ea2dc\n4f 1cd21\na0 2607c0\na0 cd2ae\nac 2608e6\n83 c9161\n8f 25c799\ne 979f\na8 260917\nd4 12106e\nc3 2791b0\nac ce758\ne6 27d327\ne2 e9e46\n65 1bb318\nee 27d47e\nea 27d4af\n1 19376d\n54 581c3\nf5 ebda4\n43 1b0305\n85 25c895\n81 25c8c6\nd4 12131c\nc3 27945e\n4a 1b723b\nef e9fbf\n84 263674\n25 19f9e7\n8c 2637cb\nce e7389\nff ea920\n94 263fd5\nca e73ba\nfb ea951\n90 264006\n35 1a0348\n9c 26412c\nc2 28023d\n67 1bc57f\nca 280394\nef ea26d\n84 263922\nff eabce\n94 264283\na9 ce71a\n8c d18eb\nc6 2804ba\nef eb281\n84 264936\nca 281656\nb5 1073c5\n3 1cb926\nbd 10751c\nb 1cba7d\n85 264bd7\n13 1cc287\n1b 1cc3de\n76 1b4b81\n22 1cface\n7e 1b4cd8\n72 216a0\n2a 1cfc25\nb5 d5f81\n32 1d042f\n3a 1d0586\n87 294a4e\nf6 ea7c8\nae 298d4d\nb6 299557\nb2 299588\nbe 2996ae\n76 1b4e2f\n22 1cfd7c\n32 1d06dd\nb5 108687\n3 1ccbe8\nbd 1087de\nb 1ccd3f\n22 1d0d90\n72 22962\n50 1af6f0\n2a 1d0ee7\nfc 1242f6\nb5 d7243\n25 1cfdfb\n83 295d41\n83 10282f\n8f 295e67\n56 1b8d28\n2 1d3c75\n5e 1b8e7f\n52 25847\na 1d3dcc\n12 1d45d6\n1a 1d472d\n86 29cd9d\n86 29d04b\n96 29d9ac\n2 1d4f37\n52 26b09\na 1d508e\n82 102ad0\n8e 296108\nbf 29969f\n5 1d3fa2\n86 29e05f\n82 29e090\nd6 efc31\n82 10ab7e\n8e 29e1b6\n84 25b378\n8c 25b4cf\n94 25bcd9\n82 102adc\n5 1d3fae\n8e 296114\n9c 25be30\n35 19805c\nc6 1206a6\na5 25f520\n50 1f2482\nad 25f677\n80 c9409\n8c 25ca41\nbd 25ffd8\ne7 12484e\n99 cae72\nce 278067\nce e5e19\n9 19bc12\nd6 278871\nd6 e6623\n11 19c41c\nde 2789c8\nde e677a\n19 19c573\n95 d2094\ne7 27c0b8\n9d d21eb\nef 27c20f\nc2 e5fa1\nce 2795d9\nff 27cb70\n84 25b626\na5 25f7ce\nef 124a07\nd6 278b1f\n80 c9159\n8c 25c791\na5 2607e2\na1 cd301\nad 260939\nc6 2791d2\n6b 62979\nc2 e5cf1\na5 25f76c\nce 279329\n95 d3356\ne7 27d37a\n9d d34ad\ne3 e9e99\nef 27d4d1\n84 25c8e8\nd8 e69fe\n29 4608f\na5 260a90\n4f 1f09f1\na4 25f51f\nac 25f676\n25 1d8155\na2 106c83\nd4 278ac4\nae 29a2bb\n85 2636c7\n8d 26381e\n9d 26417f\nc7 1289f5\n94 d2093\nb1 ceec2\ne6 27c0b7\ne6 e9e69\n21 19fc62\nb9 cf019\n9c d21ea\nee 27c20e\nee e9fc0\n29 19fdb9\nf6 27ca18\nf6 ea7ca\n31 1a05c3\nfe 27cb6f\nfe ea921\n39 1a071a\n85 263975\n1a 3b4b1\ncf 128bae\nb1 cf170\ne6 27c365\n71 2ad0e\nf6 27ccc6\n43 5e67b\n4f 1f1cb3\na4 2607e1\n85 264989\n81 d14a8\n8d 264ae0\ndd e6a2e\n94 d3355\ne6 27d379\n9c d34ac\ne2 e9e98\nee 27d4d0\nc7 281521\ne4 28566e\nc3 ee040\ncf 281678\ne0 f218d\nec 2857c5\ne6 27d627\nc4 277f09\ncc 278060\na2 106a29\nae 29a061\nd4 27886a\nc2 11f66d\n45 1f0b3f\nce 2b2ca5\naa 106b80\ndc 2789c1\ne5 27c0b1\ned 27c208\nc0 e5f9a\ncc 2795d2\nfd 27cb69\n96 295402\n9e 295559\n86 296013\nb7 2995aa\nc4 2781b7\ne5 27c35f\nf9 f3f5a\n96 2956b0\nc4 2791cb\n69 62972\nc0 e5cea\ncc 279322\ne5 27d373\ne1 e9e92\ned 27d4ca\n86 295d63\nc4 279479\n69 62c20\ne5 27d621\ne4 27c0b0\nec 27c207\nf4 27ca11\n65 1f4ce6\ne2 123814\nee 2b6e4c\nfc 27cb68\nc5 280258\ncd 2803af\nae 298d9f\nb6 2995a9\nbe 299700\n87 29cdf0\na4 2a0f3d\n8f 29cf47\nac 2a1094\n97 29d751\nee 124c52\nb4 2a189e\n9f 29d8a8\nbc 2a19f5\ne4 27c35e\nf4 27ccbf\nc5 280506\nb6 299857\ne4 27d372\ne0 e9e91\nec 27d4c9\nc5 28151a\nc1 ee039\ncd 281671\n9d 1035bf\na6 299f0a\n87 29e0b2\n83 10abd1\n8f 29e209\na0 10ed1e\nac 2a2356\ne4 27d620\nc5 2817c8\na6 29a1b8\n2d 19fb30\n21 c4f8\n29 c64f\n4 3f\n39 cfb0\n3b cfb7\n3d cfe1\n5b 1b8e51\nfa 28640c\n6d 1bc6c1\n61 29089\n6f 1bc6c8\n63 29090\n65 290ba\ne7 27c374\n7b 1f5704\n60 1ec658\n67 290c1\n4c 1e98fb\n40 562c3\n69 291e0\n6b 291e7\n6d 29211\n68 1ec7af\n6f 29218\n48 5641a\n7d 1bd022\n71 299ea\n44 1cbd0\n79 29b41\n46 1cbd7\n7b 29b48\n7d 29b72\n78 1ed110\n7f 29b79\n58 56d7b\nad 268c27\na1 d55ef\naf 268c2e\na3 d55f6\na5 d5620\n79 29e5f\na9 d5746\nab d574d\nad d5777\nbd 269588\nb1 d5f50\nbf 26958f\nb3 d5f57\n84 c9136\nb9 d60a7\nbb d60ae\nbd d60d8\ned 2857b8\ne1 f2180\nef 2857bf\ne3 f2187\ne5 f21b1\ne7 f21b8\ne9 f22d7\neb f22de\ned f2308\nef f230f\nc8 11f511\nfd 286119\nf1 f2ae1\nff 286120\nf3 f2ae8\nf5 f2b12\n6a 1ec7b6\nf7 f2b19\ndc 2b3353\nd0 11fd1b\nc4 e5cc7\nf9 f2c38\nc6 e5cce\nfb f2c3f\nfd f2c69\nff f2c70\nd8 11fe72\n2d 19fb3e\n21 c506\n25 c537\n3b 1d8b81\n29 c65d\n2d c68e\n7f 1f674b\n73 63113\n65 290c8\n7b 1f5712\n6d 2921f\nad 268c35\na1 d55fd\na5 d562e\na9 d5754\nad d5785\nff 2bf842\na 1c2\nf3 12c20a\nbd 269596\nb1 d5f5e\n16 192beb\n94 10cacd\n83 264c0f\nb9 d60b5\n1e 192d42\n9c 10cc24\n8b 264d66\nfd 286127\nf1 f2aef\n56 1af77c\nc3 2817a0\nf9 f2c46\n5e 1af8d3\ncb 2818f7\n86 25b37f\n8e 25b4d6\n96 25bce0\n9e 25be37\nf9 284e88\nc4 277f17\nff 125616\na2 106a37\nae 29a06f\nd4 278878\naa 106b8e\ndc 2789cf\ne5 27c0bf\ned 27c216\nc4 279489\nf5 27ca20\ncc 2795e0\nc0 e5fa8\nfd 27cb77\n96 25bf8e\nf9 285136\nc4 2781c5\ne5 27c36d\n86 25c641\n5d 1b7c29\nda e6757\n78 1ee432\n2b 45de8\n82 c9160\n51 1e90bf\n8e 25c798\nf9 28614a\nc4 2791d9\nc0 e5cf8\ncc 279330\ne5 27d381\ne1 e9ea0\ned 27d4d8\nf9 2863f8\nc4 279487\n88 2637ee\ne5 27d62f\nb6 25fe87\ne4 27c0be\nec 27c215\nf4 27ca1f\nfc 27cb76\ncd 2803bd\nf3 f3daa\n63 1ec962\nb6 260135\n45 1aedb1\ne4 27c36c\nc5 280514\n87 264990\n83 d14af\n8f 264ae7\na0 d55fc\nac 268c34\ne4 27d380\n5a 1e8f62\ne0 e9e9f\nec 27d4d7\n45 1b0073\ne4 27d62e\nc5 2817d6\nb9 2a1a19\n84 294aa8\nce 280673\n4a 1e98c1\n99 d347e\neb 27d4a2\n94 295409\n9c 295560\nc4 e728b\nad 298da7\nfe 2b64dd\n84 29601a\nb5 2995b1\nd4 e7bec\n8c 296171\n80 102b39\nbd 299708\nb9 2a1cc7\n84 294d56\n94 2956b7\na5 298efe\nb5 29985f\nb9 2a2cdb\n84 295d6a\na1 106a31\nad 29a069\nf2 124167\nfe 2b779f\nb9 2a2f89\n84 296018\na4 298c4f\nb4 2995b0\n85 29cdf7\nd6 2ba52d\n5 1cb942\na4 298efd\n77 5ae04\n15 1cc2a3\nb4 29985e\n85 29d0a5\n95 29da06\nea 27c48b\na4 299f11\n5 1ccc04\na4 29a1bf\nde 2b33bc\nb5 2997ff\nd2 11fd84\n77 5c0c6\n70 1b4bab\n2f 19fb99\n23 c561\n29 c6b1\n78 1b4d02\n2b c6b8\nba cf021\n3d 1a04f3\n31 cebb\n5d 1b9137\n51 25aff\n2b 472f6\n3f 1a04fa\n33 cec2\n39 d012\n3b d019\n69 29242\n6b 29249\nfa ebbb2\n7d 1bd084\n71 29a4c\n6b 63e87\n7f 1bd08b\n73 29a53\n79 29ba3\n7b 29baa\n8 41bc7\na9 d57a8\n18 42528\n84 c9198\nb9 d6109\ne9 f2339\n4a 5e75f\neb f2340\n69 5b884\nc4 e5d29\nf9 f2c9a\nc6 e5d30\n5a 5f0c0\nfb f2ca1\n79 5c1e5\n3d 1a0501\nce 128b4b\n31 cec9\n39 d020\n69 29250\nfa ebbc0\n7d 1bd092\n71 29a5a\n79 29bb1\n18 42536\nb9 d6117\n48 5e766\ne9 f2347\n58 5f0c7\nf9 f2ca8\n85 25b379\n8d 25b4d0\n9d 25be31\nac 25f678\nbc 25ffd9\n9c d21ec\nb9 cf01b\nee 27c210\n85 25b627\n85 25c63b\ne7 ea188\n46 565a7\n7b 63518\n81 c915a\n8d 25c792\n94 d3357\ne6 27d37b\n9c d34ae\ne2 e9e9a\nee 27d4d2\n66 290ce\n85 25c8e9\n8c 26381f\nc2 ef5bf\nf3 f2b56\nff 28618e\n9c 264180\nd6 280bc1\n9 193678\n54 1ea105\nde 280d18\n50 56c24\n5c 1ea25c\nd6 280e6f\nb 41e7d\nf9 27e0a8\n9 193926\n54 1ea3b3\nd8 1213e0\nc6 281522\na5 267abc\nc2 ee041\nce 281679\nc5 277f0a\nec 27c209\nfc 27cb6a\na6 298c4a\nb6 2995ab\nbe 299702\nc5 2781b8\na6 298ef8\ne4 eb192\nb6 299859\nc5 2791cc\nc1 e5ceb\ncd 279323\ne0 e9e93\nec 27d4cb\na6 299f0c\nc5 27947a\ncc 2803b0\naa 10eed0\ndc 280d11\n96 29d752\n9e 29d8a9\n86 29d09f\na9 29a2d8\nc4 ef339\n96 29da00\nb9 29ac39\nc0 ee03a\ncc 281672\n86 29e0b3\n6f 1bc976\n63 2933e\n65 29368\n60 1ec906\n67 2936f\n4c 1e9ba9\n7d 1ed140\n40 56571\n75 29cc9\nad 268ed5\nca ef45a\na1 d589d\naf 268edc\na3 d58a4\nce ef48b\na5 d58ce\nbd 269836\nda efdbb\nb1 d61fe\nbf 26983d\nb3 d6205\nde efdec\nb5 d622f\n7e 1b4f86\n72 2194e\n2a 1cfed3\ned 285a66\ne1 f242e\nef 285a6d\ne3 f2435\ne5 f245f\ne7 f2466\ncc 2b2ca0\nfd 2b6237\nc0 11f668\nfd 2863c7\nf1 f2d8f\nff 2863ce\nf3 f2d96\nf5 f2dc0\n6a 1eca64\nf7 f2dc7\ndc 2b3601\nd0 11ffc9\n25 c7e5\n65 29376\nad 268ee3\nca ef468\na1 d58ab\nce ef499\na5 d58dc\nbd 269844\nda efdc9\n62 1ec64f\nb1 d620c\nfd 2863d5\nf1 f2d9d\nb6 25fe89\nc5 277f18\ne4 27c0c0\nec 27c217\nfc 27cb78\nb6 260137\nc5 2781c6\ne4 27c36e\nc5 2791da\nc1 e5cf9\ncd 279331\ne0 e9ea1\nec 27d4d9\nc5 279488\nc4 280267\ncc 2803be\naa 10eede\ndc 280d1f\nf1 27dca5\na2 10f035\nf 1d50b2\n3 41a7a\nae 2a266d\nd4 280e76\n98 10484f\n86 264991\na4 298c51\nb4 2995b2\n85 294d57\na4 298eff\nb4 299860\n85 295d6b\nd6 2b34a1\n81 10288a\n8d 295ec2\nd2 11ffc0\nde 2b35f8\na4 299f13\n85 296019\nef 123743\n84 29cdf8\nd4 ee9ca\n8c 29cf4f\n9c 29d8b0\na1 299ed5\nef 1239f1\n84 29d0a6\nb1 29a836\nff 124352\n94 29da07\n7f 21ae5\ne4 2bf0a0\n31 197fc9\nba cf2cf\n3d 1a07a1\n31 d169\ne2 2b6a78\nc7 28026f\n3f 1a07a8\n33 d170\n6f 1bc9d8\n63 293a0\n7f 1bd339\n73 29d01\nad 268f37\nc 1d5356\n0 41d1e\na1 d58ff\n6b 1f5dc5\nef 285acf\n42 5e8b6\n4e 1f1eee\ne3 f2497\n61 5b9db\nfd 286429\nf1 f2df1\n3d 1a07af\nce 128df9\nff 12c390\n31 d177\nfa ebe6e\n7d 1bd340\n71 29d08\nad 268f45\nc 1d5364\n0 41d2c\na1 d590d\ned 285ad6\n40 5e8bd\n4c 1f1ef5\ne1 f249e\n29 d911\n61 2a34b\n63 2a352\n6 1cbbf8\n69 2a4a2\ne8 123964\na1 d68b1\ncc 2805fa\nec 123995\na5 d68e2\n79 2b121\na9 d6a08\nad d6a39\ne1 f3442\ne5 f3473\ne7 f347a\nc0 12067c\n86 294cef\ne9 f3599\ned f35ca\nef f35d1\nc8 1207d3\n61 2a359\n6 1cbc06\n69 2a4b0\na5 d68f0\nc5 2b9988\nad d6a47\ncd 2b9adf\na 1484\nf3 12d4cc\ne1 f3450\ne5 f3481\n86 294cfd\ne9 f35a7\ned f35d8\n9c 25be3e\nc6 1206b4\n18 1d5c88\n84 25c8f8\nb5 25fe8f\n80 c9417\n8c 25ca4f\nbd 25ffe6\ne7 12485c\nd6 27887f\nde 2789d6\nef 27c21d\nc2 e5faf\nce 2795e7\nff 27cb7e\nb9 2685a5\n18 1d49c4\n84 25b634\na5 106cb0\nce 12086d\n39 1d8b6c\na5 25f7dc\nef 124a15\nb5 26013d\nff 125376\nd6 278b2d\n80 c9167\n8c 25c79f\nd2 2b3224\n39 1d9b80\na5 2607f0\nf6 27df26\na1 cd30f\nad 260947\n75 1bbf17\nf2 eaa45\nfe 27e07d\ne3 e9ea7\nef 27d4df\nb9 269867\n18 1d5c86\n84 25c8f6\n39 1d9e2e\na5 260a9e\nfb 2863ff\n5a 1f281e\nc6 27948e\na4 25f52d\nb4 25fe8e\nb1 ceed0\ne6 27c0c5\nb9 cf027\nee 27c21c\nf6 27ca26\nfe 27cb7d\nd7 280bce\ndf 280d25\n85 263983\ncf 128bbc\n95 2642e4\ndf 12951d\n47 1aedb8\nb1 cf17e\ne6 27c373\n2d 4330\n71 2ad1c\n57 1af719\nf6 27ccd4\na0 cd30e\nac 260946\nd7 280bc2\nf2 2b73cb\ne6 27d387\ne2 e9ea6\nee 27d4de\n47 1b007a\ne6 27d635\n8e 294c06\n96 295410\n31 1d87d9\n6b 5bb8d\n9e 295567\n86 296021\nb7 2995b8\nf9 f3f68\n96 2956be\nbb 2a2ce2\n86 295d71\n82 102890\n8e 295ec8\nbb 2a2f90\n86 29601f\nb6 2995b7\n17 1cc2aa\nb6 299865\n87 29e0c0\n83 10abdf\n8f 29e217\n29 d973\n6 1cbc5a\n69 2a504\n6b 2a50b\n0 42d32\ne8 1239c6\na1 d6913\ncc 28065c\n8 42e89\na9 d6a6a\na 42e90\nab d6a71\ne8 2bdc56\ne1 f34a4\n86 294d51\ne9 f35fb\n61 2a3bb\n6 1cbc68\n69 2a512\n40 5f8d1\ne1 f34b2\n21 da68\n61 2a5f9\n63 2a600\n34 4b3b\na1 d6b5f\n74 216cc\ne1 f36f0\n61 2a607\n74 216da\ne1 f36fe\n95 25bce8\n9d 25be3f\n38 1d88bf\na4 25f52f\nbc 25ffe7\nd7 278880\ndf 2789d7\nb9 cf029\nee 27c21e\nf6 27ca28\nfe 27cb7f\n19 1d49c5\n85 25b635\n95 25bf96\n38 1d8b6d\na4 25f7dd\nf6 27ccd6\n19 1d59d9\n85 25c649\n27 1d9410\nd6 279d7f\n7b 63526\n81 c9168\n8d 25c7a0\n23 45f2f\nd2 e689e\n55 1b7d70\n2f 1d9567\nde 279ed6\n2c 4731f\nd3 2b3225\na0 cd310\nac 260948\nf2 2b73cd\ne2 e9ea8\nee 27d4e0\n19 1d5c87\n85 25c8f7\n75 1f68a7\nef ea021\n84 2636d6\nff ea982\nce e73eb\n94 264037\n9c 26418e\nd6 280bcf\nb 41bdd\nf9 27de08\nde 280d26\nb1 261114\n10 1cd533\nff eac30\n94 2642e5\ne9 27d755\nf3 27dcac\n52 1ea0cb\nd6 280e7d\nb 41e8b\nf9 27e0b6\n8f 294c07\n98 29eddf\nb6 2995b9\nf1 284fdd\nb6 299867\n87 295d72\n83 102891\n8f 295ec9\n86 29e0c1\n82 10abe0\nd6 efc93\n8e 29e218\n21 daca\n63 2a662\n0 42fe0\na1 d6bc1\n2 42fe7\n4b 566c0\na3 d6bc8\ne0 2bddad\ne1 f3752\n61 2a669\n0 42fee\n49 566c7\na1 d6bcf\n40 5fb7f\ne1 f3760\n8 1925f3\n18 192f54\n39 1970fc\nfc eac2a\n4a 1af18b\n6b 1b3333\n7b 1b3c94\nda e67a9\nbd 260224\neb 27c48c\n46 1e8797\nfb 27cded\n56 1e90f8\n8 1938b5\nfc ebeec\n4a 1b044d\n6b 1b45f5\n88 25ca0e\na1 cd54d\nca e710a\nad 260b85\n28 19679a\n38 1970fb\n19 19b2a3\n5b 1b7e3b\n32 465f1\n3e 1d9c29\n8d 263a6a\n89 263a9b\n9d 2643cb\n8 398db\n42 1e84aa\n99 2643fc\nfa 27cdec\ncb 280633\n34 19805b\n49 1e9b77\ndb 280f94\n8a ca579\n59 1ea4d8\n28 197a5c\n9 19bc04\nfd f423b\n4b 1b879c\n48 1af184\n58 1afae5\n69 1b332c\n79 1b3c8d\ned 27c454\ne9 27c485\n66 5ba02\n44 1e8790\nfd 27cdb5\nf9 27cde6\n9 192664\n76 5c363\n54 1e90f1\nd2 2bb830\n8e 294e44\nb8 110da8\n4e 1f09e2\n19 437ed\n9e 2957a5\nab 29901d\n48 1b0446\nc0 e5f36\ncc 27956e\nc8 27959f\n82 102ace\n5 1d3fa0\n8e 296106\nd1 278ae8\nab 29a2df\n68 1b332b\n78 1b3c8c\nec 27c453\nfc 27cdb4\n72 63182\n7e 1f67ba\ncd 2805fb\nc9 28062c\nd6 e6683\n8e 294c08\nd9 280f8d\n9e 295569\n4 3aa23\na5 ce604\n6e 1f4b89\n39 47994\naa 29901c\nbe 29994c\n8b 29d1c4\n68 1b45ed\ne0 ea0dd\nec 27d715\n2e 3da58\nc1 ee285\n72 64444\ncd 2818bd\n2a 3da89\n5c 1af8ca\nc9 2818ee\nd6 e7945\n82 102892\n8e 295eca\na2 106c75\nd4 278ab6\n25 1d8147\nae 29a2ad\nbc ce099\na 1925fa\n1a 192f5b\n3b 197103\n58 1afaf3\n79 1b3c9b\ned 27c462\n92 103491\n9e 296ac9\n1 19b7ff\ne9 27c493\nbc cf35b\na 1938bc\n3c 3f922\n2b 197a64\n48 1b0454\nc8 2795ad\ne1 ea0ec\ned 27d724\n2a 1967a1\n85 ca699\n3a 197102\n1b 19b2aa\ndf 2b20eb\n96 29ea12\n68 1b3339\n78 1b3c9a\n49 1b74e1\n59 1b7e42\naa 25f8fa\nba 26025b\n49 5fa8d\nd6 2ba52f\n9c 10b960\n8b 263aa2\n9 1ccfe6\n9b 264403\n19 1cd947\n49 1aeed7\n0 19b7fe\ne8 27c492\ncd 280609\nc9 28063a\nd9 280f9b\n2a 197a63\nbd d76aa\n1c 43ac9\nb 19bc0b\n49 1b87a3\n49 1b0199\ne8 27d754\n2e 3da66\nc1 ee293\ncd 2818cb\n2a 3da97\n5c 1af8d8\nc9 2818fc\n29 1cfecb\n39 1d082c\n8c 294e4b\nca 11f578\nad 298ff3\n80 102ad5\n8c 29610d\na1 106c7d\nca 12083a\nad 29a2b5\n2f c933\n28 1cfeca\n3f d294\n38 1d082b\n51 1f2423\nac 298ff2\naf d5a8c\n9 1cba68\na8 299023\n32 47851\n7b 5af2a\nbc 299953\nbf d63ed\n19 1cc3c9\nb8 299984\n8d 29d19a\n9d 29dafb\n2f dbf5\nd 19a983\n28 1d118c\na0 106c7c\nac 29a2b4\n81 10ae24\n8d 29e45c\n19 192f55\n28 19679c\n9d 25c07d\n99 25c0ae\nac 25f8c4\na1 298c13\na8 25f8f5\nc0 ef548\nf1 f2adf\nfd 286117\nbc 260225\nb1 299574\nb8 260256\n59 26cbe\nee 27c45c\n9d 29edbf\n91 10b787\ne3 2b57ab\nea 27c48d\nb7 110c7a\n95 29da08\nfe 27cdbd\nf3 2b610c\nc 1cbaa6\nad 25f687\nfa 27cdee\n9 1938b6\nfd ebeed\n4b 1b044e\n8 19a943\n7b 1bd2b2\n8c 263a6b\nf3 f2da2\nff 2863da\n81 29cdba\n88 263a9c\n4a 26363\n21 c7a6\n2d 19fdde\nfb 28640b\nce 280603\nc3 2b9952\n68 1b332d\n78 1b3c8e\n80 264c05\nc9 2782de\nec 27c455\ne8 27c486\nfc 27cdb6\nf8 27cde7\n8b 294e76\n9b 2957d7\naa 29901e\nec eb287\nbe 29994e\ne8 eb2b8\nba 29997f\n49 1b0447\nc1 e5f37\n72 5c0f6\ncd 27956f\nc9 2795a0\n48 1b74d4\n58 1b7e35\ncc 2805fc\nc8 28062d\n61 29337\n6d 1bc96f\naa 10f11c\ndc 280f5d\nd8 280f8e\n71 29c98\n7d 1bd2d0\n8e 29d194\n8a 29d1c5\n46 5f909\ne7 f34ea\ncc ef42e\n9e 29daf5\nc8 ef45f\n9a 29db26\n56 6026a\nf7 f3e4b\n1b 192f5c\n2a 1967a3\nd6 2b21e1\n9c 103612\n8b 25b754\n9b 25c0b5\n19 a12d\nae 25f8cb\na3 298c1a\naa 25f8fc\nf3 f2ae6\nc2 ef54f\nff 28611e\nbe 26022c\nb3 29957b\nba 26025d\n84 264be2\ncd 2782bb\n80 264c13\nc9 2782ec\nb2 d6266\nbe 26989e\n61 1bc59b\n4e 2531c\n0 19b800\ne8 27c494\n1c 3b77b\nbd cf35c\nb 1938bd\n49 1b0455\nc1 e5f45\ncd 27957d\nc9 2795ae\na 19a94a\n9d 295553\n48 1b74e2\n8e 263a72\n83 29cdc1\ncc 28060a\ne3 12cbcb\nc1 2b9959\nc8 28063b\n61 29345\n6d 1bc97d\n9 1cbd24\n19 1cc685\nc0 128c78\n42 1aed96\n8 1c7\nf1 12c20f\nfd 2bf847\n1e 4280e\nbf d63ef\nb8 299986\n50 25aee\n5c 1b9126\n8 1d4073\n7 12fb\n18 1d49d4\nd4 eec16\n8c 29d19b\n9c 29dafc\n87 ca454\n98 29db2d\n8 192601\n29 1967a9\n39 19710a\n35 e14c\n7c 5b1ff\n6b 1b3341\n7b 1b3ca2\nca e5e56\nad 25f8d1\nf4 2b638f\na9 25f902\nb9 260263\ne3 124ad9\n8 1938c3\nfc ebefa\n4a 1b045b\n7c 5c4c1\n6b 1b4603\n88 25ca1c\na1 cd55b\nca e7118\nad 260b93\nc2 e5f4b\n45 1b741d\nce 279583\nc3 2b28d2\n9f 29555a\n28 1967a8\n5e 1e8f91\nb6 269499\nff 27cb72\ned 284806\n38 197109\n9 19a950\n19 19b2b1\n7d 21824\n34 e14b\n6a 1b3340\nc5 e7238\n7a 1b3ca1\n8d 29e450\n81 10ae18\n8d 263a78\nd4 2ba536\n89 263aa9\n9d 2643d9\n99 26440a\n56 56c5c\nc3 128c80\n59 26cca\nee 27c468\ne3 2b57b7\n16 1cc567\n79 2ae11\n4b 1aeede\nfd ea97d\n2 19b805\nb4 d72a4\nea 27c499\ncc 28034c\n61 1b44fb\nfe 27cdc9\nc2 2b2b81\nf3 2b6118\n25 1cfaeb\ndc 1284ff\ncb 280641\n93 1031e4\n9f 29681c\n28 197a6a\n7d 22ae6\n6a 1b4602\n81 d1702\n8d 264d3a\n65 1bb5c4\ne2 ea0f2\nee 27d72a\ne3 2b6a79\n5e 1af8df\ndc 1297c1\ncb 281903\n2b 1cfed2\n53 1f242a\n19 4385b\nae 298ff9\n6e 1f4b97\n39 479a2\n29 460f\n63 1b31de\n8c 29cedd\n21 1d108c\nbe 29995a\nd0 278af5\nb 1ccd31\nbd 1087d0\naa 29a2ec\n29 58d1\n63 1b44a0\n80 10ab67\n8c 29e19f\n9 192602\n19 192f63\n6a 1b3342\n8d 25b72a\nd4 2b21e8\nf6 12545a\n89 25b75b\n9d 25c08b\n68 1ecabd\n21 19fa0a\n99 25c0bc\ne 878b\na8 25f903\nf1 f2aed\nc0 ef556\nfd 286125\n1e 90ec\nb8 260264\n95 d30fe\ndc 1201b1\ncb 2782f3\ndb 278c54\n9 1938c4\n5c 5831a\nfd ebefb\n4b 1b045c\n81 c93b4\n8d 25c9ec\nd4 2b34aa\n89 25ca1d\ndc 121473\ncb 2795b5\n8 19a951\n8c 263a79\na3 11003a\nd5 281e7b\n81 29cdc8\n88 263aaa\n4a 26371\n21 c7b4\n2d 19fdec\nce 280611\nca 280642\n3a 1d0834\nd6 eec1d\n8e 29d1a2\n9e 29db03\n8c 25b77d\n50 1f2730\nad 25f925\nbd 260286\ne7 124afc\n80 c9407\n8c 25ca3f\na1 cd5af\nad 260be7\n8d 263acc\n9d 26442d\n42 1e850c\nc7 128ca3\nb9 cf2c7\nee 27c4bc\n16 1cc5bb\n79 2ae65\nfe 27ce1d\ne2 ea146\nee 27d77e\ned 27c4b6\nfd 27ce17\n8e 294ea6\nae 298fed\n19 4384f\n9e 295807\nc0 e5f98\ncc 2795d0\ne1 ea140\ned 27d778\nec 27c4b5\nfc 27ce16\ncd 28065d\n53 1f247e\nae 29904d\n39 479f6\nbe 2999ae\ne0 ea13f\nec 27d777\nc1 ee2e7\ncd 28191f\n3f 1d9bba\n33 46582\n37 465b3\n6 39768\n3b 466d9\n3f 4670a\nd2 279db0\na7 10ed49\n82 294d2e\nd6 279de1\n7b 63588\na5 10ed50\na2 107fa7\n3 3a9ec\nd4 279de8\nda 279f07\nfd f3f29\na9 10ee76\nd8 279f0e\naf 10eea0\n8a 294e85\nd2 e6900\nde 279f38\nad 10eea7\nd0 e6907\naa 1080fe\nb 3ab43\ndc 279f3f\nbf 2a2cb1\nb3 10f679\nb7 10f6aa\n86 10285f\nbb 10f7d0\nb9 10f7d7\n1e 1cc464\n8b 29e488\nbf 10f801\n6f 1bc6d6\n63 2909e\n67 290cf\n6b 291f5\n6f 29226\n7f 29b87\n3f 1d9bc8\n33 46590\n37 465c1\n3f 46718\ne7 f21c6\neb f22ec\nef f231d\nf4 2b73a1\na7 10ed57\nd6 279def\nd2 e690e\nde 279f46\n5 19b830\ned 27c4c4\n15 19c191\nfd 27ce25\nc0 e5fa6\ncc 2795de\ne1 ea14e\ned 27d786\n53 1b8d5c\nae 25f92b\n39 e2d4\nbe 26028c\n4 19b82f\n4d 1aef08\nec 27c4c3\n5d 1af869\n2b 3da28\n14 19c190\nfc 27ce24\ncd 28066b\nf3 f4058\na2 cd5b5\nae 260bed\n4d 1b01ca\ne0 ea14d\n41 1cb92\nec 27d785\nc1 ee2f5\ncd 28192d\n9c 29580e\nad 299055\nbd 2999b6\na1 106cdf\nad 29a317\nd 1cba99\n51 1f2485\nac 299054\n36 47882\n7f 5af5b\n1d 1cc3fa\nbc 2999b5\n8d 29d1fc\nb3 110be9\n9d 29db5d\n5d 1b7c1b\nda e6749\n2b 45dda\n3b 4673b\n39 46742\n94 25cf9b\n86 1028c1\nbb 10f832\nb9 10f839\n6b 29257\n7f 1bd099\n73 29a61\n7b 29bb8\nde 279c38\n70 1ee2db\n55 1b7ad2\nd2 e6600\n2f 1d92c9\n23 45c91\n3f 1d9c2a\n33 465f2\n3b 46749\n96 25cfa2\n4a 5e76d\neb f234e\n8d 25b77e\n9d 25c0df\nac 25f926\nbc 260287\nb9 cf2c9\nee 27c4be\nfe 27ce1f\n6e 29225\n81 c9408\n8d 25ca40\n8c 263acd\nf3 f2e04\nff 28643c\n84 264c36\ncd 27830f\nec 27c4b7\nfc 27ce18\nae 29904f\nec eb2e9\nbe 2999b0\nc1 e5f99\ncd 2795d1\ncc 28065e\naa 10f17e\ndc 280fbf\n8e 29d1f6\ncc ef490\n9e 29db57\n55 1b7d10\n2f 1d9507\n23 45ecf\n4a 5fa93\n75 2af89\n2d 1d950e\n21 45ed6\n90 c9aba\n9c 25d0f2\n27 45f00\n3f 1d9e68\n33 46830\n5a 603f4\n3d 1d9e6f\n31 46837\n37 46861\nad 2a2605\nca 128b8a\nf5 f4080\na1 10efcd\na7 10eff7\nbf 2a2f5f\nb3 10f927\nbd 2a2f66\nda 1294eb\nb1 10f92e\nb7 10f958\n6f 1bc984\n63 2934c\n67 2937d\n92 c9ac1\n9e 25d0f9\n74 1ee558\n27 45f0e\n3f 1d9e76\n33 4683e\n37 4686f\ne7 f2474\nae 25f92d\nd0 11fd19\n8f 10ad07\ndc 2b3351\nbe 26028e\n4 19b831\nec 27c4c5\n14 19c192\n2b 3da2a\nfc 27ce26\nc1 e5fa7\ncd 2795df\nf4 eaad1\nac 299056\nbc 2999b7\n81 102b38\n8d 296170\nb9 29a98d\n9c 29db5e\nbf 2a2fc1\nb3 10f989\nbd 2a2fc8\nb1 10f990\n7f 1bd347\n73 29d0f\nde 279ee6\n70 1ee589\nd2 e68ae\n2f 1d9577\n55 1b7d80\n23 45f3f\n3f 1d9ed8\n33 468a0\nef 285add\n4e 1f1efc\n42 5e8c4\ne3 f24a5\nff 28643e\n52 5f225\n5e 1f285d\nf3 f2e06\n4a 1f095d\nbf 2a2fcf\nb3 10f997\n9c 25c07e\n29 47041\nd5 281e1b\na3 10ffda\nce 2b9d23\na7 11000b\n14 8f8e\n7b 6484a\ndd 281f72\nd1 ee93a\nab 110131\na9 110138\nd5 ee96b\naf 110162\nad 110169\n63 2a360\n6b 2a4b7\n51 25851\n5d 1b8e89\n2b 47048\nbe cf2fe\n9c 25c08c\ne3 f3457\ne7 f3488\neb f35ae\nef f35df\na7 110019\nd5 ee979\nf0 125182\naf 110170\nbd 260294\ne7 124b0a\nde 278c84\n80 c9415\n8c 25ca4d\nd2 2b34d2\na1 cd5bd\nad 260bf5\nf3 2b767a\nc2 e5fad\nce 2795e5\n9d 26443b\nc7 128cb1\n42 1e851a\n6 19b836\nb9 cf2d5\n4f 1aef0f\nee 27c4ca\n16 1cc5c9\n79 2ae73\n5f 1af870\n16 19c197\nfe 27ce2b\n25 1cfb4d\ne2 ea154\n43 1cb99\n4f 1b01d1\nee 27d78c\n9e 295815\n82 102b3e\n8e 296176\n53 1f248c\nf 1cbaa0\nae 29905b\n39 47a04\n1f 1cc401\nbe 2999bc\na2 106ce5\nf 1ccd62\n3 3972a\nd4 278b26\nae 29a31d\n51 258a5\nda e7a0b\n5d 1b8edd\n2b 4709c\n9c 25c0e0\ndd 281fd4\nd1 ee99c\nab 110193\n63 2a3c2\n6b 2a519\n51 258b3\n5d 1b8eeb\nda e7a19\n2b 470aa\nbe cf360\n9c 25c0ee\n42 5f8d8\ne3 f34b9\n4a 5fa2f\neb f3610\nd5 281e8b\na3 11004a\n99 10cb9e\neb 2b6bc2\nce 2b9d93\ndd 281fe2\nd1 ee9aa\nab 1101a1\n55 1b8fd2\n23 47191\nd5 2820c9\n36 3e264\na3 110288\n63 2a60e\n55 1b8fe0\n23 4719f\n76 216e1\ne3 f3705\n9d 25c0ed\nbc 260295\nb9 cf2d7\n6 19b838\nee 27c4cc\n16 19c199\nfe 27ce2d\n81 c9416\n8d 25ca4e\nd3 2b34d3\nf6 eaad8\nae 29905d\nbe 2999be\nd2 e7b62\n55 1b9034\n23 471f3\nd0 e7b69\n21 471fa\nd5 28212b\na3 1102ea\na1 1102f1\n63 2a670\n42 5fb86\ne3 f3767\nd5 282139\na3 1102f8\n10 193e11\n14 19b18a\n31 197fb9\n52 1b09a9\n5a 1b0b00\n56 1b7d22\n73 1b4b51\n5e 1b7e79\n7b 1b4ca8\n90 25cf6a\n90 c9a58\n9c 25d090\n98 25d0c1\nb5 2610e1\n94 2642e3\nb1 261112\n27 1d9162\nd6 279ad1\n7b 63278\n2f 1d92b9\n23 45c81\nd2 e65f0\n55 1b7ac2\nde 279c28\n45 1b86d3\nf3 ea798\nc2 e7201\nff 27ddd0\n56 1ea10c\n10 1940bf\n73 1b4dff\n94 25d1e7\n39 4698e\n90 25d218\nb1 2613c0\n30 197fb8\nf9 27cdf4\n11 19c160\n19 19c2b7\n72 1b4b50\n7a 1b4ca7\n5b 1b8e4f\n53 5ef7a\n5f 1f25b2\nb4 2610e0\n51 1b7d41\n2b 1d9538\n5b 1f25e3\nb0 261111\nb0 cdbff\n5b 5f0d1\nbc 261237\n95 265288\n91 d1da7\n9d 2653df\nf6 27dc78\n75 1bbc69\nf2 ea797\n44 1b86d2\nfe 27ddcf\nfa 27de00\n30 198266\n11 19c40e\n95 265536\nd3 2820ff\nf 19b990\n3 8358\n50 1b09a2\nb 84af\n58 1b0af9\n54 1b7d1b\n71 1b4b4a\nb3 26111b\n12 1cd53a\n16 1d48b3\n33 1d16e2\n1e 1d4a0a\n3b 1d1839\n25 1d915b\na2 107c89\n87 d1480\nd4 279aca\n4b 1f1f22\n79 63271\nd0 e65e9\naa 107de0\n8f d15d7\n21 45c7a\n2d 1d92b2\ndc 279c21\n8b d1608\nd8 279c52\nf5 27dc72\nc0 e71fa\nf1 ea791\nfd 27ddc9\n96 296662\n92 296693\n92 103181\n15 1d4653\n9e 2967b9\n9a 2967ea\nb7 29a80a\n18 1cc6da\n96 29da0c\nb3 29a83b\n35 19f386\nb2 cdeb4\nbe 2614ec\n9e 29db63\nbb 29a992\n3 8606\nf 19bc3e\n50 1b0c50\nb3 2613c9\n12 1cd7e8\n33 1d1990\n25 1d9409\na2 107f37\n87 d172e\nd4 279d78\n79 6351f\n8f 264d97\n83 d175f\nd0 279da9\nad 2a2349\na1 10ed11\n96 296910\n92 296941\nb3 29aae9\n2f 19fb37\n23 c4ff\n70 1b4b49\n2b c656\n78 1b4ca0\n59 1b8e48\na7 d5627\nf4 27dc71\n6b 1f60c9\nf0 ea790\naf d577e\nfc 27ddc8\nab d57af\na 41bce\nf8 27ddf9\nb6 29a809\n97 264031\nb2 29a83a\nb2 107328\n4 1d5263\n35 1d87fa\nbe 29a960\n97 29e9b1\n64 1bb309\n93 29e9e2\n92 d205b\n9e 265693\n93 10b4d0\n9f 29eb08\n6c 1bb460\n9b 29eb39\n2f 19fde5\n23 c7ad\n70 1b4df7\n17 19b186\n32 1d198f\n13 1d5b37\na7 d58d5\nf4 27df1f\n3e 1d18cb\n32 3e293\nd1 2820f8\nb6 29aab7\n64 1bb5b7\n93 29ec90\n12 193e18\n33 197fc0\n3b 198117\nd8 279c60\n12 1940c6\n33 19826e\n50 1b0c5e\n96 25d1ee\n3b 46995\n92 25d21f\nb3 2613c7\n25 1d9417\na2 107f45\nd4 279d86\nf5 27df2e\n32 197fbf\nfb 27cdfb\n13 19c167\n1b 19c2be\n70 1b4b57\n78 1b4cae\n59 1b8e56\nf4 27dc7f\n4 19a81b\n6b 1f60d7\nf0 ea79e\nfc 27ddd6\na 41bdc\nf8 27de07\n32 19826d\n13 19c415\n70 1b4e05\nb6 261395\n93 26556e\nf4 27df2d\n3e 1d18d9\n32 3e2a1\nd1 282106\n97 d3103\n90 29669a\nb7 269498\n9f d325a\n98 2967f1\nb3 d5fb7\n82 d2a20\nbf 2695ef\nb1 29a842\nb0 cdebb\nbc 2614f3\n31 1d1997\nb3 268453\n94 296917\n97 d33b1\n90 296948\nb7 269746\nb1 29aaf0\n15 19aedf\n37 e151\n30 1d16e8\n1d 19b036\n3f e2a8\n38 1d183f\n11 1d5890\n95 264038\nb7 d72aa\nb0 29a841\nb0 10732f\nbc 29a967\n9d 26418f\nbf d7401\nb8 29a998\n95 29e9b8\n91 29e9e9\n90 d2062\ne2 27c086\n9c 26569a\n37 e3ff\n15 19b18d\n30 1d1996\nb4 29aabe\n95 29ec66\n7e 21ad6\n30 197fba\n38 5f83\n72 1b4b52\n91 25cf6b\n91 c9a59\n9d 25d091\n99 25d0c2\n27 3ee80\nb8 26126a\nbc cf0ab\nf6 27dc7a\nfa 27de02\n11 1940c0\n72 299fe\n7e 1bd036\n91 25d219\nb4 261390\nd3 279db1\nbc cf359\nf6 27df28\n39 1d9b82\n4 1ccc11\na5 2607f2\n5e 25c7d\nf8 27cdf5\n10 19c161\n7 43027\n98 265411\n4e 566f2\n2 41d33\ne 1d536b\nf0 27df5e\n51 1b09a3\n70 1b4b4b\n78 1b4ca2\n32 1d16e3\n3a 1d183a\ne7 124b6c\n8e 10afb2\nd1 e65ea\nab 107de1\ndd 279c22\n8a 10afe3\nd9 279c53\nf4 27dc73\nf0 ea792\nfc 27ddca\na 41bd0\n67 5ba11\nf8 27ddfb\nb6 29a80b\nb2 29a83c\nba 29a993\n51 1b0c51\n70 1b4df9\n13 1cd7e9\n32 1d1991\n90 d1db4\n9c 2653ec\nd1 279daa\nf4 27df21\n64 1b3269\n93 296942\nb2 29aaea\n58 1b8e49\n1a 1d59e1\nc7 128d13\n33 60e4\n96 29e9b2\n92 29e9e3\n92 10b4d1\n3b 623b\n9e 29eb09\n9a 29eb3a\n92 29ec91\n32 197fc1\nd7 2bb7f0\nd1 279b0a\nd9 279c61\na 41bde\n5e 26c91\nf8 27de09\n51 1b0c5f\n93 25d220\nb6 261397\nd1 279db8\nfa 27cdfc\n12 19c168\n96 26553e\n92 26556f\n91 29669b\n90 c9d14\n9c 25d34c\n5f 56da4\n16 436cb\nb7 d72ac\nfe 12435f\nb0 29a843\n92 c9d6f\n9e 25d3a7\n1e 43822\nbf d7403\nb8 29a99a\nb7 d755a\n16 43979\n5f 57052\nb0 29aaf1\n18 1d59e8\n90 10b4d8\n39 6242\n9c 29eb10\n98 29eb41\n8a c92b9\n7b 5c48a\n10 1d5b3f\nfb 1255e3\n90 29ec98\n90 25cf78\n98 25d0cf\nb1 261120\nb9 261277\nc5 2817ca\n10 1940cd\n94 25d1f5\n90 25d226\nd2 e7922\nb5 26139d\nb1 2613ce\n27 1d941e\nd6 279d8d\ne5 2856c3\n30 197fc6\ned 28581a\ne1 f21e2\n38 19811d\n19 19c2c5\n72 1b4b5e\n5b 1b8e5d\nb4 2610ee\n51 1b7d4f\n73 2afc1\n2b 1d9546\nb0 26111f\nb0 cdc0d\nbc 261245\nb8 261276\n91 d1db5\n9d 2653ed\ne5 285971\n30 198274\nb4 26139c\n11 193e12\nb0 2613cd\n95 265544\nf6 27df34\n92 2966a1\n9a 2967f8\nb3 29a849\nc7 2baef3\n12 1cd7f6\n33 1d199e\n96 29691e\n92 29694f\nb3 29aaf7\n95 29e9aa\ne7 2bedec\n32 1d16ef\n13 1d5897\n1b 1d59ee\nb2 29a848\n4 1d5271\nb2 107336\n35 1d8808\nbe 29a96e\nba 29a99f\nf1 2862af\nca e5e48\nad 25f8c3\n50 1f26ce\n97 29e9bf\n64 1bb317\n93 29e9f0\n93 10b4de\n9f 29eb16\ne7 2bf09a\n32 1d199d\n13 1d5b45\nb6 29aac5\n13 1cd53b\nb2 29aaf6\n31 60db\n94 29e9a9\n64 1bb5c5\n93 29ec9e\n91 25cf79\n99 25d0d0\nfe eac3d\n5f 1d682\n16 9fa9\nb0 261121\n1e a100\nb8 261278\n11 1940ce\n95 25d1f6\n91 25d227\n5f 1d930\n16 a257\nb0 2613cf\n90 d1db6\n9c 2653ee\n98 26541f\n4e 56700\nff ebe90\nab 106ddd\n94 265545\nfb ebec1\n90 265576\nb2 29a84a\nba 29a9a1\nca e5e4a\nad 25f8c5\n50 1f26d0\n13 1cd7f7\n32 1d199f\nb2 29aaf8\n31 60dd\n94 29e9ab\n12 1d5898\n1a 1d59ef\n9e 296a69\n92 103431\n15 1d4903\n92 29e9f1\n92 10b4df\n9e 29eb17\n9a 29eb48\n12 1d5b46\n92 29ec9f\nb5 261143\nb1 cdc62\n80 ca6cb\nbd 26129a\n82 294a80\nd6 279b33\n7b 632da\n8a 294bd7\nd2 e6652\nb5 2600cd\nde 279c8a\n94 25d249\n39 469f0\nb5 2613f1\nb0 cdc61\n5b 5f133\nbc 261299\n95 2652ea\n91 d1e09\n9d 265441\nf2 ea7f9\naa 298d7e\nfe 27de31\n80 294a79\na2 107ceb\n87 d14e2\nd4 279b2c\n79 632d3\n88 294bd0\nd0 e664b\naa 107e42\n8f d1639\ndc 279c83\n6d 1f609f\n61 62a67\nb7 29a86c\n80 294d27\na2 107f99\n87 d1790\nd4 279dda\na5 10ed42\n79 63581\naf d57e0\ne 41bff\na8 298d77\nf0 ea7f2\nfc 27de2a\nb6 29a86b\nb2 10738a\nbe 29a9c2\n69 1b333a\n97 29ea13\n93 10b532\n9f 29eb6a\nb0 10f67f\nbc 2a2cb7\nb6 29ab19\n21 19e748\n29 19e89f\n31 19f0a9\n65 1bb2a8\n67 1bb2af\ne6 f24d5\n62 5b723\n47 24f1a\n40 1e84b1\n61 1bb2d9\n63 1bb2e0\n9 1c8\n54 56c55\nc1 128c79\n6d 1bb3ff\n69 1bb430\n1 12d1\n6b 1bb437\n5c 56dac\nc9 128dd0\n75 1bbc09\n77 1bbc10\ne2 12b90a\nee 2bef42\nf6 f2e36\n57 2587b\n72 5c084\n50 1e8e12\n71 1bbc3a\n73 1bbc41\n19 b29\nd1 1295da\n7d 1bbd60\n44 1aee20\n79 1bbd91\n25 19e725\n21 19e756\n72 1bbe8c\n2d 19e87c\n29 19e8ad\nc5 ef57a\n7a 1bbfe3\nc2 127701\nce 2bad39\n6b 2246b\n31 19f0b7\n61 1bb2e7\n75 1bbc17\ne0 12b911\nec 2bef49\n71 1bbc48\nb7 26114a\na2 107cf9\nd4 279b3a\nd0 e6659\naa 107e50\ndc 279c91\nf 41c0e\nf1 ea801\nc0 e726a\nfd 27de39\n96 25d250\n3b 469f7\nb6 261149\n97 2652f1\nfd 27cb17\nb4 26943e\n93 d1e10\n9f 265448\nb0 d5f5d\nbc 269595\nf0 ea800\ne 41c0d\nfc 27de38\nb6 2613f7\na3 1102f6\nd5 282137\n94 2966cb\n90 1031ea\n9c 296822\nb5 29a873\nb1 107392\n80 103dfb\nbd 29a9ca\n94 296979\nb5 29ab21\nb4 29a872\nb0 107391\nbc 29a9c9\n15 1cd565\nb4 29ab20\n4 41aa1\na5 d5682\nbb 2a1ccc\nc 41bf8\nad d57d9\n1c 42559\nb 19a69b\nbd d613a\n30 4b16\n3c 19814e\ne5 f2213\nfb 2be85d\ne0 2b57b1\n46 5e639\ne7 f221a\ncc 2b2a54\nc0 11f41c\n65 5b75e\n38 4c6d\ned f236a\n4e 5e790\ne8 2b5908\nef f2371\nc8 11f573\n6d 5b8b5\nf0 2b6112\n56 5ef9a\nf7 f2b7b\ndc 2b33b5\nd0 11fd7d\n75 5c0bf\n4b 1b722c\nfd f2ccb\nf8 2b6269\n5e 5f0f1\nff f2cd2\nd8 11fed4\n7d 5c216\na2 cd2a7\nae 2608df\n25 19e779\naa cd3fe\n2d 19e8d0\nee 27d470\ne2 e9e38\n65 1bb30a\n79 5b1cf\n67 1bb311\n62 5b785\n40 1e8513\nea e9f8f\n6d 1bb461\n5 1302\n6f 1bb468\n6a 5b8dc\n48 1e866a\ncd 128e01\nf2 ea799\nfe 27ddd1\n75 1bbc6b\nca 2b9d64\n63 62a6e\n6f 1f60a6\n77 1bbc72\n72 5c0e6\n50 1e8e74\nd5 12960b\nfa ea8f0\n7d 1bbdc2\n14 42410\nb5 d5ff1\n1c 42567\nbd d6148\n44 5e640\ne5 f2221\nfb 2be86b\n4a 5e7c3\n2d 1d823e\n4c 5e797\ned f2378\n54 5efa1\n43 1b70e3\nf5 f2b82\n5a 5f124\n3d 1d8b9f\n5c 5f0f8\n4b 1b723a\nfd f2cd9\nf2 ea7a7\nfe 27dddf\n75 1bbc79\n91 c9abb\n9d 25d0f3\nb0 cdc63\nbc 26129b\nf2 ea7fb\nfe 27de33\nd6 281e83\nb5 26841d\nd2 ee9a2\nde 281fda\nd6 282131\nd1 e664c\nab 107e43\n8e 10b014\ndd 279c84\nf0 ea7f4\ne 41c01\nfc 27de2c\nb6 29a86d\nb2 10738c\nbe 29a9c4\nd0 ee99b\naa 110192\ndc 281fd3\n68 1b333b\n96 29ea14\n92 10b533\n9e 29eb6b\n21 19e9f6\n23 19e9fd\n51 1f2421\n31 19f357\n33 19f35e\n65 1bb556\n61 1bb587\n63 1bb58e\n75 1bbeb7\n71 1bbee8\n73 1bbeef\nef 2bdc1d\n21 19ea04\nc2 1279af\nce 2bafe7\nff 2be57e\n31 19f365\n61 1bb595\n71 1bbef6\nb6 26114b\nd1 e665a\nab 107e51\ndd 279c92\nf0 ea802\ne 41c0f\nfc 27de3a\nb6 2613f9\n96 2652f2\n92 d1e11\n9e 265449\naa 1101a0\nd0 ee9a9\ndc 281fe1\n96 2655a0\na2 1102f7\n3 42d3c\neb 1239d0\nd4 282138\n91 1031eb\n9d 296823\nb4 29a874\nb0 107393\nbc 29a9cb\nb4 29ab22\ne0 2b5a5f\n46 5e8e7\ne7 f24c8\n65 5ba0c\na2 cd555\nae 260b8d\n25 19ea27\nee 27d71e\ne2 ea0e6\n65 1bb5b8\n6a 1bc6a4\n7a 1bd005\n77 1bbf20\n72 5c394\n50 1e9122\n4 41d5d\n21 3eb8c\na5 d593e\n44 5e8ee\ne5 f24cf\n38 19f19d\n25 19ea35\nc6 1279e0\n35 19f396\nee 27d72c\ne2 ea0f4\n65 1bb5c6\nfe 27e08d\nf2 eaa55\n75 1bbf27\n29 19fb61\n2b 19fb68\n69 1bc6f2\n21 19fa18\n72 1bd14e\n29 19fb6f\n7a 1bd2a5\n61 1bc5a9\n94 25cfa9\n90 c9ac8\n9c 25d100\nb5 261151\nb1 cdc70\n80 ca6d9\nbd 2612a8\nd6 279b41\nd2 e6660\nb5 2600db\nde 279c98\nc2 e7271\nf3 ea808\nff 27de40\n8c 25b4d1\n94 25d257\nb5 2613ff\nb4 261150\nb0 cdc6f\nbc 2612a7\nf2 ea807\nfe 27de3f\nd7 281e90\nd3 ee9af\ndf 281fe7\n15 193e43\nb4 2613fe\n96 2966d2\n92 1031f1\n9e 296829\nb7 29a87a\n96 296980\nb6 29a879\n97 29ea21\n93 10b540\n9f 29eb78\n17 1cd56c\nb6 29ab27\n4 42d63\nec 1239f7\na5 d6944\nbb 2a2f8e\nc 42eba\nad d6a9b\ne0 2b6a73\n46 5f8fb\ne7 f34dc\nc0 1206de\ne8 2b6bca\n4e 5fa52\nef f3633\nc8 120835\naa ce6c0\n21 c55a\n2d 19fb92\nea eb251\n61 290eb\n6d 1bc723\n5c 5f3a8\n4b 1b74ea\n63 290f2\n6f 1bc72a\n48 1e992c\n44 5f902\ne5 f34e3\n88 29d1be\nfb 2bfb2d\n4c 5fa59\ned f363a\ne2 eb108\n65 1bc5da\nea eb25f\n61 290f9\n6d 1bc731\n21 19fcb8\n61 1bc849\n61 1bc857\n95 25cfaa\n91 c9ac9\n9d 25d101\nb4 261152\nb0 cdc71\nbc 2612a9\nf2 ea809\nfe 27de41\n95 25d258\nb4 261400\n90 d1e18\n9c 265450\nd6 281e91\nb5 26842b\nd2 ee9b0\nde 281fe8\nff ebef2\nab 106e3f\n94 2655a7\nd6 28213f\n96 29ea22\n92 10b541\n9e 29eb79\ne0 2b6d21\n46 5fba9\ne7 f378a\nfd 2b755b\nf1 123f23\nc0 12098c\n4 4301f\n4d 566f8\na5 d6c00\n44 5fbb0\ne5 f3791\n38 1a045f\ne2 eb3b6\n65 1bc888\n18 194216\n7b 1b4f56\n90 c9d06\n9c 25d33e\n91 29668d\n98 25d36f\nda e7a6b\nb1 cdeae\nbd 2614e6\nfb 27e0af\n56 1ea3ba\n38 1983bd\n19 19c565\n5b 1b90fd\n91 d2055\n9d 26568d\n8 3ab9d\n42 1e976c\nfa 27e0ae\ndb 282256\nb 875d\n58 1b0da7\naa 10808e\n8f d1885\nd0 e6897\n21 45f28\n2d 1d9560\ndc 279ecf\n8b d18b6\nd8 279f00\na9 10ee68\n92 10342f\n15 1d4901\n9e 296a67\nf0 eaa3e\naf d5a2c\nfc 27e076\na 41e7c\nab d5a5d\nf8 27e0a7\n3a 3e3ea\nd9 28224f\nb2 1075d6\n35 1d8aa8\nbe 29ac0e\n1a 19421d\n58 1b0db5\n9a 25d376\n21 45f36\naa 10809c\nd0 e68a5\n2d 1d956e\ndc 279edd\nd1 2b322c\nf1 eaa4d\nfd 27e085\n1b 19c56c\n78 1b4f5c\n59 1b9104\n9b 2656c5\nf0 eaa4c\nfc 27e084\nf1 2b73d3\na 41e8a\n59 1b0afa\nf8 27e0b5\n3a 3e3f8\nd9 28225d\n90 103436\n9c 296a6e\n9f d3508\n98 296a9f\nb3 d6265\nbf 26989d\nda 12119b\nb1 1075de\nbd 29ac16\n3f e556\n1d 19b2e4\n38 1d1aed\nb0 1075dd\nbc 29ac15\ne3 2b57a9\n91 10b785\n9d 29edbd\n19 194217\n5b 1b0daf\n91 c9d07\n9d 25d33f\n7a 29b55\n99 25d370\nb0 cdeaf\nbc 2614e7\ndb 279f08\nf2 eaa47\n75 1bbf19\nfe 27e07f\n0 39730\nc 1ccd68\na1 cd311\nad 260949\nfa 27e0b0\n59 1b0da8\n78 1b4f50\nab 10808f\nd1 e6898\ndd 279ed0\nd9 279f01\nf0 eaa40\nfc 27e078\na 41e7e\nf8 27e0a9\n6c 1b33c0\n9b 296a99\nba 29ac41\n9a 29ede8\n1b 19421e\n59 1b0db6\n9b 25d377\nb2 cdeb6\n35 19f388\nbe 2614ee\nab 10809d\nd1 e68a6\ndd 279ede\nd9 279f0f\na 41e8c\n5e 26f3f\nf8 27e0b7\n1e 43ad0\nbf d76b1\nb8 29ac48\n18 1d5c96\n90 10b786\ne2 2b57aa\n9c 29edbe\n98 29edef\n5a 1b0dbc\n7b 1b4f64\n8 1925f5\n98 25d37d\nda e7a79\nb1 cdebc\nbd 2614f4\nb9 261525\ned 285ac8\ne1 f2490\n38 1983cb\n7a 1b4f63\n14 3a0b6\n81 10c0da\n5b 1b910b\n19 193f69\nb8 261524\n91 d2063\ne3 27c087\n9d 26569b\n75 1bbf25\nf2 eaa53\nfe 27e08b\nf3 2b73da\n25 1d0dad\n80 294a6b\n92 10343d\n15 1d490f\n9e 296a75\n35 1d8ab6\nb2 1075e4\nbe 29ac1c\n19 194225\n5b 1b0dbd\n91 c9d15\n9d 25d34d\n99 25d37e\n1e a3ae\nb8 261526\ndb 279f16\n1b 1cd94e\n3a 1d1af6\n44 1e8544\nba 29ac4f\n90 10b4ca\n39 6234\n9c 29eb02\n1a 1d5c9d\n9a 29edf6\n90 c9d68\n9c 25d3a0\nb1 cdf10\nbd 261548\n5b 5f3e1\nb0 cdf0f\nbc 261547\nf2 eaaa7\naa 29902c\nfe 27e0df\n88 294e7e\naa 1080f0\nd0 e68f9\n8f d18e7\ndc 279f31\nad 10ee99\ne 41ead\naf d5a8e\nf0 eaaa0\na8 299025\nfc 27e0d8\nb2 107638\nbe 29ac70\n9c c9be0\n27 1d7e40\n25 1d7e47\n44 26482\n75 29a19\n2d 1d7f9e\n2b 1d7fc8\n71 29a4a\n40 264b3\nfa ebbb0\n7d 1bd082\n29 1d7fcf\nb1 cf180\ne6 27c375\n7a 1f5705\n37 1d87a1\n3f 1d88f8\n54 26de3\n3d 1d88ff\n6 1cb9b8\n3b 1d8929\n50 26e14\n39 1d8930\n67 1bb2bd\n63 1bb2ee\n6f 1bb414\n6b 1bb445\n77 1bbc1e\ne2 12b918\nee 2bef50\nad 10f145\n73 1bbc4f\n77 1bcf32\n98 c9c1f\n23 1d7e7f\n73 29a51\n42 264ba\n7f 1bd089\n2b 1d7fd6\n33 1d87e0\na7 10edab\naf 10ef02\nb7 10f70c\n3 1d3c74\nb5 10f713\nbf 10f863\nb 1d3dcb\nbd 10f86a\nd6 278811\n9c c9c42\n27 1d7ea2\n3f 1d895a\nba 10748f\n3d 1d8961\nf 41ebc\nf1 eaaaf\nfd 27e0e7\nb2 cdf16\nbe 26154e\n5d 1b0b2b\n51 1d4f3\n2b 3ecea\nf0 eaaae\ne 41ebb\nfc 27e0e6\n90 103498\n9c 296ad0\nb1 107640\nbd 29ac78\nb0 10763f\n11 3a084\n1d 1cd6bc\nbc 29ac77\n46 5e647\ne7 f2228\n2f 1d8245\n4e 5e79e\nef f237f\n56 5efa8\nf7 f2b89\n6d 2a4df\n3f 1d8ba6\n5e 5f0ff\nff f2ce0\nb7 10f71a\nbf 10f871\n67 1bb31f\n6f 1bb476\n77 1bbc80\n7e 29b86\n91 c9d69\n9d 25d3a1\nb0 cdf11\nbc 261549\nf2 eaaa9\nfe 27e0e1\nab 1080f1\nd1 e68fa\ndd 279f32\ne 41eaf\nf0 eaaa2\nfc 27e0da\naa 110440\nd0 eec49\ndc 282281\n9c c9e8e\n27 1d80ee\n42 5e67a\n4e 1f1cb2\n25 1d80f5\n98 c9ebf\n23 1d811f\nf2 ebd07\n75 1bd1d9\n21 1d8126\n65 2a388\n37 1d8a4f\n52 5efdb\n5e 1f2613\n35 1d8a56\n61 2a3b9\n33 1d8a80\n31 1d8a87\n63 1bb59c\n73 1bbefd\n98 c9ecd\n77 1bd1e0\n23 1d812d\n33 1d8a8e\nb7 10f9ba\n61 1b41f9\n3 1d3f22\nb5 10f9c1\nd4 278ac6\nae 29a2bd\nf6 ebd38\na2 106c85\n25 1d8157\n37 1d8ab1\nb2 1075e6\nbe 29ac1e\n35 1d8ab8\nb2 cdf18\nbe 261550\nab 1080ff\nd1 e6908\ndd 279f40\ne 41ebd\nf0 eaab0\n2b 3ecec\nfc 27e0e8\n91 103499\n9d 296ad1\nb0 107641\nbc 29ac79\n63 5b724\n46 5e8f5\ne7 f24d6\n85 d273b\ncc 11f7ee\n3a 19f1a4\nb7 10f9c8\n67 1bb5cd\n80 10be8d\n77 1bbf2e\n51 1b7a93\n2b 1d928a\n71 2ad0c\n29 1d9291\ne6 27d637\n7a 1f69c7\n63 1bc5b0\n6b 1bc707\n98 caee1\n23 1d9141\n51 1b7aa1\n73 2ad13\n2b 1d9298\na7 11006d\nc2 2804e9\nd5 ee9cd\naf 1101c4\nde 279c2a\n23 45c83\n55 1b7ac4\nd2 e65f2\n2f 1d92bb\n90 c9d76\n9c 25d3ae\nb1 cdf1e\nbd 261556\n1d 193f9a\nb0 cdf1d\n11 962\nbc 261555\n5f 1b0b32\nf2 eaab5\n53 1d4fa\nfe 27e0ed\n25 1d0e0f\n92 10349f\n9e 296ad7\n1f 1cd6c3\nb2 107646\n13 3a08b\nbe 29ac7e\n4e 5fa60\nef f3641\na7 11007b\nd5 ee9db\nf0 1251e4\naf 1101d2\n67 1bc5e1\n63 29100\n6f 1bc738\n63 1bc85e\n98 cb18f\n23 1d93ef\na7 11031b\na5 110322\nd4 279d88\na2 107f47\n25 1d9419\n91 c9d77\n9d 25d3af\nb0 cdf1f\nbc 261557\nf2 eaab7\nfe 27e0ef\n46 5fbb7\ne7 f3798\ncc 120ab0\n3a 1a0466\na7 110329\n67 1bc88f\n21 19e6e4\n0 19baae\n31 19f045\n63 1bb27c\n42 1b8646\n8 9a77\n73 1bbbdd\n74 2197a\n8c 2637bb\nfb 2bf881\n94 263fc5\n9c 26411c\na1 26783d\nad 267963\n84 264bd6\nb5 26816d\n80 264c07\nb1 26819e\n80 d16f5\n8c 264d2d\nbd 2682c4\nc6 2801fc\nc2 28179f\nf3 284d36\n71 1ee27a\n4a 1b854f\n21 19e992\n6b 63bcb\n5a 1b8eb0\n31 19f2f3\ne7 eb19c\n46 575bb\n7b 6452c\n63 1bb52a\n73 1bbe8b\nef 12ccf3\nd2 ee9a0\n8a 29cf25\nde 281fd8\nb5 26841b\nff 12d654\nb1 26844c\nc6 120714\nfb 12d685\nc6 2804aa\nf3 284fe4\n80 25b357\na2 ce5c9\n71 1ee528\n21 19f9a6\n29 19fafd\n63 1bc53e\n6b 1bc695\nc9 278030\n80 264957\n74 22c3c\n80 d1445\n8c 264a7d\n70 22c6d\n88 264aae\na1 268aff\na1 d55ed\nad 268c25\ne3 285697\ne3 f2185\nef 2857bd\n49 1af185\n0 19baac\n21 19fc54\nf4 f40e3\n42 1b8644\n63 1bc7ec\n72 5ae34\n7e 1ee46c\ncd 2782ad\n84 264bd4\ne7 285914\n61 1bb275\n40 1b863f\n71 1bbbd6\n23 1d7e0d\n32 60e3\nde 2b208a\n2b 1d7f64\n2 1d51d7\n33 1d876e\na 1d532e\n3b 1d88c5\ne2 2856a6\ne1 2843ce\nc0 281798\nf1 284d2f\n33 4e20\n3f 198458\n96 29d6ee\n3b 4f77\n9e 29d845\na3 2a0f66\naf 2a108c\nab 2a10bd\n86 29e2ff\nb7 2a1896\n82 29e330\nb3 2a18c7\n82 10ae1e\n8e 29e456\nbf 2a19ed\n8a 29e487\nbb 2a1a1e\na3 267af4\nb4 10f9b2\n2 1d3f13\nb3 268455\n12 1d4874\n23 1d80bb\n33 1d8a1c\nc4 2804a3\n86 29d03b\n96 29d99c\n6c 2a4d2\na7 2a11e3\n68 2a503\na3 2a1214\n7c 2ae33\nb7 2a1b44\n78 2ae64\nb3 2a1b75\n61 1bc537\n69 1bc68e\n23 1d90cf\n51 1b7a2f\n2b 1d9226\ne5 28565f\ne1 285690\ne1 f217e\ned 2857b6\ncb 2b1759\n82 29e080\n5a 5f132\n3d 1d8bad\n82 10ab6e\n2b 58d8\n76 5c365\n8e 29e1a6\na3 2a2228\na3 10ed16\nd5 280b57\naf 2a234e\nd1 280b88\nab 2a237f\n23 1d937d\ne5 28590d\ncb 2b1a07\n82 29e32e\na7 2a24a5\n36 1d04b2\na3 2a24d6\n9c 296816\n90 1031de\n22 1d103c\n35 3f520\n18 3a23e\n98 103335\n50 1af99c\n2a 1d1193\n3d 3f677\n23 19e6eb\n2f 1d7ff9\nde 278968\n2 19bab5\n33 19f04c\n40 1b864d\n71 1bbbe4\n48 1b87a4\n79 1bbd3b\naf 26796a\ne1 2843dc\ne9 284533\nc0 2817a6\nf1 284d3d\nc8 2818fd\nf9 284e94\nb4 d6290\n2 19a7f1\n12 19b152\n23 19e999\n2f 1d82a7\nde 278c16\n33 19f2fa\n2d 47064\n40 1b7389\n71 1bbe92\n86 263919\n96 26427a\na7 267ac1\nb7 268422\ne1 28468a\n23 19f9ad\n2b 19fb04\n61 1bc545\n69 1bc69c\n76 22c43\n82 d144c\n8e 264a84\na3 268b06\na3 d55f4\naf 268c2c\ne5 28566d\ne1 28569e\ne1 f218c\ned 2857c4\ne9 2857f5\nb4 d7552\nfd eac2b\n4b 1af18c\n2 19bab3\n23 19fc5b\n40 1b864b\n61 1bc7f3\ncf 2782b4\n86 264bdb\ncb 2782e5\n82 264c0c\na7 268d83\ne5 28591b\n74 1b3928\ne1 28594c\n21 1d7e14\n30 60ea\nfe 125303\ndc 2b2091\n40 2644f\n29 1d7f6b\n0 1d51de\n6b 5bb29\n31 1d8775\n50 26db0\n8 1d5335\n39 1d88cc\n21 44c6\n2d 197afe\n84 29cd94\n29 461d\n74 5b0aa\n63 1b31ec\n8c 29ceeb\n3d 19845f\n31 4e27\nce 120aa9\n94 29d6f5\n39 4f7e\n73 1b3b4d\n9c 29d84c\na5 2a0f3c\na1 2a0f6d\nc4 ef577\nad 2a1093\neb 124c82\n80 29e337\nb1 2a18ce\na1 267afb\n0 1d3f1a\nb1 26845c\n10 1d487b\n5a 1f25e0\n31 1d8a23\n84 29d042\n94 29d9a3\nce 2bada7\nc2 12776f\na5 2a11ea\nb1 2a1b7c\n21 1d90d6\n29 1d922d\n21 5788\ncd 2b172f\n84 29e056\n74 5c36c\n80 10ab75\n29 58df\n63 1b44ae\n8c 29e1ad\na5 2a21fe\na1 10ed1d\nad 2a2355\na1 268dbd\n34 196d99\n49 1e88b5\n0 1d51dc\ncd 2b19dd\n84 29e304\nc9 2b1a0e\n80 29e335\nae 106e0b\n53 6023c\nc2 128a31\na5 2a24ac\ndf 2bb6a9\nd3 128071\n10 1cc2d5\n95 10ca6c\n18 1cc42c\n9d 10cbc3\n20 19e6e5\n28 19e83c\n30 19f046\n62 1bb27d\n6a 1bb3d4\n72 1bbbde\nc5 ef2cc\n7a 1bbd35\n20 19e993\n30 19f2f4\n62 1bb52b\n28 19fafe\n62 1bc53f\n6a 1bc696\na8 268c57\ne6 285667\nd9 eed4d\ne2 285698\ne2 f2186\nee 2857be\nea 2857ef\n60 1bb276\n68 1bb3cd\n70 1bbbd7\n78 1bbd2e\ne8 284526\n67 62a9d\nf8 284e87\nae 2a108d\nb6 2a1897\nbe 2a19ee\n60 1bb524\n70 1bbe85\na6 2a11e4\nb6 2a1b45\n60 1bc538\n68 1bc68f\n50 1b7a30\n2a 1d9227\ne4 285660\ne0 f217f\nec 2857b7\ne8 2857e8\nd0 280b89\naa 2a2380\n77 1b3be0\n80 103b3f\n7f 1b3d37\n88 103c96\n8 1ccd8d\nd4 e7b8c\nbd 2996a8\n22 19e6ec\nc4 11f697\n32 19f04d\n22 19e99a\n84 29600c\nb5 2995a3\n32 19f2fb\naa 268c5e\ne0 28569f\n8 1d5327\na5 25f52e\n39 1d88be\ne8 2857f6\na8 2a10c5\nb8 2a1a26\n4f 5e791\ne9 2b5909\na0 2a2230\n82 d175c\n8e 264d94\na8 2a2387\n21 19e6f2\naa 106b2e\nfe ebbe1\n2d 1d8000\ndc 27896f\n29 19e849\n0 19babc\n6b 22407\n31 19f053\n8 19bc13\n39 19f1aa\nf4 f2b81\n42 1b70e2\nfc f2cd8\n4a 1b7239\n63 1bb28a\n42 1b8654\n8 9a85\n73 1bbbeb\n4a 1b87ab\n7b 1bbd42\nce e7387\n94 263fd3\n69 2a750\na5 26781a\na1 26784b\nad 267971\neb eb560\n80 264c15\nb1 2681ac\nc6 28020a\nce 280361\ne7 2843b2\n10 1c33\ne3 2843e3\nef 284509\n18 1d8a\neb 28453a\n4a 1b855d\n21 19e9a0\n6b 63bd9\n5a 1b8ebe\n31 19f301\n7b 6453a\n63 1bb538\nef 12cd01\na1 267af9\n84 d1476\neb 12cd32\nb1 26845a\n94 d1dd7\nfb 12d693\nc6 2804b8\ne7 284660\n10 1ee1\ne3 284691\n29 19fb0b\nf4 f3e43\n42 1b83a4\n63 1bc54c\na5 268adc\na1 268b0d\na1 d55fb\nad 268c33\nf4 2bf6f1\na9 268c64\nc6 2814cc\nc2 2814fd\nc2 edfeb\nce 281623\nca 281654\ne3 2856a5\ne3 f2193\nef 2857cb\n24 196684\neb 2857fc\n49 1af193\n0 19baba\nf4 f40f1\n55 26b36\n42 1b8652\n63 1bc7fa\nc6 28177a\ne7 285922\n76 1b392f\ne3 285953\nb4 10f712\n2 1d3c73\nbc 10f869\na 1d3dca\n12 1d45d4\n1a 1d472b\n23 1d7e1b\n42 26456\n2b 1d7f72\n2 1d51e5\n33 1d877c\n52 26db7\na 1d533c\n3b 1d88d3\n86 29cd9b\n76 5b0b1\n8e 29cef2\n96 29d6fc\n6b 63e79\n1 1d4f85\n9e 29d853\na7 2a0f43\na3 2a0f74\nc6 ef57e\naf 2a109a\n82 29e33e\nb3 2a18d5\nb4 10f9c0\n15 42405\n2 1d3f21\n12 1d4882\n33 1d8a2a\n86 29d049\n2e 1d9514\n54 1b7d1d\n22 45edc\n71 1b4b4c\n96 29d9aa\n6c 2a4e0\na7 2a11f1\n78 2ae72\nb3 2a1b83\n51 1b7a3d\n2b 1d9234\n82 10ab7c\n76 5c373\n8e 29e1b4\n72 5c3a4\n8a 29e1e5\na3 2a2236\na3 10ed24\nd5 280b65\naf 2a235c\ncf 2b19e4\n86 29e30b\na7 2a24b3\n62 1bb28b\n6a 1bb3e2\n72 1bbbec\nc5 ef2da\n7a 1bbd43\n62 1bb539\nc4 2b2bab\nf5 2b6142\n62 1bc54d\n48 1e8606\ne9 27c1e7\n4f 2506f\na0 268b0e\na8 268c65\ne6 285675\ne2 f2194\nee 2857cc\nea 2857fd\n22 1d7e1c\n2a 1d7f73\n32 1d877d\n85 10be6b\n3a 1d88d4\na6 2a0f44\nae 2a109b\nb6 2a18a5\nbe 2a19fc\nba 2a1a2d\nb 1483\n56 57f10\n22 1d80ca\n32 1d8a2b\n50 1b7a3e\n2a 1d9235\nd4 278ab8\nae 29a2af\na2 106c77\n25 1d8149\na2 10ed25\nd4 280b66\nae 2a235d\n84 2636c6\n8c 26381d\n94 264027\n82 10ae2a\n8e 29e462\na5 26786e\nad 2679c5\n84 264c38\nb5 2681cf\na3 10efd2\nd5 280e13\naf 2a260a\n80 d1757\n8c 264d8f\nbd 268326\ne7 12cb9c\na5 267b1c\n3a 3f658\nef 12cd55\nb5 26847d\nff 12d6b6\ncd 278061\n84 264988\n80 d14a7\n8c 264adf\na5 268b30\na1 d564f\nad 268c87\nc6 281520\nc2 ee03f\na5 267aba\nce 281677\nc6 2817ce\nc4 280257\ncc 2803ae\n86 29cdef\na7 2a0f97\naf 2a10ee\n86 29e361\nb7 2a18f8\n8e 29e4b8\n82 10ae80\nbf 2a1a4f\nc4 280505\nd4 280e66\n86 29d09d\na7 2a1245\nb7 2a1ba6\nc4 281519\nc0 ee038\ncc 281670\ne5 2856c1\ne1 f21e0\ned 285818\ncf 2b178a\n86 29e0b1\na7 2a2259\nc4 2817c7\ne5 28596f\ncf 2b1a38\n86 29e35f\na7 2a2507\nc4 280265\nff 12d964\ne1 27d342\nc4 280513\nf 1d50b0\nf1 27dca3\n3 41a78\nd4 280e74\ncf 278068\n86 26498f\ne9 124c29\n82 d14ae\n51 1f140d\n8e 264ae6\ne5 2856cf\ne1 f21ee\ned 285826\ncf 278316\n86 264c3d\nc4 2817d5\ne5 28597d\n84 29cdf6\nce 120b0b\n94 29d757\n73 1b3baf\n9c 29d8ae\n84 29e368\nef 124cb3\nb5 2a18ff\n8c 29e4bf\n80 10ae87\nd4 eff3a\nbd 2a1a56\na1 299ed3\n84 29d0a4\n57 5efab\nb1 29a834\n94 29da05\na5 2a124c\nb5 2a1bad\na1 10ed7f\nad 2a23b7\ncd 2b1a3f\n84 29e366\n57 6026d\ndd eed7e\nf8 125587\ne6 2856c9\na6 2a0f98\nae 2a10ef\na6 2a1246\ne0 f21e1\nec 285819\nef 2b5933\n9d 10b90f\na6 2a225a\n29 c8fd\n78 1b4f4e\n2b c904\n4 2ed\n39 d25e\n61 1f4a07\ncf 280364\n3b d265\n3d d28f\n69 2948e\n6b 29495\n6d 294bf\n68 1eca5d\n6f 294c6\n48 566c8\n44 1ce7e\n79 29def\n46 1ce85\n7b 29df6\n7d 29e20\n78 1ed3be\n7f 29e27\n58 57029\nad d5a25\n84 c93e4\nb9 d6355\nbb d635c\nbd d6386\ned f25b6\nef f25bd\nc8 11f7bf\nfd f2f17\nff f2f1e\nd8 120120\n29 c90b\n2d c93c\n39 d26c\n6d 294cd\n79 29dfd\na9 d5a02\nad d5a33\nff 2bfaf0\na 470\nf3 12c4b8\n6a 1ec7a6\nb9 d6363\n8 41e75\na9 d5a56\n18 427d6\n84 c9446\nb9 d63b7\ne0 f21ef\nec 285827\n5 1d3c92\na4 2a124d\n15 1d45f3\nb4 2a1bae\n29 c96d\n39 d2ce\n69 294fe\n8 41e83\na9 d5a64\n6a 1ec808\n18 427e4\nb9 d63c5\n84 2636d4\nbf 110dd3\nce e73e9\n94 264035\n69 2a7b2\nef eb591\n84 264c46\nb5 2681dd\n8c 264d9d\n80 d1765\nbd 268334\ne7 12cbaa\n91 d3077\nc6 28026c\n99 d31ce\nce 2803c3\nd6 280bcd\nde 280d24\n14 1c64\ne7 284414\n1c 1dbb\nef 28456b\nc6 2817de\nf7 284d75\nc2 ee2fd\nce 281935\nff 284ecc\na5 267b2a\nef 12cd63\nb5 26848b\nff 12d6c4\n91 d3325\ne3 27d349\nc6 28051a\nf3 27dcaa\nd6 280e7b\n14 1f12\ne7 2846c2\nf7 285023\na5 268b3e\na1 d565d\nad 268c95\nf3 2bf71a\nc6 28152e\nc2 ee04d\na5 267ac8\nce 281685\ne7 2856d6\ne3 f21f5\nef 28582d\nc6 2817dc\ne7 285984\na7 2a0fa5\nc6 ef5e0\naf 2a10fc\n86 29e36f\nb7 2a1906\n82 10ae8e\n8e 29e4c6\nd6 eff41\nbf 2a1a5d\na7 2a1253\nb7 2a1bb4\n82 10abde\n8e 29e216\na7 2a2267\na3 10ed86\nd5 280bc7\naf 2a23be\ncf 2b1a46\n86 29e36d\na7 2a2515\n29 dbbf\n3c 4c92\na9 d6cb6\n3e 4c99\nab d6cbd\ncb 2b9d55\n7c 21823\ne9 f3847\n69 2a75e\n3c 4ca0\na9 d6cc4\nc9 2b9d5c\n7c 21831\ne9 f3855\n29 dc21\n6b 2a7b9\n8 43137\na9 d6d18\na 4313e\nab d6d1f\ne8 2bdf04\ne9 f38a9\na0 d565e\nac 268c96\nf2 2bf71b\ne2 f21f6\nee 28582e\na6 2a0fa6\nae 2a10fd\nb6 2a1907\nbe 2a1a5e\n7 1d3c99\na6 2a1254\n17 1d45fa\nb6 2a1bb5\na2 10ed87\nd4 280bc8\nae 2a23bf\n29 dc2f\n69 2a7c0\n8 43145\na9 d6d26\n48 5fcd6\ne9 f38b7\n8 19a941\n18 19b2a2\n29 19eae9\n39 19f44a\n7b 1bbfe2\nca ee196\nad 267c11\n8 19bc03\n29 19fdab\nfc f423a\n4a 1b879b\ne3 f2433\nef 285a6b\n48 1b74d2\n58 1b7e33\ndc 280f5b\ne6 284407\nf8 1242c5\nb1 d7212\n21 1cfdca\n8e 29d192\n9e 29daf3\naf 2a133a\nab 2a136b\nbf 2a1c9b\n48 1b8794\ne1 f242c\ned 285a64\n82 10ae1c\n8e 29e454\na3 10efc4\nd5 280e05\naf 2a25fc\n3e 1d0609\nd1 280e36\nab 2a262d\nbc d63e7\na 19a948\n1a 19b2a9\n2b 19eaf0\n3b 19f451\n48 1b74e0\n58 1b7e41\ne7 12389c\n79 1bbfe9\naf 267c18\nbf 268579\ne9 2847e1\nf9 285142\nbc d76a9\na 19bc0a\n2b 19fdb2\n48 1b87a2\n69 1bc94a\n8a 264d63\na3 d58a2\naf 268eda\ne1 f243a\ned 285a72\n7c 1b3a7f\ne9 285aa3\na9 267c52\n8 1d4071\nb9 2685b3\n18 1d49d2\n29 1d8219\n39 1d8b7a\n8c 29d199\n9c 29dafa\nca 1278c6\nad 2a1341\na9 268f14\n3c 196ef0\n8 1d5333\nee 1236e2\n29 1d94db\n9c 10b90c\nb9 10873b\nee 2b5930\n80 10ae23\n8c 29e45b\na1 10efcb\nca 128b88\nad 2a2603\ndb 1281c8\n6a 1bb682\n68 1bb67b\n78 1bbfdc\ne8 2847d4\nf8 285135\n8 19a94f\nfa 2b7522\ndf 280d19\n29 19eaf7\n39 19f458\n5a 1b7e48\n6b 1bb68f\n7b 1bbff0\nca ee1a4\nad 267c1f\nce 28060f\nc3 2b995e\nef 2847b7\n18 2038\neb 2847e8\nfb 285149\n4a 250a1\n2d 19eb1c\n8 19bc11\n5d 26c8d\nfc f4248\n4a 1b87a9\n6b 1bc951\nc2 ee299\nce 2818d1\nc3 2bac20\ne3 f2441\nef 285a79\n7e 1b3a86\neb 285aaa\nbc 10fb17\n1d 4255c\na 1d4078\n2b 1d8220\n8e 29d1a0\n2a 46033\n5c 1b7e74\n79 1b4ca3\n1 1d5233\n9e 29db01\naf 2a1348\n6a 1bb690\ncc 2b2d02\nc0 11f6ca\nfd 2b6299\nc5 ef588\n7a 1bbff1\n2a 1d8221\n85 10c119\n3a 1d8b82\n1b 1d4720\nba 2a1cdb\nb 1731\n56 581be\nad 267c73\nc2 ee2ed\nce 281925\n7b 21806\n4a 1e26f\ne0 2bedc1\ndc 280fbd\nf8 124327\n10 43693\nb1 d7274\n8e 29d1f4\naf 2a139c\nbf 2a1cfd\nc0 ee2e6\ncc 28191e\ne1 f248e\ned 285ac6\nb 41bcf\nf9 27ddfa\ndc 280fcb\n59 56d7a\n10 436a1\nb1 d7282\ne1 f249c\ned 285ad4\nb9 29a98b\n9c 29db5c\nad 2a13a3\nbd 2a1d04\nae 2a139d\n5d 1b7e67\n2b 46026\n6 39a16\n3b 46987\n3f 469b8\ndd 280f5e\nab 10f11d\nfd f41d7\na9 10f124\naf 10f14e\nf7 f40eb\n56 6050a\nf0 2b7682\n86 102b0d\nbb 10fa7e\nb9 10fa85\nbf 10faaf\n6b 294a3\n6f 294d4\n7b 29e04\n7f 29e35\n7c 1ee6af\n70 5b077\n2f 46065\n3f 469c6\neb f259a\nef f25cb\nfb f2efb\nbb 10fa8c\n3b 469e9\ndd 280fc0\nab 10f17f\nfd f4239\na9 10f186\nb9 10fae7\nd 1d3de9\nac 2a13a4\n1d 1d474a\nbc 2a1d05\n4a 5ea1b\neb f25fc\n5a 5f37c\nfb f2f5d\nbb 10faee\nad 267c81\nf3 2be706\neb 27d4a0\n99 d347c\nce 280671\nfb 27de01\nde 280fd2\n82 102822\n8e 295e5a\n5 1d3cf4\n1c 2069\nef 284819\nff 28517a\nc2 ee2fb\nce 281933\ne3 f24a3\nef 285adb\naf 2a13aa\nbf 2a1d0b\n82 10ae8c\nd1 279afc\n8e 29e4c4\na3 10f034\nd5 280e75\naf 2a266c\n51 25af1\n5d 1b9129\n2b 472e8\ndd 282220\n3e 3e3bb\nd1 eebe8\nab 1103df\n3c 3e3c2\na9 1103e6\n6b 2a765\n7e 21838\neb f385c\nd8 e7cc0\n29 47351\ndd 282282\nd1 eec4a\nab 110441\na9 110448\n1f 1d4751\nbe 2a1d0c\n6b 2a7c7\nb5 26848d\n14 1d48ac\n31 1d16db\n4a 5fcdd\neb f38be\ndd 282290\nd1 eec58\nab 11044f\n59 1af838\nf8 27cdf3\n10 19c15f\n31 1a0307\n39 1a045e\n5a 1b8e4e\n73 1bce9f\n7b 1bcff6\nab 106b1f\ndd 278960\n94 265287\n90 d1da6\n9c 2653de\nb5 26942f\nb1 d5f4e\n80 d29b7\nbd 269586\n59 1afae6\n10 19c40d\n31 1a05b5\n73 1bd14d\nab 106dcd\ndd 278c0e\n94 265535\nd9 278c3f\n90 265566\nd2 efc62\n8a 29e1e7\nb5 2696dd\nf7 286275\n58 1b8e47\n71 1bce98\n79 1bcfef\n33 60e2\ndf 2b2089\n96 29e9b0\ndb 2b20ba\n92 29e9e1\n3b 6239\n92 10b4cf\n9e 29eb07\n9a 29eb38\nb3 2a2b89\nb3 10f677\n82 10c0e0\nbf 2a2caf\nbb 2a2ce0\n33 1d9cde\nca 2bad78\nf5 28626e\ndb 2b2368\n92 29ec8f\nb7 2a2e06\nb3 2a2e37\n5b 1af83f\nfa 27cdfa\n12 19c166\n33 1a030e\n3b 1a0465\n58 1b8e55\ne7 1248b0\n71 1bcea6\n79 1bcffd\nf9 286156\n5b 1afaed\n12 19c414\n33 1a05bc\n71 1bd154\ndf 278c15\n96 26553c\ndb 278c46\n92 26556d\nb7 2696e4\nb3 269715\nf5 28627c\nb1 269470\n59 1e8f68\nff f2f8c\n10 1d588f\nb9 2695c7\n18 1d59e6\n31 1d9a37\n39 1d9b8e\n31 60e9\ndd 2b2090\n94 29e9b7\nd9 2b20c1\n90 29e9e8\n73 1b4e0f\n90 10b4d6\n39 6240\n9c 29eb0e\n98 29eb3f\nb5 2a2b5f\nb1 2a2b90\nb1 10f67e\n80 10c0e7\nbd 2a2cb6\nab cd45f\nf6 123eec\n31 1d9ce5\nf6 2b613a\ndd 2b233e\n94 29ec65\nd9 2b236f\n90 29ec96\nd2 129392\nb5 2a2e0d\n72 1bcea0\n7a 1bcff7\nb0 d5f4f\nbc 269587\nf6 285fc8\nf2 f2ae7\nfe 28611f\nfa 286150\nf6 286276\n70 1bce99\n78 1bcff0\n3a 1d9b88\ne7 12ceba\nf0 f2ae0\nfc 286118\n67 63d5f\nf8 286149\nb2 10f678\nbe 2a2cb0\nba 2a2ce1\n47 1f0838\n7b 1ed3b8\n32 1d9cdf\nf4 28626f\nc5 1206ac\n7a 1ed115\nfb 2b6511\nb2 2a2e38\nb4 25fe80\n5f 1f1352\nc4 120959\n7b 1b39e8\n32 1a030f\nf8 286157\nff 27cdbe\nb6 2696e5\nf9 2b626a\n5f 5f0f2\nb0 2a2b91\n92 d20bd\n9e 2656f5\nb8 2a2ce8\n11 1d5884\nf9 2b6518\n5f 5f3a0\nb0 2a2e3f\n59 1af846\n10 19c16d\n39 1a046c\nab 106b2d\ndd 27896e\n94 265295\nb5 26943d\nb1 d5f5c\n80 d29c5\nbd 269594\n59 1afaf4\n10 19c41b\nab 106ddb\ndd 278c1c\n94 265543\nd9 278c4d\n90 265574\nd2 efc70\nb5 2696eb\n3b 1d9b95\ndf 2b2097\n96 29e9be\ndb 2b20c8\n92 29e9ef\n92 10b4dd\n9e 29eb15\n9a 29eb46\nb3 2a2b97\nb3 10f685\n82 10c0ee\nbf 2a2cbd\n5b 1e921d\n12 1d5b44\n33 1d9cec\ndf 2b2345\n96 29ec6c\ndb 2b2376\n92 29ec9d\nb7 2a2e14\nb3 2a2e45\n72 1bceae\n7b 1ed118\n32 1d9a3f\n3a 1d9b96\nb2 1075d8\nbe 29ac10\n35 1d8aaa\nff 2b6240\nb6 2a2b67\nfb 2b6271\nb2 2a2b98\nb2 10f686\nbe 2a2cbe\nba 2a2cef\n7b 1ed3c6\n32 1d9ced\nfb 2b651f\n13 1d588b\nb2 2a2e46\nab 106b81\ndd 2789c2\n94 2652e9\n90 d1e08\n9c 265440\nb5 269491\nb1 d5fb0\n80 d2a19\nbd 2695e8\nab 2a10cd\nc2 ef5b1\nf3 f2b48\nff 286180\ndf efdeb\n8b 10ad38\nd8 2b3382\nab 106e2f\ndd 278c70\n94 265597\n82 29d07c\nd6 28212f\na9 2a10c6\nc0 ef5aa\nf1 f2b41\nfd 286179\nb7 2a2bba\nb3 10f6d9\n82 10c142\nbf 2a2d11\n80 29d075\neb 1239c0\nd4 282128\nb7 2a2e68\ndf 2789c9\n96 2652f0\n46 1f1aeb\nf9 12558a\n92 d1e0f\n9e 265447\nc0 ef5b8\nf1 f2b4f\nfd 286187\ndf 278c77\n96 26559e\n3 42d3a\neb 1239ce\nd4 282136\ndd 2b20f2\n94 29ea19\n90 10b538\n73 1b4e71\n9c 29eb70\nb5 2a2bc1\nb1 10f6e0\n80 10c149\nbd 2a2d18\ndd 2b23a0\n94 29ecc7\nf2 f2b49\nfe 286181\nf0 f2b42\nfc 28617a\n62 1ec65d\n70 1b3b35\n2f 19eb23\nf 878e\n2a 3ef97\n5c 1b0dd8\n50 1d7a0\n8 1cbd25\n29 19eb4d\n2b 19eb54\n59 1f2578\n6d 1bb6ad\n6f 1bb6b4\n69 1bb6de\n1 157f\n6b 1bb6e5\n7d 1bc00e\n44 1af0ce\n79 1bc03f\n4a 250af\n2d 19eb2a\n29 19eb5b\n69 1bb6ec\nc 41ea6\nad d5a87\n1c 42807\nb 19a949\nbd d63e8\n38 4f1b\ned f2618\n4e 5ea3e\ne8 2b5bb6\n0 1d4f22\nef f261f\nc8 11f821\n6d 5bb63\n4b 1b74da\nfd f2f79\nd8 120182\n89 10accd\n7d 5c4c4\nfa eab9e\n7d 1bc070\nf5 2b73a2\nf0 f2b50\nfc 286188\nfd 2b6549\n15 1d58b5\nb4 2a2e70\nc 41eb4\n29 3ece3\nad d5a95\nff 2bfb52\nf3 12c51a\n1c 42815\n39 3f644\n6e 1ec839\nbd d63f6\n4c 5ea45\ned f2626\n4b 1b74e8\n5c 5f3a6\nfd f2f87\n2d 19eb8c\n73 1f5611\nea ea24b\n6d 1bb71d\nab 106b8f\ndd 2789d0\n94 2652f7\nb5 26949f\nb1 d5fbe\n80 d2a27\nbd 2695f6\nd6 281e8f\nd2 ee9ae\nb5 268429\nde 281fe6\nab 106e3d\ndd 278c7e\n94 2655a5\nd6 28213d\ndf 2b20f9\n96 29ea20\n92 10b53f\n9e 29eb77\nb7 2a2bc8\nb3 10f6e7\n82 10c150\nbf 2a2d1f\ndf 2b23a7\n96 29ecce\nb7 2a2e76\n29 19fe0f\n2b 19fe16\n69 1bc9a0\neb 12b9fe\n29 19fe1d\n69 1bc9ae\n38 61dd\ned f38da\ne8 2b6e78\n4e 5fd00\nef f38e1\nf9 12407a\nc8 120ae3\naa ce96e\n21 c808\n2d 19fe40\nea eb4ff\n61 29399\n6d 1bc9d1\nb0 d5fbf\nbc 2695f7\nf2 f2b57\nfe 28618f\nff 2b62a2\nb6 2a2bc9\nb2 10f6e8\nbe 2a2d20\nc 43176\nad d6d57\nf3 12d7dc\n4c 5fd07\ned f38e8\nef 12ba2f\n21 c816\n2d 19fe4e\n73 1f68d3\nea eb50d\n61 293a7\n6d 1bc9df\n18 19c564\n39 1a070c\n7b 1bd2a4\n90 d2054\n9c 26568c\n91 29e9db\n98 2656bd\nf3 f2d94\nff 2863cc\nf1 f2d8d\nfd 2863c5\n92 10b77d\n9e 29edb5\nb3 10f925\nbf 2a2f5d\n1a 19c56b\n58 1b9103\ne7 124b5e\n79 1bd2ab\n9a 2656c4\nb3 d6203\nbf 26983b\nf1 f2d9b\nfd 2863d3\nf9 286404\nb9 269875\n18 1d5c94\nfe 124043\n39 1d9e3c\nfe 2b6291\n90 10b784\ne2 2b57a8\n9c 29edbc\n98 29eded\nb1 10f92c\nda 1294e9\nbd 2a2f64\nb0 d61fd\nbc 269835\nf2 f2d95\nfe 2863cd\nfa 2863fe\nf0 f2d8e\nfc 2863c6\nf8 2863f7\n4e 1e863e\nef 27c21f\nba 2a2f8f\n47 1f0ae6\nb2 d6204\nbe 26983c\n19 1d59db\nb8 2a2f96\n5a 1b910a\n98 2656cb\nb1 d620a\nda efdc7\nbd 269842\n28 3ed52\n62 1ed921\n92 10b78b\n9e 29edc3\nb3 10f933\nbf 2a2f6b\n7a 1bd2b3\n19 19c2b9\nb8 269874\n1b 1d59e2\nba 2a2f9d\n90 d20b6\n9c 2656ee\nbc 107519\nf6 2b60e8\nd2 eec4e\n8a 29d1d3\nde 282286\nac 298d36\n5a 1ebd0\nf0 2bf722\nab 2a137b\nf3 f2df6\nff 28642e\ndf f0099\n8b 10afe6\nd8 2b3630\nd0 eec47\n88 29d1cc\ndc 28227f\na9 2a1374\nf1 f2def\nfd 286427\nb3 10f987\nbf 2a2fbf\nd0 eec55\nb 42e91\ndc 28228d\nf1 f2dfd\nfd 286435\n90 10b7e6\n9c 29ee1e\nf2 f2df7\nfe 28642f\nf0 f2df0\nfc 286428\n62 1ec90b\n75 29cc7\n4a 5e7d1\n2d 1d824c\n71 29cf8\nfa ebe5e\n7d 1bd330\n29 1d827d\n69 2a510\n6 1cbc66\n3b 1d8bd7\n39 1d8bde\n6f 1bb6c2\n6b 1bb6f3\n3b 1d8be5\nf0 f2dfe\n51 25843\n5d 1b8e7b\n2b 4703a\nfc 286436\naf 10f1b0\nad 10f1b7\nbf 10fb11\n69 1b4350\nb 1d4079\nbd 10fb18\nfe ebe8f\naa 106ddc\ndc 278c1d\n2d 1d82ae\n3f 1d8c08\nba 10773d\n3d 1d8c0f\n10 1d5891\n7b 5c1dc\n5e 5f3ad\nff f2f8e\nbf 10fb1f\n6f 1bb724\n90 d20c4\n9c 2656fc\nd2 eec5c\nde 282294\n82 103ae4\n5 1d4fb6\n92 10b7ed\n9e 29ee25\nb3 10f995\nbf 2a2fcd\n71 2afba\n29 1d953f\n6b 1bc9b5\nb0 d626d\n11 8cb2\n1d 19c2ea\nbc 2698a5\n53 2584a\nf2 f2e05\n5f 1b8e82\nfe 28643d\nd5 eec7b\naf 110472\nad 110479\nde 279ed8\n55 1b7d72\nd2 e68a0\n23 45f31\n2f 1d9569\ndc 279edf\naa 10809e\nd0 e68a7\n21 45f38\n2d 1d9570\n4e 5fd0e\nef f38ef\nb5 29a803\nd5 eec89\nf0 125492\naf 110480\n63 293ae\n6f 1bc9e6\n63 1ec65e\n6b 1ec7b5\n42 1e9a28\n8 3ae59\n73 1ecfbf\n4a 1e9b7f\n7b 1ed116\nd6 2b1f3f\nde 2b2096\nef 2b58dd\neb 2b590e\nca 2b2cd8\nfb 2b626f\n2d 1cfc42\n63 1ec90c\n73 1ed26d\nc6 2b188c\nac 106e04\n51 60235\nd6 2b21ed\ne3 2b5a65\n79 2b0bf\nf3 2b63c6\n63 1ed920\nc2 2b28d1\n63 1edbce\nc6 2b2b4e\ne3 2b6d27\n72 1ecfbe\n53 1f1166\n27 196432\na5 110314\n5b 1f12bd\n2f 196589\nad 11046b\nee 2b58dc\n44 1f0b40\nfe 2b623d\n26 3dbaf\ncb 2b9ab5\n72 1ed26c\nf6 2b6394\nc3 2b9c0c\nd3 2ba56d\n5 1d3f40\n47 1b7116\n62 1ed91f\n65 1f4a38\ne2 123566\nee 2b6b9e\n4 1cbbff\n26 3ee71\ncb 2bad77\n47 1b73c4\n62 1edbcd\ne6 2b6cf5\n56 1e8eaa\nc3 2baece\n26 3d903\nc5 281768\n53 1e8e18\na5 107fc6\n12 1d5896\n5b 1e8f6f\nad 10811d\n7a 1ed117\n82 29e08e\ncb 2b1767\nee 2b58de\nf2 2b6119\nfe 2b623f\n28 3dd3e\n62 1ec90d\n38 3e69f\n72 1ed26e\n19 3a23d\nf2 2b63c7\n6a 1eda78\nf5 f3dd4\nac 107e7a\ne6 2b6a49\ne2 123568\n65 1f4a3a\nee 2b6ba0\nea 2b6bd1\n8 1739\n39 4cd0\nf1 12d781\n73 1b389f\n8 41c37\n42 1f0806\n18 42598\n52 1f1167\n5a 1f12be\nc2 2b995f\ndc e7a41\n67 1f5ca1\nf5 284fac\nca 2b9ab6\n9c 10b6c0\n73 219c1\n7f 1b4ff9\nd6 2ba28f\nd2 2ba2c0\n77 1f6602\n7b 21b18\nde 2ba3e6\nda 2ba417\n18 42846\n52 1f1415\na9 107e3c\n8c 10b00d\nc6 2b9bdc\nc2 2b9c0d\ndc e7cef\n67 1f5f4f\nb9 10879d\n9c 10b96e\nd6 2ba53d\nd2 2ba56e\n77 1f68b0\n8 42ef9\n42 1f1ac8\n4a 1f1c1f\naf cd6dc\n52 604e7\nf3 f40c8\n94 2953fb\n9c 295552\na5 298c42\nc6 2b2ba4\nf7 2b613b\n84 294d48\n94 2956a9\na5 298ef0\nb5 299851\nf7 2b63e9\nc6 2b28f4\n52 1eadb\na4 298c41\n5a 1ec32\nac 298d98\nb4 2995a2\n85 29cde9\n39 e590\n95 29d74a\na4 298eef\n77 5adf6\nb4 299850\n85 29d097\n95 29d9f8\nf6 2b63e8\na4 299f03\n85 29e0ab\nc7 2bac43\ne4 2bed90\nc3 127762\ncf 2bad9a\ne0 12b8af\nec 2beee7\n3b e527\ne5 2b57e1\ned 2b5938\ne5 2b5a8f\ne4 2b6d50\na4 298c43\nb4 2995a4\nf6 2b613c\nfe 2b6293\n85 294d49\na4 298ef1\nb4 299852\nf6 2b63ea\n84 29cdea\nf7 2bf759\n8c 29cf41\nf3 12c278\nc2 128ce1\nff 2bf8b0\n94 29d74b\n9c 29d8a2\n91 10c78d\nc6 2b9982\n99 10c8e4\nce 2b9ad9\n43 1e9a27\nf5 1254c6\nd6 2ba2e3\nde 2ba43a\n84 29d098\nf7 2bfa07\n94 29d9f9\nd6 2ba591\n1f af1\n84 29e0ac\nc6 2bac44\nee 28455e\n84 ca3f8\nb9 d7369\n29 1cff21\nbd d739a\n2d 1cff52\nf1 f3da3\nf7 f3ddb\n67 1ec993\nd0 120fdd\nfd f3f2b\nff f3f32\n26 1d9411\n6f 1ecaea\nd8 121134\nf1 f3db1\n61 1ec969\nf5 f3de2\n65 1ec99a\n96 29565e\nf9 f3f08\n20 1d93e7\n69 1ecac0\nfd f3f39\n24 1d9418\n6d 1ecaf1\nd6 2b1fa1\nde 2b20f8\nc6 2b2bb2\nf7 2b6149\nd6 2b224f\nfb 2bfb21\nc6 2b2bb0\nf6 2b6148\n57 1e8e3b\nf6 2b63f6\ne6 2b6aa9\n47 1e979c\ne6 2b6d57\n73 2ad15\n7b 2ae6c\n84 ca45a\n18 437ea\nb9 d73cb\n1a 437f1\nbb d73d2\nf8 2be5b7\nf1 f3e05\n52 6022b\nf3 f3e0c\n39 e2e2\n18 437f8\nb9 d73d9\n50 60232\nad cd427\nf1 f3e13\n66 1bb5be\n31 e3c9\n8d d2900\nc7 2814cf\n71 2af5a\ne6 2846b5\nb1 d74c0\nf1 f4051\n71 2af68\n2d 1cfca4\nf1 f405f\nb1 1085f4\ne6 2b57e9\nf6 2b614a\nb1 1088a2\ne6 2b5a97\nf6 2b63f8\ne6 2b6aab\nd6 2ba2f1\nf9 2b752a\nde 2ba448\ne3 2b6a6d\n91 10ca49\nc6 2b9c3e\ne9 2b6e77\nf3 2b73ce\nd6 2ba59f\nf9 2b77d8\n31 e42b\nc7 281531\n73 2afc3\nf1 f40b3\n2d 45de\n71 2afca\n6b 1eca55\nd0 2bb829\n8c 294e3d\n9c 29579e\nf1 2bf9d1\nca 11f56a\nad 298fe5\na9 299016\nb9 299977\nef 2b5b7d\neb 2b5bae\nff 2b64de\neb 27d750\n4a 1e9b6f\n6b 1edd17\n80 102ac7\n8c 2960ff\na1 106c6f\nca 12082c\nad 29a2a7\nc2 11f65f\n31 197015\nce 2b2c97\ne3 123807\nef 2b6e3f\neb 2b6e70\n6a 1eca54\n36 19f0e0\n4b 1f0bfc\na8 299015\n32 47843\n7b 5af1c\nb8 299976\n8d 29d18c\n89 29d1bd\n99 29db1e\nee 2b5b7c\nea 2b5bad\n4f 1b750d\n6a 1edd16\n7f 1b3a7b\n36 1a03a2\n4b 1f1ebe\na0 106c6e\nac 29a2a6\nda 11fe6b\n81 10ae16\n8d 29e44e\ne2 123806\nee 2b6e3e\n69 1eca5c\naa cd45e\n79 1ed3bd\ned 2b5b84\n1 1d4f21\ne9 2b5bb5\nfd 2b64e5\n11 1d5882\nf9 2b6516\nc0 11f666\ncc 2b2c9e\ne1 12380e\ned 2b6e46\nd9 2ba6bd\n6a 1eda68\n4f 1b725f\nee 28481a\nb9 d7625\n6f 2a786\n4d 1b7514\n68 1edd1d\ne0 12380d\nec 2b6e45\n5c 1e8ffa\nc9 2bb01e\na3 268db6\nb4 110c74\n36 196d92\n2 1d51d5\n4b 1e88ae\nb3 269717\n12 1d5b36\n5b 1e920f\n6a 1eca56\n9d 29579f\n99 2957d0\nac 298fe6\na8 299017\nb8 299978\n86 29e2fd\ncf 2b19d6\n96 29ec5e\ndf 2b2337\nee 2b5b7e\nea 2b5baf\nfe 2b64df\nc4 e728d\nf5 ea824\nad 298da9\nfa 2b6510\n36 198054\n4b 1e9b70\nc3 11f660\ncf 2b2c98\n4a 1f0bfd\n21 1d8124\nbd 10876e\n8c 29d18d\nf3 12c4c4\nff 2bfafc\n9c 29daee\n98 29db1f\n54 60263\n43 1b83a5\nf5 f3e44\nce 2b9d25\nca 2b9d56\nde 2ba686\naf 1101c2\nd5 ee9cb\n8d 29cf50\nda 2ba6b7\n73 633c1\n7f 1f69f9\nb1 26971e\n8a c92b7\n10 1d5b3d\n59 1e9216\n35 4786e\n6b 1eca63\n3 1d4f28\nb5 1109c7\neb 2b5bbc\neb 2b6e7e\na7 268ad7\n59 603ec\nee 2b5b8a\n79 64533\na3 268b08\n2 1d4f27\nb4 1109c6\n4b 1e8600\nea 2b5bbb\nb7 269438\nfe 2b64eb\ncb 2b9d63\n5e 1e9001\ncb 2bb025\n7a 1ed3c5\n2 1d4f29\nea 2b5bbd\n12 1d588a\nfa 2b651e\n5a 1f156c\nce 2b9d33\nde 2ba694\nda 2ba6c5\n8c 294e9f\n9c 295800\nad 299047\nef 2b5bdf\nff 2b6540\ne3 123869\nef 2b6ea1\nac 299046\n36 47874\n7f 5af4d\n8d 29d1ee\n9d 29db4f\nb9 1089e9\nee 2b5bde\nfe 2b653f\n5 1d4f52\ned 2b5be6\n15 1d58b3\nfd 2b6547\n8d 294ea0\n9d 295801\nac 299048\nfe 2b6541\n8c 29d1ef\nf3 12c526\nff 2bfb5e\n9c 29db50\nde 2ba6e8\n6 3aa2a\n3b 4799b\nb7 11096c\nbf 110ac3\n73 2acc1\n7b 2ae18\n3b 479a9\nf3 f3db8\n63 1ec970\nf7 f3de9\n67 1ec9a1\nfb f3f0f\n22 1d93ee\n6b 1ecac7\nff f3f40\n26 1d941f\n6f 1ecaf8\nb3 110949\nde 2ba692\nb7 11097a\nbb 110aa0\nbf 110ad1\na7 268b39\n4f 1e8631\n6 1d4f58\nb9 1089f7\nee 2b5bec\n79 64595\nb7 26949a\n5f 1e8f92\n16 1d58b9\nfe 2b654d\n3b 479fd\n86 103b83\nbb 110af4\n2f 4337\n73 2ad23\n7b 2ae7a\n3b 47a0b\naf cd42e\n52 60239\nf3 f3e1a\n5a 60390\nfb f3f71\nb3 1109ab\nbb 110b02\n33 47b00\n48 1e8668\nf3 f4066\naf 298da2\nb3 110bf7\n6 1d4f5a\nb9 1089f9\nee 2b5bee\n16 1d58bb\nfe 2b654f\nfb 2b7525\nde 2ba6f6\n33 47b54\n31 47b5b\nb3 110c4b\n2f 45e5\n73 2afd1\nb3 110c59\nfb 27de03\nde 280fd4\n5a 1ea222\nff 28517c\n5e 1f159b\n7b 1ee3ca\n90 29668c\n90 10317a\n9c 2967b2\n98 2967e3\nd6 2b31f3\nd2 11fd12\n10 194131\nde 2b334a\nda 2b337b\nf7 2b739b\nc2 120923\nf3 123eba\n31 1982d9\nff 2b74f2\nde 2ba6f4\nfb 2b7523\nb1 2613c2\n10 1cd7e1\n31 1d1989\n94 296909\n90 29693a\nb1 29aae2\nf7 2b7649\n5f 1b7bc0\nfe 28517b\n7a 1ee3c9\n95 26402a\nb0 29a833\nb0 107321\nbc 29a959\nf6 2b739a\n30 1982d8\nf2 123eb9\nfe 2b74f1\n11 1d5b30\n95 29ec58\nf1 123ec1\nc0 12092a\nfd 2b74f9\n87 10ae5e\nd4 2b34a8\nf5 2b7650\nf1 2b7681\n55 1b7a70\n23 45c2f\n77 2ace2\n2f 1d9267\n70 1ee279\n2b 45d86\n5d 1b7bc7\n7f 2ae39\n78 1ee3d0\nf0 123ec0\naf 10eeae\nfc 2b74f8\n77 2af90\n2f 1d9515\n55 1b7d1e\n23 45edd\n70 1ee527\na7 10f005\nf4 2b764f\nd1 2bb828\n9d 26443d\nbf d76af\nb8 29ac46\n19 1cd68b\n7e 5b1f8\n30 1d16dc\n38 1d1833\ne5 124b65\ndf 280fd5\nfa 2b77de\n5b 1ea223\n7a 1ee3cb\n91 10317b\n9d 2967b3\n99 2967e4\nb4 29a804\nfe 124351\nb0 29a835\nb0 107323\nbc 29a95b\nb8 29a98c\nd7 2b31f4\nd3 11fd13\ndf 2b334b\ndb 2b337c\nbc 1087cd\nab 26090f\nf6 2b739c\nf2 123ebb\nfe 2b74f3\n11 1cd7e2\n30 1d198a\n72 63120\n7e 1f6758\n91 29693b\nb4 29aab2\nb0 29aae3\nd7 2b34a2\nab 260bbd\nbc 108a7b\nf6 2b764a\na5 299f14\nf8 2b6517\n5e 5f39f\nff f2f80\n10 1d5883\n7a 1f5775\nc5 128d0c\nc6 2791e2\n5a 1f2572\nde 1284f8\n90 29e9dc\n98 29eb33\n10 1d5b31\n94 29ec59\n90 29ec8a\nd9 2b3383\na9 260916\nf4 2b73a3\n60 1b44a4\nd7 2b3256\nf0 123ec2\nfc 2b74fa\nd3 11fd75\n68 1b45fb\ndf 2b33ad\na9 260bc4\nf4 2b7651\nd7 2b3504\nad 25f617\n50 1f2422\n58 1f2579\nfb 2b7531\n21 3d8cc\n2d 1d0f04\n88 294bc2\nf3 27df68\n52 1ea387\n73 1ee52f\n0 1cbbc0\nd6 2b34af\nf3 2b7688\n42 575e0\n25 1d105b\n80 294d19\n57 1b7a77\n72 1ee280\n53 1f2428\n75 1f5399\nf2 123ec7\n44 1f1e02\nfe 2b74ff\n57 1b7d25\n72 1ee52e\n53 1f26d6\nf6 2b7656\nd3 2bb82f\n5 1d5202\nf2 2b73db\nfa 2b7532\n38 3f961\n72 1ee530\n19 3b4ff\nf2 2b7689\n22 1cfd7a\n35 3e25e\n18 4385a\naf 25f61e\n52 1f2429\n5a 1f2580\naf 25f8cc\n18 43b08\n52 1f26d7\nd6 2b3255\nb5 2997ef\nd2 11fd74\nde 2b33ac\nf7 2b73fd\nd6 2b3503\nf7 2b76ab\nb4 29a864\n95 29ea0c\nf6 2b73fc\na7 10edb9\nf4 2b7403\nf0 123f22\naf 10ef10\nfc 2b755a\na7 10f067\nf4 2b76b1\nf6 2b73fe\nf2 123f1d\nfe 2b7555\nf6 2b76ac\n94 29ea0d\n90 10b52c\n9c 29eb64\nd6 2bb5a5\nb5 2a1b3f\nd2 1280c4\nde 2bb6fc\n94 29ecbb\nd6 2bb853\nd4 2bb5ac\nd0 1280cb\n26 19793f\ndc 2bb703\nd4 2bb85a\n78 1ed41e\n31 1a036b\n7a 1ed425\nc5 1209bc\n33 1a0372\n71 1bcefc\na8 10f185\n9 41bca\n44 1b00e2\n79 1bd053\nc2 1289c3\n31 1a0379\nd6 2b3263\nd2 11fd82\nb5 2997fd\nde 2b33ba\nf7 2b740b\nd6 2b3511\nf6 2b740a\nf2 123f29\nfe 2b7561\n57 1ea0fd\nf6 2b76b8\n56 6025c\nf0 2b73d4\nf7 f3e3d\nd0 12103f\n5e 603b3\nf8 2b752b\nff f3f94\nd8 121196\n5d 56dab\n14 436d2\nb5 d72b3\n1c 43829\nbd d740a\n5c 603ba\n4b 1b84fc\nfd f3f9b\nc6 1289f4\n35 1a03aa\n31 1a0619\n33 1a0620\n71 1bd1aa\n9 41e78\nf3 12c208\na 1c0\nc2 128c71\nff 2bf840\n31 1a0627\nf6 2b740c\nf2 123f2b\nfe 2b7563\nf6 2b76ba\nd6 2bb5b3\nb5 2a1b4d\nd2 1280d2\nde 2bb70a\nd6 2bb861\n90 103428\n9c 296a60\n98 296a91\nb1 1075d0\nda 12118d\nbd 29ac08\nf3 124168\nff 2b77a0\nb0 1075cf\nbc 29ac07\nd0 11ffc7\n8f 10afb5\ndc 2b35ff\nf1 12416f\nfd 2b77a7\n7f 2b0e7\n5d 1b7e75\n2b 46034\n78 1ee67e\nf0 12416e\naf 10f15c\nfc 2b77a6\ndd 280fce\nff f4240\nab 10f18d\nf8 2b77d7\nd9 2bb97f\n5b 1ea4d1\n91 103429\n9d 296a61\n7a 63277\n99 296a92\nb0 1075d1\nbc 29ac09\nb8 29ac3a\nd3 11ffc1\ndf 2b35f9\ndb 2b362a\nf2 124169\nfe 2b77a1\nf5 ebae6\na1 106a33\nad 29a06b\n90 10b778\n9c 29edb0\n98 29ede1\nf0 124170\nfc 2b77a8\nd3 120023\ndf 2b365b\n55 1f14a0\nd2 11ffce\nde 2b3606\n75 1f5647\nf2 124175\nfe 2b77ad\n5b 1ea4df\n7a 1ee687\nfa 2b77e0\n2a 1cfed1\n3d 3e3b5\n5a 1f282e\nd2 120022\nde 2b365a\nf3 1241ca\nff 2b7802\nf0 1241d0\naf 10f1be\nfc 2b7808\nf2 1241cb\nfe 2b7803\n90 10b7da\n9c 29ee12\nd2 128372\nde 2bb9aa\nd0 128379\ndc 2bb9b1\n6 1ccc7a\n3b 1d9beb\n39 1d9bf2\n33 1d9aa2\n3b 1d9bf9\nd2 120030\nde 2b3668\n53 56c1c\nf2 1241d7\n5f 1ea254\nfe 2b780f\n3 1d4f36\nb5 1109d5\nd2 280e4a\nbf 110b25\nb2 1085fa\n35 1d9acc\n33 465e4\n3f 1d9c1c\nba 108751\n31 465eb\n3d 1d9c23\n5e 603c1\nff f3fa2\nb7 1109dc\nbf 110b33\n33 1d9d42\na 398e2\n31 1d9d49\n33 1d9d50\nf2 1241d9\nfe 2b7811\nd2 128380\nde 2bb9b8\nb7 110c7c\n3 1d51e4\nb5 110c83\n37 1d9d73\nb2 1088a8\n35 1d9d7a\nb7 110c8a\n21 1d7e06\n29 1d7f5d\n0 1d51d0\n31 1d8767\ne3 2843d7\n42 1f07f6\n63 1f499e\n6b 1f4af5\n4a 1f1ebf\ne7 27c0c6\n7b 1f5456\nb 40d\na1 2a0f5f\n5b 26f1f\nad 2a1085\na9 2a10b6\n84 29e2f8\nb5 2a188f\n80 29e329\n1b d6e\nb1 2a18c0\n88 29e480\nb9 2a1a17\nca 2bb018\nfb 2be5af\na1 267aed\n0 1d3f0c\nb1 26844e\n10 1d486d\n4a 1f1c71\n21 1d80b4\n5a 1f25d2\n31 1d8a15\n63 1f4c4c\n84 29d034\n94 29d995\na1 2a120d\nb1 2a1b6e\nc6 2b9bcc\n21 1d90c8\n29 1d921f\n63 1f5c60\n6b 1f5db7\n80 29e079\na1 10ed0f\nad 2a2347\na9 2a2378\ne7 2bed88\ne3 2bedb9\ne3 12b8a7\n21 19fcc6\nef 2beedf\n24 1cfd98\neb 2bef10\na1 268daf\n34 196d8b\n0 1d51ce\ne3 285947\n76 1b3923\n42 1f1d66\n84 29e2f6\n61 1f49a5\n69 1f4afc\n40 1f1d6f\n71 1f5306\n48 1f1ec6\n79 1f545d\n61 1f4c53\n71 1f55b4\ne1 2bddac\n61 1f5c67\n69 1f5dbe\n4b 1e26e\ne1 2bedc0\ne1 12b8ae\ned 2beee6\ne9 2bef17\n61 1f5f15\n74 1ed04a\ne1 2bf06e\n62 1f499f\na1 ce571\n6a 1f4af6\nb1 ceed2\ne6 27c0c7\n7a 1f5457\nac 2a1086\nb4 2a1890\n80 103ae9\nea 2bdc4f\n90 10444a\nfa 2be5b0\n62 1f4c4d\ne2 2bdda6\n20 1d90c9\n28 1d9220\n62 1f5c61\n6a 1f5db8\na 16d0\n3b 4c67\na0 2a2222\na8 2a2379\ne6 2bed89\ne2 12b8a8\nee 2beee0\nea 2bef11\ne8 2bef18\ne3 2843e5\nf4 12c2a3\n42 1f0804\n63 1f49ac\n6b 1f4b03\n8 431a7\n42 1f1d76\n73 1f530d\n4a 1f1ecd\n7b 1f5464\n63 2105e\n6f 1b4696\nc6 2b992c\n6b 211b5\nce 2b9a83\n10 3b355\ne3 2bdb05\n18 3b4ac\neb 2bdc5c\nc2 2baecf\nf3 2be466\n25 1d7e39\nca 2bb026\nfb 2be5bd\n2d 1d7f90\nf3 284ff4\n52 1f1413\n63 1f4c5a\n73 1f55bb\nc6 2b9bda\nd6 2ba53b\n10 3b603\ne3 2bddb3\nf3 2be714\n42 5e66c\n4e 1f1ca4\n25 1d80e7\ne3 2856a7\nf4 12d565\n42 1f1ac6\n63 1f5c6e\ne3 2bedc7\ne3 12b8b5\nef 2beeed\n24 1cfda6\neb 2bef1e\ne3 285955\n76 1b3931\nf4 12d813\n42 1f1d74\n63 1f5f1c\ne7 2bf044\n76 1ed051\ne3 2bf075\n62 1f49ad\n6a 1f4b04\n72 1f530e\nfa 2be5be\n62 1f4c5b\n72 1f55bc\n53 1f115a\n19 4258b\nf2 2be715\n62 1f5c6f\n6a 1f5dc6\ne6 2bed97\ne2 12b8b6\nee 2beeee\n63 1b3240\n8c 29cf3f\n94 29d749\n73 1b3ba1\n9c 29d8a0\nf 43e\n53 26e2a\na5 2a0f90\n5b 26f81\nad 2a10e7\n1f d9f\n84 29e35a\nb5 2a18f1\n84 29d096\nf6 12c558\n57 5ef9d\n94 29d9f7\na5 2a123e\nb5 2a1b9f\n84 29e0aa\na1 10ed71\nad 2a23a9\ne7 2bedea\n84 29e358\nf6 12d81a\n57 6025f\n13 193e19\nc6 2baef0\n4f 1e29f\ne5 2bedf1\ne1 12b910\ned 2bef48\n11 193e20\nc4 2baef7\ne5 2bf09f\ne 43f\na4 2a0f91\nac 2a10e8\n1e da0\nb4 2a18f2\na4 2a123f\nb4 2a1ba0\n3f 4c98\ne 1701\na4 2a2253\n7f 21837\n4e 1e2a0\ne4 2bedf2\n91 10c799\nc6 2b998e\n99 10c8f0\nce 2b9ae5\n91 10ca47\ne3 2b6a6b\nc6 2b9c3c\nf3 2b73cc\nd6 2ba59d\nc6 2bac50\ne7 2bedf8\ne3 12b917\nef 2bef4f\nc6 2baefe\ne7 2bf0a6\nee 28480c\n84 ca6a6\nb9 d7617\nc4 e7237\nf9 f41a8\nf9 f41b6\ne6 2bedf9\n39 e582\nc3 ee050\ncf 281688\n79 2b113\n18 43a98\n84 ca708\nb9 d7679\n1a 43a9f\nbb d7680\ne7 eb18c\nf8 2be865\nc4 e7299\nf9 f420a\n4f 1b72c1\n18 43aa6\n6a 1edaca\nb9 d7687\n58 60637\nf9 f4218\n6b 1f4da3\n8c 29d18b\nca 1278b8\nad 2a1333\na9 2a1364\nb9 2a1cc5\neb 285a9e\n7e 1b3a7a\n4a 1f1ebd\n6b 1f6065\n80 10ae15\n8c 29e44d\nca 128b7a\na1 10efbd\nad 2a25f5\ndb 1281ba\ne3 12bb55\nef 2bf18d\n7e 1ed19a\neb 2bf1be\n69 1f4daa\n79 1f570b\ne9 285aa5\n7c 1b3a81\n48 1f1ec4\n69 1f606c\n7c 1ed1a1\ne9 2bf1c5\na1 ce81f\n6a 1f4da4\n80 103d97\nea 2bdefd\n6b 1f4db1\n18 3b75a\neb 2bdf0a\n7e 1ed1a8\neb 2bf1cc\n6a 1f4db2\nc5 128caa\n7a 1f5713\n5b 1f12b1\nfa 2be86c\n8c 29d1ed\nfe 12c6af\n5f 5f0f4\n9c 29db4e\nad 2a1395\ne1 12bbbe\ned 2bf1f6\nac 2a1396\ne3 12bbc5\nef 2bf1fd\n86 103dcf\nbb 110d40\n3b 47c57\nfb f41bd\nbb 110d4e\nc5 12779c\n39 47cb2\n3b 47cb9\n5a 6063e\nfb f421f\nbb 110db0\ne7 27d388\n7b 1f6718\n90 29e9da\n98 29eb31\nb5 2a2b51\n1b 2030\nb1 2a2b82\n2 1921f7\n80 10c0d9\nb1 10f670\nbd 2a2ca8\n94 29ec57\n71 1f65c8\n79 1f671f\n5b 1ebcf\nf1 2bf721\nf9 2bf878\neb e9ff0\n80 2636a5\n71 1f6876\nf1 2bf9cf\n30 1d9a2a\na4 2607f1\n38 1d9b81\n67 1b2fd1\ne5 12ceb3\ne6 27d389\n7a 1f6719\nb4 2a2b52\n1a 2031\nb0 2a2b83\nb0 10f671\nbc 2a2ca9\nb8 2a2cda\n67 63aa3\n45 1f0831\nab 268c5d\nf6 2bf6ea\nf2 12c209\nfe 2bf841\nb4 2a2e00\nb0 2a2e31\nda 11fe79\n5d 1f134b\na7 298c59\n3e 196ee7\nab 268f0b\nf6 2bf998\nf8 2bf879\nac 298fe4\nf0 2bf9d0\nf3 286008\naf 25f61c\n52 1f2427\nf3 2862b6\naf 25f8ca\n52 1f26d5\n72 1f65d0\n7a 1f6727\nf6 2bf6f8\nae 298d3d\nf2 2bf729\nf2 12c217\nfe 2bf84f\nfa 2bf880\n72 1f687e\n53 1f241c\n19 4384d\nae 298feb\nf2 2bf9d7\n35 465ac\n94 29ea0b\n73 1b4e63\n90 10b52a\n9c 29eb62\n1f 2061\nb5 2a2bb3\nf7 2bf74b\n94 29ecb9\n5f 1ec00\nf5 2bf752\n42 1aedf8\nc0 128cda\nf1 12c271\nfd 2bf8a9\nd4 2bb858\nf5 2bfa00\n1e 2062\nb4 2a2bb4\nb0 10f6d3\nbc 2a2d0b\nb4 2a2e62\n5e 1ec01\nf4 2bf753\nf0 12c272\nfc 2bf8aa\nf4 2bfa01\nd6 2bb85f\nf6 2bf75a\nf2 12c279\nfe 2bf8b1\n44 1b0390\n79 1bd301\n5e 60661\nf8 2b77d9\nff f4242\nd8 121444\nfa ebe60\n71 29cfa\n7d 1bd332\n62 5a4c3\n6e 1edafb\n1c 43ad7\nbd d76b8\n5c 60668\n4b 1b87aa\nfd f4249\nda 1294db\n2 1924a5\nb1 10f91e\nbd 2a2f56\na 46e\nf3 12c4b6\nff 2bfaee\nf9 286406\n58 1f2825\n88 2637fc\n79 1f69cd\nf9 2bfb26\nb0 10f91f\nbc 2a2f57\nb8 2a2f88\n67 63d51\n45 1f0adf\nc6 e7294\nf7 ea82b\naf 298db0\nf2 12c4b7\nfe 2bfaef\nf8 2bfb27\n7a 1f69d5\n5b 1f2573\nfa 2bfb2e\n3d 46703\nd0 128377\ndc 2bb9af\n42 1af0a6\nf1 12c51f\nfd 2bfb57\n90 10b7d8\n9c 29ee10\nb0 10f981\nbc 2a2fb9\nf0 12c520\nfc 2bfb58\nd2 12837e\nde 2bb9b6\n5f 1f25a4\n53 5ef6c\nf2 12c527\nfe 2bfb5f\n6 1ccf28\n3b 1d9e99\n39 1d9ea0\n3b 1d9ea7\nb 1d533b\nbd 110dda\n33 46892\n3f 1d9eca\nba 1089ff\n31 46899\n3d 1d9ed1\n5e 6066f\nff f4250\nbf 110de1\na5 1069f4\n30 47b5a\n79 5b233\n29 3da23\na9 106b1a\nad 106b4b\nbd 29a95c\nb1 107324\nb5 107355\n39 3e384\nb9 10747b\nbd 1074ac\n46 562ed\n1 1d51dd\n35 196d9a\nc6 11f3e4\nc4 11f3eb\n4a 56413\n39 196ec0\nca 11f50a\n4e 56444\n3d 196ef1\n9 1d5334\nce 11f53b\n4c 5644b\ncc 11f542\nb 1c1\nc3 128c72\n56 56c4e\n67 5a495\ne7 12358c\n65 5a49c\ne5 123593\n54 1b8d23\n22 46ee2\n6b 5a5bb\nd4 281e1a\na2 10ffd9\neb 1236b2\n20 46ee9\n69 5a5c2\na0 10ffe0\ne9 1236b9\n26 46f13\n6f 5a5ec\na6 11000a\nef 1236e3\n24 46f1a\n6d 5a5f3\na4 110011\ned 1236ea\nf7 123eed\n75 5adfd\nf5 123ef4\nb2 11093a\nfb 124013\n30 4784a\n79 5af23\nb0 110941\nf9 12401a\nb6 11096b\nff 124044\n34 4787b\n7d 5af54\nb4 110972\nfd 12404b\na9 106b7c\n29 1cfc73\n3d 1d05a3\nb9 1074dd\n4a 1e8663\ncf 128dfa\nb 1923af\n89 10c291\n56 1e8e3c\n5e 1e8f93\n7e 5c20c\n5c 1e8f9a\n5a 1e8fc4\ndf 12975b\n67 1ec683\n65 1ec68a\n26 1d9101\n6f 1ec7da\n24 1d9108\n6d 1ec7e1\na2 11003b\nd4 281e7c\neb 123714\n22 1d9132\n19 427e7\n6b 1ec80b\na0 110042\ne9 12371b\n77 1ecfe4\n36 1d9a62\n7f 1ed13b\n34 1d9a69\n7d 1ed142\n1c 1cd96b\n10 3a333\nb2 11099c\nfb 124075\ndb 1294dc\n14 3a364\nb0 1109a3\nf9 12407c\n9c 296a62\n90 10342a\n94 10345b\n2d 1d11b2\n4a 57737\n21 3db7a\nad 29a2a9\nca 12082e\na1 106c71\nce 12085f\na5 106ca2\n5a 58098\n3d 1d1b13\n31 3e4db\nbd 29ac0a\nda 12118f\nb1 1075d2\n35 3e50c\nde 1211c0\nb5 107603\n4e 1e9ba2\n7f 1ed139\n42 5656a\nb 46f\n56 56efc\nef 2b6e41\ne3 123809\n6d 1edd51\n61 5a719\ned 2b6e48\ne1 123810\n67 5a743\ne7 12383a\ne5 123841\nc2 2791a3\n7d 1ee6b2\nae ce753\n71 5b07a\nfd 2b77a9\nf1 124171\n8f 29cee5\n77 5b0a4\nf7 12419b\nf5 1241a2\ndb 2bb6ca\n14 1cc552\n9c 296ac4\n90 10348c\n35 3f7ce\n10 1cc583\nbd 29ac6c\nb1 107634\n31 1d072b\n7a 1f5465\nc5 1289fc\n46 1e8789\nb 19265d\n56 1e90ea\n67 1ec931\n65 1ec938\nef 2b6ea3\ne3 12386b\ned 2b6eaa\ne1 123872\n77 1ed292\nfd 2b780b\nf1 1241d3\n6f 1f4b28\n71 1ed2ca\n84 10ae56\na1 107c85\na5 107cb6\n79 5c4f5\nc 41eb6\n29 3ece5\n8c 10afad\na9 107ddc\nad 107e0d\nc4 1206ad\n39 198182\nca 1207cc\ncc 120804\ne5 124855\ne9 12497b\ned 1249ac\n8c 10b00f\na9 107e3e\nc 1d4106\n29 1d0f35\n67 1ed945\nc2 2b1603\n65 1ed94c\ne2 12487c\nc7 ee073\nc0 2b160a\n63 5a464\n6f 1eda9c\nca 2b175a\n61 5a46b\n6d 1edaa3\nea 1249d3\ncf ee1ca\nc8 2b1761\n19 43aa9\n4e 1f0c9e\n6b 1edacd\ne9 1249dd\n31 3e22b\n3d 1d1863\n0 3ac94\nb1 107322\nbd 29a95a\n80 103d8b\nb5 107353\n84 103dbc\n21 3ee3c\nde 128258\na1 107f33\na5 107f64\ne1 124ad2\ne5 124b03\nbd 29a9bc\nb1 107384\n80 103ded\n67 1edbf3\nc2 2b18b1\ne1 124b34\n89 10acc1\nb0 10f98f\n1d 1d5a0c\nbc 2a2fc7\n11 423d4\n9d 29eb03\n91 10b4cb\nb8 10fae6\n19 4252b\n99 10b622\n4f 1f1c43\ne2 12bbc6\nee 2bf1fe\n43 5e60b\n4d 1f1c4a\n41 5e612\ne6 12bbf7\n47 5e63c\nc7 127733\nc5 12773a\nea 12bd1d\n4b 5e762\ncb 127859\n49 5e769\nc9 127860\nee 12bd4e\n4f 5e793\ncf 12788a\ncd 127891\n5d 1f25ab\n51 5ef73\ndd 2bb6a2\nd1 12806a\nd7 128094\n55 5efa4\nd5 12809b\nd9 1281c1\ndf 1281eb\ndd 1281f2\n9 1d3e1a\nee 2bdf3c\nb9 110d47\n4f 1f0981\ncb 1278bb\n4b 1f09b2\nc9 1278c2\n6b 63c2b\n49 1f09b9\nf6 2be746\n57 1f118b\nfe 2be89d\n5f 1f12e2\nd 1d5359\n1 41d21\n1d 1d5cba\n11 42682\n9d 29edb1\n91 10b779\n15 426b3\n95 10b7aa\ncf 2bafe8\nc3 1279b0\ncd 2bafef\nc1 1279b7\n47 5e8ea\nc7 1279e1\nc5 1279e8\ndd 2bb950\nd1 128318\n57 5f24b\nd7 128342\nd5 128349\n1 1d3f71\n15 1d48a1\n11 1d48d2\n47 1f0ad8\ncf 2bb04a\n1a 1cd94d\nc3 127a12\n43 1f0b09\n63 63d82\n41 1f0b10\n57 1f1439\na8 110447\n9 42e8c\n41 5f8d4\nc1 1289cb\ncb 128b1b\n49 5fa2b\nc9 128b22\ncd 128b53\n39 1d0882\n9 1d50dc\n45 1f1af3\n4b 1f1c74\nc9 128b84\n49 1f1c7b\n1 42fe3\n47 1f1d9a\n45 1f1da1\nf7 ebaed\na3 106a3a\naf 29a072\nc3 128cd4\n61 1b2f29\n29 4361\n63 1b2f30\n20 19f9a7\n69 1b3080\n21 196398\n22 19f9ae\n6b 1b3087\n2e 1d8252\n71 1b388a\n29 1964ef\n30 1a0308\n79 1b39e1\n23 1cfac1\n2b 1cfc18\n7d 1b4cd2\n71 2169a\n40 1e103\n29 1cfc1f\n33 1d0422\n3b 1d0579\n50 1ea64\n39 1d0580\nc4 277ea9\ncc 278000\nce 278007\ne5 27c051\nad cd489\n50 60294\ne7 27c058\ne1 27c082\n9d 26569d\n42 1e84a8\n91 d2065\ne3 27c089\na4 268acf\ned 27c1a8\n0 1cb910\na1 25f4f1\n58 603eb\na6 268ad6\nef 27c1af\na0 268b00\ne9 27c1d9\n99 d21bc\n4a 1e85ff\na2 268b07\neb 27c1e0\nbd cddea\n8c ca853\nf7 27c9b9\n8 1cba67\na9 25f648\nf 1d3df0\nae 2a13ab\nf1 27c9e3\na 1cba6e\nbc 10750d\nab 25f64f\n41 1b737e\n52 1e8e09\nf3 27c9ea\nb4 269430\nfd 27cb09\nb6 269437\nff 27cb10\nb0 269461\nf9 27cb3a\n5a 1e8f60\nb2 269468\nfb 27cb41\n86 294a41\n5a 1e9280\n18 1cc3c8\nb9 25ffa9\n1a 1cc3cf\nbb 25ffb0\n2b 1d94d6\n51 1b7cdf\n29 3dd33\n8e 294b98\ne6 eb129\nc4 277eb7\nee eb280\ncc 27800e\n96 2953a2\na2 1069d7\nf6 eba8a\n25 1d7ea9\nae 29a00f\nd4 278818\n9e 2954f9\na7 298be9\n32 1d9d4f\n7b 1ed428\ne5 27c05f\naf 298d40\nc4 e722b\nf5 ea7c2\nad 298d47\nab 298d71\n40 1e84af\n47 24f18\ne1 27c090\nc0 e725c\nf1 ea7f3\nf 41c00\nfd 27de2b\na9 298d78\nb7 29954a\na4 268add\ned 27c1b6\nbf 2996a1\nbb 2996d2\n50 1e8e10\nf 1d3dfe\n57 25879\nf1 27c9f1\n58 1e8f67\n5f 259d0\nb0 26946f\nf9 27cb48\n40 1af02f\n2a 1d1187\n50 1af990\n61 1b31d7\n71 1b3b38\n2 1cbbc7\n40 1de61\n12 1cc528\n75 1b4e29\n4a 1e9933\n21 1cfd76\n96 25bf2e\ne5 27c2ff\ne1 27c330\n42 1e8756\ne3 27c337\n0 1cbbbe\na1 25f79f\nf 1d409e\nf1 27cc91\n52 1e90b7\nf3 27cc98\n12 1cc526\nb3 260107\nc4 e6f89\nf9 f3efa\n96 295650\na3 298ec8\n40 1e875d\n47 251c6\ne1 27c33e\ne1 eb162\nb3 299829\nf 1d40ac\n50 1e90be\n57 25b27\nf1 27cc9f\n44 1b73bc\n61 1b41eb\n46 1b73c3\n29 5623\n63 1b41f2\n6f 21176\n4 19a82b\n21 19765a\n34 3f51f\n23 197661\n4c 1b7513\n69 1b4342\n3c 3f676\n2b 1977b8\n54 25b2d\n71 2295c\n29 1d0ee1\nc4 27916b\n8c ca5a3\nc6 279172\nc0 e5c8a\ncc 2792c2\n88 25c762\n45 1b7163\nc2 e5c91\nce 2792c9\nc8 2792f3\nca 2792fa\na4 1102bf\ned 123998\ne5 27d313\nad ce74b\ne7 27d31a\nef ea2cf\n0 1ccbd2\n84 263984\na1 2607b3\nc4 280515\ne1 27d344\n42 1e976a\n91 d3327\nc6 28051c\ne3 27d34b\ne1 e9e32\ned 27d46a\ne3 e9e39\nef 27d471\n8 1ccd29\n8c 263adb\na9 26090a\ncc 28066c\ne9 27d49b\nbc 1087cf\na 1ccd30\nab 260911\n41 1b8640\n86 295d03\n5a 1ea542\nc4 279179\nc0 e5c98\ncc 2792d0\nc8 279301\n8a 295e8b\n8f d28fb\nd0 e790d\n88 295e92\ne5 27d321\n40 1e9771\n47 261da\ne1 27d352\ne1 e9e40\ned 27d478\nf5 eba84\na1 1069d1\nad 29a009\nd6 eec7f\n8e 29d204\nab 29a033\nf1 ebab5\nd4 eec86\nf 42ec2\na9 29a03a\n21 1d1038\nc2 e6fb5\na5 260a30\n0 1cce80\na1 260a61\ne1 27d5f2\n42 1e9a18\ne3 27d5f9\n40 1e9a1f\n47 26488\ne1 27d600\na0 267afa\n1 19a53f\n14 42404\n3 19a546\ne0 28468b\n41 1b70d0\n9 8508\ne2 284692\n43 1b70d7\na8 267c51\n9 19a696\n1c 4255b\nb 19a69d\ne8 2847e2\n49 1b7227\nea 2847e9\n4b 1b722e\n2b 1d9228\nf0 284fec\n51 1b7a31\n29 3da85\nf8 285143\n59 1b7b88\na2 2a1223\n3 1d3c68\n23 46ee1\n55 1b8d22\n1 1d3c6f\naa 2a137a\nb 1d3dbf\n2b 47038\n5d 1b8e79\n51 25841\n9 1d3dc6\nb2 2a1b84\n13 1d45c9\n33 47842\n11 1d45d0\n6b 2a4a7\n49 1b7235\n3b 47999\n19 1d4727\nc5 2801f8\nf4 124203\n42 1e8764\ne3 27c345\n8d d1630\nc7 2801ff\nc1 280229\nc3 280230\ncd 28034f\ncf 280356\nc9 280380\ncb 280387\naf 2a2350\na3 10ed18\nd5 280b59\nad 106bad\n52 1e90c5\nf3 27cca6\n81 263698\nab 10ee6f\ndd 280cb0\n94 10b55d\n83 26369f\ndf 280cb7\n89 2637ef\n9c 10b6b4\n8b 2637f6\n87 29cd90\nb0 2600fd\n5b 1f15cf\na7 110009\n85 29cd97\n8f 29cee7\naf 110160\nd5 ee969\n8d 29ceee\n8b 29cf18\nab 110191\nd1 ee99a\ndd 281fd2\n89 29cf1f\n97 29d6f1\nb7 11096a\n95 29d6f8\n9f 29d848\nbf 110ac1\n9d 29d84f\nef f35cf\ncd 28035d\neb f3600\n24 4488\nc9 28038e\nab 10ee7d\nff f3f30\ndd 280cbe\n3 1d3f16\n23 4718f\n55 1b8fd0\n1 1d3f1d\n41 261b0\n13 1d4877\n33 47af0\n11 1d487e\nc5 2804a6\n81 263946\n2e 1cfcaa\nc1 2804d7\n94 10b80b\n83 26394d\nc3 2804de\naf 2a25fe\na3 10efc6\nd5 280e07\nad 106e5b\n95 264276\na7 1102b7\n85 29d045\n83 29d06f\ne7 f3726\nc5 2804b4\na3 1102e8\nd5 282129\n81 29d076\ne3 f3757\n2e 1cfcb8\nc1 2804e5\nb7 110c18\n95 29d9a6\ne0 28594d\n41 1b8392\ne2 285954\n9 97ca\n43 1b8399\n14 436c6\n4a 1e88bb\nfc 12435a\neb 27c49c\n3 19b808\ne8 285aa4\n49 1b84e9\nea 285aab\n4b 1b84f0\na8 268f13\n9 19b958\n1c 4381d\nb 19b95f\n4f 5ea4d\n1 1d4f31\n49 1b84f7\nc5 2814ba\nf4 1254c5\n42 1e9a26\ne3 27d607\n8d d28f2\nc7 2814c1\ncf ee476\nc8 2b1a0d\n81 26495a\nc1 2814eb\nc3 2814f2\nca 2b1a14\n94 10c81f\n83 264961\nc1 edfd9\ncd 281611\n4a 1e9b7d\nfc 12561c\neb 27d75e\nc3 edfe0\ncf 281618\nc9 281642\ncb 281649\n89 264ab1\n9c 10c976\n8b 264ab8\n85 29e059\nd5 efc2b\n81 10ab78\n8d 29e1b0\n8b 29e1da\n3 1d51d8\n1 1d51df\n54 1af775\n2e 1d0f6c\n22 3d934\nc1 281799\n26 3d911\nc5 281776\n2e 1d0f7a\n54 1af783\n22 3d942\nc1 2817a7\n21 1cfaba\n29 1cfc11\n31 1d041b\n39 1d0572\n29 3da83\n63 1ec652\n22 1d90d0\n6b 1ec7a9\n20 1d90d7\n69 1ec7b0\n32 1d9a31\n7b 1ed10a\n30 1d9a38\n79 1ed111\n98 295523\n31 3e22d\n3d 1d1865\na5 298be2\n30 1d9d48\n79 1ed421\nad 298d39\na9 298d6a\nbd 29969a\nb9 2996cb\ne6 12484b\nc4 2b15d9\n22 5782\nce 2b1729\nee 1249a2\n20 5789\ncc 2b1730\n9c 103364\n8b 25b4a6\nd6 2b1f33\nf6 1251ac\n89 25b4ad\nd4 2b1f3a\nd2 2b1f64\n77 1ee2a6\nae ce751\n7d 1ee6b0\n71 5b078\n8c 25b4df\nda 2b20bb\n73 5adc5\n7f 1ee3fd\nfa 125334\ndf eeb2b\nd8 2b20c2\n71 5adcc\n7d 1ee404\nad 106bab\ne7 2b577a\na6 2a21f8\nef 2b58d1\na4 2a21ff\ned 2b58d8\n99 10b8de\na2 2a2229\neb 2b5902\nb6 2a2b59\nff 2b6232\nb4 2a2b60\nfd 2b6239\nb2 2a2b8a\nfb 2b6263\n10 1cc521\n4a 1e9925\n21 1cfd68\n5a 1ea286\n31 1d06c9\n8 39b89\n42 1e8758\n61 1ec907\na2 cd309\nae 260941\n71 1ed268\n94 295649\nce 2b2a4d\nc2 11f415\na5 298e90\na1 298ec1\nb1 299822\nd2 2b2212\n77 1ee554\nae ce9ff\n8c 25b78d\nad 106e59\ne7 2b5a28\ne3 2b5a59\nbd 1077ba\nf7 2b6389\nf3 2b63ba\nc 1cbd54\nad 25f935\n4 1d3f4d\n6f 5a898\n21 1d0d7c\n54 25b1f\nc 1d40a4\n29 1d0ed3\n46 1f0ae5\n29 3ed45\n63 1ed914\n4e 1f0c3c\n6b 1eda6b\n69 1eda72\n88 295e84\na5 299ea4\n79 1ee6e3\na1 1069c3\nad 299ffb\nd4 eec78\n8c 29d1fd\na9 29a02c\nc4 2b289b\n31 196d69\nc2 11f3b3\nce 2b29eb\nc0 11f3ba\ncc 2b29f2\nca 2b2a1c\ncf ef48c\nc8 2b2a23\nad 107e6d\ne7 2b6a3c\ne3 12355b\nef 2b6b93\ne1 123562\ned 2b6b9a\n99 10cba0\nce 2b9d95\neb 2b6bc4\n5e 1f134f\n21 1d102a\n61 1edbc9\nc2 1206d7\na5 29a152\nad 10811b\ne7 2b6cea\na0 2a121c\n1 1d3c61\na8 2a1373\n9 1d3db8\nb0 2a1b7d\n11 1d45c2\na7 ce5fb\nb8 2a1cd4\n19 1d4719\ne2 2bddb4\n9 41c2a\n43 1f07f9\n63 63a72\n41 1f0800\nea 2bdf0b\n4b 1f0950\n6b 63bc9\n49 1f0957\n8d 29cee0\nf3 eaaa8\nff 27e0e0\nab 29902d\n89 29cf11\n9d 29d841\nbb 29998e\n23 dad1\ncf 2b9a78\n21 dad8\nef 12ccf1\ncd 2b9a7f\ncb 2b9aa9\neb 12cd22\n24 3dbaa\nc9 2b9ab0\n9d 10b6b3\nd7 2ba282\n33 e432\ndf 2ba3d9\n1 1d3f0f\n11 1d4870\n9 41ed8\n43 1f0aa7\n63 63d20\n41 1f0aae\nf3 ea7fa\nc2 e7263\nff 27de32\nab 298d7f\n81 29d068\n95 29d998\n8d 10b000\nc7 2b9bcf\ne7 12ce48\nc5 2b9bd6\nc3 2b9c00\ne3 12ce79\nc1 2b9c07\n9d 10b961\nd7 2ba530\ne9 2b5bb7\na0 2a24de\n4f 5ea3f\n1 1d4f23\ne2 2bf076\n9 42eec\n43 1f1abb\n41 1f1ac2\n81 10ab6a\n8d 29e1a2\nf3 ebd6a\nab 29a2ef\n89 29e1d3\nc3 127702\ncf 2bad3a\nc1 127709\ncd 2bad41\ncb 2bad6b\n24 3ee6c\nc9 2bad72\n32 47af1\n7b 5b1ca\n1 1d51d1\n9 4319a\n43 1f1d69\n41 1f1d70\nf3 ebabc\nd6 eec8d\nab 29a041\n85 29e2f9\nf 1923e0\n8d 10c2c2\nc7 2bae91\nd 1923e7\nc5 2bae98\n39 3e3e6\n9 41c2c\n19 4258d\n29 3ed47\nf0 2862ae\nac 25f8c2\n51 1b8cf3\n9 42eee\n4a 56421\nd1 eff0a\n1c 1cc46b\n89 29e48f\n4e 56452\n81 10ae88\nd5 eff3b\n8d 29e4c0\nc6 11f3f2\n4d 1f09ea\nca 11f518\nce 11f549\n4c 1e995d\n40 56325\n2d 1cfef0\n4a 56475\n48 5647c\n5c 1ea2be\n50 56c86\n58 56ddd\n2d 1cfefe\n75 21979\n4a 56483\n4e 1e9bb0\n7f 1ed147\n42 56578\n4e 1e9c04\n7f 1ed19b\n42 565cc\n4e 1e9c12\n7f 1ed1a9\n42 565da\nde 2b366a\nd2 120032\n83 10ab7d\n8f 29e1b5\n77 5c374\n41 5e674\n4d 1f1cac\nca 1207da\n40 575e7\n48 5773e\n7f 1ee45d\n73 5ae25\n42 5788e\nf7 1241a9\n71 5ae2c\n7d 1ee464\n40 57895\n73 5ae33\n7f 1ee46b\n42 5789c\n67 5a4a3\n22 46ef0\n54 1b8d31\n6b 5a5c9\n26 46f21\n6f 5a5fa\na4 26077f\n4f 1f1c51\n43 5e619\na0 cd29e\nac 2608d6\n4b 5e770\na2 10ffe7\nd4 281e28\neb 1236c0\na6 110018\nef 1236f1\nb2 110948\nfb 124021\ncf 2bad48\nc3 127710\nc7 127741\ncb 127867\ncf 127898\nd7 1280a2\ndf 1281f9\n22 46f44\n54 1b8d85\n6b 5a61d\n20 46f4b\n69 5a624\n30 478ac\n79 5af85\n4b 5e7c4\n49 5e7cb\n54 1b8d93\n22 46f52\n6b 5a62b\n81 d16f4\n32 478b3\n8d 264d2c\n7b 5af8c\na0 cd300\nac 260938\n4b 5e7d2\na2 110049\nd4 281e8a\neb 123722\nb2 1109aa\nfb 124083\ncb 1278c9\n6f 1edd58\n63 5a720\n67 5a751\n7f 1ee6b9\n73 5b081\n8f 29cef3\n77 5b0b2\n4f 1f1eff\na4 260a2d\n43 5e8c7\n5f 1f2860\nb4 26138e\n53 5f228\n57 5f259\nef 2b6e4f\ne3 123817\ne7 123848\nff 2b77b0\nf3 124178\ncf 2baff6\nc3 1279be\nc7 1279ef\ndf 2bb957\nd3 12831f\nd7 128350\n52 1e8e17\nf3 27c9f8\n5f 1f28b4\n53 5f27c\na4 260a8f\n4f 1f1f61\n43 5e929\n5f 1f28c2\nb4 2613f0\n53 5f28a\nef 2b6eb1\ne3 123879\nff 2b7812\nf3 1241da\ncf 2bb058\nc3 127a20\neb 124982\ncb 128b29\n69 5b8e6\n61 5ba3d\n43 5fbdd\n63 5ba44\n43 5fbeb\ne3 124b3b\nc3 128ce2\n94 10320f\n9c 103366\n81 29e32a\n14 1cc306\n89 29e481\n1c 1cc45d\n84 103b70\n8c 103cc7\n0 39786\nc 1ccdbe\nb5 1073b5\n84 103e1e\na5 106a56\na2 298ed3\n3 1cb918\nb5 1073b7\nb 1cba6f\naa 29902a\nbd 10750e\n82 29d07a\n95 10b55e\n3d 1d0605\n8a 102979\nd 1d3e4b\n9e 2967bb\n92 103183\n15 1d4655\n9a 1032da\n1d 1d47ac\na5 107d18\nc0 278194\nad 107e6f\n21 3d92e\n2d 1d0f66\n8a 103c3b\n1 41ad5\nd 1d510d\n25 1d10bd\nb3 107329\n82 103d92\nbf 29a961\n5 1d5264\n52 1e8e7b\nd7 129612\n5e 1e8fa1\n5a 1e8fd2\ndf 129769\ncc 11f5a4\nd4 11fdae\nb0 108903\n46 1e853d\nb8 108a5a\n4e 1e8694\nc3 2baec2\n56 1e8e9e\nc1 2baec9\n54 1e8ea5\ncb 2bb019\n5e 1e8ff5\nc6 11f454\nce 11f5ab\nd6 11fdb5\n46 1e854b\nc3 2baed0\n56 1e8eac\n52 1e9129\nc6 11f6f4\nc4 11f6fb\nd6 120055\nd4 12005c\n56 1e914c\n54 1e9153\nc6 11f702\nd6 120063\n56 1e915a\nc6 120708\nc4 12070f\ncc 120866\n46 1e97ff\n44 1e9806\n42 5631e\n25 1cfd99\n4e 1e9956\nc6 120716\n46 1e980d\nf7 123f4d\nc6 1209b6\n75 1ed04b\n44 1e9ab4\n77 1ed052\n46 1e9abb\n26 1d910f\n6f 1ec7e8\n22 1d9140\n6b 1ec819\n36 1d9a70\n7f 1ed149\na0 25f4ee\n4b 1f09c0\n57 1f1199\nb4 25fe1e\n5f 1f12f0\ne5 1235f5\na6 11006c\nef 123745\na4 110073\ned 12374c\n43 1e84b7\nf5 123f56\n2 1d4f35\nb4 1109d4\n4b 1e860e\nfd 1240ad\n12 3a098\n1e 1cd6d0\nc7 127795\n1a 3a1ef\ncf 1278ec\ncd 1278f3\nd5 1280fd\n27 4490\ndd 128254\n15 426c1\n67 1ec6e5\n65 1ec6ec\n26 1d9163\n1d 42818\n6f 1ec83c\n24 1d916a\n6d 1ec843\n77 1ed046\n36 1d9ac4\n7f 1ed19d\n34 1d9acb\n7d 1ed1a4\nb1 110c52\n47 1f088c\nce 2b29f9\nc2 11f3c1\n45 1f0893\nb9 110da9\n4f 1f09e3\n57 1f11ed\nde 2b335a\nd2 11fd22\n55 1f11f4\n5f 1f1344\ne7 1235fc\na6 11007a\nef 123753\nf7 123f5d\nb6 1109db\nff 1240b4\nc7 1277a3\ncf 1278fa\nd7 128104\ndf 12825b\n67 1ec6f3\n26 1d9171\n6f 1ec84a\n77 1ed054\n85 263913\n36 1d9ad2\n7f 1ed1ab\n47 1f089a\n57 1f11fb\n67 1ec93f\n77 1ed2a0\n73 1ed2d1\n43 1f0b17\n57 1f1447\n53 1f1478\nf7 1241fd\n43 1e8765\nf5 124204\n1e 1cd97e\n12 3a346\nc7 127a43\nd7 1283a4\nd5 1283ab\n77 1ed2f4\n47 1f0b3a\nce 2b2ca7\nc2 11f66f\nff 2b623e\n45 1f0b41\n57 1f149b\nde 2b3608\nd2 11ffd0\n55 1f14a2\nf7 12420b\nd7 1283b2\n77 1ed302\n47 1f0b48\n57 1f14a9\n67 1ed953\n63 5a472\n6f 1edaaa\n47 1f1afa\ne5 1248b7\ned 124a0e\n12 3b35a\nc7 128a57\n7a 1f54c7\nc5 128a5e\ncd 128bb5\n65 1ed9ae\n63 5a4c6\n1d 43ada\n6f 1edafe\n61 5a4cd\n6d 1edb05\n47 1f1b4e\nc2 120683\n45 1f1b55\n43 5e66d\n4f 1f1ca5\ne7 1248be\nc7 128a65\n67 1ed9b5\n63 5a4d4\n6f 1edb0c\n47 1f1b5c\n47 1f1da8\n12 3b608\nc7 128d05\n67 1edc55\n65 1edc5c\n47 1f1dfc\n75 6314b\nc2 120931\nf3 123ec8\nff 2b7500\n45 1f1e03\n67 1edc63\n47 1f1e0a\n18 3a48a\n1c 3a4bb\n98 103581\n9c 1035b2\n98 1035e3\n3d 3f925\n39 3e382\n8 3adeb\nb9 107479\n88 103ee2\nbd 1074aa\n8c 103f13\nb9 1074db\n88 103f44\n29 3dcd1\n39 3e632\n3d 3e663\n19 427d9\n1d 4280a\na9 106dc8\nad 106df9\nb9 107729\nbd 10775a\n89 10af6f\n99 10b8d0\n9d 10b901\n39 3e694\na9 106e2a\nb9 10778b\n29 3ef93\n9 4313a\na9 10808a\nad 1080bb\n1c 3a20d\n89 10c231\n29 3eff5\na9 1080ec\n4a 566c1\n4c 566f9\n70 1ed019\n2f 1d8007\nde 278976\n5e 57053\n5c 5705a\n39 19716e\nca 11f7b8\n3d 19719f\nce 11f7e9\ncc 11f7f0\nda 120119\n4a 566cf\n5a 57030\n5e 57061\nce 11f7f7\n5d 1f15f9\nda 120127\n4a 56723\n5a 57084\nca 11f81a\n35 1d87a8\n6f 5bb5c\n4a 56731\nca 11f828\n6f 5bb6a\nda 120189\n8b 10acd4\n7f 5c4cb\n7b 5af1a\n10 1d45cf\n4a 57983\n7f 5af4b\n14 1d4600\n4e 579b4\nff 124042\n3d 198461\n94 29d6f7\n31 4e29\nce 120aab\n7f 5af59\n4e 579c2\n10 1d4631\n7b 5af7c\n4a 579e5\nb6 110c27\nff 124300\n79 5af83\n48 579ec\nfb 124073\n90 29d728\nca 120adc\n7b 5af8a\n4a 579f3\nfb 124081\nca 120aea\n54 1b8fd1\n22 47190\n6b 5a869\n26 471c1\n6f 5a89a\n36 47b22\n7f 5b1fb\n4b 5ea10\n49 5ea17\n4f 5ea41\n4d 5ea48\n40 1f1dd1\n71 1f5368\ndf 280cc5\n5f 5f3a2\na6 1102b8\nef 123991\nb6 110c19\nff 1242f2\nb4 110c20\nfd 1242f9\ncb 127b07\nc9 127b0e\ncf 127b38\ncd 127b3f\ndb 128468\ne2 2bedba\nd9 12846f\ndf 128499\ne6 2bedeb\ndd 1284a0\n54 1b8fdf\n22 4719e\n6b 5a877\n26 471cf\n6f 5a8a8\n32 47aff\n7b 5b1d8\n36 47b30\n7f 5b209\nac 260b84\na0 cd54c\n4b 5ea1e\na4 cd57d\n4f 5ea4f\nb0 cdead\nbc 2614e5\n5b 5f37f\nb4 cdede\n5f 5f3b0\na6 1102c6\nef 12399f\ncb 127b15\ncf 127b46\ndb 128476\ndf 1284a7\n22 471f2\n54 1b9033\n6b 5a8cb\n20 471f9\n69 5a8d2\n5a 1e8f6e\nfb 27cb4f\n5b 5f3d3\ncb 127b69\n22 47200\n54 1b9041\n6b 5a8d9\ncb 127b77\n4b 5fcd2\n49 5fcd9\n4f 5fd03\nb5 2a189f\nef 124c53\ned 124c5a\n5e 56da5\ncb 128dc9\na4 ce83f\n4f 5fd11\n69 5bb94\ne9 124c8b\ncb 128e2b\n4 2df\n6b 5bb9b\n84 c93d6\neb 124c92\ncb 128e39\n9c 103614\n1c 1cc70b\nbd 10750c\nf7 2b60db\n8c 103f75\n3d 1d0603\n0 39a34\nc 1cd06c\n9 1d40c8\n19 1d4a29\nb 1cbd1d\nbd 1077bc\n8d 10b002\n9d 10b963\n3d 1d08b3\n8a 102c27\nd 1d40f9\n9a 103588\n1d 1d4a5a\n29 1d11e3\n9 1d538a\n8d 10c2c4\n21 3dbdc\n2d 1d1214\nbb 107480\n1 41d83\n8a 103ee9\nd 1d53bb\n4e 1e88e0\n5e 1e9241\n7e 5c4ba\n5c 1e9248\n5a 1e9272\n5e 1e924f\ncc 11f852\n5e 1e92a3\n5c 1e92aa\nce 11f859\n5e 1e92b1\nff 1240a4\n94 29d759\nce 120b0d\nfd 1240ab\ncc 120b14\nff 1240b2\nce 120b1b\n26 1d93af\n6f 1eca88\n24 1d93b6\n6d 1eca8f\n36 1d9d10\n7f 1ed3e9\n4f 1f0c2f\n6b 63ed9\n49 1f0c67\n5b 1f15c1\n26 1d93bd\n6f 1eca96\n36 1d9d1e\n7f 1ed3f7\na6 11031a\nef 1239f3\na4 110321\n5 42d66\ned 1239fa\nb6 110c7b\nff 124354\n4b 1e88bc\nb4 110c82\n15 436c7\n2 1d51e3\nfd 12435b\n1a 3a49d\ncf 127b9a\ncd 127ba1\ndf 1284fb\ndd 128502\n36 1d9d72\n7f 1ed44b\n34 1d9d79\n7d 1ed452\n4f 1f0c91\n7 42d6d\na6 110328\nef 123a01\nb6 110c89\n17 436ce\nff 124362\ncf 127ba8\ndf 128509\n36 1d9d80\n7f 1ed459\n63 5a712\n6f 1edd4a\n69 1edd82\n43 5e8b9\n4f 1f1ef1\n49 1f1f29\nb5 2a1901\nef 124cb5\ned 124cbc\n1a 3b75f\ncf 128e5c\ncd 128e63\n63 5a774\n6f 1eddac\n61 5a77b\n6d 1eddb3\n43 5e91b\n4f 1f1f53\n7d 632a2\nef 124cc3\ncf 128e6a\n63 5a782\n6f 1eddba\ne3 2bdaf7\n10 3b347\ne7 2bdb28\n14 3b378\neb 2bdc4e\n18 3b49e\nef 2bdc7f\n1c 3b4cf\n90 10443e\n94 10446f\n98 104595\n0 1d3f7c\n9c 1045c6\n10 3b3a9\n18 3b500\n90 1044a0\n98 1045f7\ne3 2bdda5\n10 3b5f5\ne7 2bddd6\n14 3b626\n90 1046ec\n94 10471d\n10 3b657\n90 10474e\n14 426c0\n66 1ec6e4\n31 3f4ef\n1c 42817\n6e 1ec83b\n39 3f646\n94 10b7b7\ne6 2b57db\nb1 1085e6\n9c 10b90e\nee 2b5932\nb9 10873d\n39 3f6a8\n9c 10b970\nb9 10879f\n66 1ec992\n31 3f79d\n46 1f0b39\n11 43944\n15 43975\ne6 2b5a89\nb1 108894\nb5 1088c5\nc6 2b9c30\n91 10ca3b\n31 3f7ff\nb1 1088f6\n8b ca57a\nd6 121007\n89 ca581\nd4 12100e\nda 12112d\n52 57eed\nd6 121015\n35 1d19bc\n52 57f41\n50 57f48\n58 5809f\n35 1d19ca\n52 57f4f\nb5 29aac1\nd2 121046\n52 5819b\n52 581ef\n50 581f6\n52 581fd\nd2 1212f4\n55 60266\nf7 1251af\nf5 1251b6\nde 1284a6\nfb 1252d5\nf9 1252dc\nfd 12530d\nd1 12932c\nd7 129356\nd5 12935d\ndb 12947c\nd9 129483\ndd 1294b4\nf3 12518c\nf7 1251bd\nfb 1252e3\nd3 129333\nd7 129364\ndb 12948a\n79 5c247\nf9 12533e\n57 6050d\nf1 125433\nf7 12545d\n73 5c343\n53 604ea\n57 6051b\nf3 12543a\nf7 12546b\nd3 1295e1\n53 6053e\n8b 29e1e6\n73 5c3a5\nf 39b60\n53 6054c\nf3 12549c\n14 1cd566\n10 3a085\n1c 1cd6bd\n18 1cd6ee\n94 1044d1\n9c 104628\n10 3a0e7\n1c 1cd71f\n14 1cd814\ncb 2b1a15\n82 29e33c\n95 10c820\n31 3e28f\n3d 1d18c7\n92 104445\n15 1d5917\n11 42436\n9a 10459c\n1d 1d5a6e\n31 1d19ed\n15 1d5b63\n11 1d5b94\n3 1cce88\nb5 108927\n95 10cace\n92 1046f3\n15 1d5bc5\nb 193671\n56 1ea0fe\n52 56c1d\n5e 1ea255\n52 1ea13d\n52 56c2b\n5e 1ea263\n31 1d06d7\n5a 1ea294\nd6 121069\nd4 121070\n56 1ea160\n54 1ea167\n35 1d06fa\n52 56c7f\n5e 1ea2b7\nd6 121077\n56 1ea16e\nb 19391f\n56 1ea3ac\n52 1ea3eb\nd6 121317\n56 1ea40e\n54 1ea415\n56 1ea41c\nf6 2bfa08\n57 1f244d\n77 1ee2b4\n73 5add3\n7f 1ee40b\n57 1f245b\n43 1e9779\nf5 125218\n4b 1e98d0\nfd 12536f\nd5 1293bf\n27 5752\ndd 129516\n77 1ee308\n73 5ae27\n7f 1ee45f\n71 5ae2e\n7d 1ee466\n57 1f24af\nd2 120fe4\n55 1f24b6\n53 5efce\n5f 1f2606\n51 5efd5\nda 12113b\n5d 1f260d\na7 299f1b\nf7 12521f\nd7 1293c6\n77 1ee316\n85 264bd5\n73 5ae35\n7f 1ee46d\n57 1f24bd\n53 5efdc\nb4 261142\n5f 1f2614\n71 1ee58c\n57 1f26fb\n73 1ee593\n57 1f2709\nf 1cbd4e\n53 1f273a\nf7 1254bf\nd7 129666\nd5 12966d\n83 29cdbf\n77 1ee5b6\n57 1f275d\nd2 121292\n55 1f2764\nf7 1254cd\nd7 129674\n83 29cdcd\n77 1ee5c4\n57 1f276b\neb 2bdefc\n18 3b74c\nef 2bdf2d\n1c 3b77d\n98 104843\n9c 104874\n18 3b7ae\n98 1048a5\n6e 1ecae9\n39 3f8f4\n4e 1f0c90\n19 43a9b\n1d 43acc\nee 2b5be0\nb9 1089eb\nbd 108a1c\nce 2b9d87\n99 10cb92\n39 3f956\nb9 108a4d\n5a 582e4\nea 1236bf\n6d 1f4b91\n5e 58315\nda 1213db\n5a 582f2\n5e 58323\n51 5f283\n5d 1f28bb\nda 1213e9\n5a 58346\nda 12144b\n5b 60633\n5f 60664\nfb 125583\nfd 1255bb\ndb 12972a\n7b 5c49a\nb0 cf16f\n5b 60641\nb4 cf1a0\n5f 60672\nfb 125591\nff 1255c2\n5b 60695\n46 1f1b4d\nf9 1255ec\n94 c9d37\nfb 1255f3\n18 1cd99c\n9c 1048d6\n10 3a395\n1c 1cd9cd\n39 1d1b44\n19 1d5ceb\nb 1ccfdf\nbd 108a7e\n9d 10cc25\n31 3e53d\n3d 1d1b75\n11 426e4\n9a 10484a\n1d 1d5d1c\n52 56ecb\n5e 1ea503\n52 56ed9\n5e 1ea511\n52 56f2d\n5e 1ea565\n50 56f34\n5c 1ea56c\n52 56f3b\n5e 1ea573\n73 5b073\n7f 1ee6ab\n5b 1f2883\n4b 1e9b7e\nfd 12561d\ndf 1297bd\ndd 1297c4\n73 5b0d5\n8b 29cf16\n7f 1ee70d\n89 29cf1d\n71 5b0dc\n7d 1ee714\nff 125624\ndf 1297cb\n8b 29cf24\n73 5b0e3\n7f 1ee71b\n3d 1d9bb3\n31 4657b\n4 39761\na5 cd342\n39 466d2\nbd 2a2caa\nb1 10f672\nb5 10f6a3\n84 102858\nb9 10f7c9\nbd 10f7fa\nd8 e6742\n29 45dd3\n39 46734\na9 10eeca\n84 1028ba\nb9 10f82b\n4a 5fa85\n2d 1d9500\n21 45ec8\n5a 603e6\n3d 1d9e61\n31 46829\n35 4685a\nad 2a25f7\nca 128b7c\na1 10efbf\nce 128bad\na5 10eff0\nbd 2a2f58\nda 1294dd\nb1 10f920\nde 12950e\nb5 10f951\nbd 2a2fba\nb1 10f982\n21 46edc\n29 47033\na1 10ffd3\na5 110004\n79 64843\na9 11012a\nad 11015b\nd8 e7a04\n29 47095\na9 11018c\n21 4718a\n34 3e25d\n23 19639f\na1 110281\nd0 e7b5b\n21 471ec\n23 196401\na1 1102e3\n6f 1f5dea\n63 627b2\n67 627e3\n65 627ea\n6b 62909\n6f 6293a\n6d 62941\n77 63144\ne7 e9eda\n46 562f9\n7b 6326a\n7f 6329b\nef 2beee1\ne3 12b8a9\ne7 12b8da\ne5 12b8e1\neb 12ba00\ne9 12ba07\nef 12ba31\ned 12ba38\nf7 12c23b\nf5 12c242\nc6 11f3f0\nfb 12c361\nf9 12c368\nff 12c392\nfd 12c399\n6f 1f5df8\n63 627c0\n67 627f1\n6f 62948\n7f 1f6759\n73 63121\n77 63152\n7f 632a9\ne7 12b8e8\nef 12ba3f\n6b 6296b\n7b 632cc\neb 12ba62\ne9 12ba69\nc6 11f452\nfb 12c3c3\nf9 12c3ca\n6f 1f6098\n63 62a60\n67 62a91\n7d 1f6a00\nae d6aa1\n71 633c8\n77 633f2\nef 2bf18f\ne3 12bb57\ned 2bf196\ne1 12bb5e\ne7 12bb88\nfd 2bfaf7\n8 477\nf1 12c4bf\nf7 12c4e9\n67 62a9f\n7f 1f6a07\n73 633cf\n77 63400\ne7 12bb96\nff 2bfafe\nf3 12c4c6\nff 2bfb60\nf3 12c528\n69 63bd2\ne9 12ccc9\ned 12ccfa\n69 63c34\ne9 12cd2b\n61 63d29\n63 63d84\n63 1b2fa0\ne1 12ce82\n63 63d92\ne3 12ce89\n29 1d7fc1\n35 1d879a\n3d 1d88f1\n4 1cb9b1\n39 1d8922\na5 10eda4\nad 10eefb\n3 1d3c66\nb5 10f705\nb 1d3dbd\nbd 10f85c\na2 1069c9\nae 29a001\nd4 27880a\n25 1d7e9b\naa 106b20\ndc 278961\n2d 1d7ff2\nb2 10732a\nbe 29a962\n35 1d87fc\nba 107481\n3d 1d8953\n21 1d8118\n52 5efcd\n5e 1f2605\n35 1d8a48\n31 1d8a79\n29 1d9283\nd4 279acc\na2 107c8b\n25 1d915d\n57 1b8fd7\n3 1d3f24\ndc 279c23\n21 45c7c\naa 107de2\nd0 e65eb\n2d 1d92b4\n53 25af6\n5f 1b912e\nb 1d407b\nd4 279d7a\na2 107f39\n25 1d940b\ndc e6771\n67 1f49d1\n1 3a9f3\n6b 1f4b59\n77 1f5332\n7f 1f5489\n11 3b354\n46 1e8549\n7b 1f54ba\ndc e677f\n67 1f49df\n6f 1f4b36\n6b 1f4b67\n77 1f5340\n7f 1f5497\n7b 1f54c8\n32 3e23f\n3e 1d1877\ne7 12b93c\ne5 12b943\n3a 3e396\nef 12ba93\ned 12ba9a\nf7 12c29d\n43 1f0805\nf5 12c2a4\nff 12c3f4\n4b 1f095c\nfd 12c3fb\n5 3aa24\n6f 1f4b8a\n15 3b385\n7f 1f54eb\nfa 124020\n7d 1f54f2\ne7 12b94a\nef 12baa1\nf7 12c2ab\nff 12c402\ndc e67e1\n67 1f4a41\n6f 1f4b98\n77 1f53a2\n7f 1f54f9\ndc e6a1f\n67 1f4c7f\nd8 e6a50\n63 1f4cb0\n61 1f4cb7\n77 1f55e0\ndc e6a2d\n67 1f4c8d\nd8 e6a5e\n63 1f4cbe\n77 1f55ee\n73 1f561f\nf7 12c54b\nee 2b6e4e\ne2 123816\n65 1f4ce8\n77 1f5642\nf2 124177\nfe 2b77af\n75 1f5649\nf7 12c559\ndc e6a8f\n67 1f4cef\n77 1f5650\n6b 1f5e1b\n4 19a56d\n6b 1f5e29\n32 3f501\ne7 12cbfe\ne5 12cc05\ned 12cd5c\ne2 12482a\n65 1f5cfc\n63 62814\n6f 1f5e4c\n61 6281b\nea 124981\n6d 1f5e53\ne7 12cc0c\ndc e7aa3\n67 1f5d03\n63 62822\n6f 1f5e5a\n32 3f7af\ne7 12ceac\ne2 124ad8\n65 1f5faa\nd7 ee970\n8f 29cef5\ndc e7d51\n67 1f5fb1\n29 4601f\n4 39a0f\na5 cd5f0\n39 46980\n3d 469b1\na9 10f116\nad 10f147\n84 102b06\nb9 10fa77\nbd 10faa8\nd8 e69f0\n29 46081\n39 469e2\na9 10f178\n84 102b68\nb9 10fad9\n29 472e1\n3c 3e3b4\n2b 1964f6\na9 1103d8\nd8 e7cb2\n29 47343\n2b 196558\na9 11043a\n6b 62bb7\n6f 62be8\n7f 63549\nef 12bcdf\nff 12c640\n6f 62bf6\n7f 63557\nef 12bced\n7b 6357a\n69 63e80\n69 63ee2\n6b 1b30f7\ne9 12cfd9\n84 d1724\neb 12cfe0\n29 1d826f\n4 1cbc5f\n39 1d8bd0\nad 10f1a9\nb 1d406b\nbd 10fb0a\naa 106dce\ndc 278c0f\n2d 1d82a0\nba 10772f\n3d 1d8c01\n29 1d9531\ndc 279ed1\n21 45f2a\naa 108090\nd0 e6899\n2d 1d9562\n6f 1f4dd6\n69 1f4e0e\n7f 1f5737\n11 3b602\n46 1e87f7\n7b 1f5768\n6f 1f4de4\n7f 1f5745\n7b 1f5776\n3a 3e644\nef 12bd41\ned 12bd48\n4b 1f0c0a\nfd 12c6a9\nea 12396d\n6d 1f4e3f\n15 3b633\n7f 1f5799\nfa 1242ce\n7d 1f57a0\nef 12bd4f\nff 12c6b0\n6f 1f4e46\n7f 1f57a7\n69 1f60d0\n3a 3f906\nef 12d003\n6f 1b3128\ned 12d00a\n63 62ac2\n6f 1f60fa\n61 62ac9\nea 124c2f\n6d 1f6101\nef 12d011\n63 62ad0\n6f 1f6108\nb5 110965\nbd 110abc\n84 103b7c\nb9 110aed\n66 1f4ce0\n31 47aeb\ne6 2bddd7\n33 196d00\nb1 110be2\n31 47b4d\n33 196d62\nb1 110c44\nf7 12d4fd\nc6 1206b2\nfb 12d623\n73 643e3\nf3 12d4da\nf7 12d50b\nfb 12d631\n71 6468a\n73 64691\nf3 12d788\n73 646e5\n73 1b3901\nad 106df7\nf1 12d7e3\n2f 3dd07\n73 646f3\naf 106dfe\nf3 12d7ea\n4 1ccc73\n39 1d9be4\nb2 1085ec\n35 1d9abe\n13 1d4885\n31 465dd\nba 108743\n3d 1d9c15\n1b 1d49dc\n31 1d9d3b\n37 196d93\n3 1d51d6\nb5 110c75\nb2 10889a\n35 1d9d6c\n46 1e980b\n7b 1f677c\n14 19aece\n7b 1f678a\nf7 12d55f\n43 1f1ac7\nf5 12d566\n98 103343\n77 1f6656\nf2 12518b\n75 1f665d\n73 63175\n7f 1f67ad\n71 6317c\nfa 1252e2\n7d 1f67b4\nf7 12d56d\n77 1f6664\n73 63183\n7f 1f67bb\n2f 1cfef5\n73 1f68e1\nf7 12d80d\n77 1b3932\n43 1f1d75\nf5 12d814\n98 1035f1\n77 1f6904\nf2 125439\n75 1f690b\n9f 29d856\nf7 12d81b\n77 1f6912\n4 3acd1\na5 ce8b2\n6e 1f4e37\n39 47c42\n3b 196e57\n84 103dc8\nee 2bdf2e\nb9 110d39\n39 47ca4\n3b 196eb9\n84 103e2a\nb9 110d9b\n79 647e1\nc6 120960\nfb 12d8d1\n7b 1b39f6\nf9 12d8d8\n7b 647e8\nfb 12d8df\n7b 1b3a58\nf9 12d93a\n94 d2085\nfb 12d941\n4 1ccf21\n39 1d9e92\n3f 196eea\nb 1d532d\nbd 110dcc\nba 1089f1\n31 4688b\n3d 1d9ec3\n46 1e9ab9\n7b 1f6a2a\n14 19b17c\n7b 1f6a38\n7f 1b3a89\n4b 1f1ecc\nfd 12d96b\n73 63423\n7f 1f6a5b\nfa 125590\n71 6342a\n7d 1f6a62\nff 12d972\n73 63431\n7f 1f6a69\n29 1964fd\n2b d916\n9 19a6a4\n29 1977bf\n9 19b966\nc8 128dcf\nf9 12c366\n4a 1aeeed\nd0 1295d9\n18 b28\n52 1af6f7\n56 1b09da\n2 1cb927\n12 1cc288\n1a 1cc3df\nf9 12d628\n4a 1b01af\n2 1ccbe9\ne1 12ce20\n29 436f\n74 5adfc\n63 1b2f3e\n7c 5af53\ne9 12cf77\n6b 1b3095\n9 8516\n54 5efa3\n43 1b70e5\n5c 5f0fa\n4b 1b723c\n77 1b4b82\n23 1cfacf\n73 216a1\n7f 1b4cd9\n42 1e10a\n2b 1cfc26\n33 1d0430\n52 1ea6b\n3b 1d0587\n57 1b8d29\n3 1d3c76\n53 25848\n5f 1b8e80\nb 1d3dcd\n13 1d45d7\n1b 1d472e\n77 1b4e30\n23 1cfd7d\n33 1d06de\n74 5c0be\n29 5631\n63 1b4200\n7c 5c215\n6b 1b4357\n9 97d8\n54 60265\n43 1b83a7\n5c 603bc\n4b 1b84fe\n73 5b071\n7f 1ee6a9\n8e 25b4d8\n96 25bce2\n9c 25be32\n9e 25be39\ncc 278062\nce 278069\n9 19bc14\nbe cf0b2\n9c 25be40\nee eb2e2\ncc 278070\na6 ce846\n84 25b5d4\n58 26c59\n96 25bf90\n18 1d49c6\na6 ce8a8\n84 25b636\ne6 eb439\nc4 2781c7\nce e6135\n80 25c619\n88 25c770\n86 25c643\nc6 2791d4\nc0 e5cec\ncc 279324\nc2 e5cf3\na5 25f76e\nce 27932b\n18 1d59da\n84 25c64a\n71 5c33a\n80 c9169\n8c 25c7a1\nc0 e5cfa\ncc 279332\na5 25f4ce\n0 1cb91e\n7 8387\na1 25f4ff\nad 25f625\nf 84de\n8 1cba75\na9 25f656\n1f 8e3f\n18 1cc3d6\nb9 25ffb7\naf d6a3e\n8d 2637cc\nbf d739f\n9d 26412d\nc 1cba98\n50 1f2484\nad 25f679\n1c 1cc3f9\nbd 25ffda\na0 299ed2\n85 2636c9\n99 10358e\na2 299ed9\n87 2636d0\na8 29a029\n8d 263820\nd0 278839\naa 29a030\n8f 263827\nb8 29a98a\n9d 264181\nba 29a991\n9f 264188\ne5 27c0b3\na4 268b31\ned 27c20a\n9d d21ed\n4e 1e8630\nb8 1089f6\na6 268b38\nef 27c211\nb4 269492\nfd 27cb6b\n10 192b5d\nc5 28025a\nd9 12011f\ne2 2b6a6a\n90 10ca46\nc7 280261\n18 192cb4\ncd 2803b1\nab 10eed1\ndd 280d12\n1c 1cc407\nbd 25ffe8\n4c 1e8637\na4 268b3f\ned 27c218\n5c 1e8f98\nb4 2694a0\nfd 27cb79\ne8 2b6bc8\nef f3631\ncd 2803bf\nab 10eedf\nff f3f92\nf8 2b7529\ndd 280d20\n0 1cbbcc\n7 8635\na1 25f7ad\nb7 d74f6\n95 264284\ne5 27c361\n10 192e0b\nc5 280508\n39 1d8b6e\n4 1cbbfd\na5 25f7de\nb7 d7558\n11 1cd534\nb0 29aaef\n95 2642e6\n44 1e878e\ne5 27c36f\ne7 f3788\ne0 2b6d1f\nc5 280516\naf 2a266e\nf0 2b7680\nf7 f40e9\na3 10f036\nd5 280e77\na5 260790\n0 1ccbe0\nef ea2dd\n7 9649\na1 2607c1\na1 cd2af\nad 2608e7\n8 1ccd37\nf 97a0\na9 260918\n81 d1456\n8d 264a8e\n4 1ccc03\na5 2607e4\nc 1ccd5a\n0 39722\na1 cd303\nad 26093b\ncc 2b1a3e\n85 26498b\n99 104850\nce 2b1a45\n87 264992\n81 d14aa\n8d 264ae2\n83 d14b1\nd0 279afb\n8f 264ae9\ne5 27d375\n95 d3358\n46 1e979b\ne7 27d37c\ne1 e9e94\ned 27d4cc\n9d d34af\n4e 1e98f2\n42 562ba\ne3 e9e9b\nef 27d4d3\n10 193e1f\nc5 28151c\nd9 1213e1\nc7 281523\nc1 ee03b\n18 193f76\ncd 281673\nc3 ee042\ncf 28167a\n44 1e97a2\ne5 27d383\n4c 1e98f9\n40 562c1\ne1 e9ea2\ned 27d4da\n0 1cce8e\n7 98f7\na1 260a6f\n4 1cceb1\na5 260a92\ne5 27d623\n39 1d9e30\n4 1ccebf\na5 260aa0\n44 1e9a50\ne5 27d631\nc5 2817d8\n86 294a4f\n86 294aa3\na6 107d1c\n84 294aaa\nae 107e73\nd4 e667c\n8c 294c01\n96 295404\n9e 29555b\nbe 1087d4\n9c 295562\nd6 278881\nde 2789d8\n8c c959d\nc6 27816c\n9c c9efe\n27 1d815e\nd6 278acd\na6 107fca\n84 294d58\nc4 e6feb\nf9 f3f5c\n96 2956b2\n5a 1f155e\nc6 2781ce\nd6 278b2f\n48 5fa28\ne9 f3609\n86 294d5f\n58 60389\nf9 f3f6a\n96 2956c0\nca 279308\n86 295d11\n82 295d42\nd6 e78e3\n82 102830\n5 1d3d02\n8e 295e68\nd2 e7914\nb5 26138f\n8a 295e99\n86 295d65\n84 295d6c\n80 10288b\nd4 e793e\n8c 295ec3\nad cd497\ne7 27c066\nf4 123f55\n42 1e84b6\ne3 27c097\nef 27c1bd\n4a 1e860d\nfc 1240ac\neb 27c1ee\nd4 1280fc\nc3 28023e\n26 448f\ndc 128253\ncb 280395\nc6 e7232\nf7 ea7c9\naf 298d4e\n87 29cd9e\n97 29d6ff\na7 298c4b\nb7 2995ac\nbf 299703\n87 29cdf2\n8f 29cf49\n20 19e9a1\n97 29d753\n28 19eaf8\n9f 29d8aa\nbf 110b23\n9d 29d8b1\n5e 1e8f9f\nff 27cb80\nf2 2b73d9\nd7 280bd0\nfa 2b7530\ndf 280d27\nb7 2995ba\nd4 1283aa\nc3 2804ec\na5 298f00\na7 110319\n85 29d0a7\nad ce759\ne7 27d328\nf4 125217\n42 1e9778\ne3 27d359\ne3 e9e47\nef 27d47f\n4a 1e98cf\nfc 12536e\neb 27d4b0\nd4 1293be\nc3 281500\n26 5751\ndc 129515\ncb 281657\n83 29e091\nd3 efc63\n8b 29e1e8\na7 299f0d\na3 106a2c\naf 29a064\n87 29e0b4\n83 10abd3\n8f 29e20b\n4e 1e9900\n42 562c8\ne3 e9ea9\nef 27d4e1\n87 29e0c2\nd7 efc94\n83 10abe1\nd0 2b322b\n8f 29e219\n56 1af78a\nd4 12966c\nc3 2817ae\n16 1cc31b\n83 29e33f\na5 29a1c2\n50 1b09a4\n12 193e1a\n18 1ddc\n52 1b09ab\n58 1b0afb\n12 1cd53c\n1a 1cd693\nb9 10fa75\na 1925fc\n48 1af186\n4a 1af18d\n58 1afae7\n2a 5875\n8 192603\n6a 22406\n48 1af194\n20 19fc55\n69 1b332e\n22 19fc5c\n6b 1b3335\n30 1a05b6\n79 1b3c8f\n49 1b74d5\n4b 1b74dc\n59 1b7e36\n29 1967ab\n2b dbc4\n9 19a952\n6b 2a755\n49 1b74e3\n3c 3f924\n2b 197a66\n9 19bc06\n1c 43acb\nb 19bc0d\n49 1b8797\n4b 1b879e\n49 1b87a5\n7d 1b4f80\n71 21948\n29 1cfecd\n39 1d082e\n2b 472e6\n51 25aef\n5d 1b9127\n9 1d4074\n3b 47c47\n19 1d49d5\n7c 5b201\n6b 1b3343\n7f 1b4f87\n73 2194f\n2b 1cfed4\n3b 1d0835\n71 22c0a\n29 1d118f\n51 26db1\n9 1d5336\n88 10accc\n7c 5c4c3\n6b 1b4605\n5c 6066a\n4b 1b87ac\n30 1d0728\n9e 25c085\ncc 2782ae\nce 2782b5\nae ce99d\n8c 25b72b\nee eb52e\ncc 2782bc\n9e 25c0e7\ncc 278310\nce 278317\nee eb590\ncc 27831e\na 1cbd1c\nbc 1077bb\nab 25f8fd\nda e67ab\nbd 260226\n18 1cc676\nb9 260257\n1a 1cc67d\nbb 26025e\n89 263a9d\n9c 10b962\n8b 263aa4\na4 268d7d\ned 27c456\na0 268dae\ne9 27c487\na2 268db5\n4a 1e88ad\neb 27c48e\nb4 2696de\nfd 27cdb7\nb0 26970f\nf9 27cde8\n5a 1e920e\nb2 269716\nfb 27cdef\ncd 2805fd\nc9 28062e\ncb 280635\nf 878c\n8 1cbd23\na9 25f904\n1f 90ed\n18 1cc684\nb9 260265\naf d6cec\n8d 263a7a\na0 268dbc\n48 1e88b4\n4f 2531d\n1 19b801\ne9 27c495\nef f387d\ncd 28060b\neb f38ae\nc9 28063c\nc 1cbd46\n50 1f2732\nad 25f927\n1c 1cc6a7\nbd 260288\na4 268ddf\ned 27c4b8\nb4 269740\nfd 27ce19\n18 192f62\ncd 28065f\n1c 1cc6b5\nbd 260296\na4 268ded\n5 19b832\n4c 1e88e5\ned 27c4c6\n5c 1e9246\n15 19c193\nb4 26974e\nfd 27ce27\nef f38df\ne8 2b6e76\ncd 28066d\na 1ccfde\nbc 108a7d\nab 260bbf\n81 d16f6\n8d 264d2e\ne9 27d749\n2e 3da5a\nc1 ee287\ncd 2818bf\n5c 1af8cc\n2a 3da8b\nc9 2818f0\nf 9a4e\n8 1ccfe5\na9 260bc6\n81 d1704\n8d 264d3c\n48 1e9b76\n4f 265df\ne9 27d757\n2e 3da68\nc1 ee295\ncd 2818cd\n5c 1af8da\n2a 3da99\nc9 2818fe\n0 399d0\na1 cd5b1\nc 1cd008\nad 260be9\ne1 ea142\ned 27d77a\nc1 ee2e9\n18 194224\ncd 281921\n0 399de\nc 1cd016\na1 cd5bf\nad 260bf7\n40 5656f\ne1 ea150\n4c 1e9ba7\ned 27d788\nc1 ee2f7\ncd 28192f\ncc e70e0\n9e 2957a7\n60 1ec966\nce 2782c3\n70 1ed2c7\n2f 1d82b5\nde 278c24\nae 108121\nd4 e692a\n8c 294eaf\ncc e7142\n9e 295809\nbe 108a82\n9c 295810\nce 278325\nde 278c86\nd6 e6931\n8e 294eb6\n9e 295817\nab 29901f\nf 41eae\nf1 eaaa1\nfd 27e0d9\na9 299026\ne9 eb2b9\nbb 299980\n1f 4280f\nb9 299987\naf 11040e\nd5 eec17\n8d 29d19c\n8b 29d1c6\nd1 eec48\nab 11043f\ndd 282280\n89 29d1cd\nbf 110d6f\n9d 29dafd\ndc 128501\ncb 280643\nf5 eaad2\nad 299057\nbd 2999b8\naf 110470\nd5 eec79\n8d 29d1fe\nbf 110dd1\n9d 29db5f\nab 29a2e1\nf1 ebd63\nf 43170\na9 29a2e8\nd5 efed9\n81 10ae26\n8d 29e45e\n5e 1af8e1\ndc 1297c3\ncb 281905\nf5 ebd94\na1 106ce1\nad 29a319\n50 1b0c52\n14 19b18c\n7f 21ad7\n31 197fbb\ne6 2bf099\n33 197fc2\n58 1e9215\n5f 25c7e\nb0 26971d\nf9 27cdf6\n11 19c162\n5a 1e921c\nfb 27cdfd\n13 19c169\n39 5f84\n56 1b7d24\n73 1b4b53\nf8 286405\n59 1b8e4a\n" diff --git a/libs/blueprint/include/nil/blueprint/gate_id.hpp b/libs/blueprint/include/nil/blueprint/gate_id.hpp new file mode 100644 index 000000000..763862f4d --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/gate_id.hpp @@ -0,0 +1,382 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_GATE_ID_HPP +#define CRYPTO3_BLUEPRINT_GATE_ID_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + // Helper class for gate_id + // Encapsulates storing values at random points for gate comparison + template + class value_set { + private: + using value_type = typename BlueprintFieldType::value_type; + using var = nil::crypto3::zk::snark::plonk_variable; + + static constexpr std::size_t starting_constraint_mults_size = 20; + + boost::random::random_device dev; + nil::crypto3::random::algebraic_engine random_engine = + nil::crypto3::random::algebraic_engine(dev); + + std::array, 3>, 2> witnesses; + std::array, 3>, 2> constants; + std::array, 3>, 2> selectors; + // Used to separate constraints from each other in ids. + std::vector constraint_mults; + // Used to separate lookup variables from each other in ids. + std::vector lookup_constraint_mults; + // Used to separate lookup constraints by table id. + std::vector lookup_table_mults; + + value_type generate_constraint_mult() { + value_type val = random_engine(); + // Here it's critical that the values are non-zero + // Otherwise some of the constraints would never actually matter + // The probability for that is extremely low, but checking for this + // might still be worthwhile + while (val == value_type::zero()) { + val = random_engine(); + } + return val; + } + + value_set() { + + constraint_mults.reserve(starting_constraint_mults_size); + for (std::size_t i = 0; i < starting_constraint_mults_size; i++) { + constraint_mults.emplace_back(generate_constraint_mult()); + } + lookup_constraint_mults.reserve(starting_constraint_mults_size); + for (std::size_t i = 0; i < starting_constraint_mults_size; i++) { + lookup_constraint_mults.emplace_back(generate_constraint_mult()); + } + lookup_table_mults.reserve(starting_constraint_mults_size); + for (std::size_t i = 0; i < starting_constraint_mults_size; i++) { + lookup_table_mults.emplace_back(generate_constraint_mult()); + } + } + + inline const value_type& get_power_helper(std::vector &container, std::size_t index) { + while (index >= container.size()) { + container.push_back(generate_constraint_mult()); + } + return container[index]; + } + + inline const value_type& get_value_helper( + std::array, 3>, 2> &container, + std::size_t point, std::size_t index, std::size_t rotation) { + BOOST_ASSERT_MSG(point == 0 || point == 1, "Point must be either 0 or 1."); + return get_power_helper(container[point][rotation + 1], index); + } + + public: + // Singleton + static value_set& get_value_set() { + static value_set instance; + return instance; + } + + const value_type& get_witness(std::size_t point, std::size_t index, std::size_t rotation) { + return get_value_helper(witnesses, point, index, rotation); + } + + const value_type& get_constant(std::size_t point, std::size_t index, std::size_t rotation) { + return get_value_helper(constants, point, index, rotation); + } + + const value_type& get_selector(std::size_t point, std::size_t index, std::size_t rotation) { + return get_value_helper(selectors, point, index, rotation); + } + + value_type get_power(std::size_t index) { + return get_power_helper(constraint_mults, index); + } + + value_type get_lookup_power(std::size_t index) { + return get_power_helper(lookup_constraint_mults, index); + } + + value_type get_table_power(std::size_t index) { + return get_power_helper(lookup_table_mults, index); + } + + const value_type& get_var_value(std::size_t point, const var &var) { + BOOST_ASSERT_MSG(point == 0 || point == 1, "Index must be either 0 or 1."); + BOOST_ASSERT_MSG(var.relative == true, "Absolute variables should not belong to a gate."); + switch (var.type) { + case var::column_type::witness: + return this->get_witness(point, var.index, var.rotation); + case var::column_type::constant: + return this->get_constant(point, var.index, var.rotation); + case var::column_type::selector: + return this->get_selector(point, var.index, var.rotation); + case var::column_type::public_input: + BOOST_ASSERT_MSG(false, "Public input variables should not be in a gate."); + case var::column_type::uninitialized: + BOOST_ASSERT_MSG(false, "Uninitialized variable should not be inside a gate."); + } + __builtin_unreachable(); + }; + + const value_type& get_first_value(const var &var) { + return get_var_value(0, var); + }; + + const value_type& get_second_value(const var &var) { + return get_var_value(1, var); + }; + }; + + // Implements a comparison between gates + // First, calculates a value of product of all constraints at a random + // This uses [Schwartz–Zippel lemma](https://en.wikipedia.org/wiki/Schwartz%E2%80%93Zippel_lemma) + // to guarantee a really small probability of collision : degree/field_size + // We do that at two random points, because I am paranoid. + template + class gate_id { + private: + using value_type = typename BlueprintFieldType::value_type; + using var = nil::crypto3::zk::snark::plonk_variable; + using expression_type = nil::crypto3::math::expression; + using value_set_type = value_set; + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using gate_type = crypto3::zk::snark::plonk_gate; + + value_set_type &values = value_set_type::get_value_set(); + + value_type value_1, value_2; + // We preserve this in order to be able to easily access the original gate. + std::size_t selector_index; + public: + std::pair eval_constraint(const constraint_type& constraint) const { + nil::crypto3::math::expression_evaluator evaluator_1( + constraint, + [this](const var &var) -> const value_type& { return this->values.get_first_value(var); }); + + nil::crypto3::math::expression_evaluator evaluator_2( + constraint, + [this](const var &var) -> const value_type& { return this->values.get_second_value(var); }); + return {evaluator_1.evaluate(), evaluator_2.evaluate()}; + } + + // Note that constraits have to be sorted in order to enforce equality between differently ordered gates. + #define GATE_ID_INIT_MACRO(constraints_container) \ + value_1 = value_2 = BlueprintFieldType::value_type::zero(); \ + if (constraints_container.empty()) { \ + return; \ + } \ + std::vector> constraint_values; \ + constraint_values.reserve(constraints_container.size()); \ + for (std::size_t i = 0; i < constraints_container.size(); i++) { \ + constraint_values.emplace_back(eval_constraint(constraints_container[i])); \ + } \ + std::stable_sort(constraint_values.begin(), constraint_values.end(), \ + [](const std::pair &a, const std::pair &b) { \ + return a.first < b.first || (a.first == b.first && a.second < b.second); \ + }); \ + for (std::size_t i = 0; i < constraint_values.size(); i++) { \ + value_1 += values.get_power(i) * constraint_values[i].first; \ + value_2 += values.get_power(i) * constraint_values[i].second; \ + } + + gate_id(const gate_type &gate) : selector_index(gate.selector_index) { + GATE_ID_INIT_MACRO(gate.constraints); + } + + gate_id(const std::vector &constraints) : selector_index(0) { + GATE_ID_INIT_MACRO(constraints); + } + + gate_id(const constraint_type constraint) : selector_index(0) { + auto value_pair = eval_constraint(constraint); + value_1 = value_pair.first; + value_2 = value_pair.second; + } + + gate_id(const std::initializer_list &&constraints) : selector_index(0) { + GATE_ID_INIT_MACRO(constraints); + } + + #undef GATE_ID_INIT_MACRO + + bool operator==(const gate_id &other) const { + return (value_1 == other.value_1) && (value_2 == other.value_2); + } + + bool operator!=(const gate_id &other) const { + return !(*this == other); + } + + bool operator<(const gate_id &other) const { + return (value_1 < other.value_1) || ((value_1 == other.value_1) && (value_2 < other.value_2)); + } + + const std::size_t get_selector() { + return selector_index; + } + + gate_id& operator=(const gate_id& other) { + value_1 = other.value_1; + value_2 = other.value_2; + selector_index = other.selector_index; + return *this; + } + + std::string to_string() const { + std::stringstream ss; + ss << "Gate ID: " << value_1.data << " " << value_2.data; + return ss.str(); + } + }; + + // Similar idea to gate_id, but implemented for lookup gates + template + class lookup_gate_id { + private: + using value_type = typename BlueprintFieldType::value_type; + using var = nil::crypto3::zk::snark::plonk_variable; + using expression_type = nil::crypto3::math::expression; + using value_set_type = value_set; + using constraint_type = nil::crypto3::zk::snark::plonk_lookup_constraint; + using gate_type = crypto3::zk::snark::plonk_lookup_gate; + + value_set_type &values = value_set_type::get_value_set(); + + value_type value_1, value_2; + // We preserve this in order to be able to easily access the original gate. + std::size_t tag_index; + public: + std::pair eval_constraint(const constraint_type& constraint) const { + value_type value_1 = BlueprintFieldType::value_type::zero(), value_2 = BlueprintFieldType::value_type::zero(); + for (std::size_t i = 0; i < constraint.lookup_input.size(); i++) { + nil::crypto3::math::expression_evaluator evaluator_1( + constraint.lookup_input[i], + [this](const var &var) -> const value_type& { + return this->values.get_first_value(var); + }); + nil::crypto3::math::expression_evaluator evaluator_2( + constraint.lookup_input[i], + [this](const var &var) -> const value_type& { + return this->values.get_second_value(var); + }); + value_1 += values.get_lookup_power(i) * evaluator_1.evaluate(); + value_2 += values.get_lookup_power(i) * evaluator_2.evaluate(); + } + auto table_power = values.get_table_power(constraint.table_id); + return {table_power * value_1, table_power * value_2}; + } + + // Note that constraits have to be sorted in order to enforce equality between differently ordered gates. + #define LOOKUP_GATE_ID_INIT_MACRO(constraints_container) \ + value_1 = value_2 = BlueprintFieldType::value_type::zero(); \ + if (constraints_container.empty()) { \ + return; \ + } \ + std::vector> constraint_values; \ + constraint_values.reserve(constraints_container.size()); \ + for (std::size_t i = 0; i < constraints_container.size(); i++) { \ + constraint_values.emplace_back(eval_constraint(constraints_container[i])); \ + } \ + std::stable_sort(constraint_values.begin(), constraint_values.end(), \ + [](const std::pair &a, const std::pair &b) { \ + return a.first < b.first || (a.first == b.first && a.second < b.second); \ + }); \ + for (std::size_t i = 0; i < constraint_values.size(); i++) { \ + value_1 += values.get_power(i) * constraint_values[i].first; \ + value_2 += values.get_power(i) * constraint_values[i].second; \ + } + + lookup_gate_id(const gate_type &gate) : tag_index(gate.tag_index) { + LOOKUP_GATE_ID_INIT_MACRO(gate.constraints); + } + + lookup_gate_id(const std::vector &constraints) : tag_index(0) { + LOOKUP_GATE_ID_INIT_MACRO(constraints); + } + + lookup_gate_id(const constraint_type &constraint) : tag_index(0) { + auto value_pair = eval_constraint(constraint); + value_1 = value_pair.first; + value_2 = value_pair.second; + } + + lookup_gate_id(const std::initializer_list &&constraints) : tag_index(0) { + LOOKUP_GATE_ID_INIT_MACRO(constraints); + } + + #undef LOOKUP_GATE_ID_INIT_MACRO + + bool operator==(const lookup_gate_id &other) const { + return (value_1 == other.value_1) && (value_2 == other.value_2); + } + + bool operator!=(const lookup_gate_id &other) const { + return !(*this == other); + } + + bool operator<(const lookup_gate_id &other) const { + return (value_1 < other.value_1) || ((value_1 == other.value_1) && (value_2 < other.value_2)); + } + + const std::size_t get_selector() { + return tag_index; + } + + lookup_gate_id& operator=(const lookup_gate_id& other) { + value_1 = other.value_1; + value_2 = other.value_2; + tag_index = other.tag_index; + return *this; + } + + std::string to_string() const { + std::stringstream ss; + ss << "Lookup Gate ID: " << value_1.data << " " << value_2.data; + return ss.str(); + } + }; + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_GATE_ID_HPP diff --git a/libs/blueprint/include/nil/blueprint/lookup_library.hpp b/libs/blueprint/include/nil/blueprint/lookup_library.hpp new file mode 100644 index 000000000..755b34008 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/lookup_library.hpp @@ -0,0 +1,450 @@ + +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Tatuzova Elena +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_LOOKUP_LIBRARY_HPP +#define CRYPTO3_LOOKUP_LIBRARY_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + template + struct component_use_lookup : std::false_type{ }; + + template + struct component_use_lookup::value>::type> : std::true_type + { }; + + template + constexpr bool use_lookups(){ + if(component_use_lookup::value){ + return true; + } + return false; + } + + template + struct component_use_custom_lookup_tables : std::false_type{ }; + + template + struct component_use_custom_lookup_tables::value>::type> : std::true_type + { }; + + template + class lookup_library { + using lookup_table_definition = typename nil::crypto3::zk::snark::lookup_table_definition; + using dynamic_table_definition = typename nil::crypto3::zk::snark::dynamic_table_definition; + using filled_lookup_table_definition = typename nil::crypto3::zk::snark::filled_lookup_table_definition; + + class byte_range_table_type: public lookup_table_definition{ + public: + using lookup_table_definition = typename nil::crypto3::zk::snark::lookup_table_definition; + + byte_range_table_type(): lookup_table_definition("byte_range_table"){ + this->subtables["full"] = {{0}, 0, 255}; + } + virtual void generate(){ + this->_table.push_back({}); + for( std::size_t i = 0; i < 256; i++){ + this->_table[0].push_back({i}); + } + } + virtual std::size_t get_columns_number(){ return 1; } + virtual std::size_t get_rows_number(){ return 256; } + }; + + class zkevm_opcode_table: public lookup_table_definition{ + public: + static constexpr std::size_t opcodes_num = 149; + + zkevm_opcode_table(): lookup_table_definition("zkevm_opcodes"){ + this->subtables["full"] = {{0, 1, 2}, 0, opcodes_num}; + this->subtables["opcodes_only"] = {{0}, 0, opcodes_num}; + } + virtual void generate(){ + // opcodes + this->_table.push_back({ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, //12 + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, //14 + 0x20, //1 + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, //16 + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, //11 + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, //16 + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, //16 + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, //16 + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, //16 + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, //16 + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, //5 + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xfa, 0xfd, 0xfe, 0xff //10 + }); + // push_size + this->_table.push_back({ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //12 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //14 + 0x0, //1 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //16 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //11 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //16 + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, //16 + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, //16 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //16 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, //16 + 0x0, 0x0, 0x0, 0x0, 0x0, //5 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 //10 + }); + + this->_table.push_back({}); + for( std::size_t i = 0; i < opcodes_num; i++) this->_table[2].push_back(1); + + // unselected rows virtualization + this->_table[0].push_back(0); + this->_table[1].push_back(0); + this->_table[2].push_back(0); + } + virtual std::size_t get_columns_number(){ return 1; } + virtual std::size_t get_rows_number(){ return 256; } + }; + + class binary_xor_table_type : public lookup_table_definition{ + public: + binary_xor_table_type(): lookup_table_definition("binary_xor_table"){ + this->subtables["full"] = {{0,1,2}, 0, 3}; + } + virtual void generate(){ + this->_table = { + {0u, 0u, 1u, 1u}, + {0u, 1u, 0u, 1u}, + {0u, 1u, 1u, 0u} + }; + } + virtual std::size_t get_columns_number(){ return 3; } + virtual std::size_t get_rows_number(){ return 4; } + }; + + class binary_and_table_type : public lookup_table_definition{ + public: + binary_and_table_type(): lookup_table_definition("binary_and_table"){ + this->subtables["full"] = {{0,1,2}, 0, 3}; + } + virtual void generate(){ + this->_table = { + {0u, 0u, 1u, 1u}, + {0u, 1u, 0u, 1u}, + {0u, 0u, 0u, 1u} + }; + } + virtual std::size_t get_columns_number(){ return 3; } + virtual std::size_t get_rows_number(){ return 4; } + }; + + class sparse_values_base4_table: public lookup_table_definition { + public: + sparse_values_base4_table(): lookup_table_definition("sha256_sparse_base4"){ + this->subtables["full"] = {{0,1}, 0, 16383}; + this->subtables["first_column"] = {{0}, 0, 16383}; + }; + virtual void generate(){ + this->_table.resize(2); + std::vector value_sizes = {14}; + + // lookup table for sparse values with base = 4 + for (typename BlueprintFieldType::integral_type i = 0u; + i < typename BlueprintFieldType::integral_type(16384u); + i++ + ) { + std::vector value(14); + for (std::size_t j = 0; j < 14; j++) { + value[14 - j - 1] = boost::multiprecision::bit_test(i, j); + } + std::array, 2> value_chunks = + components::detail::split_and_sparse(value, value_sizes, 4); + this->_table[0].push_back(value_chunks[0][0]); + this->_table[1].push_back(value_chunks[1][0]); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 16384;} + }; + + class reverse_sparse_sigmas_base4_table : public lookup_table_definition { + public: + reverse_sparse_sigmas_base4_table(): lookup_table_definition("sha256_reverse_sparse_base4"){ + this->subtables["full"] = {{0,1}, 0, 65535}; + }; + + virtual void generate() { + bool status = components::detail::load_lookup_table_from_bin( + "8_split_4", + this->_table); + if (!status) { + std::cerr << "Failed to load table 8_split_4 from binary!" << std::endl; + BLUEPRINT_RELEASE_ASSERT(0); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 65536;} + }; + + class sparse_values_base7_table: public lookup_table_definition{ + public: + sparse_values_base7_table(): lookup_table_definition("sha256_sparse_base7"){ + this->subtables["full"] = {{0,1}, 0, 16383}; + this->subtables["first_column"] = {{0}, 0, 16383}; + this->subtables["second_column"] = {{1}, 0, 16383}; + }; + virtual void generate(){ + this->_table.resize(2); + std::vector value_sizes = {14}; + for (typename BlueprintFieldType::integral_type i = 0u; + i < typename BlueprintFieldType::integral_type(16384u); + i++) { + std::vector value(14); + for (std::size_t j = 0; j < 14; j++) { + value[14 - j - 1] = boost::multiprecision::bit_test(i, j); + } + std::array, 2> value_chunks = + components::detail::split_and_sparse(value, value_sizes, 7); + this->_table[0].push_back(value_chunks[0][0]); + this->_table[1].push_back(value_chunks[1][0]); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 16384;} + }; + + class reverse_sparse_sigmas_base7_table: public lookup_table_definition{ + public: + reverse_sparse_sigmas_base7_table(): lookup_table_definition("sha256_reverse_sparse_base7"){ + this->subtables["full"] = {{0,1}, 0, 43903}; + }; + virtual void generate() { + bool status = components::detail::load_lookup_table_from_bin( + "8_split_7", + this->_table); + if (!status) { + std::cerr << "Failed to load table 8_split_7 from binary!" << std::endl; + BLUEPRINT_RELEASE_ASSERT(0); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 43904;} + }; + + class maj_function_table: public lookup_table_definition{ + public: + maj_function_table(): lookup_table_definition("sha256_maj"){ + this->subtables["full"] = {{0,1}, 0, 65535}; + this->subtables["first_column"] = {{0}, 0, 65535}; + }; + virtual void generate(){ + this->_table.resize(2); + std::vector value_sizes = {8}; + for (typename BlueprintFieldType::integral_type i = 0u; + i < typename BlueprintFieldType::integral_type(65536u); + i++ + ) { + std::array, 2> + value = components::detail::reversed_sparse_and_split_maj(i, value_sizes, 4); + this->_table[0].push_back(value[0][0]); + this->_table[1].push_back(value[1][0]); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 65536;} + }; + + class ch_function_table: public lookup_table_definition{ + public: + ch_function_table(): lookup_table_definition("sha256_ch"){ + this->subtables["full"] = {{0,1}, 0, 5764800}; + this->subtables["first_column"] = {{0}, 0, 5764800}; + }; + virtual void generate(){ + this->_table.resize(2); + std::vector value_sizes = {8}; + for (typename BlueprintFieldType::integral_type i = 0u; + i < typename BlueprintFieldType::integral_type(5764801u); + i++ + ) { + std::array, 2> + value = components::detail::reversed_sparse_and_split_ch(i, value_sizes, 7); + this->_table[0].push_back(value[0][0]); + this->_table[1].push_back(value[1][0]); + } + } + + virtual std::size_t get_columns_number(){return 2;} + virtual std::size_t get_rows_number(){return 5764801;} + }; + + class chunk_16_bits_table: public lookup_table_definition{ + public: + chunk_16_bits_table(): lookup_table_definition("chunk_16_bits"){ + this->subtables["full"] = {{0}, 0, 65535}; + }; + virtual void generate(){ + this->_table.resize(1); + for (std::size_t i = 0; i < 65536; i++) { + this->_table[0].push_back(i); + } + } + + virtual std::size_t get_columns_number(){return 1;} + virtual std::size_t get_rows_number(){return 65536;} + }; + public: + using bimap_type = boost::bimap, boost::bimaps::set_of>; + using left_reserved_type = typename bimap_type::left_map; + using right_reserved_type = typename bimap_type::right_map; + + lookup_library() { + tables = {}; + reserved_all = false; + tables["chunk_16_bits"] = std::shared_ptr(new chunk_16_bits_table()); + tables["binary_xor_table"] = std::shared_ptr(new binary_xor_table_type()); + tables["binary_and_table"] = std::shared_ptr(new binary_and_table_type()); + tables["sha256_sparse_base4"] = std::shared_ptr(new sparse_values_base4_table()); + tables["sha256_reverse_sparse_base4"] = std::shared_ptr(new reverse_sparse_sigmas_base4_table()); + tables["sha256_sparse_base7"] = std::shared_ptr(new sparse_values_base7_table()); + tables["sha256_reverse_sparse_base7"] = std::shared_ptr(new reverse_sparse_sigmas_base7_table()); + tables["sha256_maj"] = std::shared_ptr(new maj_function_table()); + tables["sha256_ch"] = std::shared_ptr(new ch_function_table()); + tables["byte_range_table"] = std::shared_ptr(new byte_range_table_type()); + tables["zkevm_opcodes"] = std::shared_ptr(new zkevm_opcode_table()); + } + + void register_lookup_table(std::shared_ptr table){ + tables[table->table_name] = table; + } + + void register_dynamic_table(std::string table_name){ + BOOST_ASSERT(tables.find(table_name) == tables.end()); + dynamic_tables[table_name] = std::shared_ptr(new dynamic_table_definition(table_name)); + } + + void reserve_table(std::string name){ + BOOST_ASSERT(!reserved_all); + std::string table_name = name.substr(0, name.find("/")); + // Necessary for dynamic and for fixed tables + BOOST_ASSERT(tables.find(table_name) != tables.end()); + std::string subtable_name = name.substr(name.find("/")+1, name.size()); + BOOST_ASSERT(tables[table_name]->subtables.find(subtable_name) != tables[table_name]->subtables.end()); + reserved_tables.insert(name); + reserved_tables_indices.left.insert(std::make_pair(name, reserved_tables.size())); + } + + void reserve_dynamic_table(std::string name){ + BOOST_ASSERT(tables.find(name) == tables.end()); + BOOST_ASSERT(!reserved_all); + + register_dynamic_table(name); + reserved_tables.insert(name); + reserved_tables_indices.left.insert(std::make_pair(name, reserved_tables.size())); + } + + void define_dynamic_table(std::string table_name, const crypto3::zk::snark::plonk_lookup_table &lookup_table){ + register_dynamic_table(table_name); + auto table = dynamic_tables[table_name]; + BOOST_ASSERT(!table->is_defined()); + table->define(lookup_table); + BOOST_ASSERT(table->is_defined()); + } + + std::shared_ptr get_dynamic_table_definition(std::string table_name){ + auto table = dynamic_tables[table_name]; + BOOST_ASSERT(table->is_defined()); + return std::shared_ptr(table); + } + + void reservation_done() const { + if(reserved_all) return; + + reserved_all = true; + for (auto &name : reserved_tables){ + if( dynamic_tables.find(name) != dynamic_tables.end() ){ + reserved_dynamic_tables_map[name] = dynamic_tables.at(name); + } else { + auto slash_pos = name.find("/"); + std::string table_name = name.substr(0, slash_pos); + BOOST_ASSERT(tables.find(table_name) != tables.end()); + auto const &table = tables.at(table_name); + + std::string subtable_name = name.substr(slash_pos + 1, name.size()); + BOOST_ASSERT(table->subtables.find(subtable_name) != + table->subtables.end()); + + if( reserved_tables_map.find(table_name) == reserved_tables_map.end() ){ + filled_lookup_table_definition *filled_definition = + new filled_lookup_table_definition(*(table)); + reserved_tables_map[table_name] = std::shared_ptr(filled_definition); + } + reserved_tables_map[table_name]->subtables[subtable_name] = + table->subtables[subtable_name]; + } + } + } + + const bimap_type &get_reserved_indices() const { + return reserved_tables_indices; + } + + const std::map> &get_reserved_tables() const { + reservation_done(); + return reserved_tables_map; + } + + const std::map> &get_reserved_dynamic_tables() const { + reservation_done(); + return reserved_dynamic_tables_map; + } + protected: + mutable bool reserved_all; + + std::set reserved_tables; + bimap_type reserved_tables_indices; + std::map> tables; + mutable std::map> reserved_tables_map; + std::map> dynamic_tables; + mutable std::map> reserved_dynamic_tables_map; + }; + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_LOOKUP_TABLE_HPP diff --git a/libs/blueprint/include/nil/blueprint/manifest.hpp b/libs/blueprint/include/nil/blueprint/manifest.hpp new file mode 100644 index 000000000..e38052d0b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/manifest.hpp @@ -0,0 +1,1093 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENT_MANIFEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENT_MANIFEST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + class plonk_component; + } // namespace components + + struct compiler_manifest; + + struct manifest_lookup_type { + enum class type { + NONE, + UNSAT, + REQUIRED, + OPTIONAL, + } t; + + manifest_lookup_type(type t_) : t(t_) {} + manifest_lookup_type() : t(type::NONE) {} + + bool operator==(manifest_lookup_type lt) const { + return t == lt.t; + } + + bool operator!=(manifest_lookup_type lt) const { + return t != lt.t; + } + + bool operator<(manifest_lookup_type lt) const { + return t < lt.t; + } + + manifest_lookup_type intersect(manifest_lookup_type lt) const { + if (t == manifest_lookup_type::type::UNSAT || + lt == manifest_lookup_type::type::UNSAT) { + + return manifest_lookup_type::type::UNSAT; + } else if (t == manifest_lookup_type::type::REQUIRED) { + if (lt == manifest_lookup_type::type::NONE) { + return manifest_lookup_type::type::UNSAT; + } else { + return manifest_lookup_type::type::REQUIRED; + } + } else if (lt == manifest_lookup_type::type::REQUIRED) { + if (t == manifest_lookup_type::type::NONE) { + return manifest_lookup_type::type::UNSAT; + } else { + return manifest_lookup_type::type::REQUIRED; + } + } else if (t == manifest_lookup_type::type::NONE || + lt == manifest_lookup_type::type::NONE) { + return manifest_lookup_type::type::NONE; + } else if (t == manifest_lookup_type::type::OPTIONAL) { + return lt; + } + return manifest_lookup_type::type::UNSAT; + } + + manifest_lookup_type merge_with(manifest_lookup_type lt) const { + if (t == manifest_lookup_type::type::UNSAT || + lt == manifest_lookup_type::type::UNSAT) { + + return manifest_lookup_type::type::UNSAT; + } else if (t == manifest_lookup_type::type::REQUIRED || + lt == manifest_lookup_type::type::REQUIRED) { + return manifest_lookup_type::type::REQUIRED; + } else if (t == manifest_lookup_type::type::NONE) { + return lt; + } else if (t == manifest_lookup_type::type::OPTIONAL) { + return manifest_lookup_type::type::OPTIONAL; + } + return manifest_lookup_type::type::NONE; + } + }; + + inline std::ostream& operator<<(std::ostream &os, const manifest_lookup_type& t) { + switch (t.t) { + case manifest_lookup_type::type::NONE: + os << "NONE"; + break; + case manifest_lookup_type::type::UNSAT: + os << "UNSAT"; + break; + case manifest_lookup_type::type::REQUIRED: + os << "REQUIRED"; + break; + case manifest_lookup_type::type::OPTIONAL: + os << "OPTIONAL"; + break; + } + return os; + } + + struct manifest_constant_type { + enum class type { + NONE, + UNSAT, + REQUIRED, + } t; + + manifest_constant_type(type t_) : t(t_) {} + manifest_constant_type() : t(type::NONE) {} + manifest_constant_type(bool b) : t(b ? type::REQUIRED : type::NONE) {} + + bool operator==(manifest_constant_type lt) const { + return t == lt.t; + } + + bool operator!=(manifest_constant_type lt) const { + return t != lt.t; + } + + bool operator<(manifest_constant_type lt) const { + return t < lt.t; + } + + manifest_constant_type intersect(const compiler_manifest &manifest) const; + + manifest_constant_type merge_with(manifest_constant_type lt) const { + if (t == manifest_constant_type::type::UNSAT || + lt == manifest_constant_type::type::UNSAT) { + + return manifest_constant_type::type::UNSAT; + } else if (t == manifest_constant_type::type::REQUIRED || + lt == manifest_constant_type::type::REQUIRED) { + return manifest_constant_type::type::REQUIRED; + } + return manifest_constant_type::type::NONE; + } + }; + + inline std::ostream& operator<<(std::ostream &os, const manifest_constant_type& t) { + switch (t.t) { + case manifest_constant_type::type::NONE: + os << "NONE"; + break; + case manifest_constant_type::type::UNSAT: + os << "UNSAT"; + break; + case manifest_constant_type::type::REQUIRED: + os << "REQUIRED"; + break; + } + return os; + } + + class manifest_param_iterator; + + class manifest_param { + public: + // We need this because having just a value does not enable const-time next operation for set type. + using it_type = boost::variant::const_iterator>; + + virtual bool check_manifest_param(std::uint32_t value, bool strict = true) = 0; + virtual bool is_satisfiable() = 0; + virtual std::shared_ptr intersect(std::shared_ptr other) = 0; + virtual std::shared_ptr subtract(std::set values) = 0; + virtual std::shared_ptr merge_with(std::shared_ptr other) = 0; + virtual ~manifest_param() = default; + + virtual std::ostream& operator<<(std::ostream &os) const = 0; + + virtual manifest_param_iterator begin() const = 0; + virtual manifest_param_iterator end() const = 0; + virtual it_type next(it_type prev) const = 0; + // Valid only if is_satisfiable() == true + virtual std::uint32_t max_value_if_sat() const = 0; + + enum class type { + UNSAT, + SINGLE_VALUE, + RANGE, + SET + }; + }; + + inline std::ostream& operator<<(std::ostream &os, const manifest_param& p) { + return p.operator<<(os); + } + + // Iterates over all valid params in a manifest. + // We still need a custom iterator type for each component, but at least some boilerplate is encapsulated here. + // Please do NOT change the underlying object while iteration is going on, this leads to undefined behavior. + class manifest_param_iterator { + public: + using it_type = manifest_param::it_type; + + using iterator_category = std::forward_iterator_tag; + using value_type = std::uint32_t; + using difference_type = std::ptrdiff_t; + using pointer = std::uint32_t*; + using reference = std::uint32_t&; + + const manifest_param* obj; + it_type value; + + manifest_param_iterator(const manifest_param* param, it_type value_) : obj(param), value(value_) {} + + value_type operator*() const { + if (value.type() == typeid(std::uint32_t)) { + return boost::get(value); + } else { + return *boost::get::const_iterator>(value); + } + } + + manifest_param_iterator& operator++() { + value = obj->next(value); + return *this; + } + + manifest_param_iterator operator++(int) { + manifest_param_iterator tmp(obj, value); + ++(*this); + return tmp; + } + + bool operator!=(const manifest_param_iterator& other) const { + return value != other.value || obj != other.obj; + } + + bool operator==(const manifest_param_iterator& other) const { + return value == other.value && obj == other.obj; + } + }; + + // In order to correctly handle intersection, we need to know the type of the manifest parameter, + // and to use a specific implementation of intersect for each type. + // This function returns the type of the manifest parameter. + manifest_param::type get_manifest_param_type(std::shared_ptr a); + + class manifest_unsat_param : public manifest_param { + public: + using it_type = manifest_param::it_type; + + bool check_manifest_param(std::uint32_t value, bool strict = true) override { + return false; + } + + bool is_satisfiable() override { + return false; + } + + std::shared_ptr intersect(std::shared_ptr other) override { + return std::shared_ptr(new manifest_unsat_param()); + } + + std::shared_ptr subtract(std::set values) override { + return std::shared_ptr(new manifest_unsat_param()); + } + + std::shared_ptr merge_with(std::shared_ptr other) override { + return std::shared_ptr(new manifest_unsat_param()); + } + + std::ostream& operator<<(std::ostream &os) const override { + os << "UNSAT"; + return os; + } + + manifest_param_iterator begin() const override { + return manifest_param_iterator(this, 0); + } + + manifest_param_iterator end() const override { + return manifest_param_iterator(this, 0); + } + + it_type next(it_type prev) const override { + return 0; + } + + std::uint32_t max_value_if_sat() const override { + return 0; + } + + bool operator==(const manifest_unsat_param& other) const { + return true; + } + }; + + class manifest_single_value_param : public manifest_param { + public: + using it_type = manifest_param::it_type; + + std::uint32_t value; + + manifest_single_value_param(std::uint32_t value) : value(value) {} + + bool check_manifest_param(std::uint32_t value, bool strict = true) override { + if (strict) { + return this->value == value; + } else { + return this->value <= value; + } + } + + bool is_satisfiable() override { + return true; + } + + std::shared_ptr intersect(std::shared_ptr other) override { + if (other->check_manifest_param(this->value)) { + return std::shared_ptr(new manifest_single_value_param(this->value)); + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } + + std::shared_ptr subtract(std::set values) override { + if (values.find(this->value) != values.end()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_single_value_param(this->value)); + } + } + + std::shared_ptr merge_with(std::shared_ptr other) override; + + std::ostream& operator<<(std::ostream &os) const override { + os << "VALUE(" << value << ")"; + return os; + } + + manifest_param_iterator begin() const override { + return manifest_param_iterator(this, value); + } + + manifest_param_iterator end() const override { + return manifest_param_iterator(this, value + 1); + } + + it_type next(it_type prev) const override { + return value + 1; + } + + std::uint32_t max_value_if_sat() const override { + return value; + } + + bool operator==(const manifest_single_value_param& other) const { + return value == other.value; + } + }; + + class manifest_range_param : public manifest_param { + public: + // We don't use unsigned types for start/finish because of overflow leading to undefined behavior + // Should probably also move step to std::int32_t? Unsure. + std::int32_t start; + std::int32_t finish; + std::uint32_t step; + + // please do not make finish too large + // save the optimizers + manifest_range_param(std::int32_t start, std::int32_t finish_, std::uint32_t step = 1) + : start(start), finish(finish_), step(step) {} + + bool is_satisfiable() override { + return start < finish && std::abs(start - finish) >= int(start % step); + } + + bool check_manifest_param(std::uint32_t value, bool strict = true) override { + std::int32_t value_signed = static_cast(value); + if (strict) { + return (value_signed >= start) && (value_signed < finish) && ((value_signed - start) % step == 0); + } else { + return (value_signed >= start) && is_satisfiable(); + } + } + + std::shared_ptr intersect(std::shared_ptr other) override; + std::shared_ptr subtract(std::set values) override; + std::shared_ptr merge_with(std::shared_ptr other) override; + + std::ostream& operator<<(std::ostream &os) const override { + os << "RANGE(" << start << ", " << finish << ", " << step << ")"; + return os; + } + + manifest_param_iterator begin() const override { + return manifest_param_iterator(this, start); + } + + manifest_param_iterator end() const override { + return manifest_param_iterator(this, finish); + } + + it_type next(it_type prev) const override { + std::uint32_t prev_value = boost::get(prev); + return (prev_value + step < std::uint32_t(finish)) ? (prev_value + step) : finish; + } + + std::uint32_t max_value_if_sat() const override { + return finish - ((finish % step) ? (finish % step) : step); + } + + // Isn't strict equality: some ranges are isomorphic, and we don't check that. + bool operator==(const manifest_range_param& other) const { + return start == other.start && finish == other.finish && step == other.step; + } + }; + + class manifest_set_param : public manifest_param { + public: + using it_type = manifest_param::it_type; + + std::set set; + + manifest_set_param(std::set set_) : set(std::move(set_)) {} + + void add_value(std::uint32_t value) { + set.insert(value); + } + + bool check_manifest_param(std::uint32_t value, bool strict = true) override { + if (strict) { + return set.find(value) != set.end(); + } else { + return set.lower_bound(value) != set.end(); + } + } + + bool is_satisfiable() override { + return !set.empty(); + } + + std::shared_ptr intersect(std::shared_ptr other) override; + std::shared_ptr subtract(std::set values) override { + std::set new_set; + std::set_difference(set.begin(), set.end(), values.begin(), values.end(), + std::inserter(new_set, new_set.begin())); + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } + + std::shared_ptr merge_with(std::shared_ptr other) override; + + std::ostream& operator<<(std::ostream &os) const override { + os << "SET("; + for (auto it = set.begin(); it != set.end(); ++it) { + os << *it; + if (std::next(it) != set.end()) { + os << ", "; + } + } + os << ")"; + return os; + } + + manifest_param_iterator begin() const override { + return manifest_param_iterator(this, set.begin()); + } + + manifest_param_iterator end() const override { + return manifest_param_iterator(this, set.end()); + } + + it_type next(it_type prev) const override { + auto prev_it = boost::get::const_iterator>(prev); + return ++prev_it; + } + + std::uint32_t max_value_if_sat() const override { + return *set.rbegin(); + } + + bool operator==(const manifest_set_param& other) const { + return set == other.set; + } + }; + + inline manifest_param::type get_manifest_param_type(manifest_param* a) { + using type = manifest_param::type; + if (dynamic_cast(a)) { + return type::UNSAT; + } else if (dynamic_cast(a)) { + return type::SINGLE_VALUE; + } else if (dynamic_cast(a)) { + return type::RANGE; + } else if (dynamic_cast(a)) { + return type::SET; + } else { + BOOST_ASSERT_MSG(false, "Unknown manifest param type"); + return type::UNSAT; + } + } + + inline manifest_param::type get_manifest_param_type(std::shared_ptr a) { + return get_manifest_param_type(a.get()); + } + + inline std::shared_ptr manifest_single_value_param::merge_with( + std::shared_ptr other) { + using type = manifest_param::type; + type other_type = get_manifest_param_type(other); + if (other_type == type::UNSAT) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (other_type == type::SINGLE_VALUE) { + auto other_value = dynamic_cast(other.get())->value; + if (other_value == this->value) { + return std::shared_ptr(new manifest_single_value_param(this->value)); + } else { + return std::shared_ptr(new manifest_single_value_param( + std::max(this->value, other_value))); + } + } else if (other_type == type::SET) { + const std::set &other_set = dynamic_cast(other.get())->set; + std::set new_set; + for (auto it = other_set.lower_bound(this->value); it != other_set.end(); it++) { + new_set.insert(*it); + } + if (new_set.empty()) { + new_set.insert(this->value); + } + return std::shared_ptr(new manifest_set_param(new_set)); + } if (other_type == type::RANGE) { + auto range = dynamic_cast(other.get()); + std::uint32_t new_start = std::max( + this->value + (range->step - (this->value % range->step)) % range->step, + range->start); + if (new_start < std::uint32_t(range->finish)) { + return std::shared_ptr( + new manifest_range_param(new_start, range->finish, range->step)); + } else { + return std::shared_ptr(new manifest_single_value_param(this->value)); + } + } + return std::shared_ptr(new manifest_unsat_param()); + } + + inline std::shared_ptr manifest_set_param::merge_with(std::shared_ptr other) { + using type = manifest_param::type; + type other_type = get_manifest_param_type(other); + if (other_type == type::UNSAT) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (other_type == type::SINGLE_VALUE) { + auto other_value = dynamic_cast(other.get())->value; + std::set new_set; + for (auto it = set.lower_bound(other_value); it != set.end(); it++) { + new_set.insert(*it); + } + if (new_set.empty()) { + new_set.insert(other_value); + } + return std::shared_ptr(new manifest_set_param(new_set)); + } else if (other_type == type::SET) { + auto other_set = dynamic_cast(other.get())->set; + if (other_set.empty() || set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } + std::uint32_t min_1 = *set.begin(), + min_2 = *other_set.begin(); + std::uint32_t max_min = std::max(min_1, min_2); + std::set new_set; + for (auto it = set.lower_bound(max_min); it != set.end(); it++) { + new_set.insert(*it); + } + for (auto it = other_set.lower_bound(max_min); it != other_set.end(); it++) { + new_set.insert(*it); + } + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } if (other_type == type::RANGE) { + auto range = dynamic_cast(other.get()); + std::uint32_t new_start = std::max( + *set.lower_bound(range->start), + range->start); + std::set new_set = {new_start}; + for (auto it = set.lower_bound(new_start); it != set.end(); it++) { + new_set.insert(*it); + } + std::uint32_t step = range->step; + for (auto it = new_start + (step - new_start % step) % step; std::int32_t(it) < range->finish; it += step) { + new_set.insert(it); + } + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } + return std::shared_ptr(new manifest_unsat_param()); + } + + inline std::shared_ptr manifest_range_param::merge_with(std::shared_ptr other) { + using type = manifest_param::type; + type other_type = get_manifest_param_type(other); + if (other_type == type::UNSAT) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (other_type == type::SINGLE_VALUE) { + std::uint32_t other_value = dynamic_cast(other.get())->value; + std::uint32_t new_start = std::max( + other_value + (step - (other_value % step)) % step, + start); + if (new_start < std::uint32_t(finish)) { + return std::shared_ptr( + new manifest_range_param(new_start, finish, step)); + } else { + return std::shared_ptr(new manifest_single_value_param(other_value)); + } + } else if (other_type == type::SET) { + auto other_set = dynamic_cast(other.get()); + std::uint32_t new_start = std::max( + *other_set->set.lower_bound(start), + start); + std::set new_set = {new_start}; + for (auto it = other_set->set.lower_bound(new_start); it != other_set->set.end(); it++) { + new_set.insert(*it); + } + for (auto it = new_start + (step - new_start % step) % step; it < std::uint32_t(finish); it += step) { + new_set.insert(it); + } + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } if (other_type == type::RANGE) { + auto other_range = dynamic_cast(other.get()); + std::int32_t new_start = std::max( + start, + other_range->start); + // technically, there are some other cases there this might be resolved as range + // this might be good enough though + if (step == other_range->step) { + std::int32_t new_finish = std::max(finish, other_range->finish); + if (new_start < new_finish) { + return std::shared_ptr( + new manifest_range_param(new_start, new_finish, step)); + } else { + return std::shared_ptr(new manifest_single_value_param(new_start)); + } + } else if (new_start >= other_range->finish) { + return std::shared_ptr(new manifest_range_param(start, finish, step)); + } else if (new_start >= finish) { + return std::shared_ptr(new manifest_range_param(other_range->start, other_range->finish, other_range->step)); + } + std::set new_set = {std::uint32_t(new_start)}; + for (auto value : *this) { + if (value > std::uint32_t(new_start)) { + new_set.insert(value); + } + } + for (auto value : *other_range) { + if (value > std::uint32_t(new_start)) { + new_set.insert(value); + } + } + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } + return std::shared_ptr(new manifest_unsat_param()); + } + + inline std::shared_ptr manifest_range_param::intersect( + std::shared_ptr other) { + using type = manifest_param::type; + type other_type = get_manifest_param_type(other); + if (other_type == type::UNSAT) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (other_type == type::SINGLE_VALUE) { + if (check_manifest_param(dynamic_cast(other.get())->value)) { + return other; + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } else if (other_type == type::RANGE) { + if (!is_satisfiable() || !other->is_satisfiable()) { + return std::shared_ptr(new manifest_unsat_param()); + } + manifest_range_param* other_range = dynamic_cast(other.get()); + std::int32_t other_start = other_range->start; + std::int32_t other_finish = other_range->finish; + std::uint32_t other_step = other_range->step; + std::int32_t new_start, new_finish, new_step; + if (step == other_step) { + new_start = std::max(start, other_start); + new_finish = std::min(finish, other_finish); + new_step = step; + } else { + auto [step_gcd, m, n] = + boost::integer::extended_euclidean(step, other_step); + if (start % step_gcd != other_start % step_gcd) { + return std::shared_ptr(new manifest_unsat_param()); + } + new_step = step * (other_step / step_gcd); + std::int32_t modulo_new_step = + (new_step + (other_start * int(step) * m + start * int(other_step) * n) / + step_gcd % new_step) % new_step; + new_start = std::max(start, other_start); + new_start = new_start + (new_step + int(modulo_new_step - new_start) % new_step) % new_step; + new_finish = std::min(finish, other_finish); + } + if (new_start >= new_finish) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (new_start == new_finish - 1) { + return std::shared_ptr(new manifest_single_value_param(new_start)); + } else { + return std::shared_ptr(new manifest_range_param(new_start, new_finish, new_step)); + } + } else if (other_type == type::SET) { + std::set new_set; + manifest_set_param* other_set = dynamic_cast(other.get()); + for (std::uint32_t value : other_set->set) { + if (check_manifest_param(value)) { + new_set.insert(value); + } + } + return std::shared_ptr(new manifest_set_param(new_set)); + } else { + BOOST_ASSERT_MSG(false, "Unknown manifest param type"); + } + return std::shared_ptr(new manifest_unsat_param()); + } + + inline std::shared_ptr manifest_range_param::subtract(std::set values) { + std::set filtered_set; + for (std::uint32_t value : values) { + if (check_manifest_param(value)) { + filtered_set.insert(value); + } + } + if (filtered_set.empty()) { + return std::shared_ptr(new manifest_range_param(start, finish, step)); + } else { + // Three distict cases: + // 1) contigious values at range start + // 2) contigious values at range end + // 3) values in the middle -- have to return set + bool start_contigious = false; + + std::uint32_t count = 0; + std::int32_t curr_value = start; + for (auto value : filtered_set) { + if (std::int32_t(value) != curr_value) { + break; + } else { + curr_value += step; + } + count++; + } + start_contigious = count == filtered_set.size(); + if (start_contigious) { + if (curr_value < finish) { + return std::shared_ptr(new manifest_range_param(curr_value, finish, step)); + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } + + bool end_contigious = false; + count = 0; + curr_value = finish - finish % step; + for (auto it = filtered_set.rbegin(); it != filtered_set.rend(); it++) { + if (*it != std::uint32_t(curr_value)) { + break; + } else { + curr_value -= step; + } + count++; + } + end_contigious = count == filtered_set.size(); + if (end_contigious) { + if (curr_value > start) { + return std::shared_ptr(new manifest_range_param(start, curr_value, step)); + } else if (curr_value == start) { + return std::shared_ptr(new manifest_single_value_param(start)); + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } + + std::set new_set; + for (std::uint32_t i = start; i < std::uint32_t(finish); i += step) { + if (filtered_set.find(i) == filtered_set.end()) { + new_set.insert(i); + } + } + if (new_set.empty()) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (new_set.size() == 1) { + return std::shared_ptr(new manifest_single_value_param(*new_set.begin())); + } else { + return std::shared_ptr(new manifest_set_param(new_set)); + } + } + } + + inline std::shared_ptr manifest_set_param::intersect( + std::shared_ptr other) { + using type = manifest_param::type; + type other_type = get_manifest_param_type(other); + if (other_type == type::UNSAT) { + return std::shared_ptr(new manifest_unsat_param()); + } else if (other_type == type::SINGLE_VALUE) { + if (check_manifest_param(dynamic_cast(other.get())->value)) { + return other; + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } else if (other_type == type::RANGE) { + std::set new_set; + for (std::uint32_t value : set) { + if (other->check_manifest_param(value)) { + new_set.insert(value); + } + } + if (!new_set.empty()) { + return std::shared_ptr(new manifest_set_param(new_set)); + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } else if (other_type == type::SET) { + std::set new_set; + manifest_set_param* other_set = dynamic_cast(other.get()); + for (std::uint32_t value : other_set->set) { + if (check_manifest_param(value)) { + new_set.insert(value); + } + } + if (!new_set.empty()) { + return std::shared_ptr(new manifest_set_param(new_set)); + } else { + return std::shared_ptr(new manifest_unsat_param()); + } + } else { + BOOST_ASSERT_MSG(false, "Unknown manifest param type"); + } + return std::shared_ptr(new manifest_unsat_param()); + } + + // Describes the set of parameters (e.g. amount of witness columns, lookup columns etc.) + // which are suitable for a component. + // Almost all parameters are assumed to be independent of each other: + // e.g. there can be no dependency between witness column amount and lookup column amount. + // The one and only exception is: lookup size can depend on lookup column amount. + class plonk_component_manifest { + public: + using param_type = manifest_param; + using param_ptr_type = std::shared_ptr; + using lookup_size_func_type = std::function; + + static std::uint32_t empty_lookup_size_for_column_amount(std::uint32_t) { + return 0; + } + + param_ptr_type witness_amount; + manifest_constant_type constant_required; + + plonk_component_manifest(param_ptr_type witness_params, manifest_constant_type constant_required) + : witness_amount(witness_params), + constant_required(constant_required) {} + + plonk_component_manifest(const plonk_component_manifest &other) { + witness_amount = other.witness_amount; + constant_required = other.constant_required; + } + + // Checks if the manifest would be satisfied for passed params. + bool check_manifest(std::uint32_t witness_amount, std::uint32_t constant_amount, + bool strict = false) const { + if (!this->witness_amount->check_manifest_param(witness_amount, strict)) { + return false; + } + if (constant_required == manifest_constant_type::type::UNSAT || + (constant_required == manifest_constant_type::type::REQUIRED && constant_amount == 0)) { + return false; + } + return true; + } + + // Checks if the manifest is satisfied for the component. + // This is a runtime check in order to prevent bad intialization of components. + template + bool check_manifest( + const nil::blueprint::components::plonk_component + &component) const { + return check_manifest(component.witness_amount(), component.constant_amount()); + } + + // merge_with is intended to be used to automatically calculate new manifest in case of one component + // using another component. You can specify only the parts you are directly using, and count on the system + // automatically calculating correct manifest for you. + // Thus this is mostly an intersection of params. The only exception is lookup_usage, which is a union, + // and lookup_size_for_column_amount, which is a sum. + plonk_component_manifest merge_with(const plonk_component_manifest &other) const { + return plonk_component_manifest( + witness_amount->merge_with(other.witness_amount), + constant_required.merge_with(other.constant_required) + ); + } + + // Checks if there is at least a single set of parameters which satisfies this manifest + bool is_satisfiable() const { + return witness_amount->is_satisfiable() && + constant_required != manifest_constant_type::type::UNSAT; + } + }; + + inline std::ostream& operator<<(std::ostream& os, const plonk_component_manifest &manifest) { + os << "witness_amount: " << (*manifest.witness_amount) << " " + << "constant_required: " << manifest.constant_required; + return os; + } + + // Describes the maximum values of parameters compiler is willing to give to a component. + struct compiler_manifest { + private: + using manifest_param_ptr = std::shared_ptr; + + std::uint32_t max_witness_columns; + + manifest_param_ptr witness_amount; + public: + bool has_constant; + + void set_max_witness_amount(std::uint32_t max) { + max_witness_columns = max; + witness_amount = std::shared_ptr(new manifest_range_param(0, max)); + } + + std::uint32_t get_max_witness_amount() const { + return max_witness_columns; + } + + // Intended to be used only in tests to put this into an std::map + bool operator<(const compiler_manifest &other) const { + return std::tie(max_witness_columns, has_constant) < + std::tie(other.max_witness_columns, other.has_constant); + } + + compiler_manifest(std::uint32_t max_witness_columns, bool has_constant) + : max_witness_columns(max_witness_columns), + has_constant(has_constant) { + + witness_amount = std::shared_ptr(new manifest_range_param(0, max_witness_columns + 1)); + } + + // Generates a new component manifest based on intersection with given compiler manifest. + plonk_component_manifest intersect(const plonk_component_manifest &component_manifest) const { + return plonk_component_manifest( + component_manifest.witness_amount->intersect(witness_amount), + component_manifest.constant_required.intersect(*this) + ); + } + }; + + inline manifest_constant_type manifest_constant_type::intersect( + const compiler_manifest &manifest) const { + if (t == manifest_constant_type::type::UNSAT || + (t == manifest_constant_type::type::REQUIRED && manifest.has_constant == false)) { + return manifest_constant_type::type::UNSAT; + } else if (t == manifest_constant_type::type::REQUIRED && manifest.has_constant == true) { + return manifest_constant_type::type::REQUIRED; + } else { + return manifest_constant_type::type::NONE; + } + } + + // Base class for all component gate manifests + class component_gate_manifest { + public: + virtual ~component_gate_manifest() = default; + virtual std::uint32_t gates_amount() const = 0; + // This is called in comparison function + // Derived classes should only support compariosn with instances of themselves + // The case of different classes is already handled in the comparator + // Default implementation is to return false, meaning equivalence of all instances in set terms. + virtual bool operator<(const component_gate_manifest *other) const { + return false; + } + }; + + class component_gate_manifest_comparison { + public: + bool operator()(const std::shared_ptr &a, + const std::shared_ptr &b) const { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpotentially-evaluated-expression" + if (typeid(*(a.get())) != typeid(*(b.get()))) { + return std::type_index(typeid(*(a.get()))) < std::type_index(typeid(*(b.get()))); + #pragma clang diagnostic pop + } else { + return a->operator<(b.get()); + } + } + }; + + // We use this to avoid having "duplicate" gates counted in gates count. + // Done via references to allow constexpr access to gates amount. + struct gate_manifest { + private: + std::uint32_t gates_amount; + + void calc_gates_amount() { + std::uint32_t result = 0; + for (auto gate : component_gate_manifests) { + result += gate.get()->gates_amount(); + } + gates_amount = result; + } + + public: + virtual ~gate_manifest() = default; + + std::set, component_gate_manifest_comparison> + component_gate_manifests = {}; + + gate_manifest() : gates_amount(0) {} + gate_manifest(const gate_manifest &other) : gates_amount(other.gates_amount) { + component_gate_manifests = other.component_gate_manifests; + } + template + gate_manifest(const GateManifestType &gate) : gates_amount(0) { + add(gate); + } + + template + gate_manifest& add(const GateManifestType &gate) { + component_gate_manifests.insert(std::make_shared(gate)); + calc_gates_amount(); + return *this; + } + + std::uint32_t get_gates_amount() const { + return gates_amount; + } + + gate_manifest& merge_with(const gate_manifest &other) { + component_gate_manifests.insert(other.component_gate_manifests.begin(), + other.component_gate_manifests.end()); + calc_gates_amount(); + return *this; + } + }; + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENT_MANIFEST_HPP diff --git a/libs/blueprint/include/nil/blueprint/utils/connectedness_check.hpp b/libs/blueprint/include/nil/blueprint/utils/connectedness_check.hpp new file mode 100644 index 000000000..ed7567c0b --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/utils/connectedness_check.hpp @@ -0,0 +1,490 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_UTILS_PLONK_CONNECTEDNESS_CHECK_HPP +#define CRYPTO3_BLUEPRINT_UTILS_PLONK_CONNECTEDNESS_CHECK_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace detail { + template + std::size_t copy_var_address( + const std::size_t row_size, + const std::size_t start_row_index, const std::size_t rows_amount, + const nil::crypto3::zk::snark::plonk_variable &variable) { + + using var = nil::crypto3::zk::snark::plonk_variable; + const std::size_t ouptutless_var_amount = row_size * rows_amount; + + if (variable.type == var::column_type::public_input) { + // Assumes a single file public input + return ouptutless_var_amount + variable.rotation; + } else if (variable.type == var::column_type::witness) { + return (variable.rotation - start_row_index) * row_size + variable.index; + } else { + // Constant + return (variable.rotation - start_row_index) * row_size + row_size - 1; + } + }; + + template + void export_connectedness_zones( + boost::disjoint_sets_with_storage<> zones, + const nil::blueprint::assignment> &assignment, + const std::vector>> + &input_variables, + const std::size_t start_row_index, std::size_t rows_amount, + std::ostream &os) { + + using var = nil::crypto3::zk::snark::plonk_variable; + const std::size_t row_size = assignment.witnesses_amount() + assignment.constants_amount(); + const std::size_t end_row = start_row_index + rows_amount; + + nil::blueprint::assignment> output_assignment( + assignment.witnesses_amount(), assignment.public_inputs_amount(), + assignment.constants_amount(), assignment.selectors_amount() + ); + + // We do '+1' in all the assignments to separate the unassigned cells (0 by default) + // from the ones which actually got checked. + for (std::size_t witness_column = 0; witness_column < assignment.witnesses_amount(); witness_column++) { + std::size_t last_row = + std::min(end_row, assignment.witness_column_size(witness_column)); + for (std::size_t row = start_row_index; row < last_row; row++) { + output_assignment.witness(witness_column, row) = + zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, + var(witness_column, row, false, var::column_type::witness))) + 1; + } + } + for (std::size_t constant_column = 0; constant_column < assignment.constants_amount(); + constant_column++) { + + std::size_t last_row = + std::min(end_row, assignment.constant_column_size(constant_column)); + for (std::size_t row = start_row_index; row < last_row; row++) { + output_assignment.constant(constant_column, row) = + zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, + var(constant_column, row, false, var::column_type::constant))) + 1; + } + } + for (auto &variable : input_variables) { + const auto output_value = + zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, variable)) + 1; + const var &unwrapped_var = variable.get(); + switch (unwrapped_var.type) { + case var::column_type::constant: + output_assignment.constant(unwrapped_var.index, unwrapped_var.rotation) = output_value; + break; + case var::column_type::public_input: + output_assignment.public_input(unwrapped_var.index, unwrapped_var.rotation) = output_value; + break; + case var::column_type::witness: + output_assignment.witness(unwrapped_var.index, unwrapped_var.rotation) = output_value; + break; + case var::column_type::selector: + BOOST_ASSERT_MSG(false, "Selector variables should not be input variables."); + break; + } + } + // Copy selectors over from assignment + for (std::size_t selector = 0; selector < assignment.selectors_amount(); selector++) { + std::size_t last_row = + std::min(end_row, assignment.selector_column_size(selector)); + for (std::size_t row = start_row_index; row < last_row; row++) { + output_assignment.selector(selector, row) = + assignment.selector(selector, row); + } + } + output_assignment.export_table(os); + } + + template + void mark_set( + const nil::blueprint::assignment> &assignment, + boost::disjoint_sets_with_storage<> &zones, + const std::set> + &variable_set, + const std::function)> + &gate_var_address, + std::size_t selector_index, + std::size_t start_row_index, + std::size_t end_row_index) { + + std::size_t last_row = + std::min(end_row_index, assignment.selector_column_size(selector_index)); + for (std::size_t row = start_row_index; row < last_row; row++) { + if (assignment.selector(selector_index, row) != BlueprintFieldType::value_type::zero()) { + for (const auto &variable : variable_set) { + zones.union_set(gate_var_address(start_row_index, row, variable), + gate_var_address(start_row_index, row, *variable_set.begin())); + } + } + } + } + + template + bool check_set( + const nil::blueprint::assignment> &assignment, + boost::disjoint_sets_with_storage<> &zones, + const std::set> + &variable_set, + const std::unordered_set &expected_zones, + const std::function)> + &gate_var_address, + std::size_t selector_index, + std::size_t start_row_index, + std::size_t end_row_index) { + + std::size_t last_row = + std::min(end_row_index, assignment.selector_column_size(selector_index)); + for (std::size_t row = start_row_index; row < last_row; row++) { + if (assignment.selector(selector_index, row) != BlueprintFieldType::value_type::zero()) { + for (const auto &variable : variable_set) { + const auto var_address = gate_var_address(start_row_index, row, variable); + if (expected_zones.count(zones.find_set(var_address)) == 0) { + return false; + } + } + } + } + return true; + } + + template + boost::disjoint_sets_with_storage<> generate_connectedness_zones( + const nil::blueprint::assignment> &assignment, + const nil::blueprint::circuit> &bp, + const std::vector>> + &input_variables, + const std::size_t start_row_index, std::size_t rows_amount) { + + using var = nil::crypto3::zk::snark::plonk_variable; + + const std::size_t row_size = assignment.witnesses_amount() + assignment.constants_amount(); + const std::size_t ouptutless_var_amount = row_size * rows_amount; + const std::size_t var_amount = ouptutless_var_amount + input_variables.size(); + boost::disjoint_sets_with_storage<> zones(var_amount); + auto gate_var_address = [&row_size](std::size_t start_row_index, std::size_t row, const var &variable) { + if (variable.type == var::column_type::witness) { + return (row - start_row_index + variable.rotation) * row_size + variable.index; + } else { + // Constant + return (row - start_row_index + variable.rotation) * row_size + row_size - 1; + } + }; + const std::size_t end_row_index = start_row_index + rows_amount; + for (const auto &gate : bp.gates()) { + std::set variable_set; + std::function variable_extractor = [&variable_set](var variable) { + variable_set.insert(variable); + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &constraint : gate.constraints) { + visitor.visit(constraint); + } + mark_set(assignment, zones, variable_set, gate_var_address, gate.selector_index, + start_row_index, end_row_index); + } + for (auto &lookup_gate : bp.lookup_gates()) { + std::set variable_set; + std::function variable_extractor = [&variable_set](var variable) { + variable_set.insert(variable); + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &lookup_constraint : lookup_gate.constraints) { + for (const auto &lookup_input : lookup_constraint.lookup_input) { + visitor.visit(lookup_input); + } + } + mark_set(assignment, zones, variable_set, gate_var_address, lookup_gate.tag_index, + start_row_index, end_row_index); + } + for (auto &constraint : bp.copy_constraints()) { + zones.union_set( + copy_var_address( + row_size, start_row_index, rows_amount, constraint.first), + copy_var_address( + row_size, start_row_index, rows_amount, constraint.second)); + } + return zones; + } + } // namespace detail + + + struct connectedness_check_type { + enum class type { + NONE, + WEAK, + STRONG + } t; + + enum class island_type { + NONE, + ISLANDS + } it; + + connectedness_check_type(type t) : t(t), it(island_type::ISLANDS) {} + connectedness_check_type(type t, island_type it) : t(t), it(it) {} + }; + + // Checks if there are connected components which are separate from inputs/outputs + // This should always be true for a correct component. + // If this fails, either the component is wrong or the check is busted. + template + bool check_islands( + const nil::blueprint::assignment> &assignment, + const nil::blueprint::circuit> &bp, + boost::disjoint_sets_with_storage<> &zones, + const std::vector>> + &input_variables, + const std::vector>> + &output_variables, + std::size_t start_row_index, std::size_t rows_amount) { + + using var = nil::crypto3::zk::snark::plonk_variable; + + std::size_t row_size = assignment.witnesses_amount() + assignment.constants_amount(); + std::unordered_set expected_zones; + for (const auto &input_var : input_variables) { + expected_zones.insert(zones.find_set( + detail::copy_var_address( + row_size, start_row_index, rows_amount, input_var))); + } + for (const auto &output_var : output_variables) { + expected_zones.insert(zones.find_set( + detail::copy_var_address( + row_size, start_row_index, rows_amount, output_var))); + } + auto gate_var_address = [&row_size](std::size_t start_row_index, std::size_t row, const var &variable) { + if (variable.type == var::column_type::witness) { + return (row - start_row_index + variable.rotation) * row_size + variable.index; + } else { + // Constant + return (row - start_row_index + variable.rotation) * row_size + row_size - 1; + } + }; + const std::size_t end_row_index = start_row_index + rows_amount; + for (const auto &gate : bp.gates()) { + std::set variable_set; + std::function variable_extractor = [&variable_set](var variable) { + variable_set.insert(variable); + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &constraint : gate.constraints) { + visitor.visit(constraint); + } + if (!detail::check_set(assignment, zones, variable_set, expected_zones, gate_var_address, + gate.selector_index, start_row_index, end_row_index)) { + return false; + } + } + for (auto &lookup_gate : bp.lookup_gates()) { + std::set variable_set; + std::function variable_extractor = [&variable_set](var variable) { + variable_set.insert(variable); + }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + for (const auto &lookup_constraint : lookup_gate.constraints) { + for (const auto &lookup_input : lookup_constraint.lookup_input) { + visitor.visit(lookup_input); + } + } + if (!detail::check_set(assignment, zones, variable_set, expected_zones, gate_var_address, + lookup_gate.tag_index, start_row_index, end_row_index)) { + return false; + } + } + for (auto &constraint : bp.copy_constraints()) { + const auto first_address = + detail::copy_var_address( + row_size, start_row_index, rows_amount, constraint.first); + const auto second_address = + detail::copy_var_address( + row_size, start_row_index, rows_amount, constraint.second); + if (expected_zones.count(zones.find_set(first_address)) == 0 || + expected_zones.count(zones.find_set(second_address)) == 0) { + return false; + } + } + return true; + } + + // Ensure that output and input variables are connected via constraints. + // This failing basically guarantees that the circuit is broken (or the check is). + // There might exists rare components for which a lower level of connectedness is sufficient: + // technically this checks that all inputs can affect all outputs. + // For a weaker version, see check_weak_connectedness + template + bool check_strong_connectedness( + boost::disjoint_sets_with_storage<> &zones, + const std::vector>> + &input_variables, + const std::vector>> + &output_variables, + std::size_t row_size, std::size_t start_row_index, std::size_t rows_amount) { + + using detail::copy_var_address; + std::size_t expected_zone = zones.find_set( + copy_var_address( + row_size, start_row_index, rows_amount, input_variables[0])); + for (auto &variable : input_variables) { + if (zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, variable)) != expected_zone) { + return false; + } + } + for (auto &variable : output_variables) { + if (zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, variable)) != expected_zone) { + return false; + } + } + + return true; + } + + // Ensure that output and input variables are connected via constraints. + // This failing basically guarantees that the circuit is broken (or the check is). + // This version does not require that all inputs are connected to all outputs. + // For a stronger version, see check_strong_connectedness + template + bool check_weak_connectedness( + boost::disjoint_sets_with_storage<> &zones, + const std::vector>> + &input_variables, + const std::vector>> + &output_variables, + std::size_t row_size, std::size_t start_row_index, std::size_t rows_amount) { + + using detail::copy_var_address; + std::set expected_input_zones, + expected_output_zones; + // check that all outputs are connected to at least some input + for (auto input_var : input_variables) { + expected_input_zones.insert( + zones.find_set( + copy_var_address( + row_size, start_row_index, rows_amount, input_var))); + } + for (auto &variable : output_variables) { + if (expected_input_zones.count( + zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, variable))) == 0) { + return false; + } + } + // check that all inputs are connected to at least some output + for (auto output_var : output_variables) { + expected_output_zones.insert( + zones.find_set( + copy_var_address( + row_size, start_row_index, rows_amount, output_var))); + } + for (auto &variable : input_variables) { + if (expected_output_zones.count( + zones.find_set(copy_var_address( + row_size, start_row_index, rows_amount, variable))) == 0) { + return false; + } + } + + return true; + } + + template + bool check_connectedness( + const nil::blueprint::assignment> &assignment, + const nil::blueprint::circuit> &bp, + const std::vector>> + &input_variables, + const std::vector>> + &output_variables, + std::size_t start_row_index, std::size_t rows_amount, + connectedness_check_type type) { + + if (type.t == connectedness_check_type::type::NONE) { + return true; + } + const std::size_t row_size = assignment.witnesses_amount() + assignment.constants_amount(); + auto zones = detail::generate_connectedness_zones(assignment, bp, input_variables, + start_row_index, rows_amount); + bool check_result; + switch(type.t) { + case connectedness_check_type::type::NONE: + return true; + case connectedness_check_type::type::WEAK: + check_result = check_weak_connectedness( + zones, input_variables, output_variables, row_size, start_row_index, rows_amount); + break; + case connectedness_check_type::type::STRONG: + check_result = check_strong_connectedness( + zones, input_variables, output_variables, row_size, start_row_index, rows_amount); + break; + } + + switch(type.it) { + case connectedness_check_type::island_type::NONE: + return check_result; + case connectedness_check_type::island_type::ISLANDS: + return check_result && check_islands( + assignment, bp, zones, input_variables, output_variables, start_row_index, rows_amount); + } + return false; + } + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_UTILS_PLONK_CONNECTEDNESS_CHECK_HPP diff --git a/libs/blueprint/include/nil/blueprint/utils/gate_mover.hpp b/libs/blueprint/include/nil/blueprint/utils/gate_mover.hpp new file mode 100644 index 000000000..18572eed7 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/utils/gate_mover.hpp @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include + +#include +#include + +namespace nil { + namespace blueprint { + template + class gate_mover : public boost::static_visitor< + nil::crypto3::math::expression>> { + + using var = nil::crypto3::zk::snark::plonk_variable; + std::function var_mover; + public: + using expression = nil::crypto3::math::expression; + using term_type = nil::crypto3::math::term; + using pow_operation = nil::crypto3::math::pow_operation; + using binary_arithmetic_operation = nil::crypto3::math::binary_arithmetic_operation; + + gate_mover(std::function var_mover_) : var_mover(var_mover_) {} + + expression visit(const expression& expr) { + return boost::apply_visitor(*this, expr.get_expr()); + } + + expression operator()(const term_type& term) { + std::vector vars; + auto coeff = term.get_coeff(); + for (const auto& var: term.get_vars()) { + vars.emplace_back(var_mover(var)); + } + term_type result(vars, coeff); + return result; + } + + expression operator()(const pow_operation& pow) { + expression base = boost::apply_visitor( + *this, pow.get_expr().get_expr()); + return pow_operation(base, pow.get_power()); + } + + expression operator()( + const binary_arithmetic_operation& op) { + expression left = + boost::apply_visitor(*this, op.get_expr_left().get_expr()); + expression right = + boost::apply_visitor(*this, op.get_expr_right().get_expr()); + switch (op.get_op()) { + case nil::crypto3::math::ArithmeticOperator::ADD: + return left + right; + case nil::crypto3::math::ArithmeticOperator::SUB: + return left - right; + case nil::crypto3::math::ArithmeticOperator::MULT: + return left * right; + default: + __builtin_unreachable(); + } + } + }; + } // namespace blueprint +} // namespace nil \ No newline at end of file diff --git a/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp b/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp new file mode 100644 index 000000000..2f8799e84 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp @@ -0,0 +1,256 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Mikhail Komarov +// Copyright (c) 2022 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_UTILS_PLONK_SATISFIABILITY_CHECK_HPP +#define CRYPTO3_BLUEPRINT_UTILS_PLONK_SATISFIABILITY_CHECK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + bool is_satisfied( + const circuit> &bp, + const assignment> &assignments) { + std::set used_gates; + for (std::uint32_t i = 0; i < bp.gates().size(); i++) { + used_gates.insert(i); + } + + std::set used_lookup_gates; + for (std::uint32_t i = 0; i < bp.lookup_gates().size(); i++) { + used_lookup_gates.insert(i); + } + + std::set used_copy_constraints; + for (std::uint32_t i = 0; i < bp.copy_constraints().size(); i++) { + used_copy_constraints.insert(i); + } + + std::set selector_rows; + for (std::uint32_t i = 0; i < assignments.allocated_rows(); i++) { + selector_rows.insert(i); + } + + return is_satisfied(bp, assignments, used_gates, used_lookup_gates, used_copy_constraints, selector_rows); + } + + template + std::set> + load_dynamic_lookup( + const circuit> &bp, + const assignment> &assignments, + std::size_t table_id + ){ + std::set> result; + auto &table = bp.lookup_tables()[table_id-1]; + + crypto3::zk::snark::plonk_column selector = + assignments.crypto3::zk::snark:: + template plonk_assignment_table::selector(table.tag_index); + + for( std::size_t selector_row = 0; selector_row < assignments.rows_amount(); selector_row++ ){ + if( selector_row < selector.size() && !selector[selector_row].is_zero() ){ + for( std::size_t op = 0; op < table.lookup_options.size(); op++){ + std::vector item(table.lookup_options[op].size()); + for( std::size_t i = 0; i < table.lookup_options[op].size(); i++){ + crypto3::zk::snark::plonk_constraint expr = table.lookup_options[op][i];; + item[i] = expr.evaluate(selector_row, assignments); + } + result.insert(item); + } + } + } + + return result; + } + + template + bool is_satisfied( + const circuit> &bp, + const assignment> &assignments, + const std::set &used_gates, + const std::set &used_lookup_gates, + const std::set &used_copy_constraints, + const std::set &selector_rows){ + + const auto &gates = bp.gates(); + + const auto ©_constraints = bp.copy_constraints(); + + const auto &lookup_gates = bp.lookup_gates(); + + std::map>> used_dynamic_tables; + + for (const auto& i : used_gates) { + crypto3::zk::snark::plonk_column selector = + assignments.crypto3::zk::snark:: + template plonk_assignment_table::selector( + gates[i].selector_index); + + for (const auto& selector_row : selector_rows) { + if (selector_row < selector.size() && !selector[selector_row].is_zero()) { + for (std::size_t j = 0; j < gates[i].constraints.size(); j++) { + + typename BlueprintFieldType::value_type constraint_result = + gates[i].constraints[j].evaluate(selector_row, assignments); + + if (!constraint_result.is_zero()) { + std::cout << "Constraint " << j << " from gate " << i << " on row " << selector_row + << " is not satisfied." << std::endl; + std::cout << "Constraint result: " << constraint_result << std::endl; + std::cout << "Offending gate:" << std::endl; + for (const auto &constraint : gates[i].constraints) { + std::cout << constraint << std::endl; + } + return false; + } + } + } + } + } + + for (const auto& i : used_lookup_gates) { + crypto3::zk::snark::plonk_column selector = + assignments.crypto3::zk::snark:: + template plonk_assignment_table::selector( + lookup_gates[i].tag_index); + + for (const auto& selector_row : selector_rows) { + if (selector_row < selector.size() && !selector[selector_row].is_zero()) { + for (std::size_t j = 0; j < lookup_gates[i].constraints.size(); j++) { + std::vector input_values; + input_values.reserve(lookup_gates[i].constraints[j].lookup_input.size()); + for (std::size_t k = 0; k < lookup_gates[i].constraints[j].lookup_input.size(); k++) { + input_values.emplace_back(lookup_gates[i].constraints[j].lookup_input[k].evaluate( + selector_row, assignments)); + } + const auto table_name = + bp.get_reserved_indices_right().at(lookup_gates[i].constraints[j].table_id); + try { + if( bp.get_reserved_dynamic_tables().find(table_name) != bp.get_reserved_dynamic_tables().end() ){ + if( used_dynamic_tables.find(table_name) == used_dynamic_tables.end()){ + used_dynamic_tables[table_name] = load_dynamic_lookup(bp, assignments, lookup_gates[i].constraints[j].table_id); + } + if( used_dynamic_tables[table_name].find(input_values) == used_dynamic_tables[table_name].end() ) { + for (std::size_t k = 0; k < input_values.size(); k++) { + std::cout << input_values[k] << " "; + } + std::cout << std::endl; + std::cout << "Constraint " << j << " from lookup gate " << i << " from table " + << table_name << " on row " << selector_row << " is not satisfied." + << std::endl; + std::cout << "Offending Lookup Gate: " << std::endl; + for (const auto &constraint : lookup_gates[i].constraints) { + std::cout << "Table id: " << constraint.table_id << std::endl; + for (auto &lookup_input : constraint.lookup_input) { + std::cout << lookup_input << std::endl; + } + } + return false; + } + continue; + } + std::string main_table_name = table_name.substr(0, table_name.find("/")); + std::string subtable_name = + table_name.substr(table_name.find("/") + 1, table_name.size() - 1); + + const auto &table = bp.get_reserved_tables().at(main_table_name)->get_table(); + const auto &subtable = + bp.get_reserved_tables().at(main_table_name)->subtables.at(subtable_name); + + std::size_t columns_number = subtable.column_indices.size(); + + // Search the table for the input values + // We can cache it with sorting, or use KMP, but I need a simple solution first + bool found = false; + BOOST_ASSERT(columns_number == input_values.size()); + for (std::size_t k = 0; k < table[0].size(); k++) { + bool match = true; + for (std::size_t l = 0; l < columns_number; l++) { + if (table[subtable.column_indices[l]][k] != input_values[l]) { + match = false; + break; + } + } + if (match) { + found = true; + break; + } + } + if (!found) { + std::cout << "Input values:"; + for (std::size_t k = 0; k < input_values.size(); k++) { + std::cout << input_values[k] << " "; + } + std::cout << std::endl; + std::cout << "Constraint " << j << " from lookup gate " << i << " from table " + << table_name << " on row " << selector_row << " is not satisfied." + << std::endl; + std::cout << "Offending Lookup Gate: " << std::endl; + for (const auto &constraint : lookup_gates[i].constraints) { + std::cout << "Table id: " << constraint.table_id << std::endl; + for (auto &lookup_input : constraint.lookup_input) { + std::cout << lookup_input << std::endl; + } + } + return false; + } + } catch (std::out_of_range &e) { + std::cout << "Lookup table " << table_name << " not found." << std::endl; + std::cout << "Table_id = " << lookup_gates[i].constraints[j].table_id << " table_name " << table_name << std::endl; + return false; + } + } + } + } + } + + for (const auto& i : used_copy_constraints) { + if (var_value(assignments, copy_constraints[i].first) != + var_value(assignments, copy_constraints[i].second)) { + std::cout << "Copy constraint number " << i << " is not satisfied." + << " First variable: " << copy_constraints[i].first + << " second variable: " << copy_constraints[i].second << std::endl; + std::cout << var_value(assignments, copy_constraints[i].first) << " != " << var_value(assignments, copy_constraints[i].second) << std::endl; + return false; + } + } + return true; + } + + } // namespace blueprint +} // namespace nil +#endif // CRYPTO3_BLUEPRINT_UTILS_PLONK_SATISFIABILITY_CHECK_HPP diff --git a/libs/blueprint/include/nil/blueprint/zkevm/bytecode.hpp b/libs/blueprint/include/nil/blueprint/zkevm/bytecode.hpp new file mode 100644 index 000000000..2d09e6842 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/bytecode.hpp @@ -0,0 +1,772 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // Component for bytecode table + template + class zkevm_bytecode_table; + + template + class zkevm_bytecode_table, BlueprintFieldType> + : public plonk_component + { + public: + // Named witness columns + static constexpr std::size_t TAG = 0; + static constexpr std::size_t INDEX = 1; + static constexpr std::size_t VALUE = 2; + static constexpr std::size_t IS_OPCODE = 3; + static constexpr std::size_t HASH_HI = 4; + static constexpr std::size_t HASH_LO = 5; + + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t max_bytecode_size; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return zkevm_bytecode_table::gates_amount + zkevm_bytecode_table::lookup_gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, std::size_t max_bytecode_size) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(6)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t max_bytecode_size) { + return max_bytecode_size; + } + + constexpr static const std::size_t gates_amount = 0; + constexpr static const std::size_t lookup_gates_amount = 0; + std::size_t rows_amount = max_bytecode_size; + + struct input_type { + std::vector> bytecodes; // EVM contracts bytecodes + std::vector> bytecode_hashes; // hi, lo parts for keccak. It'll be only one value if we'll use poseidon + std::size_t full_size; + + input_type( + const std::vector> &_bytecodes, + const std::vector> &_bytecode_hashes + ) : bytecodes(_bytecodes), bytecode_hashes(_bytecode_hashes), full_size(0) { + BOOST_ASSERT(_bytecodes.size() == _bytecode_hashes.size()); + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + full_size += bytecodes[i].size(); + } + } + + std::vector> all_vars() { + std::vector> result; + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + for( std::size_t j = 0; j < bytecodes[i].size(); j++ ){ + result.push_back(bytecodes[i][j]); + } + } + return result; + } + }; + + struct result_type { + result_type(const zkevm_bytecode_table &component, std::size_t start_row_index) { + } + + std::vector> all_vars() { + std::vector> result; + return result; + } + }; + + template + explicit zkevm_bytecode_table(ContainerType witness, std::size_t _max_bytecode_size) : + component_type(witness, {}, {}, get_manifest()), max_bytecode_size(_max_bytecode_size) + {}; + + template + zkevm_bytecode_table(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t _max_bytecode_size + ) : component_type(witness, constant, public_input, get_manifest()), max_bytecode_size(_max_bytecode_size) {}; + + zkevm_bytecode_table( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _max_bytecode_size + ) : component_type(witnesses, constants, public_inputs, get_manifest()), max_bytecode_size(_max_bytecode_size){}; + }; + + template + using plonk_zkevm_bytecode_table = + zkevm_bytecode_table, BlueprintFieldType>; + + template + typename plonk_zkevm_bytecode_table::result_type generate_assignments( + const plonk_zkevm_bytecode_table &component, + assignment> + &assignment, + const typename plonk_zkevm_bytecode_table::input_type + &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_zkevm_bytecode_table; + using value_type = typename BlueprintFieldType::value_type; + + std::size_t cur = 0; + for(std::size_t i = 0; i < instance_input.bytecodes.size(); i++){ + value_type hash_hi = var_value(assignment, instance_input.bytecode_hashes[i].first); + value_type hash_lo = var_value(assignment, instance_input.bytecode_hashes[i].second); + value_type push_size = 0; + for(std::size_t j = 0; j < instance_input.bytecodes[i].size(); j++, cur++){ + auto byte = var_value(assignment, instance_input.bytecodes[i][j]); + assignment.witness(component.W(component_type::VALUE), start_row_index + cur) = byte; + assignment.witness(component.W(component_type::HASH_HI), start_row_index + cur) = hash_hi; + assignment.witness(component.W(component_type::HASH_LO), start_row_index + cur) = hash_lo; + if( j == 0){ + // HEADER + assignment.witness(component.W(component_type::TAG), start_row_index + cur) = 0; + assignment.witness(component.W(component_type::INDEX), start_row_index + cur) = 0; + assignment.witness(component.W(component_type::IS_OPCODE), start_row_index + cur) = 0; + push_size = 0; + } else { + + // BYTE + assignment.witness(component.W(component_type::TAG), start_row_index + cur) = 1; + assignment.witness(component.W(component_type::INDEX), start_row_index + cur) = j-1; + if(push_size == 0){ + assignment.witness(component.W(component_type::IS_OPCODE), start_row_index + cur) = 1; + if(byte > 0x5f && byte < 0x80) push_size = byte - 0x5f; + } else { + assignment.witness(component.W(component_type::IS_OPCODE), start_row_index + cur) = 0; + push_size--; + } + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_zkevm_bytecode_table::result_type generate_circuit( + const plonk_zkevm_bytecode_table &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_zkevm_bytecode_table::input_type + &instance_input, + const std::size_t start_row_index + ) { + using component_type = plonk_zkevm_bytecode_table; + using var = typename component_type::var; + + bp.register_dynamic_table("zkevm_bytecode"); + std::size_t selector_index = bp.get_dynamic_lookup_table_selector(); + assignment.enable_selector(selector_index, start_row_index, start_row_index + component.rows_amount - 1); + + crypto3::zk::snark::plonk_lookup_table bytecode_table; + bytecode_table.tag_index = selector_index; + bytecode_table.columns_number = 6;// tag, index, value, length, hash_hi, hash_lo + bytecode_table.lookup_options = {{ + var(component.W(component_type::TAG), 0, true), + var(component.W(component_type::INDEX), 0, true), + var(component.W(component_type::VALUE), 0, true), + var(component.W(component_type::IS_OPCODE), 0, true), + var(component.W(component_type::HASH_HI), 0, true), + var(component.W(component_type::HASH_LO), 0, true) + }}; + bp.define_dynamic_table("zkevm_bytecode", bytecode_table); + + return typename component_type::result_type(component, start_row_index); + } + + template + class zkevm_bytecode; + + template + class zkevm_bytecode, BlueprintFieldType> + : public plonk_component + { + public: + using bytecode_table_component_type = plonk_zkevm_bytecode_table; + + // Named witness columns + static constexpr std::size_t PUSH_SIZE = 6; + static constexpr std::size_t VALUE_RLC = 7; + static constexpr std::size_t LENGTH_LEFT = 8; + static constexpr std::size_t RLC_CHALLENGE = 9; + + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t max_bytecode_size; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return zkevm_bytecode::gates_amount + zkevm_bytecode::lookup_gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, std::size_t max_bytecode_size) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(11)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t max_bytecode_size) { + return max_bytecode_size; + } + + constexpr static const std::size_t gates_amount = 1; + constexpr static const std::size_t lookup_gates_amount = 1; + std::size_t rows_amount = max_bytecode_size; + + struct input_type { + std::vector> bytecodes; // EVM contracts bytecodes + std::vector> bytecode_hashes; // hi, lo parts for keccak. It'll be only one value if we'll use poseidon + var rlc_challenge; + std::size_t full_size; + + input_type( + const std::vector> &_bytecodes, + const std::vector> &_bytecode_hashes, + const var& _rlc_challenge + ) : bytecodes(_bytecodes), bytecode_hashes(_bytecode_hashes), rlc_challenge(_rlc_challenge), full_size(0) { + BOOST_ASSERT(_bytecodes.size() == _bytecode_hashes.size()); + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + full_size += bytecodes[i].size(); + } + } + + std::vector> all_vars() { + std::vector> result; + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + for( std::size_t j = 0; j < bytecodes[i].size(); j++ ){ + result.push_back(bytecodes[i][j]); + } + } + return result; + } + }; + + struct result_type { + result_type(const zkevm_bytecode &component, std::size_t start_row_index) { + } + + std::vector> all_vars() { + std::vector> result; + return result; + } + }; + + template + explicit zkevm_bytecode(ContainerType witness, std::size_t _max_bytecode_size) : + component_type(witness, {}, {}, get_manifest()), max_bytecode_size(_max_bytecode_size) + {}; + + template + zkevm_bytecode(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t _max_bytecode_size + ) : component_type(witness, constant, public_input, get_manifest()), max_bytecode_size(_max_bytecode_size) {}; + + zkevm_bytecode( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _max_bytecode_size + ) : component_type(witnesses, constants, public_inputs, get_manifest()), max_bytecode_size(_max_bytecode_size){}; + + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["byte_range_table/full"] = 0; // REQUIRED_TABLE + lookup_tables["zkevm_opcodes/full"] = 0; // REQUIRED_TABLE + return lookup_tables; + } + }; + + template + using plonk_zkevm_bytecode = + zkevm_bytecode, BlueprintFieldType>; + + template + typename plonk_zkevm_bytecode::result_type generate_assignments( + const plonk_zkevm_bytecode &component, + assignment> + &assignment, + const typename plonk_zkevm_bytecode::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_zkevm_bytecode; + using bytecode_table_component_type = typename component_type::bytecode_table_component_type; + using value_type = typename BlueprintFieldType::value_type; + + bytecode_table_component_type bytecode_table( + {component.W(0),component.W(1), component.W(2), component.W(3), component.W(4), component.W(5)}, {}, {}, + component.max_bytecode_size + ); + typename bytecode_table_component_type::input_type table_input( + instance_input.bytecodes, + instance_input.bytecode_hashes + ); + + generate_assignments(bytecode_table, assignment, table_input, start_row_index); + + value_type rlc_challenge = var_value(assignment, instance_input.rlc_challenge); + + std::size_t cur = 0; + for(std::size_t i = 0; i < instance_input.bytecodes.size(); i++){ + value_type push_size = 0; + for(std::size_t j = 0; j < instance_input.bytecodes[i].size(); j++, cur++){ + auto byte = var_value(assignment, instance_input.bytecodes[i][j]); + assignment.witness(component.W(component_type::RLC_CHALLENGE), start_row_index + cur) = rlc_challenge; + if( j == 0){ + // HEADER + assignment.witness(component.W(component_type::PUSH_SIZE), start_row_index + cur) = 0; + assignment.witness(component.W(component_type::LENGTH_LEFT), start_row_index + cur ) = var_value(assignment, instance_input.bytecodes[i][j]); + assignment.witness(component.W(component_type::VALUE_RLC), start_row_index + cur) = 0; + push_size = 0; + } else { + // BYTE + assignment.witness(component.W(component_type::LENGTH_LEFT), start_row_index + cur ) = assignment.witness(component.W(component_type::LENGTH_LEFT), start_row_index + cur - 1) - 1; + if(push_size == 0){ + if(byte > 0x5f && byte < 0x80) push_size = byte - 0x5f; + } else { + push_size--; + } + assignment.witness(component.W(component_type::PUSH_SIZE), start_row_index + cur) = push_size; + assignment.witness(component.W(component_type::VALUE_RLC), start_row_index + cur) = assignment.witness(component.W(component_type::VALUE_RLC), start_row_index + cur - 1) * rlc_challenge + assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index + cur); + } + } + } + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_zkevm_bytecode &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_zkevm_bytecode::input_type + &instance_input, + const typename lookup_library::left_reserved_type &lookup_tables_indices, + std::size_t start_row_index + ) { + using component_type = plonk_zkevm_bytecode; + using var = typename component_type::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using bytecode_table_component_type = plonk_zkevm_bytecode_table; + + bytecode_table_component_type bytecode_table( + {component.W(0),component.W(1), component.W(2), component.W(3), component.W(4), component.W(5)}, {}, {}, + component.max_bytecode_size + ); + typename bytecode_table_component_type::input_type table_input( + instance_input.bytecodes, + instance_input.bytecode_hashes + ); + + generate_circuit(bytecode_table, bp, assignment, table_input, start_row_index); + + // Named witness columns + std::size_t TAG = bytecode_table.W(bytecode_table_component_type::TAG); + std::size_t INDEX = bytecode_table.W(bytecode_table_component_type::INDEX); + std::size_t VALUE = bytecode_table.W(bytecode_table_component_type::VALUE); + std::size_t IS_OPCODE = bytecode_table.W(bytecode_table_component_type::IS_OPCODE); + std::size_t HASH_HI = bytecode_table.W(bytecode_table_component_type::HASH_HI); + std::size_t HASH_LO = bytecode_table.W(bytecode_table_component_type::HASH_LO); + std::size_t PUSH_SIZE = component.W(component_type::PUSH_SIZE); + std::size_t LENGTH_LEFT = component.W(component_type::LENGTH_LEFT); + std::size_t VALUE_RLC = component.W(component_type::VALUE_RLC); + std::size_t RLC_CHALLENGE = component.W(component_type::RLC_CHALLENGE); + + var tag = var(TAG, 0, true); + var tag_prev = var(TAG, -1, true); + var tag_next = var(TAG, 1, true); + var index = var(INDEX, 0, true); + var index_next = var(INDEX, 1, true); + var value = var(VALUE, 0, true); + var length_left = var(LENGTH_LEFT, 0, true); + var length_left_next = var(LENGTH_LEFT, 1, true); + var is_opcode = var(IS_OPCODE, 0, true); + var is_opcode_next = var(IS_OPCODE, 1, true); + var push_size = var(PUSH_SIZE, 0, true); + var push_size_next = var(PUSH_SIZE, 1, true); + var hash_hi = var(HASH_HI, 0, true); + var hash_hi_next = var(HASH_HI, 1, true); + var hash_lo = var(HASH_LO, 0, true); + var hash_lo_next = var(HASH_LO, 1, true); + var value_rlc = var(VALUE_RLC, 0, true); + var value_rlc_prev = var(VALUE_RLC, -1, true); + var rlc_challenge = var(RLC_CHALLENGE, 0, true); + var rlc_challenge_prev = var(RLC_CHALLENGE, -1, true); + + std::vector constraints; + constraints.push_back(tag * (tag - 1)); // 0. TAG is zeroes or ones -- maybe there will be third value for non-used rows + constraints.push_back((tag - 1) * (index )); // 1. INDEX for HEADER and unused bytes is zero + constraints.push_back((tag - 1) * (index_next)); // 2. INDEX for first contract byte is zero + constraints.push_back(tag * tag_next * (index_next - index - 1)); // 3. INDEX is incremented for any bytes + constraints.push_back((tag - 1) * (length_left - value)); // 4. In contract header length_left == contract length + constraints.push_back(tag_next * (length_left - length_left_next - 1)); // 5. In contract bytes each row decrement length_left + constraints.push_back(tag * (tag_next - 1) * length_left); // 6. Length_left is zero for last byte in the contract + constraints.push_back(is_opcode * (is_opcode - 1)); // 7. is_opcode is zeroes or ones + constraints.push_back((tag - 1) * is_opcode); // 8. is_opcode on HEADER are zeroes + constraints.push_back((tag - 1) * tag_next * (is_opcode_next - 1)); // 9. Fist is_opcode on BYTE after HEADER is 1 + constraints.push_back(is_opcode_next * push_size); // 11. before opcode push_size is always zero + constraints.push_back(tag_next * (is_opcode_next - 1) * (push_size - push_size_next - 1)); // 10. PUSH_SIZE decreases for non-opcodes + constraints.push_back(tag_next * (hash_hi - hash_hi_next)); //12. for all bytes hash is similar to previous + constraints.push_back(tag_next * (hash_lo - hash_lo_next)); //13. for all bytes hash is similar to previous + constraints.push_back((tag - 1) * value_rlc); // 14. value_rlc for HEADERS == 0; + constraints.push_back(tag * (value_rlc - value_rlc_prev * rlc_challenge - value)); // 15. for all bytes RLC is correct + constraints.push_back(tag * (rlc_challenge - rlc_challenge_prev)); //16. for each BYTEs rlc_challenge are similar + constraints.push_back((tag-1) * tag_prev * tag_next * (rlc_challenge - rlc_challenge_prev)); //17. rlc doesn't change during contract + + std::vector lookup_constraints; + lookup_constraint_type bytecode_range_check = {lookup_tables_indices.at("byte_range_table/full"), {tag * value}}; + + lookup_constraint_type opcode_constraint = { + lookup_tables_indices.at("zkevm_opcodes/full"), + {value * is_opcode, push_size * is_opcode , is_opcode} + }; + +// lookup_constraint_type hash_table_constraint = { +// lookup_tables_indices.at("zkevm_dynamic/hash_table"), +// {tag * (1 - tag_next) * value_rlc, tag * (1 - tag_next) * value_rlc * index + 1, tag * (1 - tag_next ) * hash_hi, tag * (1 - tag_next) * hash_lo} +// } + + lookup_constraints.push_back(bytecode_range_check); + lookup_constraints.push_back(opcode_constraint); + + std::size_t selector_id = bp.get_dynamic_table_definition("zkevm_bytecode")->lookup_table.tag_index; + bp.add_gate(selector_id, constraints); + bp.add_lookup_gate(selector_id, lookup_constraints); + + return selector_id; + } + + template + void generate_copy_constraints( + const plonk_zkevm_bytecode &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_zkevm_bytecode::input_type + &instance_input, + const std::size_t start_row_index + ) { + // TODO: add copy constraints + } + + template + typename plonk_zkevm_bytecode::result_type generate_circuit( + const plonk_zkevm_bytecode &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_zkevm_bytecode::input_type + &instance_input, + const std::size_t start_row_index + ) { + using component_type = plonk_zkevm_bytecode; + using var = typename component_type::var; + + // Selector id is already enabled by subcomponent + generate_gates(component, bp, assignment, instance_input, bp.get_reserved_indices(), start_row_index); + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + template + class bytecode_table_tester; + + template + class bytecode_table_tester, BlueprintFieldType> + : public plonk_component + { + public: + // Named witness columns -- same with bytecode_table + static constexpr std::size_t TAG = 0; + static constexpr std::size_t INDEX = 1; + static constexpr std::size_t VALUE = 2; + static constexpr std::size_t IS_OPCODE = 3; + static constexpr std::size_t HASH_HI = 4; + static constexpr std::size_t HASH_LO = 5; + + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + std::size_t max_bytecode_size; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return bytecode_table_tester::gates_amount + bytecode_table_tester::lookup_gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, std::size_t max_bytecode_size) { + gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(12)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t max_bytecode_size) { + return max_bytecode_size + 2; + } + + constexpr static const std::size_t gates_amount = 0; + constexpr static const std::size_t lookup_gates_amount = 2; + std::size_t rows_amount = max_bytecode_size + 2; + + struct input_type { + std::vector> bytecodes; // EVM contracts bytecodes + std::vector> bytecode_hashes; // hi, lo parts for keccak. It'll be only one value if we'll use poseidon + std::size_t full_size; + + input_type( + const std::vector> &_bytecodes, + const std::vector> &_bytecode_hashes + ) : bytecodes(_bytecodes), bytecode_hashes(_bytecode_hashes), full_size(0) { + BOOST_ASSERT(_bytecodes.size() == _bytecode_hashes.size()); + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + full_size += bytecodes[i].size(); + } + } + + std::vector> all_vars() { + std::vector> result; + for( std::size_t i = 0; i < bytecodes.size(); i++ ){ + for( std::size_t j = 0; j < bytecodes[i].size(); j++ ){ + result.push_back(bytecodes[i][j]); + } + } + return result; + } + }; + + struct result_type { + result_type(const bytecode_table_tester &component, std::size_t start_row_index) { + } + + std::vector> all_vars() { + std::vector> result; + return result; + } + }; + + template + explicit bytecode_table_tester(ContainerType witness, std::size_t _max_bytecode_size) : + component_type(witness, {}, {}, get_manifest()), max_bytecode_size(_max_bytecode_size) + {}; + + template + bytecode_table_tester(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, + std::size_t _max_bytecode_size + ) : component_type(witness, constant, public_input, get_manifest()), max_bytecode_size(_max_bytecode_size) {}; + + bytecode_table_tester( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t _max_bytecode_size + ) : component_type(witnesses, constants, public_inputs, get_manifest()), max_bytecode_size(_max_bytecode_size){}; + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["zkevm_bytecode"] = 1; // DYNAMIC_TABLE + return lookup_tables; + } + }; + + template + using plonk_bytecode_table_tester = + bytecode_table_tester, BlueprintFieldType>; + + template + typename plonk_bytecode_table_tester::result_type generate_assignments( + const plonk_bytecode_table_tester &component, + assignment> + &assignment, + const typename plonk_bytecode_table_tester::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using bytecode_table_component_type = plonk_zkevm_bytecode_table; + using component_type = plonk_bytecode_table_tester; + using value_type = typename BlueprintFieldType::value_type; + + bytecode_table_component_type bytecode_table( + {component.W(6),component.W(7), component.W(8), component.W(9), component.W(10), component.W(11)}, {}, {}, + component.max_bytecode_size + ); + typename bytecode_table_component_type::input_type table_input( + instance_input.bytecodes, + instance_input.bytecode_hashes + ); + + // Row above + generate_assignments(bytecode_table, assignment, table_input, start_row_index + 1); + assignment.witness(bytecode_table.W(bytecode_table_component_type::TAG), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::TAG), start_row_index + 5); + assignment.witness(bytecode_table.W(bytecode_table_component_type::INDEX), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::INDEX), start_row_index + 5); + assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index + 5); + assignment.witness(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), start_row_index + 5); + assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_HI), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_HI), start_row_index + 5); + assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_LO), start_row_index) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_LO), start_row_index + 5); + + // Row below + assignment.witness(bytecode_table.W(bytecode_table_component_type::TAG), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::TAG), start_row_index + 10); + assignment.witness(bytecode_table.W(bytecode_table_component_type::INDEX), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::INDEX), start_row_index + 10); + assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index + 10); + assignment.witness(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), start_row_index + 10); + assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_HI), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_HI), start_row_index + 10); + assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_LO), start_row_index + component.max_bytecode_size + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_LO), start_row_index + 10); + + // Row aside + assignment.witness(component.W(component_type::TAG), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::TAG), start_row_index + 15); + assignment.witness(component.W(component_type::INDEX), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::INDEX), start_row_index + 15); + assignment.witness(component.W(component_type::VALUE), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::VALUE), start_row_index + 15); + assignment.witness(component.W(component_type::IS_OPCODE), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), start_row_index + 15); + assignment.witness(component.W(component_type::HASH_HI), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_HI), start_row_index + 15); + assignment.witness(component.W(component_type::HASH_LO), start_row_index + 1) = assignment.witness(bytecode_table.W(bytecode_table_component_type::HASH_LO), start_row_index + 15); + + return typename component_type::result_type(component, start_row_index); + } + + template + typename plonk_bytecode_table_tester::result_type generate_circuit( + const plonk_bytecode_table_tester &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bytecode_table_tester::input_type + &instance_input, + const std::size_t start_row_index + ) { + using bytecode_table_component_type = plonk_zkevm_bytecode_table; + using component_type = plonk_bytecode_table_tester; + using value_type = typename BlueprintFieldType::value_type; + using var = typename component_type::var; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + + bytecode_table_component_type bytecode_table( + {component.W(6),component.W(7), component.W(8), component.W(9), component.W(10), component.W(11)}, {}, {}, + component.max_bytecode_size + ); + typename bytecode_table_component_type::input_type table_input( + instance_input.bytecodes, + instance_input.bytecode_hashes + ); + generate_circuit(bytecode_table, bp, assignment, table_input, start_row_index + 1); + + var tag = var(component.W(component_type::TAG), 0, true); + var index = var(component.W(component_type::INDEX), 0, true); + var value = var(component.W(component_type::VALUE), 0, true); + var is_opcode = var(component.W(component_type::IS_OPCODE), 0, true); + var hash_hi = var(component.W(component_type::HASH_HI), 0, true); + var hash_lo = var(component.W(component_type::HASH_LO), 0, true); + + var tag2 = var(bytecode_table.W(bytecode_table_component_type::TAG), 0, true); + var index2 = var(bytecode_table.W(bytecode_table_component_type::INDEX), 0, true); + var value2 = var(bytecode_table.W(bytecode_table_component_type::VALUE), 0, true); + var is_opcode2 = var(bytecode_table.W(bytecode_table_component_type::IS_OPCODE), 0, true); + var hash_hi2 = var(bytecode_table.W(bytecode_table_component_type::HASH_HI), 0, true); + var hash_lo2 = var(bytecode_table.W(bytecode_table_component_type::HASH_LO), 0, true); + + auto &lookup_tables_indices = bp.get_reserved_indices(); + lookup_constraint_type constraint_above = {lookup_tables_indices.at("zkevm_bytecode"), {tag2, index2, value2, is_opcode2, hash_hi2, hash_lo2}}; + std::size_t selector_above = bp.add_lookup_gate({constraint_above}); + assignment.enable_selector(selector_above, start_row_index, start_row_index); + assignment.enable_selector(selector_above, start_row_index + component.max_bytecode_size + 1, start_row_index + component.max_bytecode_size + 1); + + lookup_constraint_type constraint_aside = {lookup_tables_indices.at("zkevm_bytecode"), {tag, index, value, is_opcode, hash_hi, hash_lo}}; + std::size_t selector_aside = bp.add_lookup_gate({constraint_aside}); + assignment.enable_selector(selector_above, start_row_index+1, start_row_index+1); + + return typename component_type::result_type(component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp new file mode 100644 index 000000000..cbf2bfe91 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp @@ -0,0 +1,169 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + + template + class zkevm_add_sub_operation : public zkevm_operation { + public: + using op_type = zkevm_operation; + using gate_class = typename op_type::gate_class; + using constraint_type = typename op_type::constraint_type; + using zkevm_circuit_type = typename op_type::zkevm_circuit_type; + using assignment_type = typename op_type::assignment_type; + using value_type = typename BlueprintFieldType::value_type; + using var = typename op_type::var; + + zkevm_add_sub_operation(bool _is_add) : is_add(_is_add) {} + + bool is_add; + + constexpr static const std::size_t carry_amount = 16 / 3 + 1; + constexpr static const value_type two_16 = 65536; + constexpr static const value_type two_32 = 4294967296; + constexpr static const value_type two_48 = 281474976710656; + + std::map> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + std::vector constraints; + constexpr const std::size_t chunk_amount = 16; + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + auto var_gen = [&witness_cols](std::size_t i, int32_t offset = 0) { + return zkevm_operation::var_gen(witness_cols, i, offset); + }; + constraint_type position = zkevm_circuit.get_opcode_row_constraint(1, this->rows_amount()); + auto constraint_gen = [&constraints, &position] + (var a_0, var a_1, var a_2, + var b_0, var b_1, var b_2, + var r_0, var r_1, var r_2, + var last_carry, var result_carry, bool first_constraint = false) { + if (first_constraint) { + // no last carry for first constraint + constraints.push_back( + position * ( + (a_0 + b_0) + (a_1 + b_1) * two_16 + (a_2 + b_2) * two_32 + - r_0 - r_1 * two_16 - r_2 * two_32 - result_carry * two_48)); + + } else { + constraints.push_back( + position * ( + last_carry + (a_0 + b_0) + (a_1 + b_1) * two_16 + (a_2 + b_2) * two_32 + - r_0 - r_1 * two_16 - r_2 * two_32 - result_carry * two_48)); + } + constraints.push_back(position * result_carry * (result_carry - 1)); + }; + auto last_constraint_gen = [&constraints, &position] + (var a_0, var b_0, var r_0, var last_carry, var result_carry) { + constraints.push_back( + position * (last_carry + a_0 + b_0 - r_0 - result_carry * two_16)); + constraints.push_back(position * result_carry * (result_carry - 1)); + }; + std::vector a_chunks; + std::vector b_chunks; + std::vector r_chunks; + for (std::size_t i = 0; i < chunk_amount; i++) { + a_chunks.push_back(var_gen(i, -1)); + b_chunks.push_back(var_gen(i, 0)); + r_chunks.push_back(var_gen(i, +1)); + } + std::vector r_carry; + for (std::size_t i = 0; i < carry_amount; i++) { + r_carry.push_back(var_gen(i + chunk_amount, +1)); + } + // special first constraint + constraint_gen(a_chunks[0], a_chunks[1], a_chunks[2], + b_chunks[0], b_chunks[1], b_chunks[2], + r_chunks[0], r_chunks[1], r_chunks[2], + r_carry[0], r_carry[0], true); + for (std::size_t i = 1; i < carry_amount - 1; i++) { + constraint_gen(a_chunks[3 * i], a_chunks[3 * i + 1], a_chunks[3 * i + 2], + b_chunks[3 * i], b_chunks[3 * i + 1], b_chunks[3 * i + 2], + r_chunks[3 * i], r_chunks[3 * i + 1], r_chunks[3 * i + 2], + r_carry[i - 1], r_carry[i]); + } + last_constraint_gen(a_chunks[3 * (carry_amount - 1)], b_chunks[3 * (carry_amount - 1)], + r_chunks[3 * (carry_amount - 1)], + r_carry[carry_amount - 2], r_carry[carry_amount - 1]); + return {{gate_class::MIDDLE_OP, constraints}}; + } + + void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine) override { + zkevm_stack &stack = machine.stack; + using word_type = typename zkevm_stack::word_type; + word_type a = stack.pop(); + word_type b = stack.pop(); + word_type result = is_add ? a + b : a - b; + // TODO: after memory logic would become more complicated here + if (!is_add) { + std::swap(result, a); + } + const std::vector a_chunks = zkevm_word_to_field_element(a); + const std::vector b_chunks = zkevm_word_to_field_element(b); + const std::vector r_chunks = zkevm_word_to_field_element(result); + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + assignment_type &assignment = zkevm_circuit.get_assignment(); + const std::size_t curr_row = zkevm_circuit.get_current_row(); + // TODO: replace with memory access, which would also do range checks! + for (std::size_t i = 0; i < a_chunks.size(); i++) { + assignment.witness(witness_cols[i], curr_row) = a_chunks[i]; + } + for (std::size_t i = 0; i < b_chunks.size(); i++) { + assignment.witness(witness_cols[i], curr_row + 1) = b_chunks[i]; + } + for (std::size_t i = 0; i < r_chunks.size(); i++) { + assignment.witness(witness_cols[i], curr_row + 2) = r_chunks[i]; + } + // we might want to pack carries more efficiently? + bool carry = 0; + for (std::size_t i = 0; i < carry_amount - 1; i++) { + carry = (carry + a_chunks[3 * i ] + b_chunks[3 * i ] + + (a_chunks[3 * i + 1] + b_chunks[3 * i + 1]) * two_16 + + (a_chunks[3 * i + 2] + b_chunks[3 * i + 2]) * two_32 ) >= two_48; + assignment.witness(witness_cols[i + a_chunks.size()], curr_row + 2) = carry; + } + carry = (carry + a_chunks[3 * (carry_amount - 1)] + b_chunks[3 * (carry_amount - 1)]) >= two_16; + assignment.witness(witness_cols[a_chunks.size() + carry_amount - 1], curr_row + 2) = carry; + // reset the machine state; hope that we won't have to do this manually + stack.push(b); + if (is_add) { + stack.push(a); + } else { + stack.push(result); + } + } + + std::size_t rows_amount() override { + return 3; + } + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/div.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/div.hpp new file mode 100644 index 000000000..f170a034f --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/div.hpp @@ -0,0 +1,308 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + + template + class zkevm_div_operation : public zkevm_operation { + public: + using op_type = zkevm_operation; + using gate_class = typename op_type::gate_class; + using constraint_type = typename op_type::constraint_type; + using zkevm_circuit_type = typename op_type::zkevm_circuit_type; + using assignment_type = typename op_type::assignment_type; + using value_type = typename BlueprintFieldType::value_type; + using var = typename op_type::var; + + zkevm_div_operation() {} + + constexpr static const std::size_t carry_amount = 16 / 3 + 1; + constexpr static const value_type two_16 = 65536; + constexpr static const value_type two_32 = 4294967296; + constexpr static const value_type two_48 = 281474976710656; + constexpr static const value_type two_64 = 0x10000000000000000_cppui_modular254; + constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; + constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + + template + T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { + BOOST_ASSERT(chunk_idx < 4); + return chunks[4 * chunk_idx] + chunks[4 * chunk_idx + 1] * two_16 + + chunks[4 * chunk_idx + 2] * two_32 + chunks[4 * chunk_idx + 3] * two_48; + } + + template + T first_carryless_consrtruct( + const std::vector &a_64_chunks, const std::vector &b_64_chunks, + const std::vector &r_64_chunks, const std::vector &q_64_chunks + ) const { + return + r_64_chunks[0] * b_64_chunks[0] + q_64_chunks[0] + + two_64 * (r_64_chunks[0] * b_64_chunks[1] + r_64_chunks[1] * b_64_chunks[0] + q_64_chunks[1]) + - a_64_chunks[0] - two_64 * a_64_chunks[1]; + } + + template + T second_carryless_construct( + const std::vector &a_64_chunks, const std::vector &b_64_chunks, + const std::vector &r_64_chunks, const std::vector &q_64_chunks + ) const { + return + (r_64_chunks[0] * b_64_chunks[2] + r_64_chunks[1] * b_64_chunks[1] + + r_64_chunks[2] * b_64_chunks[0] + q_64_chunks[2] - a_64_chunks[2]) + + two_64 * (r_64_chunks[0] * b_64_chunks[3] + r_64_chunks[1] * b_64_chunks[2] + + r_64_chunks[2] * b_64_chunks[1] + r_64_chunks[3] * b_64_chunks[0] + + q_64_chunks[3] - a_64_chunks[3]); + } + + std::map> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + std::vector constraints; + constexpr const std::size_t chunk_amount = 16; + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + auto var_gen = [&witness_cols](std::size_t i, int32_t offset = 0) { + return zkevm_operation::var_gen(witness_cols, i, offset); + }; + const std::size_t range_check_table_index = + zkevm_circuit.get_circuit().get_reserved_indices().at("chunk_16_bits/full"); + constraint_type position_1 = zkevm_circuit.get_opcode_row_constraint(2, this->rows_amount()); + std::vector a_chunks; + std::vector b_chunks_1; + // we have two different constraints at two different positions + // first we prove division or zero + std::vector r_chunks_1; + std::vector q_chunks_1; + for (std::size_t i = 0; i < chunk_amount; i++) { + a_chunks.push_back(var_gen(i, -1)); + b_chunks_1.push_back(var_gen(i, 0)); + r_chunks_1.push_back(var_gen(i, +1)); + q_chunks_1.push_back(var_gen(i + chunk_amount, +1)); + } + std::vector c_1_chunks; + std::vector c_3_chunks; + for (std::size_t i = chunk_amount; i < chunk_amount + 4; i++) { + c_1_chunks.push_back(var_gen(i, -1)); + c_3_chunks.push_back(var_gen(i, 0)); + } + var c_2 = var_gen(chunk_amount + 4, -1); + var c_4 = var_gen(chunk_amount + 4, 0); + var b_sum_inverse_1 = var_gen(chunk_amount + 5, 0); + std::vector a_64_chunks = { + chunk_sum_64(a_chunks, 0), + chunk_sum_64(a_chunks, 1), + chunk_sum_64(a_chunks, 2), + chunk_sum_64(a_chunks, 3) + }; + std::vector b_64_chunks_1 = { + chunk_sum_64(b_chunks_1, 0), + chunk_sum_64(b_chunks_1, 1), + chunk_sum_64(b_chunks_1, 2), + chunk_sum_64(b_chunks_1, 3) + }; + std::vector r_64_chunks_1 = { + chunk_sum_64(r_chunks_1, 0), + chunk_sum_64(r_chunks_1, 1), + chunk_sum_64(r_chunks_1, 2), + chunk_sum_64(r_chunks_1, 3) + }; + std::vector q_64_chunks_1 = { + chunk_sum_64(q_chunks_1, 0), + chunk_sum_64(q_chunks_1, 1), + chunk_sum_64(q_chunks_1, 2), + chunk_sum_64(q_chunks_1, 3) + }; + constraint_type c_1_64 = chunk_sum_64(c_1_chunks, 0); + constraint_type c_3_64 = chunk_sum_64(c_3_chunks, 0); + // inverse or zero for b_sum_inverse + constraint_type b_sum_1; + for (std::size_t i = 0; i < chunk_amount; i++) { + b_sum_1 += b_chunks_1[i]; + } + constraints.push_back(position_1 * b_sum_inverse_1 * (b_sum_inverse_1 * b_sum_1 - 1)); + constraints.push_back(position_1 * b_sum_1 * (b_sum_inverse_1 * b_sum_1 - 1)); + // prove that the multiplication + addition is correct + constraint_type first_carryless = first_carryless_consrtruct( + a_64_chunks, b_64_chunks_1, r_64_chunks_1, q_64_chunks_1); + constraints.push_back(position_1 * (first_carryless - c_1_64 * two128 - c_2 * two192)); + constraint_type second_carryless = second_carryless_construct( + a_64_chunks, b_64_chunks_1, r_64_chunks_1, q_64_chunks_1); + constraints.push_back( + position_1 * (second_carryless + c_1_64 + c_2 * two_64 - c_3_64 * two128 - c_4 * two192)); + // add constraints for c_2/c_4: c_2 is 0/1, c_4 is 0/1/2/3 + constraints.push_back(position_1 * c_2 * (c_2 - 1)); + constraints.push_back(position_1 * c_4 * (c_4 - 1) * (c_4 - 2) * (c_4 - 3)); + // TODO: figure out how to add lookup constraints to constrain chunks of q + // force r = 0 if b = 0 + constraint_type b_zero = 1 - b_sum_inverse_1 * b_sum_1; + for (std::size_t i = 0; i < chunk_amount; i++) { + constraints.push_back(position_1 * b_zero * r_chunks_1[i]); + } + + // prove that q < result or b = r = 0 + // note that in this case we do not care about the value of q in this case and can + // just set it to be equal to a + constraint_type position_2 = zkevm_circuit.get_opcode_row_constraint(1, this->rows_amount()); + std::vector b_chunks_2; + std::vector q_chunks_2; + for (std::size_t i = 0; i < chunk_amount; i++) { + b_chunks_2.push_back(var_gen(i, -1)); + } + var b_sum_inverse_2 = var_gen(chunk_amount + 5, -1); + for (std::size_t i = chunk_amount; i < 2 * chunk_amount; i++) { + q_chunks_2.push_back(var_gen(i, 0)); + } + std::vector t; + std::vector v; + for (std::size_t i = 0; i < chunk_amount; i++) { + t.push_back(var_gen(i, +1)); + } + for (std::size_t i = chunk_amount; i < 2 * chunk_amount; i++) { + v.push_back(var_gen(i, +1)); + } + constraint_type b_sum_2; + for (std::size_t i = 0; i < chunk_amount; i++) { + b_sum_2 += b_chunks_2[i]; + } + constraint_type b_nonzero = b_sum_inverse_2 * b_sum_2; + // t_i = t_{i+1} + (1 - t_{i+1}) * v_i * (r_i - q_i) + // and v_i is inverse or zero of b_i - q_i + // first constraint is special as we start from zero: t_{-1} = 0 + // TODO: figure out how to add lookup constraints to constrain delta + constraint_type delta = b_chunks_2[chunk_amount - 1] - q_chunks_2[chunk_amount - 1]; + constraints.push_back(position_2 * b_nonzero * (t[chunk_amount - 1] - v[chunk_amount - 1] * delta)); + constraints.push_back(position_2 * b_nonzero * v[chunk_amount - 1] * (v[chunk_amount - 1] * delta - 1)); + constraints.push_back(position_2 * b_nonzero * delta * (v[chunk_amount - 1] * delta - 1)); + for (int32_t i = chunk_amount - 2; i >= 0; i--) { + delta = b_chunks_2[i] - q_chunks_2[i]; + constraints.push_back(position_2 * b_nonzero * (t[i] - t[i + 1] - (1 - t[i + 1]) * v[i] * delta)); + constraints.push_back(position_2 * b_nonzero * (v[i] * (v[i] * delta - 1))); + constraints.push_back(position_2 * b_nonzero * (delta * (v[i] * delta - 1))); + } + // last t should be 1, as we have a strict inequality + constraints.push_back(position_2 * b_nonzero * (t[0] - 1)); + + return {{gate_class::MIDDLE_OP, constraints}}; + } + + void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine) override { + using word_type = typename zkevm_stack::word_type; + zkevm_stack &stack = machine.stack; + word_type a = stack.pop(); + word_type b = stack.pop(); + using integral_type = boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<257>>; + integral_type result_integral = b != 0u ? integral_type(a) / integral_type(b) : 0u; + word_type result = word_type::backend_type(result_integral.backend()); + word_type q = b != 0u ? a % b : a; + + const std::vector a_chunks = zkevm_word_to_field_element(a); + const std::vector b_chunks = zkevm_word_to_field_element(b); + const std::vector r_chunks = zkevm_word_to_field_element(result); + const std::vector q_chunks = zkevm_word_to_field_element(q); + const std::size_t chunk_amount = a_chunks.size(); + // note that we don't assign 64-chunks for a/b, as we can build them from 16-chunks with constraints + // under the same logic we only assign the 16-bit chunks for carries + std::vector a_64_chunks, b_64_chunks, r_64_chunks, q_64_chunks; + for (std::size_t i = 0; i < 4; i++) { + a_64_chunks.push_back(chunk_sum_64(a_chunks, i)); + b_64_chunks.push_back(chunk_sum_64(b_chunks, i)); + r_64_chunks.push_back(chunk_sum_64(r_chunks, i)); + q_64_chunks.push_back(chunk_sum_64(q_chunks, i)); + } + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + assignment_type &assignment = zkevm_circuit.get_assignment(); + const std::size_t curr_row = zkevm_circuit.get_current_row(); + // caluclate first row carries + auto first_row_carries = + first_carryless_consrtruct(a_64_chunks, b_64_chunks, r_64_chunks, q_64_chunks).data >> 128; + value_type c_1 = static_cast(first_row_carries & (two_64 - 1).data); + value_type c_2 = static_cast(first_row_carries >> 64); + std::vector c_1_chunks = chunk_64_to_16(c_1); + // no need for c_2 chunks as there is only a single chunk + auto second_row_carries = + (second_carryless_construct(a_64_chunks, b_64_chunks, r_64_chunks, q_64_chunks) + + c_1 + c_2 * two_64).data >> 128; + value_type c_3 = static_cast(second_row_carries & (two_64 - 1).data); + value_type c_4 = static_cast(second_row_carries >> 64); + std::vector c_3_chunks = chunk_64_to_16(c_3); + value_type b_sum = std::accumulate(b_chunks.begin(), b_chunks.end(), value_type(0)); + // TODO: replace with memory access, which would also do range checks! + // also we can pack slightly more effectively + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row) = a_chunks[i]; + } + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row) = c_1_chunks[i]; + } + assignment.witness(witness_cols[4 + chunk_amount], curr_row) = c_2; + + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row + 1) = b_chunks[i]; + } + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row + 1) = c_3_chunks[i]; + } + assignment.witness(witness_cols[4 + chunk_amount], curr_row + 1) = c_4; + assignment.witness(witness_cols[5 + chunk_amount], curr_row + 1) = + b_sum == 0 ? 0 : b_sum.inversed(); + + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row + 2) = r_chunks[i]; + } + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row + 2) = q_chunks[i]; + } + // comparison bit calculations + bool t_val = false; + for (int32_t i = chunk_amount - 1; i >= 0; i--) { + assignment.witness(witness_cols[i], curr_row + 3) = + t_val = t_val || (b_chunks[i] > q_chunks[i]); + } + // inverse calculations + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row + 3) = + (b_chunks[i] - q_chunks[i]) != 0 ? + 1 * (b_chunks[i] - q_chunks[i]).inversed() + : 0; + } + + // reset the machine state; hope that we won't have to do this manually + stack.push(b); + stack.push(a); + } + + std::size_t rows_amount() override { + return 4; + } + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp new file mode 100644 index 000000000..a08c25d60 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp @@ -0,0 +1,106 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + + template + class zkevm_iszero_operation : public zkevm_operation { + public: + using op_type = zkevm_operation; + using gate_class = typename op_type::gate_class; + using constraint_type = typename op_type::constraint_type; + using zkevm_circuit_type = typename op_type::zkevm_circuit_type; + using assignment_type = typename op_type::assignment_type; + using value_type = typename BlueprintFieldType::value_type; + using var = typename op_type::var; + + zkevm_iszero_operation() = default; + + std::map> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + std::vector constraints; + constexpr const std::size_t chunk_amount = 16; + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + auto var_gen = [&witness_cols](std::size_t i, int32_t offset = 0) { + return zkevm_operation::var_gen(witness_cols, i, offset); + }; + constraint_type chunk_sum = var_gen(0); + std::size_t i = 1; + for (; i < chunk_amount; i++) { + chunk_sum += var_gen(i); + } + var result = var_gen(i++); + for (; i < 2 * chunk_amount; i++) { + // force other chunks to be zero + constraints.emplace_back(var_gen(i)); + } + var chunk_sum_inverse = var_gen(i); + constraints.emplace_back(chunk_sum * chunk_sum_inverse + result - 1); + constraints.emplace_back(chunk_sum * result); + return {{gate_class::MIDDLE_OP, constraints}}; + } + + void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine) override { + zkevm_stack &stack = machine.stack; + using word_type = typename zkevm_stack::word_type; + word_type a = stack.pop(); + const std::vector chunks = zkevm_word_to_field_element(a); + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + assignment_type &assignment = zkevm_circuit.get_assignment(); + std::size_t i = 0; + const std::size_t curr_row = zkevm_circuit.get_current_row(); + // TODO: replace with memory access + for (; i < chunks.size(); i++) { + assignment.witness(witness_cols[i], curr_row) = chunks[i]; + } + if (a == 0u) { + assignment.witness(witness_cols[i], curr_row) = 1; + } else { + assignment.witness(witness_cols[i], curr_row) = 0; + } + i++; + for (; i < 2 * chunks.size(); i++) { + assignment.witness(witness_cols[i], curr_row) = 0; + } + const value_type chunk_sum = std::accumulate(chunks.begin(), chunks.end(), value_type::zero()); + assignment.witness(witness_cols[i], curr_row) = + chunk_sum == 0 ? value_type::zero() : value_type::one() * chunk_sum.inversed(); + // reset the machine state; hope that we won't have to do this manually + stack.push(a); + } + + std::size_t rows_amount() override { + return 1; + } + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp new file mode 100644 index 000000000..e0ff3d2b5 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp @@ -0,0 +1,208 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + + template + class zkevm_mul_operation : public zkevm_operation { + public: + using op_type = zkevm_operation; + using gate_class = typename op_type::gate_class; + using constraint_type = typename op_type::constraint_type; + using zkevm_circuit_type = typename op_type::zkevm_circuit_type; + using assignment_type = typename op_type::assignment_type; + using value_type = typename BlueprintFieldType::value_type; + using var = typename op_type::var; + + zkevm_mul_operation() {} + + constexpr static const value_type two_16 = 65536; + constexpr static const value_type two_32 = 4294967296; + constexpr static const value_type two_48 = 281474976710656; + constexpr static const value_type two_64 = 0x10000000000000000_cppui_modular254; + constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; + constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + + template + T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { + BOOST_ASSERT(chunk_idx < 4); + return chunks[4 * chunk_idx] + chunks[4 * chunk_idx + 1] * two_16 + + chunks[4 * chunk_idx + 2] * two_32 + chunks[4 * chunk_idx + 3] * two_48; + } + + template + T first_carryless_consrtruct( + const std::vector &a_64_chunks, const std::vector &b_64_chunks, + const std::vector &r_64_chunks + ) const { + return + a_64_chunks[0] * b_64_chunks[0] + + two_64 * (a_64_chunks[0] * b_64_chunks[1] + a_64_chunks[1] * b_64_chunks[0]) + - r_64_chunks[0] - two_64 * r_64_chunks[1]; + } + + template + T second_carryless_construct( + const std::vector &a_64_chunks, const std::vector &b_64_chunks, + const std::vector &r_64_chunks + ) { + return + (a_64_chunks[0] * b_64_chunks[2] + a_64_chunks[1] * b_64_chunks[1] + + a_64_chunks[2] * b_64_chunks[0] - r_64_chunks[2]) + + two_64 * (a_64_chunks[0] * b_64_chunks[3] + a_64_chunks[1] * b_64_chunks[2] + + a_64_chunks[2] * b_64_chunks[1] + a_64_chunks[3] * b_64_chunks[0] - r_64_chunks[3]); + } + + std::map> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + std::vector constraints; + constexpr const std::size_t chunk_amount = 16; + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + auto var_gen = [&witness_cols](std::size_t i, int32_t offset = 0) { + return zkevm_operation::var_gen(witness_cols, i, offset); + }; + constraint_type position = zkevm_circuit.get_opcode_row_constraint(1, this->rows_amount()); + std::vector a_chunks; + std::vector b_chunks; + std::vector r_chunks; + for (std::size_t i = 0; i < chunk_amount; i++) { + a_chunks.push_back(var_gen(i, -1)); + b_chunks.push_back(var_gen(i, 0)); + r_chunks.push_back(var_gen(i, +1)); + } + std::vector c_1_chunks; + std::vector c_3_chunks; + for (std::size_t i = 0; i < 4; i++) { + c_1_chunks.push_back(var_gen(i + chunk_amount, -1)); + c_3_chunks.push_back(var_gen(i + chunk_amount, 0)); + } + var c_2 = var_gen(chunk_amount, +1); + var c_4 = var_gen(chunk_amount + 1, +1); + std::vector a_64_chunks = { + chunk_sum_64(a_chunks, 0), + chunk_sum_64(a_chunks, 1), + chunk_sum_64(a_chunks, 2), + chunk_sum_64(a_chunks, 3) + }; + std::vector b_64_chunks = { + chunk_sum_64(b_chunks, 0), + chunk_sum_64(b_chunks, 1), + chunk_sum_64(b_chunks, 2), + chunk_sum_64(b_chunks, 3) + }; + std::vector r_64_chunks = { + chunk_sum_64(r_chunks, 0), + chunk_sum_64(r_chunks, 1), + chunk_sum_64(r_chunks, 2), + chunk_sum_64(r_chunks, 3) + }; + constraint_type c_1_64 = chunk_sum_64(c_1_chunks, 0); + constraint_type c_3_64 = chunk_sum_64(c_3_chunks, 0); + constraint_type first_carryless = first_carryless_consrtruct( + a_64_chunks, b_64_chunks, r_64_chunks); + constraints.push_back(position * (first_carryless - c_1_64 * two128 - c_2 * two192)); + constraint_type second_carryless = second_carryless_construct( + a_64_chunks, b_64_chunks, r_64_chunks); + constraints.push_back( + position * (second_carryless + c_1_64 + c_2 * two_64 - c_3_64 * two128 - c_4 * two192)); + // add constraints for c_2/c_4: c_2 is 0/1, c_4 is 0/1/2/3 + constraints.push_back(position * c_2 * (c_2 - 1)); + constraints.push_back(position * c_4 * (c_4 - 1) * (c_4 - 2) * (c_4 - 3)); + + return {{gate_class::MIDDLE_OP, constraints}}; + } + + void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine) override { + zkevm_stack &stack = machine.stack; + using word_type = typename zkevm_stack::word_type; + word_type a = stack.pop(); + word_type b = stack.pop(); + word_type result = a * b; + const std::vector a_chunks = zkevm_word_to_field_element(a); + const std::vector b_chunks = zkevm_word_to_field_element(b); + const std::vector r_chunks = zkevm_word_to_field_element(result); + const std::size_t chunk_amount = a_chunks.size(); + // note that we don't assign 64-chunks for a/b, as we can build them from 16-chunks with constraints + // under the same logic we only assign the 16-bit chunks for carries + std::vector a_64_chunks, b_64_chunks, r_64_chunks; + for (std::size_t i = 0; i < 4; i++) { + a_64_chunks.push_back(chunk_sum_64(a_chunks, i)); + b_64_chunks.push_back(chunk_sum_64(b_chunks, i)); + r_64_chunks.push_back(chunk_sum_64(r_chunks, i)); + } + const std::vector &witness_cols = zkevm_circuit.get_opcode_cols(); + assignment_type &assignment = zkevm_circuit.get_assignment(); + const std::size_t curr_row = zkevm_circuit.get_current_row(); + // caluclate first row carries + auto first_row_carries = + first_carryless_consrtruct(a_64_chunks, b_64_chunks, r_64_chunks).data >> 128; + value_type c_1 = static_cast(first_row_carries & (two_64 - 1).data); + value_type c_2 = static_cast(first_row_carries >> 64); + std::vector c_1_chunks = chunk_64_to_16(c_1); + // no need for c_2 chunks as there is only a single chunk + auto second_row_carries = + (second_carryless_construct(a_64_chunks, b_64_chunks, r_64_chunks) + c_1 + c_2 * two_64).data >> 128; + value_type c_3 = static_cast(second_row_carries & (two_64 - 1).data); + value_type c_4 = static_cast(second_row_carries >> 64); + std::vector c_3_chunks = chunk_64_to_16(c_3); + // TODO: replace with memory access, which would also do range checks! + // also we can pack slightly more effectively + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row) = a_chunks[i]; + } + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row) = c_1_chunks[i]; + } + + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row + 1) = b_chunks[i]; + } + for (std::size_t i = 0; i < 4; i++) { + assignment.witness(witness_cols[i + chunk_amount], curr_row + 1) = c_3_chunks[i]; + } + + for (std::size_t i = 0; i < chunk_amount; i++) { + assignment.witness(witness_cols[i], curr_row + 2) = r_chunks[i]; + } + assignment.witness(witness_cols[chunk_amount], curr_row + 2) = c_2; + assignment.witness(witness_cols[1 + chunk_amount], curr_row + 2) = c_4; + // reset the machine state; hope that we won't have to do this manually + stack.push(b); + stack.push(a); + } + + std::size_t rows_amount() override { + return 3; + } + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp b/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp new file mode 100644 index 000000000..e2af7d322 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp @@ -0,0 +1,68 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include + +namespace nil { + namespace blueprint { + + class zkevm_stack { + public: + using word_type = zkevm_word_type; + + void push(const word_type& word) { + stack.push(word); + } + + word_type pop() { + word_type word = stack.top(); + stack.pop(); + return word; + } + + word_type top() { + return stack.top(); + } + + void swap() { + word_type a = pop(); + word_type b = pop(); + push(a); + push(b); + } + + void dup() { + word_type a = pop(); + push(a); + push(a); + } + private: + std::stack stack; + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/state.hpp b/libs/blueprint/include/nil/blueprint/zkevm/state.hpp new file mode 100644 index 000000000..8f02a5fcf --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/state.hpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + + // we use value_type as a default here in order to have easier time testing weird assignments like -1 + template + struct state_var { + using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; + using assignment_type = nil::blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + using column_type = typename var::column_type; + + std::size_t selector; + column_type type; + T value; + + state_var(const std::size_t selector_, const column_type type_, const T& value_) + : selector(selector_), type(type_), value(value_) {} + + state_var() = default; + + void set_value(const T &new_value) { + value = new_value; + } + + void assign_value(assignment_type &assignment, std::size_t row) const { + switch (type) { + case column_type::witness: + assignment.witness(selector, row) = value; + break; + case column_type::constant: + assignment.constant(selector, row) = value; + break; + case column_type::selector: + assignment.selector(selector, row) = value; + break; + case column_type::public_input: + BOOST_ASSERT("We should not assign a state value to public input column"); + default: + BOOST_ASSERT("Unknown column type"); + } + } + + void set_and_assign_value(assignment_type &assignment, std::size_t row, const T &new_value) { + set_value(new_value); + assign_value(assignment, row); + } + + var variable(int32_t offset = 0) const { + BOOST_ASSERT(offset == 0 || offset == -1 || offset == 1); + return var(selector, offset, true, type); + } + }; + + #define zkevm_STATE_LIST_FOR_TRANSITIONS(X) \ + X(pc) \ + X(stack_size) \ + X(memory_size) \ + X(curr_gas) \ + + // Every variable which should be tracked between rows + template + struct zkevm_state { + using state_var_type = state_var; + using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; + using assignment_type = nil::blueprint::assignment; + // variables which have custom state transitions + #define X(name) state_var_type name; + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + // variables which have generic transition rules and are not handled via + // state transition mechanism + state_var_type step_selection; + state_var_type rows_until_next_op; + state_var_type rows_until_next_op_inv; + + void assign_state(assignment_type &assignment, std::size_t row) const { + #define X(name) name.assign_value(assignment, row); + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + step_selection.assign_value(assignment, row); + rows_until_next_op.assign_value(assignment, row); + rows_until_next_op_inv.assign_value(assignment, row); + } + }; + + struct transition_type { + enum type { + DEFAULT, + ANY, + SAME_VALUE, + DELTA, + NEW_VALUE, + }; + transition_type() + :t(DEFAULT), + value(0){} + + type t; + // either new value or delta; optional + // technically we could do arbirary field values here, but unlikely to be actually required + std::int64_t value; + }; + + std::ostream& operator<<(std::ostream &os, const transition_type &t) { + switch (t.t) { + case transition_type::DEFAULT: + os << "DEFAULT"; + break; + case transition_type::ANY: + os << "ANY"; + break; + case transition_type::SAME_VALUE: + os << "SAME_VALUE"; + break; + case transition_type::DELTA: + os << "DELTA(" << t.value << ")"; + break; + case transition_type::NEW_VALUE: + os << "NEW_VALUE(" << t.value << ")"; + break; + } + return os; + } + + struct zkevm_state_transition { + #define X(name) transition_type name; + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + }; + + zkevm_state_transition generate_frozen_state_transition() { + zkevm_state_transition transition; + #define X(name) transition.name.t = transition_type::SAME_VALUE; + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + return transition; + } + + std::ostream& operator<<(std::ostream &os, const zkevm_state_transition &t) { + #define X(name) os << #name << ": " << t.name << std::endl; + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + return os; + } + + template + std::optional> handle_transition( + const state_var &var, + const transition_type &transition, + std::function( + const state_var&, const transition_type&)> default_handler + ) { + switch (transition.t) { + case transition_type::SAME_VALUE: + return var.variable(+1) - var.variable(); + case transition_type::DELTA: + return var.variable(+1) - var.variable() - transition.value; + case transition_type::NEW_VALUE: + return var.variable(+1) - transition.value; + case transition_type::DEFAULT: + return default_handler(var, transition); + case transition_type::ANY: + return std::nullopt; + } + } + + template + crypto3::zk::snark::plonk_constraint handle_pc_default( + const state_var &var, + const transition_type &transition + ) { + // same as DELTA(1) + return var.variable(+1) - var.variable() - 1; + } + + template + crypto3::zk::snark::plonk_constraint handle_stack_size_default( + const state_var &var, + const transition_type &transition + ) { + // same as SAME + return var.variable(+1) - var.variable(); + } + + template + crypto3::zk::snark::plonk_constraint handle_memory_size_default( + const state_var &var, + const transition_type &transition + ) { + // same as SAME + return var.variable(+1) - var.variable(); + } + + template + crypto3::zk::snark::plonk_constraint handle_curr_gas_default( + const state_var &var, + const transition_type &transition + ) { + // we shouldn't do this? maybe in error cases or testing? + // later should assert this out, currently SAME + return var.variable(+1) - var.variable(); + } + + template + std::vector> generate_transition_constraints( + const zkevm_state &state, + const zkevm_state_transition &transition + ) { + using constraint_type = crypto3::zk::snark::plonk_constraint; + std::vector result; + #define X(name) \ + if (auto constraint = handle_transition( \ + state.name, transition.name, handle_##name##_default)) { \ + result.push_back(*constraint); \ + } + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + return result; + } + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp b/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp new file mode 100644 index 000000000..7a4462dcf --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp @@ -0,0 +1,276 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // activates a witness column based on an input value + // is used to achive dynamic selector behavior + // actual implementation + template + class state_selector; + + template + class state_selector, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + std::size_t options_amount; + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t witness_amount; + std::size_t options_amount; + + public: + gate_manifest_type(std::size_t witness_amount_, std::size_t options_amount_) : + witness_amount(witness_amount_), options_amount(options_amount_) {}; + + bool operator<(gate_manifest_type const& other) const { + return witness_amount < other.witness_amount || + (witness_amount == other.witness_amount && options_amount < other.options_amount); + } + + std::uint32_t gates_amount() const override { + return state_selector::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t options_amount) { + gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, options_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t options_amount) { + manifest_type manifest = manifest_type( + // TODO: make the manifest depend on options_amount + // this requires the manifest rework + std::shared_ptr(new manifest_single_value_param((options_amount + 1) / 2 + 2)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t options_amount) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), options_amount); + const std::string component_name = "state selector component"; + + struct input_type { + var item_index; + std::vector> all_vars() { + return {item_index}; + } + }; + + struct result_type { + result_type(const state_selector &component, std::size_t start_row_index) {} + std::vector> all_vars() { + return {}; + } + }; + + template + explicit state_selector(ContainerType witness, std::size_t options_amount_) : + component_type(witness, {}, {}, get_manifest(options_amount_)), + options_amount(options_amount_) { + + BOOST_ASSERT(this->witness_amount() == (this->options_amount + 1) / 2 + 2); + }; + + template + state_selector(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t options_amount_) : + component_type(witness, constant, public_input, get_manifest(options_amount_)), + options_amount(options_amount_) { + + BOOST_ASSERT(this->witness_amount() == (this->options_amount + 1) / 2 + 2); + }; + + state_selector( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t options_amount_) : + component_type(witnesses, constants, public_inputs, get_manifest(options_amount_)), + options_amount(options_amount_) { + + BOOST_ASSERT(this->witness_amount() == (this->options_amount + 1) / 2 + 2); + }; + + std::vector> generate_constraints() const { + using constraint_type = crypto3::zk::snark::plonk_constraint; + std::vector constraints; + + constraint_type sum_to_one; + constraint_type idx_decompose; + std::size_t idx = 0; + for (std::size_t i = 1; i < this->witness_amount() - 1; i++) { + var curr_var = var(this->W(i), 0, true, var::column_type::witness); + sum_to_one += curr_var; + idx_decompose += idx * curr_var; + idx += 2; + constraints.push_back(curr_var * (curr_var - 1)); + } + sum_to_one -= 1; + constraints.push_back(sum_to_one); + + var pairing_var = var(this->W(this->witness_amount() - 1), 0, true, var::column_type::witness); + idx_decompose += pairing_var; + idx_decompose -= var(this->W(0), 0, true, var::column_type::witness); + constraints.push_back(idx_decompose); + + constraints.push_back(pairing_var * (pairing_var - 1)); + if (options_amount % 2 != 0) { + var last_pair = var(this->W(this->witness_amount() - 2), 0, true, var::column_type::witness); + constraints.push_back(last_pair * pairing_var); + } + + return constraints; + } + + constraint_type option_constraint(std::size_t option) const { + BOOST_ASSERT(option < options_amount); + var option_var = var(this->W(option / 2 + 1), 0, true, var::column_type::witness), + parity_var = var(this->W(this->witness_amount() - 1), 0, true, var::column_type::witness); + if (option % 2 == 0) { + return option_var * (parity_var - 1); + } else { + return option_var * parity_var; + } + } + + var option_variable(std::int32_t offset = 0) const { + return var(this->W(0), offset, true, var::column_type::witness); + } + }; + + template + using plonk_state_selector = + state_selector, + BlueprintFieldType>; + + template + typename plonk_state_selector::result_type generate_assignments( + const plonk_state_selector &component, + assignment> + &assignment, + const typename plonk_state_selector::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_state_selector; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + value_type index = var_value(assignment, instance_input.item_index); + BOOST_ASSERT(index < component.options_amount); + + // calculating this is somehow very unintuitive + const std::size_t pair_index = std::size_t(integral_type(index.data >> 1)); + const integral_type parity = integral_type(index.data & value_type(1).data); + assignment.witness(component.W(0), start_row_index) = index; + for (std::size_t i = 1; i < component.witness_amount() - 1; i++) { + assignment.witness(component.W(i), start_row_index) = 0; + } + assignment.witness(component.W(pair_index + 1), start_row_index) = 1; + assignment.witness(component.W(component.witness_amount() - 1), start_row_index) = value_type(parity); + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_state_selector &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_state_selector::input_type + &instance_input) { + + return bp.add_gate(component.generate_constraints()); + } + + template + void generate_copy_constraints( + const plonk_state_selector &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_state_selector::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_state_selector; + using var = typename component_type::var; + + bp.add_copy_constraint( + {instance_input.item_index, + var(component.W(0), start_row_index, false, var::column_type::witness)}); + } + + template + typename plonk_state_selector::result_type generate_circuit( + const plonk_state_selector &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_state_selector::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_state_selector; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, start_row_index, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp new file mode 100644 index 000000000..5aec15841 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp @@ -0,0 +1,434 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + // abstracts control over column indices + // selectors are already tracked by circuit object by default + // we implement only automatic extension of the amount of colunmns taken for convinience + template + class selector_manager { + public: + using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; + using assignment_type = nil::blueprint::assignment; + using circuit_type = nil::blueprint::circuit; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + + selector_manager(assignment_type &assignment_, circuit_type &circuit_) + : assignment(assignment_), circuit(circuit_), + witness_index(0), constant_index(0) {} + + std::size_t allocate_witess_column() { + if (witness_index >= assignment.witnesses_amount()) { + assignment.resize_witnesses(2 * witness_index); + } + return witness_index++; + } + + std::size_t allocate_constant_column() { + if (constant_index >= assignment.constants_amount()) { + assignment.resize_constants(2 * constant_index); + } + return constant_index++; + } + + template + std::size_t add_gate(const T &args) { + std::size_t selector = circuit.add_gate(args); + if (selector >= assignment.selectors_amount()) { + assignment.resize_selectors(selector + 1); + } + return selector; + } + private: + assignment_type &assignment; + circuit_type &circuit; + std::size_t witness_index; + std::size_t constant_index; + }; + + template + class zkevm_operation; + + template + class zkevm_circuit { + public: + using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; + using assignment_type = nil::blueprint::assignment; + using circuit_type = nil::blueprint::circuit; + using state_var_type = state_var; + using zkevm_state_type = zkevm_state; + using selector_manager_type = selector_manager; + using zkevm_operation_type = zkevm_operation; + using zkevm_opcode_gate_class = typename zkevm_operation::gate_class; + using state_selector_type = components::state_selector; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using value_type = typename BlueprintFieldType::value_type; + using var = typename crypto3::zk::snark::plonk_variable; + + std::map zkevm_circuit_lookup_tables() const { + std::map lookup_tables; + lookup_tables["chunk_16_bits/full"] = 0; + return lookup_tables; + } + + zkevm_circuit(assignment_type &assignment_, circuit_type &circuit_, std::size_t start_row_index_ = 1) + :assignment(assignment_), circuit(circuit_), opcodes_info_instance(opcodes_info::instance()), + sel_manager(assignment_, circuit_), + curr_row(start_row_index_), start_row_index(start_row_index_) { + + BOOST_ASSERT_MSG(start_row_index > 0, + "Start row index must be greater than zero, otherwise some gates would access non-existent rows."); + for (auto &lookup_table : zkevm_circuit_lookup_tables()) { + circuit.reserve_table(lookup_table.first); + } + init_state(); + init_opcodes(); + } + + void assign_state() { + state.assign_state(assignment, curr_row); + } + + void finalize_test() { + finalize(); + // this is done in order for the vizualiser export to work correctly before padding the circuit. + // otherwise constraints try to access non-existent rows + assignment.witness(state_selector->W(0), curr_row) = value_type(0xC0FFEE); + } + + void finalize() { + BOOST_ASSERT_MSG(curr_row != 0, "Row underflow in finalization"); + assignment.enable_selector(end_selector, curr_row - 1); + } + + void assign_opcode(const zkevm_opcode opcode, zkevm_machine_interface &machine) { + auto opcode_it = opcodes.find(opcode); + if (opcode_it == opcodes.end()) { + BOOST_ASSERT_MSG(false, (std::string("Unimplemented opcode: ") + opcode_to_string(opcode)) != ""); + } + opcode_it->second->generate_assignments(*this, machine); + // state management + state.step_selection.value = 1; + state.rows_until_next_op.value = opcode_it->second->rows_amount() - 1; + state.rows_until_next_op_inv.value = + state.rows_until_next_op.value == 0 ? 0 : state.rows_until_next_op.value.inversed(); + advance_rows(opcode, opcode_it->second->rows_amount()); + } + + void advance_rows(const zkevm_opcode opcode, std::size_t rows) { + assignment.enable_selector(middle_selector, curr_row, curr_row + rows - 1); + // TODO: figure out what is going to happen on state change + value_type opcode_val = opcodes_info_instance.get_opcode_value(opcode); + for (std::size_t i = 0; i < rows; i++) { + // TODO: switch to real bytecode + assignment.witness(state_selector->W(0), curr_row) = opcode_val; + components::generate_assignments( + *state_selector, assignment, + {var(state_selector->W(0), curr_row, false, var::column_type::witness)}, curr_row); + assignment.witness(opcode_row_selector->W(0), curr_row) = state.rows_until_next_op.value; + components::generate_assignments( + *opcode_row_selector, assignment, + {var(opcode_row_selector->W(0), curr_row, false, var::column_type::witness)}, curr_row); + assign_state(); + if (i == 0) { + state.step_selection.value = 0; + } + assignment.witness(state_selector->W(0), curr_row) = opcode_val; + state.rows_until_next_op.value = state.rows_until_next_op.value - 1; + state.rows_until_next_op_inv.value = state.rows_until_next_op.value == 0 ? + 0 : state.rows_until_next_op.value.inversed(); + curr_row++; + } + } + + zkevm_state_type &get_state() { + return state; + } + + selector_manager_type &get_selector_manager() { + return sel_manager; + } + + const std::vector &get_state_selector_cols() { + return state_selector_cols; + } + + const std::vector &get_opcode_cols() { + return opcode_cols; + } + + std::size_t get_current_row() const { + return curr_row; + } + + assignment_type &get_assignment() { + return assignment; + } + + circuit_type &get_circuit() { + return circuit; + } + + // for opcode constraints at certain row of opcode execution + // note that rows are counted "backwards", starting from opcode rows amount minus one + // and ending in zero + constraint_type get_opcode_row_constraint(std::size_t row, std::size_t opcode_height) const { + BOOST_ASSERT(row < opcode_height); + var height_var = state.rows_until_next_op.variable(); + var height_var_inv = state.rows_until_next_op_inv.variable(); + // ordering here is important: minimising the degree when possible + if (row == opcode_height - 1) { + return state.step_selection.variable(); + } + if (row == opcode_height - 2) { + return state.step_selection.variable(-1); + } + if (row == 0) { + return 1 - height_var * height_var_inv; + } + // TODO: this is probably possible to optimise + return opcode_row_selector->option_constraint(row); + } + + private: + void init_state() { + state.pc = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 1); + state.stack_size = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 0); + state.memory_size = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 0); + state.curr_gas = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 0); + state.rows_until_next_op = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 0); + state.rows_until_next_op_inv = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 0); + state.step_selection = state_var_type( + sel_manager.allocate_witess_column(), state_var_type::column_type::witness, 1); + } + + std::vector generate_generic_transition_constraints( + std::size_t start_selector, + std::size_t end_selector + ) { + std::vector constraints; + + auto rows_until_next_op_var = state.rows_until_next_op.variable(); + auto rows_until_next_op_prev_var = state.rows_until_next_op.variable(-1); + auto rows_until_next_op_next_var = state.rows_until_next_op.variable(+1); + auto rows_until_next_op_inv_var = state.rows_until_next_op_inv.variable(); + auto rows_until_next_op_inv_prev_var = state.rows_until_next_op_inv.variable(-1); + auto step_selection_var = state.step_selection.variable(); + auto step_selection_next_var = state.step_selection.variable(+1); + auto option_variable = state_selector->option_variable(0); + auto option_variable_prev = state_selector->option_variable(-1); + // inverse or zero for rows_until_next_op/rows_until_next_op_inv + constraints.push_back( + rows_until_next_op_var * rows_until_next_op_var * rows_until_next_op_inv_var + - rows_until_next_op_var); + constraints.push_back( + (rows_until_next_op_var * rows_until_next_op_inv_var - 1) * rows_until_next_op_inv_var); + // rows_until_next_op decrementing (unless we are at the last row of opcode) + constraints.push_back( + rows_until_next_op_var * (rows_until_next_op_next_var - rows_until_next_op_var + 1)); + // step is copied unless new opcode is next + constraints.push_back( + (1 - step_selection_var) * (option_variable - option_variable_prev)); + // new opcode selection is forced if new opcode is next + constraints.push_back( + (1 - rows_until_next_op_inv_prev_var * rows_until_next_op_prev_var) * (1 - step_selection_var)); + // freeze some of the the state variables unless new opcode is next + // or we are at the end of the circuit + auto partial_state_transition_constraints = generate_transition_constraints( + state, generate_frozen_state_transition()); + for (auto constraint : partial_state_transition_constraints) { + constraints.push_back( + (1 - var(end_selector, 0, true, var::column_type::selector)) * + (1 - step_selection_next_var) * constraint); + } + return constraints; + } + + void init_opcodes() { + // add all the implemented opcodes here + opcodes[zkevm_opcode::ISZERO] = std::make_shared>(); + opcodes[zkevm_opcode::ADD] = std::make_shared>(true); + opcodes[zkevm_opcode::SUB] = std::make_shared>(false); + opcodes[zkevm_opcode::MUL] = std::make_shared>(); + opcodes[zkevm_opcode::DIV] = std::make_shared>(); + + std::vector middle_constraints; + std::vector first_constraints; + std::vector last_constraints; + // first step is step selection + first_constraints.push_back(state.step_selection.variable() - 1); + start_selector = sel_manager.add_gate(first_constraints); + // TODO: proper end constraints + end_selector = circuit.add_gate(last_constraints); + + const std::size_t opcodes_amount = opcodes_info_instance.get_opcodes_amount(); + const std::size_t state_selector_cols_amount = + state_selector_type::get_manifest(opcodes_amount).witness_amount->max_value_if_sat(); + for (std::size_t i = 0; i < state_selector_cols_amount; i++) { + state_selector_cols.push_back(sel_manager.allocate_witess_column()); + } + for (std::size_t i = 0; i < max_opcode_cols; i++) { + opcode_cols.push_back(sel_manager.allocate_witess_column()); + } + state_selector = std::make_shared( + state_selector_cols, std::array({}), std::array({}), + opcodes_amount); + + auto state_selector_constraints = state_selector->generate_constraints(); + middle_constraints.insert(middle_constraints.end(), state_selector_constraints.begin(), + state_selector_constraints.end()); + + static constexpr std::size_t max_opcode_height = 20; + const std::size_t opcode_row_selection_cols_amount = + state_selector_type::get_manifest(max_opcode_height).witness_amount->max_value_if_sat(); + for (std::size_t i = 0; i < opcode_row_selection_cols_amount; i++) { + opcode_row_selection_cols.push_back(sel_manager.allocate_witess_column()); + } + opcode_row_selector = std::make_shared( + opcode_row_selection_cols, std::array({}), std::array({}), + max_opcode_height); + auto opcode_row_selector_constraints = opcode_row_selector->generate_constraints(); + middle_constraints.insert(middle_constraints.end(), opcode_row_selector_constraints.begin(), + opcode_row_selector_constraints.end()); + + auto generic_state_transition_constraints = generate_generic_transition_constraints( + start_selector, end_selector); + middle_constraints.insert(middle_constraints.end(), generic_state_transition_constraints.begin(), + generic_state_transition_constraints.end()); + + for (auto opcode_it : opcodes) { + std::size_t opcode_height = opcode_it.second->rows_amount(); + if (opcode_height > max_opcode_height) { + BOOST_ASSERT("Opcode height exceeds maximum, please update max_opcode_height constant."); + } + + std::size_t opcode_num = opcodes_info_instance.get_opcode_value(opcode_it.first); + auto curr_opt_constraint = state_selector->option_constraint(opcode_num); + // force current height to be proper value at the start of the opcode + if (opcode_height == 1) { + // minor optimisation here: we have only a single step so can just set 0 + middle_constraints.push_back(curr_opt_constraint * state.rows_until_next_op.variable()); + } else { + middle_constraints.push_back( + curr_opt_constraint * (state.rows_until_next_op.variable() - (opcode_height - 1)) * + state.step_selection.variable()); + } + + auto opcode_gates = opcode_it.second->generate_gates(*this); + for (auto gate_it : opcode_gates) { + switch (gate_it.first) { + case zkevm_opcode_gate_class::FIRST_OP: + for (auto constraint : gate_it.second) { + middle_constraints.push_back( + curr_opt_constraint * constraint * start_selector); + } + break; + case zkevm_opcode_gate_class::MIDDLE_OP: + for (auto constraint : gate_it.second) { + middle_constraints.push_back( + curr_opt_constraint * constraint); + } + break; + case zkevm_opcode_gate_class::LAST_OP: + BOOST_ASSERT("Unimplemented"); + break; + case zkevm_opcode_gate_class::NOT_LAST_OP: + BOOST_ASSERT("Unimplemented"); + break; + default: + BOOST_ASSERT("Unknown gate class"); + } + } + } + middle_selector = sel_manager.add_gate(middle_constraints); + + assignment.enable_selector(start_selector, curr_row); + assignment.enable_selector(middle_selector, curr_row); + } + + zkevm_state_type state; + // static selectors used to mark the places where the circuit starts/ends + std::size_t start_selector; + std::size_t end_selector; + // dynamic selector: indicates when the circuit is acitve + // currently represented as a selector column; hopefully this is possible to do in practice + std::size_t middle_selector; + // witness columns for opcodes + std::vector opcode_cols; + // dynamic selectors for the state selector circuit + std::vector state_selector_cols; + // columns for selecting specific rows from the opcode + std::vector opcode_row_selection_cols; + // --------------------------------------------------------------------------------------------- + // |Variables below this point are internal to the object and do not go into the actual circuit| + // --------------------------------------------------------------------------------------------- + // reference to the assignment/circuit objects + assignment_type &assignment; + circuit_type &circuit; + // information about opcode metadata (mapping, etc.) + const opcodes_info &opcodes_info_instance; + selector_manager_type sel_manager; + std::shared_ptr state_selector; + std::shared_ptr opcode_row_selector; + // opcode objects + std::map>> opcodes; + // current row maintained between different calls to the circuit object + std::size_t curr_row; + // start and end rows for the circuit; both have to be fixed + std::size_t start_row_index; + std::size_t end_row_index; + + static const std::size_t max_opcode_cols = 64; + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp new file mode 100644 index 000000000..7be562e41 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp @@ -0,0 +1,40 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +namespace nil { + namespace blueprint { + // at the time I am writing this there is no interface to the zkevm machine + // this is a placeholder + class zkevm_machine_interface { + public: + using word_type = zkevm_word_type; + zkevm_stack stack; + }; + } +} diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_opcodes.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_opcodes.hpp new file mode 100644 index 000000000..80ea81e30 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_opcodes.hpp @@ -0,0 +1,389 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include +#include + +namespace nil { + namespace blueprint { + #define ZKEVM_OPCODE_ENUM(X) \ + X(STOP) \ + X(ADD) \ + X(MUL) \ + X(SUB) \ + X(DIV) \ + X(SDIV) \ + X(MOD) \ + X(SMOD) \ + X(ADDMOD) \ + X(MULMOD) \ + X(EXP) \ + X(SIGNEXTEND) \ + X(LT) \ + X(GT) \ + X(SLT) \ + X(SGT) \ + X(EQ) \ + X(ISZERO) \ + X(AND) \ + X(OR) \ + X(XOR) \ + X(NOT) \ + X(BYTE) \ + X(SHL) \ + X(SHR) \ + X(SAR) \ + X(KECCAK256) \ + X(ADDRESS) \ + X(BALANCE) \ + X(ORIGIN) \ + X(CALLER) \ + X(CALLVALUE) \ + X(CALLDATALOAD) \ + X(CALLDATASIZE) \ + X(CALLDATACOPY) \ + X(CODESIZE) \ + X(CODECOPY) \ + X(GASPRICE) \ + X(EXTCODESIZE) \ + X(EXTCODECOPY) \ + X(RETURNDATASIZE) \ + X(RETURNDATACOPY) \ + X(EXTCODEHASH) \ + X(BLOCKHASH) \ + X(COINBASE) \ + X(TIMESTAMP) \ + X(NUMBER) \ + X(PREVRANDAO) \ + X(GASLIMIT) \ + X(CHAINID) \ + X(SELFBALANCE) \ + X(BASEFEE) \ + X(BLOBHASH) \ + X(BLOBBASEFEE) \ + X(POP) \ + X(MLOAD) \ + X(MSTORE) \ + X(MSTORE8) \ + X(SLOAD) \ + X(SSTORE) \ + X(JUMP) \ + X(JUMPI) \ + X(PC) \ + X(MSIZE) \ + X(GAS) \ + X(JUMPDEST) \ + X(TLOAD) \ + X(TSTORE) \ + X(MCOPY) \ + X(PUSH0) \ + X(PUSH1) \ + X(PUSH2) \ + X(PUSH3) \ + X(PUSH4) \ + X(PUSH5) \ + X(PUSH6) \ + X(PUSH7) \ + X(PUSH8) \ + X(PUSH9) \ + X(PUSH10) \ + X(PUSH11) \ + X(PUSH12) \ + X(PUSH13) \ + X(PUSH14) \ + X(PUSH15) \ + X(PUSH16) \ + X(PUSH17) \ + X(PUSH18) \ + X(PUSH19) \ + X(PUSH20) \ + X(PUSH21) \ + X(PUSH22) \ + X(PUSH23) \ + X(PUSH24) \ + X(PUSH25) \ + X(PUSH26) \ + X(PUSH27) \ + X(PUSH28) \ + X(PUSH29) \ + X(PUSH30) \ + X(PUSH31) \ + X(PUSH32) \ + X(DUP1) \ + X(DUP2) \ + X(DUP3) \ + X(DUP4) \ + X(DUP5) \ + X(DUP6) \ + X(DUP7) \ + X(DUP8) \ + X(DUP9) \ + X(DUP10) \ + X(DUP11) \ + X(DUP12) \ + X(DUP13) \ + X(DUP14) \ + X(DUP15) \ + X(DUP16) \ + X(SWAP1) \ + X(SWAP2) \ + X(SWAP3) \ + X(SWAP4) \ + X(SWAP5) \ + X(SWAP6) \ + X(SWAP7) \ + X(SWAP8) \ + X(SWAP9) \ + X(SWAP10) \ + X(SWAP11) \ + X(SWAP12) \ + X(SWAP13) \ + X(SWAP14) \ + X(SWAP15) \ + X(SWAP16) \ + X(LOG0) \ + X(LOG1) \ + X(LOG2) \ + X(LOG3) \ + X(LOG4) \ + X(CREATE) \ + X(CALL) \ + X(CALLCODE) \ + X(RETURN) \ + X(DELEGATECALL) \ + X(CREATE2) \ + X(STATICCALL) \ + X(REVERT) \ + X(INVALID) \ + X(SELFDESTRUCT) // ! please update LAST_ZKEVM_OPCODE below if changing this ! + + enum zkevm_opcode { + #define ENUM_DEF(name) name, + ZKEVM_OPCODE_ENUM(ENUM_DEF) + #undef ENUM_DEF + }; + + // singleton class to hold opcode to byte mapping + struct opcodes_info { + public: + static const opcodes_info& instance() { + static opcodes_info instance; + return instance; + } + + std::size_t get_opcode_value(const zkevm_opcode& opcode) const { + auto it = opcode_to_byte_map.left.find(opcode); + BOOST_ASSERT(it != opcode_to_byte_map.left.end()); + return it->second; + } + + zkevm_opcode get_opcode_from_value(const std::size_t& value) const { + auto it = opcode_to_byte_map.right.find(value); + BOOST_ASSERT(it != opcode_to_byte_map.right.end()); + return it->second; + } + + std::size_t get_opcodes_amount() const { + return opcode_to_byte_map.size(); + } + + boost::bimap, boost::bimaps::set_of> + opcode_to_byte_map; + private: + opcodes_info() { + opcode_to_byte_map.insert({zkevm_opcode::STOP, 0x00}); + opcode_to_byte_map.insert({zkevm_opcode::ADD, 0x01}); + opcode_to_byte_map.insert({zkevm_opcode::MUL, 0x02}); + opcode_to_byte_map.insert({zkevm_opcode::SUB, 0x03}); + opcode_to_byte_map.insert({zkevm_opcode::DIV, 0x04}); + opcode_to_byte_map.insert({zkevm_opcode::SDIV, 0x05}); + opcode_to_byte_map.insert({zkevm_opcode::MOD, 0x06}); + opcode_to_byte_map.insert({zkevm_opcode::SMOD, 0x07}); + opcode_to_byte_map.insert({zkevm_opcode::ADDMOD, 0x08}); + opcode_to_byte_map.insert({zkevm_opcode::MULMOD, 0x09}); + opcode_to_byte_map.insert({zkevm_opcode::EXP, 0x0a}); + opcode_to_byte_map.insert({zkevm_opcode::SIGNEXTEND, 0x0b}); + opcode_to_byte_map.insert({zkevm_opcode::LT, 0x10}); + opcode_to_byte_map.insert({zkevm_opcode::GT, 0x11}); + opcode_to_byte_map.insert({zkevm_opcode::SLT, 0x12}); + opcode_to_byte_map.insert({zkevm_opcode::SGT, 0x13}); + opcode_to_byte_map.insert({zkevm_opcode::EQ, 0x14}); + opcode_to_byte_map.insert({zkevm_opcode::ISZERO, 0x15}); + opcode_to_byte_map.insert({zkevm_opcode::AND, 0x16}); + opcode_to_byte_map.insert({zkevm_opcode::OR, 0x17}); + opcode_to_byte_map.insert({zkevm_opcode::XOR, 0x18}); + opcode_to_byte_map.insert({zkevm_opcode::NOT, 0x19}); + opcode_to_byte_map.insert({zkevm_opcode::BYTE, 0x1a}); + opcode_to_byte_map.insert({zkevm_opcode::SHL, 0x1b}); + opcode_to_byte_map.insert({zkevm_opcode::SHR, 0x1c}); + opcode_to_byte_map.insert({zkevm_opcode::SAR, 0x1d}); + opcode_to_byte_map.insert({zkevm_opcode::KECCAK256, 0x20}); + opcode_to_byte_map.insert({zkevm_opcode::ADDRESS, 0x30}); + opcode_to_byte_map.insert({zkevm_opcode::BALANCE, 0x31}); + opcode_to_byte_map.insert({zkevm_opcode::ORIGIN, 0x32}); + opcode_to_byte_map.insert({zkevm_opcode::CALLER, 0x33}); + opcode_to_byte_map.insert({zkevm_opcode::CALLVALUE, 0x34}); + opcode_to_byte_map.insert({zkevm_opcode::CALLDATALOAD, 0x35}); + opcode_to_byte_map.insert({zkevm_opcode::CALLDATASIZE, 0x36}); + opcode_to_byte_map.insert({zkevm_opcode::CALLDATACOPY, 0x37}); + opcode_to_byte_map.insert({zkevm_opcode::CODESIZE, 0x38}); + opcode_to_byte_map.insert({zkevm_opcode::CODECOPY, 0x39}); + opcode_to_byte_map.insert({zkevm_opcode::GASPRICE, 0x3a}); + opcode_to_byte_map.insert({zkevm_opcode::EXTCODESIZE, 0x3b}); + opcode_to_byte_map.insert({zkevm_opcode::EXTCODECOPY, 0x3c}); + opcode_to_byte_map.insert({zkevm_opcode::RETURNDATASIZE, 0x3d}); + opcode_to_byte_map.insert({zkevm_opcode::RETURNDATACOPY, 0x3e}); + opcode_to_byte_map.insert({zkevm_opcode::EXTCODEHASH, 0x3f}); + opcode_to_byte_map.insert({zkevm_opcode::BLOCKHASH, 0x40}); + opcode_to_byte_map.insert({zkevm_opcode::COINBASE, 0x41}); + opcode_to_byte_map.insert({zkevm_opcode::TIMESTAMP, 0x42}); + opcode_to_byte_map.insert({zkevm_opcode::NUMBER, 0x43}); + opcode_to_byte_map.insert({zkevm_opcode::PREVRANDAO, 0x44}); + opcode_to_byte_map.insert({zkevm_opcode::GASLIMIT, 0x45}); + opcode_to_byte_map.insert({zkevm_opcode::CHAINID, 0x46}); + opcode_to_byte_map.insert({zkevm_opcode::SELFBALANCE, 0x47}); + opcode_to_byte_map.insert({zkevm_opcode::BASEFEE, 0x48}); + opcode_to_byte_map.insert({zkevm_opcode::BLOBHASH, 0x49}); + opcode_to_byte_map.insert({zkevm_opcode::BLOBBASEFEE, 0x4a}); + opcode_to_byte_map.insert({zkevm_opcode::POP, 0x50}); + opcode_to_byte_map.insert({zkevm_opcode::MLOAD, 0x51}); + opcode_to_byte_map.insert({zkevm_opcode::MSTORE, 0x52}); + opcode_to_byte_map.insert({zkevm_opcode::MSTORE8, 0x53}); + opcode_to_byte_map.insert({zkevm_opcode::SLOAD, 0x54}); + opcode_to_byte_map.insert({zkevm_opcode::SSTORE, 0x55}); + opcode_to_byte_map.insert({zkevm_opcode::JUMP, 0x56}); + opcode_to_byte_map.insert({zkevm_opcode::JUMPI, 0x57}); + opcode_to_byte_map.insert({zkevm_opcode::PC, 0x58}); + opcode_to_byte_map.insert({zkevm_opcode::MSIZE, 0x59}); + opcode_to_byte_map.insert({zkevm_opcode::GAS, 0x5a}); + opcode_to_byte_map.insert({zkevm_opcode::JUMPDEST, 0x5b}); + opcode_to_byte_map.insert({zkevm_opcode::TLOAD, 0x5c}); + opcode_to_byte_map.insert({zkevm_opcode::TSTORE, 0x5d}); + opcode_to_byte_map.insert({zkevm_opcode::MCOPY, 0x5e}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH0, 0x5f}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH1, 0x60}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH2, 0x61}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH3, 0x62}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH4, 0x63}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH5, 0x64}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH6, 0x65}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH7, 0x66}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH8, 0x67}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH9, 0x68}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH10, 0x69}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH11, 0x6a}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH12, 0x6b}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH13, 0x6c}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH14, 0x6d}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH15, 0x6e}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH16, 0x6f}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH17, 0x70}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH18, 0x71}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH19, 0x72}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH20, 0x73}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH21, 0x74}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH22, 0x75}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH23, 0x76}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH24, 0x77}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH25, 0x78}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH26, 0x79}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH27, 0x7a}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH28, 0x7b}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH29, 0x7c}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH30, 0x7d}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH31, 0x7e}); + opcode_to_byte_map.insert({zkevm_opcode::PUSH32, 0x7f}); + opcode_to_byte_map.insert({zkevm_opcode::DUP1, 0x80}); + opcode_to_byte_map.insert({zkevm_opcode::DUP2, 0x81}); + opcode_to_byte_map.insert({zkevm_opcode::DUP3, 0x82}); + opcode_to_byte_map.insert({zkevm_opcode::DUP4, 0x83}); + opcode_to_byte_map.insert({zkevm_opcode::DUP5, 0x84}); + opcode_to_byte_map.insert({zkevm_opcode::DUP6, 0x85}); + opcode_to_byte_map.insert({zkevm_opcode::DUP7, 0x86}); + opcode_to_byte_map.insert({zkevm_opcode::DUP8, 0x87}); + opcode_to_byte_map.insert({zkevm_opcode::DUP9, 0x88}); + opcode_to_byte_map.insert({zkevm_opcode::DUP10, 0x89}); + opcode_to_byte_map.insert({zkevm_opcode::DUP11, 0x8a}); + opcode_to_byte_map.insert({zkevm_opcode::DUP12, 0x8b}); + opcode_to_byte_map.insert({zkevm_opcode::DUP13, 0x8c}); + opcode_to_byte_map.insert({zkevm_opcode::DUP14, 0x8d}); + opcode_to_byte_map.insert({zkevm_opcode::DUP15, 0x8e}); + opcode_to_byte_map.insert({zkevm_opcode::DUP16, 0x8f}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP1, 0x90}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP2, 0x91}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP3, 0x92}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP4, 0x93}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP5, 0x94}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP6, 0x95}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP7, 0x96}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP8, 0x97}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP9, 0x98}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP10, 0x99}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP11, 0x9a}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP12, 0x9b}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP13, 0x9c}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP14, 0x9d}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP15, 0x9e}); + opcode_to_byte_map.insert({zkevm_opcode::SWAP16, 0x9f}); + opcode_to_byte_map.insert({zkevm_opcode::LOG0, 0xa0}); + opcode_to_byte_map.insert({zkevm_opcode::LOG1, 0xa1}); + opcode_to_byte_map.insert({zkevm_opcode::LOG2, 0xa2}); + opcode_to_byte_map.insert({zkevm_opcode::LOG3, 0xa3}); + opcode_to_byte_map.insert({zkevm_opcode::LOG4, 0xa4}); + opcode_to_byte_map.insert({zkevm_opcode::CREATE, 0xf0}); + opcode_to_byte_map.insert({zkevm_opcode::CALL, 0xf1}); + opcode_to_byte_map.insert({zkevm_opcode::CALLCODE, 0xf2}); + opcode_to_byte_map.insert({zkevm_opcode::RETURN, 0xf3}); + opcode_to_byte_map.insert({zkevm_opcode::DELEGATECALL, 0xf4}); + opcode_to_byte_map.insert({zkevm_opcode::CREATE2, 0xf5}); + opcode_to_byte_map.insert({zkevm_opcode::STATICCALL, 0xfa}); + opcode_to_byte_map.insert({zkevm_opcode::REVERT, 0xfd}); + opcode_to_byte_map.insert({zkevm_opcode::INVALID, 0xfe}); + opcode_to_byte_map.insert({zkevm_opcode::SELFDESTRUCT, 0xff}); + } + }; + + std::string opcode_to_string(const zkevm_opcode& opcode) { + switch (opcode) { + #define ENUM_DEF(name) case zkevm_opcode::name: return #name; + ZKEVM_OPCODE_ENUM(ENUM_DEF) + #undef ENUM_DEF + } + return "unknown"; + } + + std::ostream& operator<<(std::ostream& os, const zkevm_opcode& opcode) { + #define ENUM_DEF(name) case zkevm_opcode::name: os << #name; break; + switch (opcode) { + ZKEVM_OPCODE_ENUM(ENUM_DEF) + } + #undef ENUM_DEF + return os; + } + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp new file mode 100644 index 000000000..0d62fbf47 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp @@ -0,0 +1,82 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + class zkevm_circuit; + + // interface class for generic zkevm operation + template + class zkevm_operation { + public: + enum class gate_class { + // gate on if the operation is first in the circuit + FIRST_OP, + // gate always on if the operation is executed + MIDDLE_OP, + // gate on if the operation is last in the circuit + LAST_OP, + // gate on if the operation is not last in the circuit + NOT_LAST_OP + }; + + using zkevm_circuit_type = zkevm_circuit; + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using assignment_type = assignment>; + using var = nil::crypto3::zk::snark::plonk_variable; + + zkevm_operation() {} + + virtual ~zkevm_operation() = default; + // note that some parts of the map may be empty + // we expect that most of the operations would only use MIDDLE_OP + virtual std::map> generate_gates(zkevm_circuit_type &zkevm_circuit) = 0; + + virtual void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine); + // should return the same rows amount for everyс operation right now + // here in case we would make it dynamic in the future + virtual std::size_t rows_amount() = 0; + // utility funciton + static var var_gen(const std::vector &witness_cols, std::size_t i, int32_t offset = 0) { + return var(witness_cols[i], offset, true, var::column_type::witness); + }; + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp new file mode 100644 index 000000000..9224795e0 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp @@ -0,0 +1,93 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include + +#include +#include + +namespace nil { + namespace blueprint { + + constexpr static const + boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<257>> zkevm_modulus = + 0x10000000000000000000000000000000000000000000000000000000000000000_cppui_modular257; + + constexpr static const boost::multiprecision::backends::modular_params< + boost::multiprecision::backends::cpp_int_modular_backend<257>> + zkevm_modular_params = zkevm_modulus.backend(); + + typedef boost::multiprecision::number< + boost::multiprecision::backends::modular_adaptor< + boost::multiprecision::backends::cpp_int_modular_backend<257>, + boost::multiprecision::backends::modular_params_ct< + boost::multiprecision::backends::cpp_int_modular_backend<257>, + zkevm_modular_params>>> + zkevm_word_type; + + template + constexpr zkevm_word_type zwordc(const T &value) { + return zkevm_word_type::backend_type(value.backend()); + } + + template + std::vector zkevm_word_to_field_element(const zkevm_word_type &word) { + using value_type = typename BlueprintFieldType::value_type; + std::vector chunks; + constexpr const std::size_t chunk_size = 16; + constexpr const std::size_t num_chunks = 256 / chunk_size; + using integral_type = boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<257>>; + constexpr const integral_type mask = + integral_type((zkevm_word_type(1) << chunk_size) - 1); + integral_type word_copy = integral_type(word); + for (std::size_t i = 0; i < num_chunks; ++i) { + chunks.push_back(word_copy & mask); + word_copy >>= chunk_size; + } + return chunks; + } + + template + std::vector chunk_64_to_16( + const typename BlueprintFieldType::value_type &value + ) { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + std::vector chunks; + constexpr const std::size_t chunk_size = 16; + constexpr const std::size_t num_chunks = 4; + constexpr const integral_type mask = (integral_type(1) << chunk_size) - 1; + integral_type value_copy = integral_type(value.data); + for (std::size_t i = 0; i < num_chunks; ++i) { + chunks.push_back(static_cast(value_copy & mask)); + value_copy >>= chunk_size; + } + return chunks; + } + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/detail/static_pow.hpp b/libs/blueprint/include/nil/detail/static_pow.hpp new file mode 100644 index 000000000..e553a835e --- /dev/null +++ b/libs/blueprint/include/nil/detail/static_pow.hpp @@ -0,0 +1,51 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef CRYPTO3_DETAIL_STATIC_POW_HPP +#define CRYPTO3_DETAIL_STATIC_POW_HPP + +namespace nil { + namespace crypto3 { + namespace detail { + template + constexpr T pow(T x, U n) { + T result = 1; + while (n > 0) { + if (n % 2 == 0) { + // n is even + x = x * x; + n = n / 2; + } else { + // n isn't even + result = result * x; + n = n - 1; + } + } + return result; + } + } // namespace detail + } // namespace crypto3 +} // namespace nil + +#endif // #ifndef CRYPTO3_DETAIL_STATIC_POW_HPP diff --git a/libs/blueprint/run_tests.sh b/libs/blueprint/run_tests.sh new file mode 100755 index 000000000..39b75c74c --- /dev/null +++ b/libs/blueprint/run_tests.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -e + +declare -a TEST_LIST=(\ + "blueprint_algebra_fields_plonk_field_operations_test" + "blueprint_algebra_fields_plonk_exponentiation_test" + "blueprint_algebra_curves_plonk_unified_addition_test" + "blueprint_algebra_curves_plonk_variable_base_scalar_mul_test" + "blueprint_verifiers_kimchi_sponge_oracles_test" + "blueprint_hashes_plonk_poseidon_test" + "blueprint_algebra_curves_plonk_endo_scalar_test" + "blueprint_algebra_fields_plonk_range_check_test" + "blueprint_algebra_fields_plonk_logic_and_flag_test" + "blueprint_algebra_fields_plonk_logic_or_flag_test" + "blueprint_algebra_fields_plonk_interpolation_test" + "blueprint_algebra_fields_plonk_non_native_addition_test" + "blueprint_algebra_fields_plonk_non_native_subtraction_test" + "blueprint_algebra_fields_plonk_non_native_multiplication_test" + "blueprint_algebra_fields_plonk_non_native_range_test" + "blueprint_algebra_fields_plonk_non_native_reduction_test" + "blueprint_algebra_fields_plonk_non_native_bit_decomposition_test" + "blueprint_algebra_fields_plonk_non_native_bit_composition_test" + "blueprint_algebra_fields_plonk_non_native_bit_shift_constant_test" + "blueprint_algebra_fields_plonk_non_native_logic_ops_test" + "blueprint_algebra_fields_plonk_non_native_lookup_logic_ops_test" + "blueprint_algebra_fields_plonk_non_native_comparison_checked_test" + "blueprint_algebra_fields_plonk_non_native_comparison_unchecked_test" + "blueprint_algebra_fields_plonk_non_native_comparison_flag_test" + "blueprint_algebra_fields_plonk_non_native_equality_flag_test" + "blueprint_algebra_fields_plonk_non_native_division_remainder_test" + "blueprint_non_native_plonk_bool_scalar_multiplication_test" + "blueprint_non_native_plonk_add_mul_zkllvm_compatible_test" + "blueprint_hashes_plonk_decomposition_test" + "blueprint_verifiers_placeholder_fri_cosets_test" + "blueprint_hashes_plonk_sha256_process_test" + "blueprint_hashes_plonk_sha512_process_test" + "blueprint_hashes_plonk_sha256_test" + "blueprint_hashes_plonk_sha512_test" + "blueprint_algebra_fields_plonk_sqrt_test" + "blueprint_verifiers_placeholder_fri_lin_inter_test" + "blueprint_verifiers_placeholder_fri_array_swap_test" + "blueprint_manifest_test" + "blueprint_detail_huang_lu_test" + "blueprint_private_input_test" + "blueprint_verifiers_placeholder_permutation_argument_verifier_test" + "blueprint_verifiers_placeholder_gate_argument_verifier_test" + "blueprint_verifiers_placeholder_lookup_argument_verifier_test" + "blueprint_verifiers_placeholder_f1_loop_test" + "blueprint_verifiers_placeholder_f3_loop_test" + "blueprint_verifiers_placeholder_gate_component_test" + "blueprint_verifiers_flexible_pow_factor_test" + "blueprint_proxy_test" + "blueprint_component_batch_test" + "blueprint_verifiers_placeholder_expression_evaluation_component_test" + "blueprint_verifiers_placeholder_final_polynomial_check_test" + "blueprint_verifiers_flexible_swap_test" + "blueprint_verifiers_flexible_additions_test" + "blueprint_verifiers_flexible_multiplications_test" + "blueprint_verifiers_flexible_poseidon_test" + "blueprint_verifiers_flexible_constant_pow_test" + "blueprint_verifiers_placeholder_verifier_test" + "blueprint_zkevm_zkevm_word_test" + "blueprint_zkevm_bytecode_test" + "blueprint_zkevm_state_selector_test" + "blueprint_zkevm_state_transition_test" + "blueprint_zkevm_opcodes_iszero_test" + "blueprint_zkevm_opcodes_add_sub_test" + "blueprint_zkevm_opcodes_mul_test" + "blueprint_zkevm_opcodes_div_test" +) +#blueprint_non_native_plonk_scalar_non_native_range_test, TODO: enable once fixed. +#blueprint_mock_mocked_components_test, TODO: Enable after code and test re-written. + +echo "building ${TEST_LIST[*]}" +ninja -k 0 -j $NIX_BUILD_CORES ${TEST_LIST[*]} + +echo "finish" diff --git a/libs/blueprint/test/CMakeLists.txt b/libs/blueprint/test/CMakeLists.txt new file mode 100644 index 000000000..926a365ea --- /dev/null +++ b/libs/blueprint/test/CMakeLists.txt @@ -0,0 +1,265 @@ +#---------------------------------------------------------------------------# +# Copyright (c) 2018-2021 Mikhail Komarov +# Copyright (c) 2022 Aleksei Moskvin +# +# Distributed under the Boost Software License, Version 1.0 +# See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt +#---------------------------------------------------------------------------# + +include(CMTest) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + +option(ZK_PLACEHOLDER_PROFILING "Build with placeholder profiling" FALSE) +option(ZK_PLACEHOLDER_DEBUG "Build with placeholder debug output" FALSE) + +if(ZK_PLACEHOLDER_PROFILING) + add_definitions(-DZK_PLACEHOLDER_PROFILING_ENABLED) +endif() + +if(ZK_PLACEHOLDER_DEBUG) + add_definitions(-DZK_PLACEHOLDER_DEBUG_ENABLED) +endif() + +option(BLUEPRINT_DEBUG "Build with blueprint debug output" FALSE) + +if(BLUEPRINT_DEBUG) + message(STATUS "BLUEPRINT DEBUG enabled") + add_definitions(-DBLUEPRINT_DEBUG_ENABLED) +endif() + +option(BLUEPRINT_PLACEHOLDER_PROOF_GEN "Build with placeholder proof generation running" FALSE) + +if(BLUEPRINT_PLACEHOLDER_PROOF_GEN) + message(STATUS "BLUEPRINT PROOF GENERATION enabled") + add_definitions(-DBLUEPRINT_PLACEHOLDER_PROOF_GEN_ENABLED) +endif() + +macro(define_blueprint_test test) + string(REPLACE "/" "_" full_test_name blueprint_${test}_test) + + set(TEST_RESULTS_DIR "${CMAKE_CURRENT_BINARY_DIR}/junit_results") + set(TEST_LOGS_DIR "${TEST_RESULTS_DIR}/logs") + set(additional_args "--log_format=JUNIT" + "--log_sink=${TEST_LOGS_DIR}/${full_test_name}.xml") + + cm_test(NAME ${full_test_name} SOURCES ${test}.cpp ARGS ${additional_args}) + + target_include_directories(${full_test_name} PRIVATE + ${Boost_INCLUDE_DIRS}) + + target_link_libraries(${full_test_name} crypto3::blueprint crypto3::algebra crypto3::zk) + + set_target_properties(${full_test_name} PROPERTIES CXX_STANDARD 17) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${full_test_name} PRIVATE "${ARGV2}" "-fconstexpr-steps=2147483647" "-ftemplate-backtrace-limit=0") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${full_test_name} PRIVATE "${ARGV2}" "-fconstexpr-ops-limit=4294967295" "-ftemplate-backtrace-limit=0") + endif() + + get_target_property(target_type Boost::unit_test_framework TYPE) + if(target_type STREQUAL "SHARED_LIB") + target_compile_definitions(${full_test_name} PRIVATE BOOST_TEST_DYN_LINK) + elseif(target_type STREQUAL "STATIC_LIB") + + endif() +endmacro() + +set(COMMON_TEST_FILES + "manifest" + "detail/huang_lu" + "gate_id" + "utils/connectedness_check" + "private_input" + "proxy" + #"mock/mocked_components" + "component_batch" + ) + +set(NON_NATIVE_TESTS_FILES + "algebra/fields/plonk/non_native/multiplication" + "algebra/fields/plonk/non_native/addition" + "algebra/fields/plonk/non_native/subtraction" + "algebra/fields/plonk/non_native/range" + "algebra/fields/plonk/non_native/reduction" + "algebra/fields/plonk/non_native/bit_decomposition" + "algebra/fields/plonk/non_native/bit_composition" + "algebra/fields/plonk/non_native/bit_shift_constant" + "algebra/fields/plonk/non_native/comparison_checked" + "algebra/fields/plonk/non_native/comparison_unchecked" + "algebra/fields/plonk/non_native/comparison_flag" + "algebra/fields/plonk/non_native/equality_flag" + "algebra/fields/plonk/non_native/division_remainder" + "non_native/plonk/bool_scalar_multiplication" + "non_native/plonk/add_mul_zkllvm_compatible" + #"non_native/plonk/scalar_non_native_range" + ) + +set(NON_NATIVE_TESTS_FILES_WITH_PROOF_GEN + "algebra/fields/plonk/non_native/logic_ops" + "algebra/fields/plonk/non_native/lookup_logic_ops" +) + +set(PLONK_TESTS_FILES + "algebra/curves/plonk/variable_base_scalar_mul" + "algebra/curves/plonk/unified_addition" + #"algebra/curves/plonk/variable_base_endo_scalar_mul" + "algebra/curves/plonk/endo_scalar" + "hashes/plonk/poseidon" + "hashes/plonk/sha256" + "hashes/plonk/sha512" + "hashes/plonk/sha256_process" + "hashes/plonk/sha512_process" + "hashes/plonk/decomposition" + #"hashes/plonk/detail/sha_table_generators_base4" + #"hashes/plonk/detail/sha_table_generators_base7" + #"verifiers/kimchi/base_field" + #"verifiers/kimchi/prepare_batch_scalar" + #"verifiers/kimchi/verify_scalar" + #"verifiers/kimchi/basic_verifier" + #"verifiers/kimchi/table_commitment" + #"verifiers/kimchi/demo_verifier" + #"verifiers/kimchi/oracles_scalar" + #"verifiers/kimchi/batch_verify_base_field" + #"verifiers/kimchi/batch_verify_scalar_field" + #"verifiers/kimchi/detail/lagrange_denominators" + #"verifiers/kimchi/detail/b_poly" + #"verifiers/kimchi/detail/b_poly_coefficients" + #"verifiers/kimchi/detail/to_group" + #"verifiers/kimchi/detail/public_evaluations" + #"verifiers/kimchi/detail/prev_chal_evals" + #"verifiers/kimchi/detail/ft_eval" + #"verifiers/kimchi/detail/combine_proof_evals" + #"verifiers/kimchi/detail//constraints/index_terms_scalars" + #"verifiers/kimchi/detail/constraints/rpn_expression" + #"verifiers/kimchi/detail/constraints/vanishes_on_last_4_rows" + #"verifiers/kimchi/detail/constraints/unnormalized_lagrange_basis" + #"verifiers/kimchi/detail/constraints/perm_scalars" + #"verifiers/kimchi/detail/constraints/generic_scalars" + #"verifiers/kimchi/detail/oracles_cip" + #"verifiers/kimchi/detail/zk_w3" + #"verifiers/kimchi/detail/prepare_scalars" + #"verifiers/kimchi/detail/zkpm_evaluate" + #"verifiers/kimchi/sponge/sponge" + "verifiers/kimchi/sponge/oracles" + #"verifiers/kimchi/sponge/transcript_fr" + #"verifiers/kimchi/sponge/transcript_fq" + #"verifiers/kimchi/sponge/compare" + "algebra/fields/plonk/field_operations" + #"algebra/fields/plonk/combined_inner_product" + #"algebra/fields/plonk/element_powers" + "algebra/fields/plonk/exponentiation" + "algebra/fields/plonk/sqrt" + "algebra/fields/plonk/range_check" + "algebra/fields/plonk/logic_and_flag" + "algebra/fields/plonk/logic_or_flag" + "algebra/fields/plonk/interpolation" + #"verifiers/pickles/verify_heterogenous_scalar" + #"verifiers/pickles/verify_heterogenous_base" + #"verifiers/pickles/scalar_details/evals_of_split_evals" + "verifiers/placeholder/permutation_argument_verifier" + "verifiers/placeholder/gate_argument_verifier" + "verifiers/placeholder/lookup_argument_verifier" + "verifiers/placeholder/gate_component" + "verifiers/placeholder/f1_loop" + "verifiers/placeholder/f3_loop" + "verifiers/placeholder/fri_cosets" + "verifiers/placeholder/fri_lin_inter" + "verifiers/placeholder/fri_array_swap" + "verifiers/placeholder/expression_evaluation_component" + "verifiers/placeholder/final_polynomial_check" + "verifiers/flexible/swap" + "verifiers/flexible/additions" + "verifiers/flexible/multiplications" + "verifiers/flexible/poseidon" + "verifiers/flexible/constant_pow" + "verifiers/flexible/pow_factor" + "verifiers/placeholder/verifier" + "zkevm/bytecode" + ) + +#set(FIELDS_TESTS_FILES + #"algebra/fields/r1cs/fp2" + #"algebra/fields/r1cs/fp2_verification" + #"algebra/fields/r1cs/fp3" + #"algebra/fields/r1cs/fp3_verification" + #"algebra/fields/r1cs/fp4" + #"algebra/fields/r1cs/fp4_verification" + #"algebra/fields/r1cs/fp6_2over3" + #"algebra/fields/r1cs/fp6_2over3_verification" + #"algebra/fields/r1cs/exponentiation" +# ) + +set(ZKEVM_TESTS_FILES + "zkevm/state_selector" + "zkevm/zkevm_word" + "zkevm/state_transition" + "zkevm/opcodes/iszero" + "zkevm/opcodes/add_sub" + "zkevm/opcodes/mul" + "zkevm/opcodes/div" + ) + +#set(CURVES_TESTS_FILES + #"algebra/curves/r1cs/montgomery" + #"algebra/curves/r1cs/twisted_edwards" + #"algebra/curves/r1cs/fixed_base_mul_zcash" +# ) + +#set(HASHES_TESTS_FILES + #"hashes/r1cs/knapsack" + #"hashes/r1cs/knapsack_verification" + #"hashes/r1cs/sha256" + #"hashes/r1cs/sha256_verification" + #"hashes/r1cs/pedersen") + +#set(PAIRING_TESTS_FILES + #"algebra/pairing/weierstrass/r1cs/miller_loop" + #"algebra/pairing/weierstrass/r1cs/precomputation") + +#set(ROUTING_TESTS_FILES + #"routing_algorithms/routing_algorithms" + #"routing/r1cs/as_waksman" + #"routing/r1cs/benes") + +#set(SCHEMES_TESTS_FILES + #"verifiers/r1cs_ppzksnark" + #"set_commitment_component") + +#set(MERKLE_TREE_TESTS_FILES +# "merkle_tree_components") + +#set(VOTING_TESTS_FILES +# "voting/r1cs/encrypted_input_voting") + +#set(BASIC_COMPONENTS_TESTS_FILES + #"basic_components" + #"basic_components_r1cs_gg_ppzksnark") + +SET(ALGEBRA_TESTS_FILES + ${FIELDS_TESTS_FILES} + ${CURVES_TESTS_FILES} + ${PAIRING_TESTS_FILES}) + +SET(ALL_TESTS_FILES + ${COMMON_TEST_FILES} + ${NON_NATIVE_TESTS_FILES} + ${PLONK_TESTS_FILES} + ${ALGEBRA_TESTS_FILES} + ${ZKEVM_TESTS_FILES} + ${HASHES_TESTS_FILES} + ${ROUTING_TESTS_FILES} + ${SCHEMES_TESTS_FILES} + ${MERKLE_TREE_TESTS_FILES} + ${VOTING_TESTS_FILES} + ${BASIC_COMPONENTS_TESTS_FILES}) + +foreach(TEST_FILE ${ALL_TESTS_FILES}) + define_blueprint_test(${TEST_FILE}) +endforeach() + +foreach(TEST_FILE ${NON_NATIVE_TESTS_FILES_WITH_PROOF_GEN}) + define_blueprint_test(${TEST_FILE} ARGS "-DBLUEPRINT_PLACEHOLDER_PROOF_GEN=True") +endforeach() diff --git a/libs/blueprint/test/algebra/curves/plonk/endo_scalar.cpp b/libs/blueprint/test/algebra/curves/plonk/endo_scalar.cpp new file mode 100644 index 000000000..e99c7b4f0 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/plonk/endo_scalar.cpp @@ -0,0 +1,229 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_endo_scalar_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +template +struct endo_scalar_params; + +template<> +struct endo_scalar_params { + using curve_type = nil::crypto3::algebra::curves::vesta; + using scalar_field_type = typename curve_type::scalar_field_type; + using base_field_type = typename curve_type::base_field_type; + constexpr static const typename scalar_field_type::value_type endo_r = + 0x12CCCA834ACDBA712CAAD5DC57AAB1B01D1F8BD237AD31491DAD5EBDFDFE4AB9_cppui_modular255; + constexpr static const typename base_field_type::value_type endo_q = + 0x2D33357CB532458ED3552A23A8554E5005270D29D19FC7D27B7FD22F0201B547_cppui_modular255; +}; + +template<> +struct endo_scalar_params { + using curve_type = nil::crypto3::algebra::curves::pallas; + using scalar_field_type = typename curve_type::scalar_field_type; + using base_field_type = typename curve_type::base_field_type; + constexpr static const typename scalar_field_type::value_type endo_r = + 0x397E65A7D7C1AD71AEE24B27E308F0A61259527EC1D4752E619D1840AF55F1B1_cppui_modular255; + constexpr static const typename base_field_type::value_type endo_q = + 0x2D33357CB532458ED3552A23A8554E5005270D29D19FC7D27B7FD22F0201B547_cppui_modular255; +}; + +template +typename CurveType::scalar_field_type::value_type calculate_endo_scalar(typename CurveType::scalar_field_type::value_type scalar) { + + using endo_params = endo_scalar_params; + using BlueprintFieldType = typename CurveType::scalar_field_type; + + typename BlueprintFieldType::value_type endo_r = endo_params::endo_r; + + const std::size_t crumbs_per_row = 8; + const std::size_t bits_per_crumb = 2; + const std::size_t bits_per_row = + bits_per_crumb * crumbs_per_row; // we suppose that ScalarSize % bits_per_row = 0 + + typename BlueprintFieldType::integral_type integral_scalar = + typename BlueprintFieldType::integral_type(scalar.data); + std::array bits_msb; + { + nil::marshalling::status_type status; + BOOST_CHECK(ScalarSize <= 255); + std::array bits_msb_all = + nil::marshalling::pack(integral_scalar, status); + BOOST_CHECK(status == nil::marshalling::status_type::success); + std::copy(bits_msb_all.end() - ScalarSize, bits_msb_all.end(), bits_msb.begin()); + + for(std::size_t i = 0; i < 255 - ScalarSize; ++i) { + BOOST_CHECK(!bits_msb_all[i]); + } + } + typename BlueprintFieldType::value_type a = 2; + typename BlueprintFieldType::value_type b = 2; + typename BlueprintFieldType::value_type n = 0; + + BOOST_CHECK (ScalarSize % bits_per_row == 0); + for (std::size_t chunk_start = 0; chunk_start < bits_msb.size(); chunk_start += bits_per_row) { + for (std::size_t j = 0; j < crumbs_per_row; j++) { + std::size_t crumb = chunk_start + j * bits_per_crumb; + typename BlueprintFieldType::value_type b0 = static_cast(bits_msb[crumb + 1]); + typename BlueprintFieldType::value_type b1 = static_cast(bits_msb[crumb + 0]); + + typename BlueprintFieldType::value_type crumb_value = b0 + b1.doubled(); + + a = a.doubled(); + b = b.doubled(); + + typename BlueprintFieldType::value_type s = + (b0 == BlueprintFieldType::value_type::one()) ? 1 : -1; + if (b1 == BlueprintFieldType::value_type::zero()) { + b += s; + } else { + a += s; + } + + n = (n.doubled()).doubled(); + n += crumb_value; + } + } + auto res = a * endo_r + b; + return res; +} + +template +void test_endo_scalar(std::vector public_input, + typename CurveType::scalar_field_type::value_type expected_res){ + using BlueprintFieldType = typename CurveType::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + constexpr static const std::size_t num_bits = 128; + + using component_type = nil::blueprint::components::endo_scalar; + + using var = nil::crypto3::zk::snark::plonk_variable; + + var challenge_var(0, 0, false, var::column_type::public_input); + typename component_type::input_type instance_input = {challenge_var}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "endo_scalar input: " << std::hex << public_input[0].data << "\n"; + std::cout << "expected result : " << std::hex << expected_res.data << "\n"; + std::cout << "real result : " << std::hex << var_value(assignment, real_res.output).data << "\n\n"; + #endif + + BOOST_CHECK_EQUAL(expected_res, var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},{},{},num_bits); + + nil::crypto3::test_component (component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_endo_scalar_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_endo_scalar_vesta) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + + typename BlueprintFieldType::value_type challenge = 0x00000000000000000000000000000000FC93536CAE0C612C18FBE5F6D8E8EEF2_cppui_modular255; + typename BlueprintFieldType::value_type result = 0x004638173549A4C55A118327904B54E5F6F6314225C8C862F5AFA2506C77AC65_cppui_modular255; + + test_endo_scalar({challenge}, result); + test_endo_scalar({1}, calculate_endo_scalar(1)); + test_endo_scalar({0}, calculate_endo_scalar(0)); + test_endo_scalar({0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255}, + calculate_endo_scalar(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255)); + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < random_tests_amount; i++){ + typename curve_type::scalar_field_type::value_type input = generate_random(); + typename curve_type::scalar_field_type::integral_type input_integral = typename curve_type::scalar_field_type::integral_type(input.data); + input_integral = input_integral & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255; + typename curve_type::scalar_field_type::value_type input_scalar = input_integral; + test_endo_scalar({input_scalar}, calculate_endo_scalar(input_scalar)); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_endo_scalar_pallas) { + using curve_type = nil::crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::scalar_field_type; + + typename BlueprintFieldType::value_type challenge = 0x00000000000000000000000000000000FC93536CAE0C612C18FBE5F6D8E8EEF2_cppui_modular255; + + test_endo_scalar({challenge}, calculate_endo_scalar(challenge)); + test_endo_scalar({1}, calculate_endo_scalar(1)); + test_endo_scalar({0}, calculate_endo_scalar(0)); + test_endo_scalar({0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255}, + calculate_endo_scalar(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255)); + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < random_tests_amount; i++){ + typename curve_type::scalar_field_type::value_type input = generate_random(); + typename curve_type::scalar_field_type::integral_type input_integral = typename curve_type::scalar_field_type::integral_type(input.data); + input_integral = input_integral & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255; + typename curve_type::scalar_field_type::value_type input_scalar = input_integral; + test_endo_scalar({input_scalar}, calculate_endo_scalar(input_scalar)); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/curves/plonk/unified_addition.cpp b/libs/blueprint/test/algebra/curves/plonk/unified_addition.cpp new file mode 100644 index 000000000..73ee8e9c8 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/plonk/unified_addition.cpp @@ -0,0 +1,194 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_unified_addition_test + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_unified_addition(std::vector public_input, + typename CurveType::template g1_type::value_type expected_res){ + + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::base_field_type; + + constexpr std::size_t WitnessColumns = 11; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::unified_addition; + + typename component_type::input_type instance_input = { + {var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}, + {var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "unified_addition test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << " " << public_input[3].data << "\n"; + std::cout << "expected: " << expected_res.X.data << " " << expected_res.Y.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.X).data << " " << var_value(assignment, real_res.Y).data << "\n\n"; + #endif + BOOST_CHECK(expected_res.X == var_value(assignment, real_res.X)); + BOOST_CHECK(expected_res.Y == var_value(assignment, real_res.Y)); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_unified_addition_with_zeroes() { + nil::crypto3::random::algebraic_engine> generate_random_point; + boost::random::mt19937 seed_seq; + generate_random_point.seed(seed_seq); + + typename CurveType::template g1_type::value_type zero_circuits = {0u, 0u}; + typename CurveType::template g1_type::value_type P = generate_random_point(); + typename CurveType::template g1_type::value_type Q = -P; + + std::vector public_input; + + public_input = {zero_circuits.X, zero_circuits.Y, zero_circuits.X, zero_circuits.Y}; + test_unified_addition(public_input, zero_circuits); + + public_input = {zero_circuits.X, zero_circuits.Y, P.X, P.Y}; + test_unified_addition(public_input, P); + + public_input = {P.X, P.Y, zero_circuits.X, zero_circuits.Y}; + test_unified_addition(public_input, P); + + public_input = {P.X, P.Y, Q.X, Q.Y}; + test_unified_addition(public_input, zero_circuits); + + public_input = {Q.X, Q.Y, P.X, P.Y}; + test_unified_addition(public_input, zero_circuits); +} + +template +void test_unified_addition_doubling() { + nil::crypto3::random::algebraic_engine> generate_random_point; + boost::random::mt19937 seed_seq; + generate_random_point.seed(seed_seq); + + typename CurveType::template g1_type::value_type P = generate_random_point(); + typename CurveType::template g1_type::value_type Q(P); + + std::vector public_input; + + public_input = {P.X, P.Y, Q.X, Q.Y}; + test_unified_addition(public_input, P+Q); + + public_input = {Q.X, Q.Y, P.X, P.Y}; + test_unified_addition(public_input, P+Q); +} + +template +void test_unified_addition_random_data() { + nil::crypto3::random::algebraic_engine> generate_random_point; + boost::random::mt19937 seed_seq; + generate_random_point.seed(seed_seq); + + typename CurveType::template g1_type::value_type P = generate_random_point(); + typename CurveType::template g1_type::value_type Q = generate_random_point(); + typename CurveType::template g1_type::value_type zero = {0u, 0u}; + typename CurveType::template g1_type::value_type expected_res; + + std::vector public_input; + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + P = generate_random_point(); + Q = generate_random_point(); + + if (Q.X == zero.X && Q.Y == zero.Y) { + expected_res = P; + } else { + if (P.X == zero.X && P.Y == zero.Y) { + expected_res = Q; + } else { + if (P.X == Q.X && P.Y == -Q.Y) { + expected_res = {0u, 0u}; + } else { + expected_res = P + Q; + } + } + } + + public_input = {P.X, P.Y, Q.X, Q.Y}; + test_unified_addition(public_input, expected_res); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_unified_addition_pallas) { + using curve_type = crypto3::algebra::curves::pallas; + test_unified_addition_with_zeroes(); + test_unified_addition_doubling(); + test_unified_addition_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_unified_addition_vesta) { + using curve_type = crypto3::algebra::curves::vesta; + test_unified_addition_with_zeroes(); + test_unified_addition_doubling(); + test_unified_addition_random_data(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/curves/plonk/variable_base_endo_scalar_mul.cpp b/libs/blueprint/test/algebra/curves/plonk/variable_base_endo_scalar_mul.cpp new file mode 100644 index 000000000..1d6e52634 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/plonk/variable_base_endo_scalar_mul.cpp @@ -0,0 +1,124 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_variable_base_endo_scalar_mul_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include "test_plonk_component.hpp" + +#include "../../../profiling.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_base_endo_scalar_mul) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + using BlueprintScalarType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + using var = zk::snark::plonk_variable; + constexpr std::size_t Lambda = 40; + using component_type = zk::components::curve_element_variable_base_endo_scalar_mul; + + //curve_type::scalar_field_type::value_type b = algebra::random_element(); + curve_type::scalar_field_type::value_type b = 2; + typename curve_type::scalar_field_type::integral_type integral_b = typename curve_type::scalar_field_type::integral_type(b.data); + BlueprintFieldType::value_type b_scalar = integral_b; + curve_type::template g1_type::value_type T = algebra::random_element>(); + var T_X_var = {0, 1, false, var::column_type::public_input}; + var T_Y_var = {0, 2, false, var::column_type::public_input}; + var scalar_var = {0, 3, false, var::column_type::public_input}; + + typename component_type::params_type assignment_params = {{T_X_var, T_Y_var},scalar_var}; + std::cout<<"random point: " << T.X.data << " " << T.Y.data < public_input = {T.X, T.Y, b_scalar}; + + constexpr static const typename BlueprintFieldType::value_type endo = component_type::endo; + typename BlueprintFieldType::value_type endo_scalar = 0x244630A7EE5033DA383B3677B4C5CA94A3EBE4156FC4FA4E08B35974929CA2C5_cppui_modular255; + + typename curve_type::template g1_type::value_type testResult = endo_scalar * T; + std::cout<<"Expected result for endo_scalar * T: "< bits = {false}; + for (std::size_t i = 0; i < 128; i++) { + bits[128 - i - 1] = multiprecision::bit_test(integral_b, i); + } + typename curve_type::template g1_type::value_type testQ; + testQ.X = endo * T.X; + testQ.Y = T.Y; + typename curve_type::template g1_type::value_type acc = T + (T + testQ) + testQ; + for (std::size_t i = 0; i < 128; i = i + 2) { + typename BlueprintFieldType::value_type b1 = bits[i]; + typename BlueprintFieldType::value_type b2 = bits[i + 1]; + if (b1 == 0){ + testQ.X = T.X; + } else { + testQ.X = endo * T.X; + } + if (b2 == 0) { + testQ.Y = -T.Y; + } else { + testQ.Y = T.Y; + } + acc = acc + testQ + acc; + } + std::cout<<"Expected result: "< (assignment_params, public_input, result_check); + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "base_endo_scalar_mul: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/curves/plonk/variable_base_scalar_mul.cpp b/libs/blueprint/test/algebra/curves/plonk/variable_base_scalar_mul.cpp new file mode 100644 index 000000000..565312482 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/plonk/variable_base_scalar_mul.cpp @@ -0,0 +1,276 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2022 Mikhail Komarov +// Copyright (c) 2020-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE variable_base_scalar_mul_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "../../../test_plonk_component.hpp" + +template +void test_variable_base_scalar_mul ( + const std::vector &public_input, + typename CurveType::template g1_type::value_type expected){ + constexpr std::size_t WitnessColumns = 15 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using BlueprintFieldType = typename CurveType::base_field_type; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + using component_type = nil::blueprint::components::curve_element_variable_base_scalar_mul; + + using var = nil::crypto3::zk::snark::plonk_variable; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},{0},{}); + + auto result_check = [&expected, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + typename CurveType::template g1_type::value_type R; + R.X = var_value(assignment, real_res.X); + R.Y = var_value(assignment, real_res.Y); + + BOOST_CHECK_EQUAL(expected.X, R.X); + BOOST_CHECK_EQUAL(expected.Y, R.Y); + }; + + var scalar_var = {0, 2, false, var::column_type::public_input}; + var T_X_var = {0, 0, false, var::column_type::public_input}; + var T_Y_var = {0, 1, false, var::column_type::public_input}; + + if constexpr (Stretched) { + using stretched_component_type = nil::blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + if constexpr (std::is_same::value) { + var high_bit = {0, 3, false, var::column_type::public_input}; + typename component_type::input_type instance_input = {{T_X_var, T_Y_var},scalar_var, high_bit}; + nil::crypto3::test_component + (stretched_instance, desc, public_input, result_check, instance_input); + } else { + typename component_type::input_type instance_input = {{T_X_var, T_Y_var},scalar_var}; + nil::crypto3::test_component + (stretched_instance, desc, public_input, result_check, instance_input); + } + } else { + if constexpr (std::is_same::value) { + var high_bit = {0, 3, false, var::column_type::public_input}; + typename component_type::input_type instance_input = {{T_X_var, T_Y_var},scalar_var, high_bit}; + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); + } else { + typename component_type::input_type instance_input = {{T_X_var, T_Y_var},scalar_var}; + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); + } + } +} + + +template +struct shift_params; + +template<> +struct shift_params { + constexpr static const typename nil::crypto3::algebra::curves::pallas::scalar_field_type::value_type shift_base = 2; + constexpr static const typename nil::crypto3::algebra::curves::pallas::scalar_field_type::value_type shift_for_1_0_neg1 = shift_base.pow(255) + 1; + constexpr static const typename nil::crypto3::algebra::curves::pallas::scalar_field_type::value_type denominator_for_1_0_neg1 = 2; +}; + +template<> +struct shift_params { + constexpr static const typename nil::crypto3::algebra::curves::vesta::scalar_field_type::value_type shift_base = 2; + constexpr static const typename nil::crypto3::algebra::curves::vesta::scalar_field_type::value_type shift_for_1_0_neg1 = shift_base.pow(255); + constexpr static const typename nil::crypto3::algebra::curves::vesta::scalar_field_type::value_type denominator_for_1_0_neg1 = 1; +}; + +template +typename CurveType::scalar_field_type::value_type shift_scalar(typename CurveType::scalar_field_type::value_type unshifted) { + typename CurveType::scalar_field_type::value_type shift_base = 2; + typename CurveType::scalar_field_type::value_type shift = shift_base.pow(255) + 1; + typename CurveType::scalar_field_type::value_type denominator = 2; + + typename CurveType::scalar_field_type::value_type shift_for_1_0_neg1 = shift_params::shift_for_1_0_neg1; + typename CurveType::scalar_field_type::value_type denominator_for_1_0_neg1 = shift_params::denominator_for_1_0_neg1; + + typename CurveType::scalar_field_type::value_type shifted; + + if ((unshifted == 1) || (unshifted == 0) || (unshifted == -1)){ + shifted = (unshifted - shift_for_1_0_neg1) * denominator_for_1_0_neg1.inversed(); + } + else { + shifted = (unshifted - shift) * denominator.inversed(); + } + + return shifted; +} + +template +void test_variable_base_scalar_mul_with_stretching( + const std::vector &public_input, + typename CurveType::template g1_type::value_type expected) { + + test_variable_base_scalar_mul(public_input, expected); + test_variable_base_scalar_mul(public_input, expected); +} + + +template +void test_vbsm( + typename CurveType::template g1_type::value_type point, + typename CurveType::scalar_field_type::value_type scalar) { + typedef typename CurveType::template g1_type::value_type g1_value_type; + g1_value_type expected; + + expected = point * scalar; + + typename CurveType::scalar_field_type::value_type shifted = shift_scalar(scalar); + typename CurveType::scalar_field_type::integral_type shifted_integral_type = typename CurveType::scalar_field_type::integral_type(shifted.data); + typename CurveType::base_field_type::value_type shifted_base_value_type = shifted_integral_type; + typename CurveType::base_field_type::value_type shifted_base_value_type_bit; + typename CurveType::scalar_field_type::value_type two = 2; + + if constexpr (std::is_same::value) { + if (shifted >= two.pow(254)) { + shifted = shifted - two.pow(254); + shifted_integral_type = typename CurveType::scalar_field_type::integral_type(shifted.data); + shifted_base_value_type = shifted_integral_type; + shifted_base_value_type_bit = 1; + } else { + shifted_base_value_type = shifted_integral_type; + shifted_base_value_type_bit = 0; + } + test_variable_base_scalar_mul_with_stretching( + {point.X, point.Y, shifted_base_value_type, shifted_base_value_type_bit}, expected); + } else { + shifted_base_value_type = shifted_integral_type; + test_variable_base_scalar_mul_with_stretching( + {point.X, point.Y, shifted_base_value_type}, expected); + } + } + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_variable_base_scalar_mul_random_scalar_pallas) { + + // regular test (base field larger than scalar field) + using curve_type = nil::crypto3::algebra::curves::vesta; + using BlueprintScalarType = typename curve_type::scalar_field_type; + + nil::crypto3::random::algebraic_engine> random_point; + boost::random::mt19937 seed_seq; + random_point.seed(seed_seq); + + nil::crypto3::random::algebraic_engine random_scalar; + boost::random::mt19937 seed_seq2; + random_scalar.seed(seed_seq2); + + typename BlueprintScalarType::value_type two = 2; + typename BlueprintScalarType::value_type threefff = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular255; + typename BlueprintScalarType::value_type unshifted_threefff = 2*threefff + two.pow(255) + 1; + + test_vbsm(random_point(), two + two.pow(255) + 1); + test_vbsm(random_point(), two - two + two.pow(255) + 1); + test_vbsm(random_point(), two - two - two + two.pow(255) + 1); + test_vbsm(random_point(), unshifted_threefff); + test_vbsm(random_point(), random_scalar()); + test_vbsm(random_point(), 1); + test_vbsm(random_point(), 0); + test_vbsm(random_point(), -1); + + test_vbsm({0, 0}, two + two.pow(255) + 1); + test_vbsm({0, 0}, two - two + two.pow(255) + 1); + test_vbsm({0, 0}, two - two - two + two.pow(255) + 1); + test_vbsm({0, 0}, unshifted_threefff); + test_vbsm({0, 0}, random_scalar()); + test_vbsm({0, 0}, 1); + test_vbsm({0, 0}, 0); + test_vbsm({0, 0}, -1); + + // decomposed test (scalar field larger than base field) + using pallas_curve_type = nil::crypto3::algebra::curves::pallas; + + nil::crypto3::random::algebraic_engine> pallas_random_point; + boost::random::random_device dev; + boost::random::mt19937 pallas_seed_seq(dev); + random_point.seed(pallas_seed_seq); + nil::crypto3::random::algebraic_engine pallas_random_scalar; + random_scalar.seed(pallas_seed_seq); + + typename pallas_curve_type::scalar_field_type::value_type pallas_two = 2; + typename pallas_curve_type::scalar_field_type::value_type pallas_threefff = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular255; + typename pallas_curve_type::scalar_field_type::value_type pallas_unshifted_threefff = 2*pallas_threefff + pallas_two.pow(255) + 1; + typename pallas_curve_type::scalar_field_type::value_type pallas_p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001_cppui_modular255; + + test_vbsm(pallas_random_point(), pallas_two + pallas_two.pow(255) + 1); + test_vbsm(pallas_random_point(), pallas_two - pallas_two + pallas_two.pow(255) + 1); + test_vbsm(pallas_random_point(), pallas_two - pallas_two - pallas_two + pallas_two.pow(255) + 1); + test_vbsm(pallas_random_point(), pallas_unshifted_threefff); + test_vbsm(pallas_random_point(), pallas_random_scalar()); + test_vbsm(pallas_random_point(), 1); + test_vbsm(pallas_random_point(), 0); + test_vbsm(pallas_random_point(), -1); + + test_vbsm({0, 0}, pallas_two + pallas_two.pow(255) + 1); + test_vbsm({0, 0}, pallas_two - pallas_two + pallas_two.pow(255) + 1); + test_vbsm({0, 0}, pallas_two - pallas_two - pallas_two + pallas_two.pow(255) + 1); + test_vbsm({0, 0}, pallas_unshifted_threefff); + test_vbsm({0, 0}, pallas_random_scalar()); + test_vbsm({0, 0}, 1); + test_vbsm({0, 0}, 0); + test_vbsm({0, 0}, -1); + + test_vbsm(pallas_random_point(), pallas_p + 2); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/curves/r1cs/fixed_base_mul_zcash.cpp b/libs/blueprint/test/algebra/curves/r1cs/fixed_base_mul_zcash.cpp new file mode 100644 index 000000000..ddf95b2ee --- /dev/null +++ b/libs/blueprint/test/algebra/curves/r1cs/fixed_base_mul_zcash.cpp @@ -0,0 +1,291 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_fixed_base_mul_zcash_component_test + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void print_field_element(std::ostream &os, const typename fields::detail::element_fp &e) { + std::cout << e.data << std::endl; +} + +template +void test_curves_g1_fixed_base_mul_zcash_component( + blueprint &bp, + const BasePoints &all_basepoints, + nil::crypto3::zk::detail::blueprint_variable_vector &in_bits, + const typename Curve::template g1_type::value_type + &expected) { + using curve_type = Curve; + using fixed_base_mul_zcash_component = components::fixed_base_mul_zcash; + using field_type = typename fixed_base_mul_zcash_component::field_type; + using field_value_type = typename field_type::value_type; + using integral_type = typename field_type::integral_type; + using twisted_edwards_group_value_type = + typename fixed_base_mul_zcash_component::twisted_edwards_element_component::group_value_type; + + static_assert(std::is_same::value_type>::value); + + std::size_t basepoints_required = components::fixed_base_mul_zcash::basepoints_required(in_bits.size()); + + for (const auto &p : all_basepoints) { + BOOST_CHECK(p.is_well_formed()); + } + + std::vector basepoints; + std::copy(all_basepoints.begin(), all_basepoints.begin() + basepoints_required, std::back_inserter(basepoints)); + + // components::element_g1 result(bp); + typename fixed_base_mul_zcash_component::twisted_edwards_element_component result(bp); + fixed_base_mul_zcash_component fixed_base_mul_instance(bp, basepoints, in_bits, result); + + fixed_base_mul_instance.generate_assignments(); + fixed_base_mul_instance.generate_gates(); + + BOOST_CHECK(expected.X == bp.lc_val(result.X)); + BOOST_CHECK(expected.Y == bp.lc_val(result.Y)); + BOOST_CHECK(bp.is_satisfied()); +} + +template +void test_curves_g1_fixed_base_mul_zcash_component( + const BasePoints &all_basepoints, + const std::vector &bits, + const typename Curve::template g1_type::value_type + &expected) { + using curve_type = Curve; + using fixed_base_mul_zcash_component = components::fixed_base_mul_zcash; + using field_type = typename fixed_base_mul_zcash_component::field_type; + + blueprint bp; + nil::crypto3::zk::detail::blueprint_variable_vector scalar; + scalar.allocate(bp, bits.size()); + scalar.fill_with_bits(bp, bits); + + test_curves_g1_fixed_base_mul_zcash_component(bp, all_basepoints, scalar, expected); +} + +template +void test_curves_g1_fixed_base_mul_zcash_component( + const BasePoints &all_basepoints, + const typename Curve::base_field_type::value_type &s, + std::size_t size, + const typename Curve::template g1_type::value_type + &expected) { + // Because one of test has different size (the on with 255) + // std::size_t size = multiprecision::msb(integral_type(s.data)) + 1; + using curve_type = Curve; + using fixed_base_mul_zcash_component = components::fixed_base_mul_zcash; + using field_type = typename fixed_base_mul_zcash_component::field_type; + + blueprint bp; + nil::crypto3::zk::detail::blueprint_variable_vector scalar; + scalar.allocate(bp, size); + scalar.fill_with_bits_of_field_element(bp, s); + + test_curves_g1_fixed_base_mul_zcash_component(bp, all_basepoints, scalar, expected); +} + +BOOST_AUTO_TEST_SUITE(blueprint_fixed_base_mul_zcash_manual_test_suite) + +// test data generated by https://github.com/zcash-hackworks/zcash-test-vectors +BOOST_AUTO_TEST_CASE(edwards_fixed_base_mul_zcash_jubjub_test) { + using curve_type = curves::jubjub; + using field_type = typename curve_type::base_field_type; + using field_value_type = typename field_type::value_type; + using integral_type = typename field_type::integral_type; + + std::vector< + typename curve_type::template g1_type::value_type> + all_basepoints = { + {field_value_type( + integral_type("14821992026951101352906249207585330645531160601076441869339940926000353872705")), + field_value_type( + integral_type("52287259411977570791304693313354699485314647509298698724706688571292689216990"))}, + {field_value_type( + integral_type("1463691854240270278606818648002136194121833583821877204193209581327298182344")), + field_value_type( + integral_type("29819841443135548958808950484163239058878703816702478211299889017771131589670"))}, + {field_value_type( + integral_type("40291265060939609650944463710328312785099355084223308258183327547022417006973")), + field_value_type( + integral_type("52192102488968215278324791125420866252464543397675384723668566547038588479994"))}, + {field_value_type( + integral_type("9727827140824687394408632390964265750934762150332666686367551954377952599690")), + field_value_type( + integral_type("19724757542882122580209648860907766139392382704367414563715710526666657068129"))}, + }; + + std::vector bits_to_hash = {0, 0, 0, 1, 1, 1}; + auto expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("3669431847238482802904025485408296241776002230868041345055738963615665974946")), + field_value_type( + integral_type("27924821127213629235056488929093463445821551452792195607066067950495472725010"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, bits_to_hash, expected); + + bits_to_hash = std::vector {0, 0, 1}; + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("37613883148175089126541491300600635192159391899451195953263717773938227311808")), + field_value_type( + integral_type("52287259411977570791304693313354699485314647509298698724706688571292689216990"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, bits_to_hash, expected); + + bits_to_hash = std::vector {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1}; + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("42176130776060636907007595971304534904965322197894055434176666599102076910022")), + field_value_type( + integral_type("41298132615767455442973386625334423316246314118050839847545855695501416927077"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, bits_to_hash, expected); +} + +// test data generated by https://github.com/HarryR/ethsnarks +BOOST_AUTO_TEST_CASE(babyjubjub_test) { + using curve_type = curves::babyjubjub; + using field_type = typename curve_type::base_field_type; + using field_value_type = typename field_type::value_type; + using integral_type = typename field_type::integral_type; + + std::vector< + typename curve_type::template g1_type::value_type> + all_basepoints = { + {field_value_type( + integral_type("13418723823902222986275588345615650707197303761863176429873001977640541977977")), + field_value_type( + integral_type("15255921313433251341520743036334816584226787412845488772781699434149539664639"))}, + {field_value_type( + integral_type("11749872627669176692285695179399857264465143297451429569602068921530882657945")), + field_value_type( + integral_type("2495745987765795949478491016197984302943511277003077751830848242972604164102"))}}; + + field_value_type scalar = + field_value_type(integral_type("6453482891510615431577168724743356132495662554103773572771861111634748265227")); + auto expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("6545697115159207040330446958704617656199928059562637738348733874272425400594")), + field_value_type( + integral_type("16414097465381367987194277536478439232201417933379523927469515207544654431390"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, scalar, 252, expected); + + scalar = field_value_type(integral_type("267")); + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("6790798216812059804926342266703617627640027902964190490794793207272357201212")), + field_value_type( + integral_type("2522797517250455013248440571887865304858084343310097011302610004060289809689"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, scalar, 9, expected); + + scalar = field_value_type( + integral_type("21888242871839275222246405745257275088548364400416034343698204186575808495616")); + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("16322787121012335146141962340685388833598805940095898416175167744309692564601")), + field_value_type( + integral_type("7671892447502767424995649701270280747270481283542925053047237428072257876309"))); + test_curves_g1_fixed_base_mul_zcash_component(all_basepoints, scalar, 255, expected); +} + +// BOOST_AUTO_TEST_CASE(edwards_fixed_base_mul_zcash_babyjubjub_bytes_test) { + +// using curve_type = curves::babyjubjub; +// using field_type = typename curve_type::base_field_type; +// using field_value_type = typename field_type::value_type; +// using integral_type = typename field_type::integral_type; +// using value_type = typename curve_type::g1_type::value_type; + +// std::cout << "Edwards curve fixed_base_mul_zcash component bytes test started" << std::endl; +// // typename curve_type::g1_type::value_type p1 = +// // random_element(); +// std::cout << "Started for BabyJubJub" << std::endl; + +// auto bits = bytes_to_bv((const uint8_t*)"abc", 3); + +// typename curve_type::g1_type::value_type expected = +// typename curve_type::g1_type::value_type ( +// field_value_type(integral_type("9869277320722751484529016080276887338184240285836102740267608137843906399765")), +// field_value_type(integral_type("19790690237145851554496394080496962351633528315779989340140084430077208474328")) +// ); + +// test_curves_g1_fixed_base_mul_zcash_component(bits, +// expected); + +// bits = bytes_to_bv((const uint8_t*)"abcdef", 6); + +// expected = +// typename curve_type::g1_type::value_type ( +// field_value_type(integral_type("3152592107782913127811973383449327981421816164636305446433885391611437772003")), +// field_value_type(integral_type("21757413191206167432148830329017031919270024158827230996476733729375089049175")) +// ); + +// test_curves_g1_fixed_base_mul_zcash_component(bits, +// expected); + +// bits = bytes_to_bv((const uint8_t*)"abcdefghijklmnopqrstuvwx", 24); + +// expected = +// typename curve_type::g1_type::value_type ( +// field_value_type(integral_type("3966548799068703226441887746390766667253943354008248106643296790753369303077")), +// field_value_type(integral_type("12849086395963202120677663823933219043387904870880733726805962981354278512988")) +// ); + +// test_curves_g1_fixed_base_mul_zcash_component(bits, +// expected); + +// } + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/curves/r1cs/montgomery.cpp b/libs/blueprint/test/algebra/curves/r1cs/montgomery.cpp new file mode 100644 index 000000000..6bd61b912 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/r1cs/montgomery.cpp @@ -0,0 +1,93 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_montgomery_test + +#include +#include +#include + +#include +#include + +#include + +#include "test_utils.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +// TODO: extend tests +BOOST_AUTO_TEST_SUITE(blueprint_montgomery_operations_manual_test_suite) + +BOOST_AUTO_TEST_CASE(babyjubjub_test) { + using curve_type = curves::babyjubjub; + using element_component = + components::element_g1; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + group_value_type p1(integral_type("13229275355733428112095997489641024783055769870913646006080868652901570030764"), + integral_type("11134533164006840987080284949303064671639289755466531605577535852885854976142")); + group_value_type p2(integral_type("7117928050407583618111176421555214756675765419608405867398403713213306743542"), + integral_type("14577268218881899420966779687690205425227431577728659819975198491127179315626")); + group_value_type p1_plus_p2( + integral_type("15566970094137508604402505312544881598484695740314362381445040160425553677096"), + integral_type("6669854856059550313288855374895200898734184719090215367165264323940796559798")); + + check_affine_montgomery_g1_operations({p1, p2, p1_plus_p2}); + // TODO: there is a little cheat applied, twisted Edwards equivalent points had better calculate separately and + // hard-code into the test + check_montgomery_to_twisted_edwards_component( + {p1, p2, p1_plus_p2}, {p1.to_twisted_edwards(), p2.to_twisted_edwards(), p1_plus_p2.to_twisted_edwards()}); +} + +BOOST_AUTO_TEST_CASE(jubjub_test) { + using curve_type = curves::jubjub; + using element_component = + components::element_g1; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + group_value_type p1(integral_type("5587996947380639047162049858166730204103969545442236298644831829013577070405"), + integral_type("3353220127577076936794824489270300729183005062496343538855887806046831862653")); + group_value_type p2(integral_type("37380265172535953876205871964221324158436172047572074969815349807835370906304"), + integral_type("26055707688826178243212294438612447599848256944592175663688341250454494541524")); + group_value_type p1_plus_p2( + integral_type("31338886305606494662271397096913232944110804555543936006670599257012320678243"), + integral_type("50113340805577397178918081218860537289046253010504685476128585225439863641470")); + + check_affine_montgomery_g1_operations({p1, p2, p1_plus_p2}); + // TODO: there is a little cheat applied, twisted Edwards equivalent points had better calculate separately and + // hard-code into the test + check_montgomery_to_twisted_edwards_component( + {p1, p2, p1_plus_p2}, {p1.to_twisted_edwards(), p2.to_twisted_edwards(), p1_plus_p2.to_twisted_edwards()}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/curves/r1cs/test_utils.hpp b/libs/blueprint/test/algebra/curves/r1cs/test_utils.hpp new file mode 100644 index 000000000..578b632f8 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/r1cs/test_utils.hpp @@ -0,0 +1,259 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_CURVES_TEST_UTILS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_CURVES_TEST_UTILS_HPP + +#include + +#include +#include + +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +enum : std::size_t { + p1, + p2, + p1_plus_p2, +}; + +template +void check_input_points(const std::vector &points) { + using curve_type = Curve; + using element_component = ElementComponent; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + for (const auto &p : points) { + BOOST_CHECK(p.is_well_formed()); + } + BOOST_CHECK(points[p1] + points[p2] == points[p1_plus_p2]); +} + +template +void check_addition_component_auto_allocation(const std::vector &points) { + using curve_type = Curve; + using element_component = ElementComponent; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + components::blueprint bp, bp_copy; + element_component p1_component(bp, points[p1]); + element_component p2_component(bp, points[p2]); + element_component p1_plus_p2_component(bp, points[p1_plus_p2]); + typename element_component::addition_component add_component(bp, p1_component, p2_component); + + add_component.generate_assignments(); + add_component.generate_gates(); + + bp.add_r1cs_constraint(snark::r1cs_constraint({points[p1_plus_p2].X}, {field_type::value_type::one()}, + {add_component.result.X})); + bp_copy = bp; + bp.add_r1cs_constraint(snark::r1cs_constraint({points[p1_plus_p2].Y}, {field_type::value_type::one()}, + {add_component.result.Y})); + BOOST_CHECK(bp.is_satisfied()); + bp_copy.add_r1cs_constraint(snark::r1cs_constraint( + {points[p1_plus_p2].Y}, {-field_type::value_type::one()}, {add_component.result.Y})); + BOOST_CHECK(!bp_copy.is_satisfied()); + + bp_copy = bp; + bp_copy.add_r1cs_constraint(snark::r1cs_constraint( + {-(points[p1_plus_p2].Y)}, {field_type::value_type::one()}, {add_component.result.Y})); + BOOST_CHECK(!bp_copy.is_satisfied()); +} + +template +void check_addition_component_manual_allocation( + const std::vector &points) { + using curve_type = Curve; + using element_component = ElementComponent; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + components::blueprint bp, bp_copy; + element_component p1_component(bp, points[p1]); + element_component p2_component(bp, points[p2]); + element_component result(bp); + // element_component p1_plus_p2_component(bp, points[p1_plus_p2]); + typename element_component::addition_component add_component(bp, p1_component, p2_component, result); + + add_component.generate_assignments(); + add_component.generate_gates(); + BOOST_CHECK(bp.is_satisfied()); + + bp.add_r1cs_constraint( + snark::r1cs_constraint({points[p1_plus_p2].X}, {field_type::value_type::one()}, {result.X})); + bp_copy = bp; + bp.add_r1cs_constraint( + snark::r1cs_constraint({points[p1_plus_p2].Y}, {field_type::value_type::one()}, {result.Y})); + BOOST_CHECK(bp.is_satisfied()); + bp_copy.add_r1cs_constraint( + snark::r1cs_constraint({points[p1_plus_p2].Y}, {-field_type::value_type::one()}, {result.Y})); + BOOST_CHECK(!bp_copy.is_satisfied()); + + bp_copy = bp; + bp_copy.add_r1cs_constraint( + snark::r1cs_constraint({-(points[p1_plus_p2].Y)}, {field_type::value_type::one()}, {result.Y})); + BOOST_CHECK(!bp_copy.is_satisfied()); +} + +template +void check_is_well_formed_component(const std::vector &points) { + using curve_type = Curve; + using element_component = ElementComponent; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + for (const auto &p : points) { + components::blueprint bp, bp_copy; + element_component p_component(bp, p); + typename element_component::is_well_formed_component is_well_component(bp, p_component); + is_well_component.generate_assignments(); + is_well_component.generate_gates(); + BOOST_CHECK(bp.is_satisfied()); + + // point is not on the curve + auto p_copy = p; + // TODO: set random field element would be better + p_copy.X = field_type::value_type::zero(); + element_component p_component_copy(bp_copy, p_copy); + typename element_component::is_well_formed_component is_well_component_copy(bp_copy, p_component_copy); + is_well_component_copy.generate_assignments(); + is_well_component_copy.generate_gates(); + BOOST_CHECK(!bp_copy.is_satisfied()); + } +} + +template< + typename Curve, + typename FromElementComponent = + components::element_g1, + typename ToElementComponent = typename FromElementComponent::to_twisted_edwards_component::to_element_component> +void check_montgomery_to_twisted_edwards_component_auto_allocation( + const std::vector &points_from, + const std::vector &points_to) { + using curve_type = Curve; + using field_type = typename FromElementComponent::field_type; + + assert(points_from.size() == points_to.size()); + check_input_points(points_from); + check_input_points(points_to); + + // TODO: extend test to check wrong values + std::for_each(boost::make_zip_iterator(boost::make_tuple(std::cbegin(points_from), std::cbegin(points_to))), + boost::make_zip_iterator(boost::make_tuple(std::cend(points_from), std::cend(points_to))), + [&](const boost::tuple &t) { + components::blueprint bp, bp_copy; + FromElementComponent p_component(bp, t.template get<0>()); + typename FromElementComponent::to_twisted_edwards_component to_tw_edwards_component(bp, + p_component); + to_tw_edwards_component.generate_assignments(); + to_tw_edwards_component.generate_gates(); + + bp.add_r1cs_constraint(snark::r1cs_constraint(t.template get<1>().X, 1, + to_tw_edwards_component.result.X)); + bp.add_r1cs_constraint(snark::r1cs_constraint(t.template get<1>().Y, 1, + to_tw_edwards_component.result.Y)); + + BOOST_CHECK(bp.is_satisfied()); + }); +} + +template< + typename Curve, + typename FromElementComponent = + components::element_g1, + typename ToElementComponent = typename FromElementComponent::to_twisted_edwards_component::to_element_component> +void check_montgomery_to_twisted_edwards_component_manual_allocation( + const std::vector &points_from, + const std::vector &points_to) { + using curve_type = Curve; + using field_type = typename FromElementComponent::field_type; + + assert(points_from.size() == points_to.size()); + check_input_points(points_from); + check_input_points(points_to); + + // TODO: extend test to check wrong values + std::for_each(boost::make_zip_iterator(boost::make_tuple(std::cbegin(points_from), std::cbegin(points_to))), + boost::make_zip_iterator(boost::make_tuple(std::cend(points_from), std::cend(points_to))), + [&](const boost::tuple &t) { + components::blueprint bp, bp_copy; + FromElementComponent p_component(bp, t.template get<0>()); + ToElementComponent result(bp); + typename FromElementComponent::to_twisted_edwards_component to_tw_edwards_component( + bp, p_component, result); + to_tw_edwards_component.generate_assignments(); + to_tw_edwards_component.generate_gates(); + + bp.add_r1cs_constraint(snark::r1cs_constraint(t.template get<1>().X, 1, result.X)); + bp.add_r1cs_constraint(snark::r1cs_constraint(t.template get<1>().Y, 1, result.Y)); + + BOOST_CHECK(bp.is_satisfied()); + }); +} + +template< + typename Curve, + typename FromElementComponent = + components::element_g1, + typename ToElementComponent = typename FromElementComponent::to_twisted_edwards_component::to_element_component> +void check_montgomery_to_twisted_edwards_component( + const std::vector &points_from, + const std::vector &points_to) { + check_montgomery_to_twisted_edwards_component_auto_allocation( + points_from, points_to); + check_montgomery_to_twisted_edwards_component_manual_allocation( + points_from, points_to); +} + +template> +void check_affine_montgomery_g1_operations(const std::vector &points) { + check_input_points(points); + check_addition_component_auto_allocation(points); + check_addition_component_manual_allocation(points); +} + +template> +void check_affine_twisted_edwards_g1_operations( + const std::vector &points) { + check_input_points(points); + check_addition_component_auto_allocation(points); + check_addition_component_manual_allocation(points); + check_is_well_formed_component(points); +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_CURVES_TEST_UTILS_HPP diff --git a/libs/blueprint/test/algebra/curves/r1cs/twisted_edwards.cpp b/libs/blueprint/test/algebra/curves/r1cs/twisted_edwards.cpp new file mode 100644 index 000000000..a6f47b9f2 --- /dev/null +++ b/libs/blueprint/test/algebra/curves/r1cs/twisted_edwards.cpp @@ -0,0 +1,94 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_twisted_edwards_test + +#include +#include +#include + +#include +#include + +#include + +#include "test_utils.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +// TODO: extend tests +BOOST_AUTO_TEST_SUITE(blueprint_twisted_edwards_operations_manual_test_suite) + +BOOST_AUTO_TEST_CASE(babyjubjub_test) { + using curve_type = curves::babyjubjub; + using element_component = + components::element_g1; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + group_value_type p1(integral_type("10031262171927540148667355526369034398030886437092045105752248699557385197826"), + integral_type("633281375905621697187330766174974863687049529291089048651929454608812697683")); + group_value_type p2(integral_type("5299619240641551281634865583518297030282874472190772894086521144482721001553"), + integral_type("16950150798460657717958625567821834550301663161624707787222815936182638968203")); + group_value_type p1_plus_p2( + integral_type("2763488322167937039616325905516046217694264098671987087929565332380420898366"), + integral_type("15305195750036305661220525648961313310481046260814497672243197092298550508693")); + check_affine_twisted_edwards_g1_operations({p1, p2, p1_plus_p2}); + + // from ethsnark - test_jubjub_add.cpp + p1 = + group_value_type(integral_type("16838670147829712932420991684129000253378636928981731224589534936353716235035"), + integral_type("4937932098257800452675892262662102197939919307515526854605530277406221704113")); + p2 = + group_value_type(integral_type("1538898545681068144632304956674715144385644913102700797899565858629154026483"), + integral_type("2090866097726307108368399316617534306721374642464311386024657526409503477525")); + p1_plus_p2 = + group_value_type(integral_type("6973964026021872993461206321838264291006454903617648820964060641444266170799"), + integral_type("5058405786102109493822166715025707301516781386582502239931016782220981024527")); + check_affine_twisted_edwards_g1_operations({p1, p2, p1_plus_p2}); +} + +BOOST_AUTO_TEST_CASE(jubjub_test) { + using curve_type = curves::jubjub; + using element_component = + components::element_g1; + using field_type = typename element_component::field_type; + using integral_type = typename field_type::integral_type; + using group_value_type = typename element_component::group_value_type; + + group_value_type p1(integral_type("29927994414980659866747158113976867771786823169860303107907009997724489194957"), + integral_type("462950763047385854792912911337076492277172577361226262929952084963852328241")); + group_value_type p2(integral_type("8076246640662884909881801758704306714034609987455869804520522091855516602923"), + integral_type("13262374693698910701929044844600465831413122818447359594527400194675274060458")); + group_value_type p1_plus_p2( + integral_type("45763976842262823160295807685326507554022491488280968540559802656136203717715"), + integral_type("28613822079681605882499475341323216283573790414551935851064205296797669937565")); + check_affine_twisted_edwards_g1_operations({p1, p2, p1_plus_p2}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/combined_inner_product.cpp b/libs/blueprint/test/algebra/fields/plonk/combined_inner_product.cpp new file mode 100644 index 000000000..185eae483 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/combined_inner_product.cpp @@ -0,0 +1,99 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_combined_inner_product_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_combined_inner_product) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + constexpr std::size_t k = 11; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::combined_inner_product; + + std::array input_var_zeta1; + std::array input_var_zeta2; + std::vector public_input; + for (std::size_t i = 0 ; i < k; i++) { + input_var_zeta1[i] = var(0, 2 * i, false, var::column_type::public_input); + input_var_zeta2[i] = var(0, 2 * i + 1, false, var::column_type::public_input); + public_input.push_back(algebra::random_element()); + public_input.push_back(algebra::random_element()); + } + + var xi = var(0, 2 * k, false, var::column_type::public_input); + var r = var(0, 2 * k + 1, false, var::column_type::public_input); + public_input.push_back(algebra::random_element()); + public_input.push_back(algebra::random_element()); + + typename component_type::params_type params = {input_var_zeta1, input_var_zeta2, xi, r}; + + + auto result_check = [](AssignmentType &assignment, + component_type::result_type &real_res) { + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "combined_inner_product_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/element_powers.cpp b/libs/blueprint/test/algebra/fields/plonk/element_powers.cpp new file mode 100644 index 000000000..8b1e8a713 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/element_powers.cpp @@ -0,0 +1,104 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_element_powers_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_element_powers) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + constexpr std::size_t n = 11; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::element_powers; + + var one(0, 0, false, var::column_type::public_input); + var base(0, 1, false, var::column_type::public_input); + typename BlueprintFieldType::value_type base_value = algebra::random_element(); + std::vector public_input = {1, base_value}; + + typename component_type::params_type params = {base, one}; + + std::vector expected_result(n); + typename BlueprintFieldType::value_type last_value = base_value; + if (expected_result.size() > 0) { + expected_result[0] = 1; + } + if (expected_result.size() > 1) { + expected_result[1] = base_value; + } + for (std::size_t i =2; i < n; i++) { + last_value = last_value * base_value; + expected_result[i] = last_value; + } + + + auto result_check = [&expected_result](AssignmentType &assignment, + component_type::result_type &real_res) { + for (std::size_t i = 0; i < n; i++) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "element_powers_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/exponentiation.cpp b/libs/blueprint/test/algebra/fields/plonk/exponentiation.cpp new file mode 100644 index 000000000..5e9049304 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/exponentiation.cpp @@ -0,0 +1,145 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_exponentiation_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +template +void test_exponentiation(std::vector public_input){ + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + constexpr std::size_t exp_size = ExpSize; + using BlueprintFieldType = FieldType; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + using component_type = nil::blueprint::components::exponentiation; + + using var = nil::crypto3::zk::snark::plonk_variable; + + var base(0, 0, false, var::column_type::public_input); + var exponent(0, 1, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {base, exponent}; + + typename BlueprintFieldType::value_type base_value = public_input[0]; + typename BlueprintFieldType::integral_type exponent_value_integral = typename BlueprintFieldType::integral_type(public_input[1].data); + typename BlueprintFieldType::value_type expected_res = power(base_value, exponent_value_integral); + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "exponentiation test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},{0},{}); + + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void exponentiation_tests(){ + for (int i = -2; i < 3; i++){ + for (int j = -2; j < 3; j++){ + typename FieldType::value_type i_fe = i > 0 ? unsigned(i) : FieldType::modulus - unsigned(-i); + typename FieldType::value_type j_fe = j > 0 ? unsigned(j) : FieldType::modulus - unsigned(-j); + + test_exponentiation({i_fe, j_fe}); + } + } + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_exponentiation({generate_random(), generate_random()}); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_exponentiation_one) { + test_exponentiation, 1>({1u, 1u}); + test_exponentiation({1u, 1u}); + test_exponentiation({1u, 1u}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_exponentiation_b1111) { + test_exponentiation, 4>({2379842u, 0b1111u}); + test_exponentiation({2379842u, 0b1111u}); + test_exponentiation({2379842u, 0b1111u}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_exponentiation_pallas) { + using field_type = nil::crypto3::algebra::curves::pallas::base_field_type; + exponentiation_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_exponentiation_vesta) { + using field_type = nil::crypto3::algebra::curves::vesta::base_field_type; + exponentiation_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_exponentiation_bls12) { + using field_type = nil::crypto3::algebra::fields::bls12_fr<381>; + exponentiation_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/field_operations.cpp b/libs/blueprint/test/algebra/fields/plonk/field_operations.cpp new file mode 100644 index 000000000..a71e981b3 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/field_operations.cpp @@ -0,0 +1,362 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_field_operations_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_add(std::vector public_input){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::addition>; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type expected_res = public_input[0] + public_input[1]; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "add test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2},{},{}); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_sub(std::vector public_input){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::subtraction>; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type expected_res = public_input[0] - public_input[1]; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "sub test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2},{},{}); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_mul(std::vector public_input){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::multiplication>; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type expected_res = public_input[0] * public_input[1]; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "mul test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2},{},{}); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_mul_by_const(std::vector public_input, + typename FieldType::value_type y){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 2; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mul_by_constant; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type expected_res = public_input[0] * y; + + auto result_check = [&expected_res, public_input, y](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "mul_by_const test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << y.data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1},{0},{},y); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_div(std::vector public_input, + typename FieldType::value_type expected_res){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 4; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::division>; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2, 3},{},{}); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_div_or_zero(std::vector public_input){ + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 5; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::division_or_zero; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + typename FieldType::value_type expected_res; + if (public_input[1] != FieldType::value_type::zero()) { + expected_res = public_input[0] * public_input[1].inversed(); + } else { + expected_res = FieldType::value_type::zero(); + } + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "div_or_zero test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2, 3, 4},{},{}); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_5_components(int i, int j) { + typename FieldType::value_type i_fe = i > 0 ? i : FieldType::modulus - std::abs(i); + typename FieldType::value_type j_fe = j > 0 ? j : FieldType::modulus - std::abs(j); + + test_add({i_fe, j_fe}); + test_sub({i_fe, j_fe}); + test_mul({i_fe, j_fe}); + test_mul_by_const({i_fe}, j_fe); + test_div_or_zero({i_fe, j_fe}); +} + +template +void test_5_components_on_random_data() { + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + typename FieldType::value_type i = generate_random(); + typename FieldType::value_type j = generate_random(); + + test_add({i, j}); + test_sub({i, j}); + test_mul({i, j}); + test_mul_by_const({i}, j); + test_div_or_zero({i, j}); +} + +template +void field_operations_test() { + for (int i = -2; i < 3; i++){ + for (int j = -2; j < 3; j++){ + test_5_components(i, j); + } + } + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + test_5_components_on_random_data(); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + field_operations_test(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + field_operations_test(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + field_operations_test(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/interpolation.cpp b/libs/blueprint/test/algebra/fields/plonk/interpolation.cpp new file mode 100644 index 000000000..bbcbd8ba1 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/interpolation.cpp @@ -0,0 +1,216 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_interpolation_coefs_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_linear_inter_coefs(const std::vector &public_input){ + constexpr std::size_t WitnessColumns = 7; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::linear_inter_coefs; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + value_type x0 = public_input[0], + z0 = public_input[1], + x1 = public_input[2], + z1 = public_input[3]; + + const bool expected_to_pass = (x0 != x1); + std::array expected_res; + + if (expected_to_pass) { + expected_res = { (x1*z0 - x0*z1) * (x1-x0).inversed(), (z1-z0) * (x1-x0).inversed() }; + } else { + expected_res = {0, 0}; + } + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + BOOST_ASSERT(var_value(assignment, real_res.output[0]) == expected_res[0]); + BOOST_ASSERT(var_value(assignment, real_res.output[1]) == expected_res[1]); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6}, {}, {}); + + if (expected_to_pass) { + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); + } else { + nil::crypto3::test_component_to_fail + (component_instance, desc, public_input, result_check, instance_input); + } +} + +using blueprint::components::detail::det3; + +template +void test_quadratic_inter_coefs(const std::vector &public_input){ + constexpr std::size_t WitnessColumns = 10; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::quadratic_inter_coefs; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input) }; + + value_type x0 = public_input[0], + z0 = public_input[1], + x1 = public_input[2], + z1 = public_input[3], + x2 = public_input[4], + z2 = public_input[5]; + + value_type d = (x1 - x0)*(x2 - x0)*(x2 - x1), + one = 1; + + const bool expected_to_pass = (d != 0); + std::array expected_res; + + if (expected_to_pass) { + expected_res = { det3(std::array{ z0, x0, x0*x0, + z1, x1, x1*x1, + z2, x2, x2*x2 }) * d.inversed(), + det3(std::array{ one, z0, x0*x0, + one, z1, x1*x1, + one, z2, x2*x2 }) * d.inversed(), + det3(std::array{ one, x0, z0, + one, x1, z1, + one, x2, z2 }) * d.inversed() + }; + } else { + expected_res = {0, 0, 0}; + } + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + BOOST_ASSERT(var_value(assignment, real_res.output[0]) == expected_res[0]); + BOOST_ASSERT(var_value(assignment, real_res.output[1]) == expected_res[1]); + BOOST_ASSERT(var_value(assignment, real_res.output[2]) == expected_res[2]); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {}, {}); + + if (expected_to_pass) { + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); + } else { + nil::crypto3::test_component_to_fail + (component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void inter_coefs_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + test_linear_inter_coefs( + {generate_random(), generate_random(), generate_random(), generate_random()}); + test_quadratic_inter_coefs( + {generate_random(), generate_random(), generate_random(), generate_random(), generate_random(), generate_random()}); + } + // one explicitly failing test + typename BlueprintFieldType::value_type x = generate_random(); + test_linear_inter_coefs({x, generate_random(), x, generate_random()}); + test_quadratic_inter_coefs({x, generate_random(), x, generate_random(), generate_random(), generate_random()}); + test_quadratic_inter_coefs({generate_random(), generate_random(), x, generate_random(), x, generate_random()}); + test_quadratic_inter_coefs({x, generate_random(), generate_random(), generate_random(), x, generate_random()}); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + inter_coefs_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + inter_coefs_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + inter_coefs_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/logic_and_flag.cpp b/libs/blueprint/test/algebra/fields/plonk/logic_and_flag.cpp new file mode 100644 index 000000000..5cfc91b7e --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/logic_and_flag.cpp @@ -0,0 +1,154 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_logic_and_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +template +auto test_logic_and_flag(std::vector public_input) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + + zk::snark::plonk_table_description desc = { + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns}; + using ArithmetizationType = + nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using component_type = nil::blueprint::components::logic_and_flag; + using var = typename component_type::var; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array()); + + var x(0, 0, false, var::column_type::public_input); + var y(0, 1, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {x, y}; + + typename BlueprintFieldType::value_type p = public_input[0] * public_input[1]; + typename BlueprintFieldType::value_type expected_result = (p.is_zero() ? p : BlueprintFieldType::value_type::one()); + + auto result_check = [&expected_result](AssignmentType &assignment, + typename component_type::result_type &real_res) { +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "logic and test: \n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_result.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; +#endif + assert(var_value(assignment, real_res.output) == expected_result); + }; + + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_logic_and_flag_random_input_and_zero() { + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + typename FieldType::value_type input = generate_random(); + test_logic_and_flag({input, 0}); + test_logic_and_flag({0, input}); +} + +template +void test_range_check_random_inputs() { + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + typename FieldType::value_type input_x = generate_random(); + typename FieldType::value_type input_y = generate_random(); + + test_logic_and_flag({input_x, input_y}); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_logic_and_test_suite) + +template +void test_witness_size() { + using field_type = typename nil::crypto3::algebra::curves::pallas::base_field_type; + test_logic_and_flag({0, 0}); + test_logic_and_flag_random_input_and_zero(); + test_range_check_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_two_witnesses_all) { + test_witness_size<2>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_three_witnesses_all) { + test_witness_size<3>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_four_witnesses_all) { + test_witness_size<4>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_five_witnesses_all) { + test_witness_size<5>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_six_witnesses_all) { + test_witness_size<6>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_and_flag_seven_witnesses_all) { + test_witness_size<7>(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/logic_or_flag.cpp b/libs/blueprint/test/algebra/fields/plonk/logic_or_flag.cpp new file mode 100644 index 000000000..e926c68ae --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/logic_or_flag.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_logic_or_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "../../../test_plonk_component.hpp" + +template +auto test_logic_or_flag(std::vector public_input) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1 + 1 * (WitnessColumns == 2); + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = + nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using component_type = nil::blueprint::components::logic_or_flag; + using var = typename component_type::var; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array()); + + var x(0, 0, false, var::column_type::public_input); + var y(0, 1, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {x, y}; + + typename BlueprintFieldType::value_type fx = + (public_input[0].is_zero() ? public_input[0] : BlueprintFieldType::value_type::one()); + typename BlueprintFieldType::value_type fy = + (public_input[1].is_zero() ? public_input[1] : BlueprintFieldType::value_type::one()); + typename BlueprintFieldType::value_type expected_result = fx + fy - fx * fy; + + auto result_check = [&expected_result](AssignmentType &assignment, + typename component_type::result_type &real_res) { +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "logic or test: \n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_result.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; +#endif + assert(var_value(assignment, real_res.output) == expected_result); + }; + + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + nil::crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_logic_or_flag_random_inputs() { + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + typename FieldType::value_type input_x = generate_random(); + typename FieldType::value_type input_y = generate_random(); + + test_logic_or_flag({input_x, input_y}); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_logic_or_test_suite) + +template +void test_witness_size() { + using field_type = typename nil::crypto3::algebra::curves::pallas::base_field_type; + test_logic_or_flag({0, 0}); + test_logic_or_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_two_all) { + test_witness_size<2>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_three_all) { + test_witness_size<3>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_four_all) { + test_witness_size<4>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_five_all) { + test_witness_size<5>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_six_all) { + test_witness_size<6>(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_logic_or_flag_seven_all) { + test_witness_size<7>(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/addition.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/addition.cpp new file mode 100644 index 000000000..49859127a --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/addition.cpp @@ -0,0 +1,151 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_field_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include <../test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp> + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_field_add(const std::vector &public_input, + const std::array &expected_res) { + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::addition>; + + std::array input_var_a = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_b = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_var_a, input_var_b}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + for (std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {}, {}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); +} + +template +void test_field_add_useable(typename NonNativeFieldType::value_type a, typename NonNativeFieldType::value_type b){ + using chunked_non_native_type = std::array; + chunked_non_native_type first = chop_non_native(a); + chunked_non_native_type second = chop_non_native(b); + chunked_non_native_type expected_result = chop_non_native(a + b); + std::vector public_input = create_public_input(first, second); + test_field_add(public_input, expected_result); +} + +template +void test_field_add_all_cases(){ + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + typename NonNativeFieldType::value_type f = 0xf; + typename NonNativeFieldType::integral_type f_integral; + for (std::size_t i = 0; i < 63; i++) { + f_integral = typename NonNativeFieldType::integral_type(f.data); + f_integral = (f_integral << 4) + 0xf; + f = typename NonNativeFieldType::value_type(f_integral); + test_field_add_useable(f, f); + } + + + test_field_add_useable(0, 0); + test_field_add_useable(1, 1); + test_field_add_useable(-1, -1); + test_field_add_useable(1, -1); + test_field_add_useable(-1, 0); + test_field_add_useable(1000, -1000); + test_field_add_useable( + glue_non_native({45524, 52353, 68769, 5431}), + glue_non_native({3724, 342453, 5425, 54222})); + + test_field_add_useable( + glue_non_native({1,1,1,1}), + glue_non_native({1,1,1,1})); + + for (std::size_t i = 0; i < 10; i++) { + test_field_add_useable(rand(), rand()); + } + +} +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_non_native_addition_pallas) { + using non_native_field_type = typename crypto3::algebra::fields::curve25519_base_field; + using field_type = crypto3::algebra::curves::pallas::base_field_type; + test_field_add_all_cases(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/bit_composition.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_composition.cpp new file mode 100644 index 000000000..e6241fba3 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_composition.cpp @@ -0,0 +1,291 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_bit_composition_test + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +using mode = blueprint::components::bit_composition_mode; + +template +void test_bit_composition(const std::vector &bits, + typename BlueprintFieldType::value_type expected_res, + std::map, typename BlueprintFieldType::value_type> + patches = {}){ + + constexpr std::size_t WitnessColumns = WitnessesAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::bit_composition; + + assert(bits.size() == BitsAmount); + + bool expected_to_pass = true; + if (CheckInput) { + expected_to_pass = std::accumulate(bits.begin(), bits.end(), true, + [](bool acc, typename BlueprintFieldType::value_type b) { + return acc && (b == 0 || b == 1); + } + ); + } + + typename component_type::input_type instance_input; + instance_input.bits.resize(BitsAmount); + for (std::size_t i = 0; i < BitsAmount; i++) { + instance_input.bits[i] = var(0, i, false, var::column_type::public_input); + } + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, + BitsAmount, CheckInput, Mode); + // Sanity check. + assert(BitsAmount + component_instance.padding_bits_amount() + component_instance.sum_bits_amount() == + WitnessColumns * component_instance.rows_amount); + + auto result_check = [&expected_res, expected_to_pass](AssignmentType &assignment, + typename component_type::result_type &real_res) { + if (expected_to_pass) { + assert(expected_res == var_value(assignment, real_res.output)); + } + }; + + if (!CustomAssignments) { + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, bits, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, CheckInput, Mode); + crypto3::test_empty_component( + component_instance, desc, bits, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, CheckInput, Mode); + } else { + crypto3::test_component_to_fail( + component_instance, desc, bits, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, CheckInput, Mode); + } + } else { + auto custom_assignments = crypto3::generate_patched_assignments(patches); + crypto3::test_component_to_fail_custom_assignments< + component_type, BlueprintFieldType, hash_type, Lambda> + (component_instance, desc, bits, result_check, + custom_assignments, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, CheckInput, Mode); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +constexpr static const std::size_t random_tests_amount = 10; + +template +void calculate_expected_and_test_bit_composition(std::vector &bits) { + using value_type = typename BlueprintFieldType::value_type; + + assert(bits.size() == BitsAmount); + + value_type composed = 0; + auto accumulator = [](value_type acc, value_type b) { + return value_type(2 * acc + (b == 1 ? 1 : 0)); + }; + if (Mode == mode::LSB) { + composed = std::accumulate(bits.rbegin(), bits.rend(), composed, accumulator); + } else { + composed = std::accumulate(bits.begin(), bits.end(), composed, accumulator); + } + + test_bit_composition(bits, composed); + test_bit_composition(bits, composed); +} + +template +std::vector generate_random_bitstring(boost::random::mt19937 &rng) { + std::vector res(BitsAmount); + for (std::size_t i = 0; i < BitsAmount; i++) { + res[i] = rng() % 2 == 1 ? 1 : 0; + } + return res; +} + +template +void test_composition() { + using value_type = typename BlueprintFieldType::value_type; + boost::random::mt19937 rng; + rng.seed(1337); + + std::vector test_bits(BitsAmount, 0); + + calculate_expected_and_test_bit_composition(test_bits); + calculate_expected_and_test_bit_composition(test_bits); + + for (std::size_t i = 0; i < BitsAmount; i++) { + test_bits[i] = 1; + } + calculate_expected_and_test_bit_composition(test_bits); + calculate_expected_and_test_bit_composition(test_bits); + + for (std::size_t j = 0; j < random_tests_amount; j++) { + auto bits = generate_random_bitstring(rng); + calculate_expected_and_test_bit_composition(bits); + calculate_expected_and_test_bit_composition(bits); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_44) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_15_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_26) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_test_9_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_composition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_composition_oops_didnt_pass_bits) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = typename field_type::value_type; + + std::map, value_type> patches; + for (std::size_t i = 0; i < 43; i++) { + value_type expected_res = value_type(2).pow(i + 1); + for (std::size_t j = 0; j < 3; j++) { + for (std::size_t k = 0; k < 15; k++) { + if (j == 2 && k == 14) { + patches[std::make_pair(j, k)] = expected_res; + } else { + patches[std::make_pair(j, k)] = 0; + } + } + } + patches[std::make_pair(2 - (i + 1) / 15, (43 - i) % 15)] = 2; + std::vector input(43); + std::fill(input.begin(), input.end(), 0); + input[42 - i] = 2; + test_bit_composition(input, expected_res, patches); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/bit_decomposition.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_decomposition.cpp new file mode 100644 index 000000000..4fb8a254e --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_decomposition.cpp @@ -0,0 +1,334 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_bit_decomposition_test + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +using mode = blueprint::components::bit_composition_mode; + +template +void test_bit_decomposition(typename BlueprintFieldType::value_type input, + std::vector expected_res, + std::map, typename BlueprintFieldType::value_type> + patches = {}) { + + constexpr std::size_t WitnessColumns = WitnessesAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + + using component_type = blueprint::components::bit_decomposition; + + typename component_type::input_type instance_input = {var(0, 0, false, var::column_type::public_input)}; + + std::vector public_input = {input}; + + bool expected_to_pass = input < value_type(2).pow(BitsAmount); + + auto result_check = [&expected_res, input, expected_to_pass](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "input: " << std::hex << public_input[0].data << "\n"; + for (std::size_t i = 0; i < expected_res.size(); i++){ + std::cout << expected_res[i].data; + } + std::cout << std::endl; + + for (std::size_t i = 0; i < real_res.output.size(); i++){ + std::cout << var_value(assignment, real_res.output[i]).data; + } + std::cout << std::endl; + #endif + if (expected_to_pass) { + for (std::size_t i = 0; i < real_res.output.size(); i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, BitsAmount, Mode); + + assert(BitsAmount + component_instance.padding_bits_amount() + component_instance.sum_bits_amount() == + WitnessColumns * component_instance.rows_amount); + + if (!CustomAssignments) { + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Mode); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Mode); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Mode); + } + } else { + auto custom_assignments = crypto3::generate_patched_assignments(patches); + crypto3::test_component_to_fail_custom_assignments< + component_type, BlueprintFieldType, hash_type, Lambda> + (component_instance, desc, public_input, result_check, custom_assignments, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Mode); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +constexpr static const std::size_t random_tests_amount = 10; + +template +void calc_expected_and_test_bit_decomposition(typename FieldType::value_type input) { + using value_type = typename FieldType::value_type; + using integral_type = typename FieldType::integral_type; + + integral_type input_integral = integral_type(input.data); + + std::vector expected_res = std::vector(BitsAmount); + for (std::size_t i = 0; i < BitsAmount; i++) { + expected_res[Mode == bit_composition_mode::MSB ? BitsAmount - i - 1 : i] = + ((input_integral >> i) & 0b1) == 1 ? value_type::one() : value_type::zero(); + } + input = value_type(input_integral); + test_bit_decomposition(input, expected_res); +} + +template +void test_decomposition_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + + value_type max_elem = (typename BlueprintFieldType::integral_type(1) << BitsAmount) - 1; + + calc_expected_and_test_bit_decomposition(1); + calc_expected_and_test_bit_decomposition(0); + calc_expected_and_test_bit_decomposition(-1); + calc_expected_and_test_bit_decomposition(45524); + calc_expected_and_test_bit_decomposition(max_elem); + calc_expected_and_test_bit_decomposition(max_elem + 1); + + calc_expected_and_test_bit_decomposition(1); + calc_expected_and_test_bit_decomposition(0); + calc_expected_and_test_bit_decomposition(-1); + calc_expected_and_test_bit_decomposition(45524); + calc_expected_and_test_bit_decomposition(max_elem); + calc_expected_and_test_bit_decomposition(max_elem + 1); +} + +template +void test_decomposition_random_inputs() { + using generator_type = nil::crypto3::random::algebraic_engine; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + generator_type rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + value_type max_value = value_type(2).pow(BitsAmount); + + for (std::size_t j = 0; j < random_tests_amount; j++) { + value_type random = rand(); + integral_type input_integral = integral_type(random.data); + input_integral = input_integral & integral_type((max_value - 1).data); + value_type input = value_type(input_integral); + // Sanity check + assert(input < max_value); + + calc_expected_and_test_bit_decomposition(input); + calc_expected_and_test_bit_decomposition(input); + } +} + +template +void test_decomposition_fail_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using generator_type = nil::crypto3::random::algebraic_engine; + generator_type rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + value_type max_value = value_type(2).pow(BitsAmount); + integral_type restriction_modulus = BlueprintFieldType::modulus - integral_type(max_value.data); + + for (std::size_t j = 0; j < random_tests_amount; j++) { + value_type random = rand(); + value_type input = max_value + (value_type(integral_type(random.data) % restriction_modulus)); + // Sanity check + assert(input >= max_value); + + calc_expected_and_test_bit_decomposition(input); + calc_expected_and_test_bit_decomposition(input); + } +} + +template +void test_decomposition() { + test_decomposition_specific_inputs(); + test_decomposition_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_44) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_15_254) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_26) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_pallas_9_254) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_decomposition_test_oops_not_bits) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = typename field_type::value_type; + + std::map, value_type> patches; + for (std::size_t i = 0; i < 43; i++) { + value_type input = value_type(2).pow(i + 1); + for (std::size_t j = 0; j < 3; j++) { + for (std::size_t k = 0; k < 15; k++) { + if (j == 2 && k == 14) { + patches[std::make_pair(j, k)] = input; + } else { + patches[std::make_pair(j, k)] = 0; + } + } + } + patches[std::make_pair(2 - (i + 1) / 15, (43 - i) % 15)] = 2; + std::vector expected_result(43); + std::fill(expected_result.begin(), expected_result.end(), 0); + expected_result[42 - i] = 2; + test_bit_decomposition(input, expected_result, patches); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/bit_shift_constant.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_shift_constant.cpp new file mode 100644 index 000000000..b03f90f27 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/bit_shift_constant.cpp @@ -0,0 +1,347 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_bit_shift_test + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +using nil::blueprint::components::bit_shift_mode; + +template +void test_bit_shift(typename BlueprintFieldType::value_type input, + typename BlueprintFieldType::value_type expected_res){ + + constexpr std::size_t WitnessColumns = WitnessesAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + + using component_type = blueprint::components::bit_shift_constant; + + typename component_type::input_type instance_input = {var(0, 0, false, var::column_type::public_input)}; + + std::vector public_input = {input}; + + bool expected_to_pass = input < value_type(2).pow(BlueprintFieldType::modulus_bits - 1); + + auto result_check = [&expected_res, expected_to_pass] + (AssignmentType &assignment, typename component_type::result_type &real_res) { + if (expected_to_pass) { + assert(var_value(assignment, real_res.output) == expected_res); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, BitsAmount, Shift, Mode); + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Shift, Mode); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, BitsAmount, Shift, Mode); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +constexpr static const std::size_t random_tests_amount = 10; + +template +void calculate_expected_and_test_bit_shift(typename BlueprintFieldType::value_type input) { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + integral_type max = integral_type(1) << BitsAmount; + integral_type input_integral = integral_type(input.data) % max; + value_type expected_res = 0; + + if (Mode == bit_shift_mode::RIGHT) { + expected_res = input_integral >> Shift; + } else if (Mode == bit_shift_mode::LEFT) { + expected_res = (input_integral << Shift) % max; + } + input = value_type(input_integral); + test_bit_shift(input, expected_res); +} + +template +void test_shift_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + value_type max_elem = value_type(integral_type(1) << (BlueprintFieldType::modulus_bits - 1) - 1); + + calculate_expected_and_test_bit_shift(1); + calculate_expected_and_test_bit_shift(0); + calculate_expected_and_test_bit_shift(45524); + calculate_expected_and_test_bit_shift(max_elem); + calculate_expected_and_test_bit_shift(max_elem + 1); +} + +template +void test_shift_random_input() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using generator_type = nil::crypto3::random::algebraic_engine; + generator_type rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + value_type max_value = value_type(2).pow(BitsAmount); + + for (std::size_t j = 0; j < random_tests_amount; j++) { + value_type random = rand(); + integral_type input_integral = integral_type(random.data); + input_integral = input_integral & integral_type((max_value - 1).data); + value_type input = value_type(input_integral); + + calculate_expected_and_test_bit_shift(input); + } +} + + +template +void test_shift() { + test_shift_specific_inputs(); + test_shift_random_input(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_254_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_9_254_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_254_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_8) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_16) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_128) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_9_254_253) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_128_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_128_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_right_test_15_128_17) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_128_32) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_128_64) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_left_test_15_128_17) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bit_shift_constant_test_zero_shift) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_shift(); + test_shift(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp b/libs/blueprint/test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp new file mode 100644 index 000000000..eca34fedc --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp @@ -0,0 +1,59 @@ +template +std::array chop_non_native(typename NonNativeFieldType::value_type input) { + typename NonNativeFieldType::integral_type input_integral = typename NonNativeFieldType::integral_type(input.data); + + std::array output; + + typename NonNativeFieldType::integral_type base = 1; + typename NonNativeFieldType::integral_type mask = (base << 66) - 1; + + output[0] = input_integral & mask; + output[1] = (input_integral >> 66) & mask; + output[2] = (input_integral >> 132) & mask; + output[3] = (input_integral >> 198) & mask; + + return output; +} + +template +typename NonNativeFieldType::value_type glue_non_native(std::array input) { + typename NonNativeFieldType::integral_type base = 1; + typename NonNativeFieldType::integral_type chunk_size = (base << 66); + + std::array input_integral; + + for (std::size_t i = 0; i < input.size(); i++) { + assert(input[i] < chunk_size); + input_integral[i] = typename FieldType::integral_type(input[i].data); + } + + typename NonNativeFieldType::integral_type output_integral = + input_integral[0] + (input_integral[1] << 66) + (input_integral[2] << 132) + (input_integral[3] << 198); + + typename NonNativeFieldType::value_type output = typename NonNativeFieldType::value_type(output_integral); + + return output; +} + +template +std::vector create_public_input(std::array a, + std::array b) { + std::vector public_input; + for (std::size_t i = 0; i < a.size(); i++) { + public_input.push_back(a[i]); + } + for (std::size_t i = 0; i < b.size(); i++) { + public_input.push_back(b[i]); + } + return public_input; +} + +template +std::vector + create_public_input_1_value(std::array b) { + std::vector public_input; + for (std::size_t i = 0; i < b.size(); i++) { + public_input.push_back(b[i]); + } + return public_input; +} \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_checked.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_checked.cpp new file mode 100644 index 000000000..88ecc8e7c --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_checked.cpp @@ -0,0 +1,326 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_comparison_checked_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using nil::blueprint::components::comparison_mode; + +template +auto test_comparison_checked(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y, + const std::map, + typename BlueprintFieldType::value_type> &patches = {}) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + zk::snark::plonk_table_description desc( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = nil::crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + using component_type = nil::blueprint::components::comparison_checked; + + var x_var(0, 0, false, var::column_type::public_input), + y_var(0, 1, false, var::column_type::public_input); + + std::vector public_input = {x, y}; + + typename component_type::input_type instance_input = {x_var, y_var}; + + auto result_check = [](AssignmentType &assignment, typename component_type::result_type &real_res) {}; + + value_type max_val = value_type(2).pow(R); + bool expected_to_pass = x < max_val && y < max_val; + switch (Mode) { + case comparison_mode::LESS_THAN: + expected_to_pass &= x < y; + break; + case comparison_mode::LESS_EQUAL: + expected_to_pass &= x <= y; + break; + case comparison_mode::GREATER_THAN: + expected_to_pass &= x > y; + break; + case comparison_mode::GREATER_EQUAL: + expected_to_pass &= x >= y; + break; + } + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessesAmount; i++) { + witnesses[i] = i; + } + + component_type component_instance = + component_type(witnesses, std::array{0}, std::array{0}, R, Mode); + + if (!CustomAssignments) { + if (expected_to_pass) { + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } else { + nil::crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } + } else { + auto custom_assignment = nil::crypto3::generate_patched_assignments< + BlueprintFieldType, component_type>(patches); + + if (expected_to_pass) { + nil::crypto3::test_component_custom_assignments( + component_instance, desc, public_input, + result_check, custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } else { + nil::crypto3::test_component_to_fail_custom_assignments( + component_instance, desc, public_input, result_check, + custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } + } +} + +template +void test_comparison_checked_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + + test_comparison_checked(0, 42); + test_comparison_checked(400 - 1, 400); + test_comparison_checked(70, 70); + test_comparison_checked(700001, 700001); + test_comparison_checked(-1, 404); + test_comparison_checked(300 - value_type(2).pow(R) + 1, 300); + test_comparison_checked(value_type(2).pow(R) + 1, value_type(2).pow(R)); + test_comparison_checked(value_type(2).pow(R), -1); +} + +template +void test_comparison_checked_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + value_type max_val = value_type(2).pow(R); + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type x = generate_random(), + y = generate_random(); + integral_type x_integral = integral_type(x.data) & integral_type((max_val - 1).data), + y_integral = integral_type(y.data) & integral_type((max_val - 1).data); + x = value_type(x_integral); + y = value_type(y_integral); + + test_comparison_checked(x, y); + test_comparison_checked(y, x); + test_comparison_checked(x, x); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_vesta_15_4_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_vesta_15_128_greater_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_15_251_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_vesta_15_253_less_equal) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_vesta_9_4_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_9_32_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_3_16_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_3_16_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_9_33_less_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_9_64_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_vesta_9_127_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_pallas_15_253_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_mnt4_15_less_equal) { + using field_type = nil::crypto3::algebra::curves::mnt4<298>::base_field_type; + test_comparison_checked_specific_inputs(); + test_comparison_checked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_oops_wrong_chunks) { + using field_type = nil::crypto3::algebra::curves::mnt4<298>::base_field_type; + using value_type = typename field_type::value_type; + + value_type x = -1, + y = value_type(2).pow(8) - 1; + + std::map, value_type> patches = {}; + for (std::size_t i = 2; i < 15; i++) { + patches[std::make_pair(0, i)] = 0; + } + for (std::size_t i = 0; i < 15; i++) { + patches[std::make_pair(1, i)] = 0; + } + patches[std::make_pair(2, 0)] = x; + patches[std::make_pair(2, 1)] = y - x; + + test_comparison_checked(x, y, patches); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_checked_oops_chunk_overflow) { + // Due to the way the component works, the only time first chunk overflow is actually harmful is + // when it occurs on maximum possible R. Testing on R less than that would not reveal if the + // first chunk constraints are correct or not. + using field_type = nil::crypto3::algebra::curves::vesta::base_field_type; + using value_type = typename field_type::value_type; + + value_type val_253 = value_type(2).pow(253) - 1, + val_254 = value_type(2).pow(254) - 1, + difference = val_253 - val_254; + std::map, value_type> patches; + value_type sum, sum_diff; + // 21 rows, 13 padding. + std::array corrections = { + 2, 0, 0, 0, 0, 0x2246, 0x98fc099, 0x4a8dd8c, 0x46eb210, 1 + }; + auto place_gate_chunks = [&patches](std::size_t row, std::size_t idx, uint32_t chunk) { + std::array bits; + for (std::size_t i = 0; i < 32; i++) { + bits[i] = chunk & (1 << (31 - i)); + } + if (idx == 1) { + for (std::size_t i = 0; i < 13; i++) { + patches[std::make_pair(row, i + 2)] = 2 * bits[4 + i * 2] + bits[4 + i * 2 + 1]; + } + patches[std::make_pair(row + 1, 0)] = 2 * bits[30] + bits[31]; + } else { + for (std::size_t i = 0; i < 14; i++) { + patches[std::make_pair(row + 1, i + 1)] = 2 * bits[4 + i * 2] + bits[4 + i * 2 + 1]; + } + } + }; + sum = 3 * value_type(2).pow(28).inversed(); + sum_diff = 0; + for (std::size_t i = 2; i < 21; i += 2) { + if (i != 2) { + place_gate_chunks(i - 2, 0, uint32_t((1 << 28) - 1)); + } else { + place_gate_chunks(i - 2, 0, uint32_t(3)); + } + patches[std::make_pair(i, 0)] = sum = sum * value_type(2).pow(28) + (i != 2) * (value_type(2).pow(28) - 1); + place_gate_chunks(i - 2, 1, uint32_t(corrections[i / 2 - 1])); + patches[std::make_pair(i, 1)] = sum_diff = sum_diff * value_type(2).pow(28) + + value_type(corrections[i / 2 - 1]); + } + assert(sum_diff == difference); + assert(sum == val_254); + test_comparison_checked(val_254, val_253, patches); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_flag.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_flag.cpp new file mode 100644 index 000000000..0a18d6eee --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_flag.cpp @@ -0,0 +1,332 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_comparison_flag_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../../test_plonk_component.hpp" + +using nil::blueprint::components::comparison_mode; + +template +auto test_comparison_flag(typename BlueprintFieldType::value_type x, typename BlueprintFieldType::value_type y, + const std::map, + typename BlueprintFieldType::value_type> &patches = {}) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + zk::snark::plonk_table_description desc( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using component_type = nil::blueprint::components::comparison_flag; + using var = nil::crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + + var x_var(0, 0, false, var::column_type::public_input), + y_var(0, 1, false, var::column_type::public_input); + + std::vector public_input = {x, y}; + + typename component_type::input_type instance_input = {x_var, y_var}; + + value_type max_value = value_type(2).pow(R) - 1; + + bool expected_to_pass = x <= max_value && y <= max_value; + + auto result_check = [&x, &y, expected_to_pass](AssignmentType &assignment, + typename component_type::result_type &real_res) { + if (!expected_to_pass || CustomAssignments) return; + value_type expected_result = 0; + switch (Mode) { + case comparison_mode::FLAG: + expected_result = x > y ? 1 + : x == y ? 0 : -1; + break; + case comparison_mode::LESS_THAN: + expected_result = x < y ? 1 : 0; + break; + case comparison_mode::GREATER_THAN: + expected_result = x > y ? 1 : 0; + break; + case comparison_mode::LESS_EQUAL: + expected_result = x <= y ? 1 : 0; + break; + case comparison_mode::GREATER_EQUAL: + expected_result = x >= y ? 1 : 0; + break; + } + assert(var_value(assignment, real_res.flag) == expected_result); + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessesAmount; i++) { + witnesses[i] = i; + } + + component_type component_instance = + component_type(witnesses, std::array{0}, std::array{0}, R, Mode); + + if (!CustomAssignments) { + if (expected_to_pass) { + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + nil::crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } else { + nil::crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } + } else { + // Currently, the only custom assignment test here is for failure + auto custom_assignment = nil::crypto3::generate_patched_assignments< + BlueprintFieldType, component_type>(patches); + + nil::crypto3::test_component_to_fail_custom_assignments( + component_instance, desc, public_input, result_check, + custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } +} + +template +void test_comparison_flag_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + + test_comparison_flag( + value_type(78), value_type(109)); + test_comparison_flag( + value_type(109), value_type(78)); + test_comparison_flag( + value_type(300), value_type(300)); + + test_comparison_flag( + value_type(-1), value_type(0)); + test_comparison_flag( + value_type(value_type(2).pow(R) - 1), value_type(R)); + test_comparison_flag( + value_type(value_type(2).pow(R) + 1), value_type(value_type(2).pow(R) + 1)); + test_comparison_flag( + value_type(value_type(2).pow(R)), value_type(value_type(2).pow(R) + 2)); +} + +template +void test_comparison_flag_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + value_type max_val = value_type(2).pow(R); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type x = generate_random(), + y = generate_random(); + integral_type x_integral = integral_type(x.data) & integral_type((max_val - 1).data), + y_integral = integral_type(y.data) & integral_type((max_val - 1).data); + x = x_integral; + y = y_integral; + // Sanity check. + assert(x < max_val && y < max_val); + test_comparison_flag(x, y); + test_comparison_flag(y, x); + test_comparison_flag(x, x); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_15_254_flag) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_254_less_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_254_less_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_254_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_254_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_15_135_flag) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_9_32_flag) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_64_FLAG) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_9_128_flag) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_64_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_64_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_77_flag) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_3_64_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_3_64_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + + test_comparison_flag_specific_inputs(); + test_comparison_flag_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_comparison_oops_wrong_chunks) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + using value_type = typename field_type::value_type; + + std::map, value_type> patches; + value_type greater_value = -4; + patches[std::make_pair(1, 0)] = 1024; + patches[std::make_pair(1, 0)] = 512; + // Modifying the chunks. + patches[std::make_pair(1, 0)] = 0; + for (std::size_t i = 3; i < 15; i += 2) { + patches[std::make_pair(1, i)] = 0; + } + // Modifying the flags. + patches[std::make_pair(0, 5)] = patches[std::make_pair(0, 6)] = 0; + patches[std::make_pair(0, 7)] = -2; + patches[std::make_pair(0, 8)] = greater_value; + for (std::size_t i = 9; i < 15; i++) { + patches[std::make_pair(0, i)] = greater_value; + } + patches[std::make_pair(1, 2)] = greater_value; + patches[std::make_pair(2, 2)] = greater_value; + patches[std::make_pair(2, 3)] = 1; + test_comparison_flag(1024, 512, patches); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_unchecked.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_unchecked.cpp new file mode 100644 index 000000000..16cd0457c --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/comparison_unchecked.cpp @@ -0,0 +1,265 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_comparison_unchecked_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using nil::blueprint::components::comparison_mode; + +template +auto test_comparison_unchecked(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y, + const std::map, + typename BlueprintFieldType::value_type> &patches = {}) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + zk::snark::plonk_table_description desc( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = nil::crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + using component_type = nil::blueprint::components::comparison_unchecked; + + var x_var(0, 0, false, var::column_type::public_input), + y_var(0, 1, false, var::column_type::public_input); + + std::vector public_input = {x, y}; + + typename component_type::input_type instance_input = {x_var, y_var}; + + auto result_check = [](AssignmentType &assignment, typename component_type::result_type &real_res) {}; + + value_type max_val = value_type(2).pow(R); + bool expected_to_pass = x < max_val && y < max_val; + switch (Mode) { + case comparison_mode::LESS_THAN: + expected_to_pass &= x < y; + break; + case comparison_mode::LESS_EQUAL: + expected_to_pass &= x <= y; + break; + case comparison_mode::GREATER_THAN: + expected_to_pass &= x > y; + break; + case comparison_mode::GREATER_EQUAL: + expected_to_pass &= x >= y; + break; + case comparison_mode::FLAG: + BOOST_ASSERT_MSG(false, "FLAG mode is not supported, use comparison_flag component instead."); + } + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessesAmount; i++) { + witnesses[i] = i; + } + + component_type component_instance = + component_type(witnesses, std::array{0}, std::array{0}, R, Mode); + + if (!CustomAssignments) { + if (expected_to_pass) { + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } else { + nil::crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } + } else { + auto custom_assignment = nil::crypto3::generate_patched_assignments< + BlueprintFieldType, component_type>(patches); + + if (expected_to_pass) { + nil::crypto3::test_component_custom_assignments( + component_instance, desc, public_input, + result_check, custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } else { + nil::crypto3::test_component_to_fail_custom_assignments( + component_instance, desc, public_input, result_check, + custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, Mode); + } + } +} + +template +void test_comparison_unchecked_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + + value_type max_val = value_type(2).pow(R); + + if (value_type(42) < max_val) { + test_comparison_unchecked(0, 42); + } + if (value_type(400) < max_val) { + test_comparison_unchecked(400 - 1, 400); + } + if (value_type(70) < max_val) { + test_comparison_unchecked(70, 70); + } + if (value_type(700001) < max_val) { + test_comparison_unchecked(700001, 700001); + } +} + +template +void test_comparison_unchecked_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + value_type max_val = value_type(2).pow(R); + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type x = generate_random(), + y = generate_random(); + integral_type x_integral = integral_type(x.data) & integral_type((max_val - 1).data), + y_integral = integral_type(y.data) & integral_type((max_val - 1).data); + x = value_type(x_integral); + y = value_type(y_integral); + + test_comparison_unchecked(x, y); + test_comparison_unchecked(y, x); + test_comparison_unchecked(x, x); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_vesta_15_4_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_vesta_15_128_greater_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_15_251_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_vesta_15_253_less_equal) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_vesta_9_4_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_9_32_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_3_16_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_3_16_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_9_33_less_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_9_64_greater_equal) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_vesta_9_127_less_than) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_pallas_15_253_greater_than) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_comparison_unchecked_mnt4_15_less_equal) { + using field_type = nil::crypto3::algebra::curves::mnt4<298>::base_field_type; + test_comparison_unchecked_specific_inputs(); + test_comparison_unchecked_random_inputs(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/division_remainder.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/division_remainder.cpp new file mode 100644 index 000000000..7ab8eb2c8 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/division_remainder.cpp @@ -0,0 +1,279 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_algebra_fields_plonk_non_native_division_remainder_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +template +auto test_division_remainder(typename BlueprintFieldType::value_type x, + typename BlueprintFieldType::value_type y, + const std::map, + typename BlueprintFieldType::value_type> &patches = {}) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + zk::snark::plonk_table_description desc( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = nil::crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + using component_type = nil::blueprint::components::division_remainder; + + var x_var(0, 0, false, var::column_type::public_input), + y_var(0, 1, false, var::column_type::public_input); + + std::vector public_input = {x, y}; + + typename component_type::input_type instance_input = {x_var, y_var}; + + value_type expected_result_quotient = y != 0 ? value_type(integral_type(x.data) / integral_type(y.data)) : 0, + expected_result_remainder = y != 0 ? value_type(integral_type(x.data) % integral_type(y.data)) : 0; + + value_type max_val = value_type(2).pow(R); + bool expected_to_pass = x < max_val && y < max_val && expected_result_remainder < max_val && + expected_result_quotient < max_val && y != value_type(0); + + auto result_check = [expected_result_quotient, expected_result_remainder, expected_to_pass] + (AssignmentType &assignment, typename component_type::result_type &real_res) { + if (!expected_to_pass) return; + assert(expected_result_quotient == var_value(assignment, real_res.quotient)); + assert(expected_result_remainder == var_value(assignment, real_res.remainder)); + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessesAmount; i++) { + witnesses[i] = i; + } + + component_type component_instance = + component_type(witnesses, std::array{0}, std::array{0}, R, CheckInputs); + + if (!CustomAssignments) { + if (expected_to_pass) { + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, CheckInputs); + nil::crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, CheckInputs); + } else { + nil::crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + R, CheckInputs); + } + } else { + auto custom_assignment = nil::crypto3::generate_patched_assignments< + BlueprintFieldType, component_type>(patches); + + nil::crypto3::test_component_to_fail_custom_assignments( + component_instance, desc, public_input, result_check, + custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R, CheckInputs); + } +} + +template +void test_division_remainder_specific_inputs() { + test_division_remainder(42, 12); + test_division_remainder(120, 0); + + if (CheckInputs) return; + + test_division_remainder(1337, -1); +} + +template +void test_division_remainder_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + boost::random::uniform_int_distribution distribution(0, R-1); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type max_val_x = value_type(2).pow(R), + max_val_y = value_type(2).pow(distribution(seed_seq)); + value_type x = generate_random(), + y = generate_random(); + integral_type x_integral = integral_type(x.data) & integral_type((max_val_x - 1).data), + y_integral = integral_type(y.data) & integral_type((max_val_y - 1).data); + x = value_type(x_integral); + y = value_type(y_integral); + + test_division_remainder(x, y); + test_division_remainder(y, x); + test_division_remainder(x, x); + } + + if (CheckInputs) return; + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type max_val_x = value_type(2).pow(R), + max_val_y = value_type(2).pow(distribution(seed_seq)); + value_type x = generate_random(), + y = generate_random(); + integral_type x_integral = integral_type(x.data) | integral_type((max_val_x).data), + y_integral = integral_type(y.data) & integral_type((max_val_y - 1).data); + x = value_type(x_integral); + y = value_type(y_integral); + + //std::cout << "x = " << x.data << std::endl; + //std::cout << "y = " << y.data << std::endl; + + test_division_remainder(x, y); + test_division_remainder(y, x); + test_division_remainder(x, x); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_vesta_15_126) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_vesta_15_64) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_pallas_15_32) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_pallas_15_16) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_pallas_9_64) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_vesta_9_32) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_vesta_9_16) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_vesta_3_64) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); + test_division_remainder_specific_inputs(); + test_division_remainder_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_oops_y_minus_r_is_zero) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + using value_type = field_type::value_type; + + std::map, value_type> patches; + value_type x = 8, y = 2; + patches[std::make_pair(0, 2)] = 8; + patches[std::make_pair(0, 3)] = 0; + patches[std::make_pair(0, 4)] = 0; + + test_division_remainder(x, y, patches); +} + + +BOOST_AUTO_TEST_CASE(blueprint_plonk_non_native_division_remainder_oops_divide_by_zero) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + using value_type = field_type::value_type; + + std::map, value_type> patches; + value_type x = 8, y = 0; + patches[std::make_pair(0, 2)] = 8; + patches[std::make_pair(0, 3)] = 0; + patches[std::make_pair(0, 4)] = -8; + + test_division_remainder(x, y, patches); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/equality_flag.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/equality_flag.cpp new file mode 100644 index 000000000..41ab617be --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/equality_flag.cpp @@ -0,0 +1,145 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_algebra_fields_plonk_non_native_equality_flag_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_equality_flag(const std::vector &public_input, + bool inequality){ + constexpr std::size_t WitnessColumns = 4; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::equality_flag; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input)}; + + typename BlueprintFieldType::value_type expected_res = + (public_input[0] == public_input[1]) ? + BlueprintFieldType::value_type::one() + : BlueprintFieldType::value_type::zero(); + + auto result_check = [&expected_res, inequality](AssignmentType &assignment, + typename component_type::result_type &real_res) { + if (inequality) { + assert(expected_res != var_value(assignment, real_res.output)); + } else { + assert(expected_res == var_value(assignment, real_res.output)); + } + }; + + component_type component_instance({0, 1, 2, 3, 4}, {}, {}, inequality); + + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, inequality); +} + +template +void equality_neq_flag_tests(bool inequality) { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + test_equality_flag({generate_random(), generate_random()}, inequality); + } +} + +template +void equality_eq_flag_tests(bool inequality) { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + auto random_value = generate_random(); + test_equality_flag({random_value, random_value}, inequality); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + equality_eq_flag_tests(true); + equality_neq_flag_tests(true); + + equality_eq_flag_tests(false); + equality_neq_flag_tests(false); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + equality_eq_flag_tests(true); + equality_neq_flag_tests(true); + + equality_eq_flag_tests(false); + equality_neq_flag_tests(false); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + equality_eq_flag_tests(true); + equality_neq_flag_tests(true); + + equality_eq_flag_tests(false); + equality_neq_flag_tests(false); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/logic_ops.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/logic_ops.cpp new file mode 100644 index 000000000..4b3b60763 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/logic_ops.cpp @@ -0,0 +1,215 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_logic_test + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_logic_component(std::map, bool> expected_mapping) { + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + std::size_t WitnessColumns = (ArgsNum) + 1; + std::size_t PublicInputColumns = 1; + std::size_t ConstantColumns = 0; + std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + constexpr std::size_t lambda = 1; + + std::array witnesses; + for (std::uint32_t i = 0; i < ArgsNum + 1; i++) { + witnesses[i] = i; + } + ComponentType component_instance(witnesses); + + typename ComponentType::input_type instance_input; + for (std::uint32_t i = 0; i < ArgsNum; i++) { + instance_input.input[i] = var(0, i, false, var::column_type::public_input); + } + + auto result_check = [](value_type expected_result) { + return [expected_result](AssignmentType &assignment, + typename ComponentType::result_type &real_res) { + assert(var_value(assignment, real_res.output) == expected_result); + }; + }; + + std::vector public_input; + public_input.resize(ArgsNum); + + for (auto item : expected_mapping) { + for (std::size_t i = 0; i < ArgsNum; i++) { + public_input[i] = item.first[i] ? 1 : 0; + } + + crypto3::test_component( + component_instance, desc, public_input, result_check(item.second ? 1 : 0), instance_input); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_not_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_not; + + std::map, bool> expected_mapping = { + {{false}, true}, {{true}, false}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_and_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_and; + + std::map, bool> expected_mapping = { + {{false, false}, false}, {{false, true}, false}, {{true, false}, false}, {{true, true}, true}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_or_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_or; + + std::map, bool> expected_mapping = { + {{false, false}, false}, {{false, true}, true}, {{true, false}, true}, {{true, true}, true}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_xor_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_xor; + + std::map, bool> expected_mapping = { + {{false, false}, false}, {{false, true}, true}, {{true, false}, true}, {{true, true}, false}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_nand_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_nand; + + std::map, bool> expected_mapping = { + {{false, false}, true}, {{false, true}, true}, {{true, false}, true}, {{true, true}, false}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_logic_nor_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::logic_nor; + + std::map, bool> expected_mapping = { + {{false, false}, true}, {{false, true}, false}, {{true, false}, false}, {{true, true}, false}, + }; + test_logic_component(expected_mapping); +} + +template +void test_select(typename BlueprintFieldType::value_type &cond, + typename BlueprintFieldType::value_type &true_branch, + typename BlueprintFieldType::value_type &false_branch) { + constexpr std::size_t WitnessColumns = 4; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + using component_type = blueprint::components::select; + constexpr std::size_t Lambda = 1; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + typename component_type::input_type instance_input; + for (std::size_t i = 0; i < 3; i++) { + instance_input.input[i] = var(0, i, false, var::column_type::public_input); + } + const value_type expected_result = cond == 1 ? true_branch : false_branch; + + component_type component_instance(std::array({0, 1, 2, 3})); + + auto result_check = [&expected_result](AssignmentType &assignment, + typename component_type::result_type &real_res) { + assert(var_value(assignment, real_res.output) == expected_result); + }; + + std::vector public_input = {cond, true_branch, false_branch}; + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const int random_tests_amount = 10; + +BOOST_AUTO_TEST_CASE(blueprint_non_native_select_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = typename field_type::value_type; + // generate random test data for select component + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + for (std::size_t j = 0; j < random_tests_amount; j++) { + value_type cond = rand().data & value_type(1).data, + true_branch = rand(), + false_branch = rand(); + + test_select(cond, true_branch, false_branch); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/lookup_logic_ops.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/lookup_logic_ops.cpp new file mode 100644 index 000000000..d26c17cec --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/lookup_logic_ops.cpp @@ -0,0 +1,116 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_logic_test + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_logic_component(std::map, bool> expected_mapping) { + using field_value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + std::size_t WitnessColumns = 3; + std::size_t PublicInputColumns = 1; + std::size_t ConstantColumns = 4; + std::size_t SelectorColumns = 4; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t lambda = 1; + + std::array witnesses; + for (std::uint32_t i = 0; i < 2 + 1; i++) { + witnesses[i] = i; + } + ComponentType component_instance(witnesses); + + typename ComponentType::input_type instance_input; + for (std::uint32_t i = 0; i < 2; i++) { + instance_input.input[i] = var(0, i, false, var::column_type::public_input); + } + + auto result_check = [](field_value_type expected_result) { + return [expected_result](AssignmentType &assignment, + typename ComponentType::result_type &real_res) { + assert(var_value(assignment, real_res.output) == expected_result); + }; + }; + + std::vector public_input; + public_input.resize(2); + + for (auto item : expected_mapping) { + for (std::size_t i = 0; i < 2; i++) { + public_input[i] = item.first[i] ? 1 : 0; + } + + crypto3::test_component( + component_instance, desc, public_input, result_check(item.second ? 1 : 0), instance_input); + } +} + + +BOOST_AUTO_TEST_CASE(blueprint_non_native_lookup_logic_and_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::lookup_logic_and; + + std::map, bool> expected_mapping = { + {{false, false}, false}, {{false, true}, false}, {{true, false}, false}, {{true, true}, true}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_lookup_logic_xor_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using component_type = blueprint::components::lookup_logic_xor; + + std::map, bool> expected_mapping = { + {{false, false}, false}, {{false, true}, true}, {{true, false}, true}, {{true, true}, false}, + }; + test_logic_component(expected_mapping); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/multiplication.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/multiplication.cpp new file mode 100644 index 000000000..1abd443b5 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/multiplication.cpp @@ -0,0 +1,227 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_field_test + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include <../test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp> + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_field_mul(const std::vector &public_input, + const std::array + &expected_res) { + + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = + blueprint::components::multiplication>; + + std::array input_var_a = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_b = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_var_a, input_var_b}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::array x, y, expected_chunks, real_chunks; + for (std::size_t i = 0; i < 4; i++) { + x[i] = public_input[i]; + y[i] = public_input[i + 4]; + expected_chunks[i] = expected_res[i]; + real_chunks[i] = var_value(assignment, real_res.output[i]); + } + + std::cout << std::hex; + + std::cout << "_________________________________________________________________________________________________" + "________________________________________________\n"; + std::cout << "input : "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << x[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(x).data << ")\n"; + + std::cout << " "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << y[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(y).data << ")\n"; + + std::cout << "expected: "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << expected_chunks[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(expected_chunks).data << ")\n"; + + std::cout << "real : "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << real_chunks[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(real_chunks).data << ")\n"; +#endif + + for (std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {}, {}); + + if constexpr (Stretched) { + using stretched_component_type = blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_field_mul_with_stretching(const std::vector &public_input, + const std::array &expected_res) { + test_field_mul(public_input, expected_res); + test_field_mul(public_input, expected_res); +} + +template +void test_field_mul_useable(typename NonNativeFieldType::value_type a, typename NonNativeFieldType::value_type b) { + using chunked_non_native_type = std::array; + chunked_non_native_type first = chop_non_native(a); + chunked_non_native_type second = chop_non_native(b); + chunked_non_native_type expected_result = chop_non_native(a * b); + std::vector public_input = + create_public_input(first, second); + test_field_mul_with_stretching(public_input, expected_result); +} + +template +void test_field_mul_all_cases() { + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + typename NonNativeFieldType::value_type f = 0xf; + typename NonNativeFieldType::integral_type f_integral; + for (std::size_t i = 0; i < 256; i++) { + f_integral = typename NonNativeFieldType::integral_type(f.data); + f_integral = (f_integral << 1) + 1; + f = typename NonNativeFieldType::value_type(f_integral); + test_field_mul_useable(f, f); + } + + test_field_mul_useable( + glue_non_native({0, 0, 0x3ffffffffffffffff_cppui_modular255, 0}), + glue_non_native({0, 0, 0x3ffffffffffffffff_cppui_modular255, 0})); + + test_field_mul_useable(0, 0); + test_field_mul_useable(1, 1); + test_field_mul_useable(1, -1); + test_field_mul_useable(-1, -1); + test_field_mul_useable(-1, 0); + test_field_mul_useable(1000, -1000); + test_field_mul_useable( + glue_non_native({45524, 52353, 68769, 5431}), + glue_non_native({3724, 342453, 5425, 54222})); + + test_field_mul_useable(glue_non_native({1, 1, 1, 1}), + glue_non_native({1, 1, 1, 1})); + + test_field_mul_useable(glue_non_native({1, 0, 0, 0}), + glue_non_native({1, 0, 0, 0})); + + test_field_mul_useable( + glue_non_native({0x2BCA8C5A0FDF3D53E_cppui_modular253, 0x39840DDF4C421B2D5_cppui_modular253, + 0x24FCE5728D26931CA_cppui_modular253, 0xFBD6153B4CE63_cppui_modular253}), + glue_non_native({0x3CD7BA9506A76AA1C_cppui_modular253, 0x15C58810F101DDB2F_cppui_modular253, + 0x1AA5750251F6DA658_cppui_modular253, 0x1323F61B67242F_cppui_modular253})); + + test_field_mul_useable( + glue_non_native( + {0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255}), + glue_non_native( + {0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255, 0xc801afd_cppui_modular255})); + for (std::size_t i = 0; i < 10; i++) { + test_field_mul_useable(rand(), rand()); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_multiplication_pallas) { + using non_native_field_type = typename crypto3::algebra::fields::curve25519_base_field; + using field_type = crypto3::algebra::curves::pallas::base_field_type; + test_field_mul_all_cases(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/range.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/range.cpp new file mode 100644 index 000000000..37fdf2d32 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/range.cpp @@ -0,0 +1,141 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_range_test + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include <../test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp> + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_field_range(const std::vector &public_input, + bool expected_to_pass){ + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::range>; + + std::array input_var = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_var}; + + auto result_check = [public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "________________________________________________________________________\ninput: " << std::hex << std::endl; + for (int i = 0; i < 4; i++){ + std::cout << public_input[3-i].data << " "; + } + std::cout << std::endl; + #endif + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{},{}); + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_non_native_range_test0) { + using non_native_field_type = typename crypto3::algebra::fields::curve25519_base_field; + using field_type = crypto3::algebra::curves::pallas::base_field_type; + + test_field_range( + {455245345345345, 523553453454343, 68753453534534689, 54355345344544}, true); + + test_field_range( + create_public_input_1_value( + chop_non_native(1) + ), true); + test_field_range( + create_public_input_1_value( + chop_non_native(0) + ), true); + test_field_range( + create_public_input_1_value( + chop_non_native(-1) + ), true); + + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + for (std::size_t i = 0; i < 10; i++) { + test_field_range( + create_public_input_1_value( + chop_non_native(rand()) + ), true); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_range_test_must_fail) { + test_field_range( //ed25519 modulus + {0x3ffffffffffffffed_cppui_modular255, 0x3ffffffffffffffff_cppui_modular255, 0x3ffffffffffffffff_cppui_modular255, 0x1ffffffffffffff_cppui_modular255}, false + ); + + test_field_range( + {0x3ffffffffffffffff_cppui_modular255, 0x3ffffffffffffffff_cppui_modular255, 0x3ffffffffffffffff_cppui_modular255, 0x1ffffffffffffff_cppui_modular255}, false + ); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/reduction.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/reduction.cpp new file mode 100644 index 000000000..421f6e21e --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/reduction.cpp @@ -0,0 +1,135 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_variable_base_decomposition_edward25519 +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_reduction(std::vector public_input, + typename BlueprintFieldType::value_type expected_res){ + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::reduction>; + + std::array input_state_var = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_state_var}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << std::hex << "___________________________________________________________________________________________________\ninput: "; + for (std::size_t i = 0; i < 8; i++) { + std::cout << public_input[7-i].data << " "; + } + std::cout << "\nexpected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << std::endl; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type ed25519_scalar_modulus = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular512; +constexpr static const crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type one = 1; +constexpr static const crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type max512 = (one<<512)-1; + +template +std::vector vector_from_extended_integral(typename crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type input) { + std::vector pub_inp; + for (std::size_t i = 0; i < 8; i++) { + typename crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type mask = 0xffffffffffffffff_cppui_modular512; + typename FieldType::value_type current = typename FieldType::value_type((input >> (64*i)) & mask); + pub_inp.push_back(current); + } + return pub_inp; +} + +template +void test_reduction_input_expended_integral_calculate_expected(typename crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type input) { + assert(input <= max512); + test_reduction(vector_from_extended_integral(input), typename FieldType::value_type(input % ed25519_scalar_modulus)); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_variable_base_decomposition_edward25519) { + + using curve_type = nil::crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + + test_reduction_input_expended_integral_calculate_expected(max512); + test_reduction_input_expended_integral_calculate_expected(0); + test_reduction_input_expended_integral_calculate_expected(1); + test_reduction_input_expended_integral_calculate_expected(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular512); + test_reduction_input_expended_integral_calculate_expected(ed25519_scalar_modulus); + test_reduction_input_expended_integral_calculate_expected(ed25519_scalar_modulus * 2); + test_reduction_input_expended_integral_calculate_expected(ed25519_scalar_modulus + 1); + test_reduction_input_expended_integral_calculate_expected(ed25519_scalar_modulus - 1); + test_reduction_input_expended_integral_calculate_expected(ed25519_scalar_modulus << 256); + test_reduction_input_expended_integral_calculate_expected( + max512 - crypto3::algebra::curves::ed25519::scalar_field_type::extended_integral_type( + 0x399411b7c309a3dceec73d217f5be65d00e1ba768859347a40611e3449c0f00_cppui_modular512)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/non_native/subtraction.cpp b/libs/blueprint/test/algebra/fields/plonk/non_native/subtraction.cpp new file mode 100644 index 000000000..5ff6e8cdf --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/non_native/subtraction.cpp @@ -0,0 +1,187 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_field_test + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include <../test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp> +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_field_sub(const std::vector &public_input, + const std::array &expected_res) { + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::subtraction>; + + std::array input_var_a = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_b = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_var_a, input_var_b}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::array x, y, expected_chunks, real_chunks; + for (std::size_t i = 0; i < 4; i++) { + x[i] = public_input[i]; + y[i] = public_input[i+4]; + expected_chunks[i] = expected_res[i]; + real_chunks[i] = var_value(assignment, real_res.output[i]); + } + + std::cout << std::hex; + + std::cout << "_________________________________________________________________________________________________________________________________________________\n"; + std::cout << "input : "; + for (std::size_t i = 0; i < 4; i++) {std::cout << x[3-i].data << " ";} + std::cout << "(" << glue_non_native(x).data << ")\n"; + + std::cout << " "; + for (std::size_t i = 0; i < 4; i++) {std::cout << y[3-i].data << " ";} + std::cout << "(" << glue_non_native(y).data << ")\n"; + + std::cout << "expected: "; + for (std::size_t i = 0; i < 4; i++) {std::cout << expected_chunks[3-i].data << " ";} + std::cout << "(" << glue_non_native(expected_chunks).data << ")\n"; + + std::cout << "real : "; + for (std::size_t i = 0; i < 4; i++) {std::cout << real_chunks[3-i].data << " ";} + std::cout << "(" << glue_non_native(real_chunks).data << ")\n"; + #endif + + for (std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_field_sub_useable(typename NonNativeFieldType::value_type a, typename NonNativeFieldType::value_type b){ + using chunked_non_native_type = std::array; + chunked_non_native_type first = chop_non_native(a); + chunked_non_native_type second = chop_non_native(b); + chunked_non_native_type expected_result = chop_non_native(a - b); + std::vector public_input = create_public_input(first, second); + test_field_sub(public_input, expected_result); +} + +template +void test_field_sub_all_cases(){ + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + typename NonNativeFieldType::value_type f = 0xf; + typename NonNativeFieldType::integral_type f_integral; + for (std::size_t i = 0; i < 63; i++) { + f_integral = typename NonNativeFieldType::integral_type(f.data); + f_integral = (f_integral << 4) + 0xf; + f = typename NonNativeFieldType::value_type(f_integral); + test_field_sub_useable(f, f); + } + + + test_field_sub_useable(0, 0); + test_field_sub_useable(1, 1); + test_field_sub_useable(-1, -1); + test_field_sub_useable(1, -1); + test_field_sub_useable(-1, 0); + test_field_sub_useable(1000, -1000); + test_field_sub_useable( + glue_non_native({45524, 52353, 68769, 5431}), + glue_non_native({3724, 342453, 5425, 54222})); + + test_field_sub_useable( + glue_non_native({1,1,1,1}), + glue_non_native({1,1,1,1})); + + test_field_sub_useable( + glue_non_native({1,0,0,0}), + glue_non_native({1,0,0,0})); + + test_field_sub_useable( + glue_non_native({0x2BCA8C5A0FDF3D53E_cppui_modular253, 0x39840DDF4C421B2D5_cppui_modular253, 0x24FCE5728D26931CA_cppui_modular253, 0xFBD6153B4CE63_cppui_modular253}), + glue_non_native({0x3CD7BA9506A76AA1C_cppui_modular253, 0x15C58810F101DDB2F_cppui_modular253, 0x1AA5750251F6DA658_cppui_modular253, 0x1323F61B67242F_cppui_modular253})); + + for (std::size_t i = 0; i < 10; i++) { + test_field_sub_useable(rand(), rand()); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_subtraction_pallas) { + using non_native_field_type = typename crypto3::algebra::fields::curve25519_base_field; + using field_type = crypto3::algebra::curves::pallas::base_field_type; + test_field_sub_all_cases(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/plonk/range_check.cpp b/libs/blueprint/test/algebra/fields/plonk/range_check.cpp new file mode 100644 index 000000000..09fc4c636 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/range_check.cpp @@ -0,0 +1,348 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_range_check_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "../../../test_plonk_component.hpp" + +template +std::size_t clz(typename BlueprintFieldType::value_type value) { + std::size_t count = 0; + typename BlueprintFieldType::integral_type integral = typename BlueprintFieldType::integral_type(value.data); + while (integral != 0) { + integral >>= 1; + ++count; + } + return count; +} + +template +auto test_range_check(typename BlueprintFieldType::value_type input, + const std::map, typename BlueprintFieldType::value_type> + &patches = {}) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + // We use either one or two depending on whether R divides chunk_size or not. + // Since we need to know SelectorColumns amount before the component is actually intialized, + // we use two. + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using component_type = nil::blueprint::components::range_check; + using var = nil::crypto3::zk::snark::plonk_variable; + using value_type = typename BlueprintFieldType::value_type; + + var x(0, 0, false, var::column_type::public_input); + + std::vector public_input = {input}; + + typename component_type::input_type instance_input = {x}; + + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "range_check_test_input: " << std::hex << public_input[0].data << "\n"; + #endif + + auto result_check = [](AssignmentType &assignment, typename component_type::result_type &real_res) {}; + const bool expected_to_pass = input < value_type(2).pow(R); + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessesAmount; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array({0}), + std::array({0}), R); + + if (!CustomAssignments) { + if (expected_to_pass) { + nil::crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R); + } else { + nil::crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R); + } + } else { + auto custom_assignment = nil::crypto3::generate_patched_assignments< + BlueprintFieldType, component_type>(patches); + + if (expected_to_pass) { + nil::crypto3::test_component_custom_assignments( + component_instance, desc, public_input, + result_check, custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R); + } else { + nil::crypto3::test_component_to_fail_custom_assignments( + component_instance, desc, public_input, result_check, + custom_assignment, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, R); + } + } +} + +template +void test_range_check_specific_inputs() { + using value_type = typename BlueprintFieldType::value_type; + + test_range_check(0); + test_range_check(1); + test_range_check(2); + test_range_check(35000); + test_range_check(value_type(2).pow(R) - 1); + test_range_check(-1); + test_range_check(value_type(2).pow(R)); + test_range_check( + 0x4000000000000000000000000000000000000000000000000000000000000000_cppui_modular256 + ); +} + +template +void test_range_check_random_inputs() { + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + value_type max_value = value_type(2).pow(R); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type input = generate_random(); + integral_type input_integral = integral_type(input.data); + input_integral = input_integral & integral_type((max_value - 1).data); + value_type input_scalar = input_integral; + // Sanity check + assert(input_scalar < max_value); + test_range_check(input_scalar); + } +} + +template +void test_range_check_fail_random_inputs(){ + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + value_type max_value = value_type(2).pow(R); + integral_type restriction_modulus = BlueprintFieldType::modulus - integral_type(max_value.data); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + value_type input = generate_random(); + input = max_value + (value_type(integral_type(input.data) % restriction_modulus)); + // Sanity check + assert(input >= max_value); + test_range_check(input); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_bls12_15_64) { + using field_type = nil::crypto3::algebra::fields::bls12_fr<381>; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_64) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_15_64) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_bls12_15_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_15_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_15_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_15_1) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_9_1) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_bls12_9_121) { + using field_type = nil::crypto3::algebra::fields::bls12_fr<381>; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_121) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_128) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_9_121) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_16_32) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_253) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_bls12_9_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_pallas_9_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_vesta_9_254) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check_specific_inputs(); + test_range_check_random_inputs(); + test_range_check_fail_random_inputs(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_oops_first_chunk_overflow) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + using value_type = typename field_type::value_type; + // 17 rows, 1 padding + std::map, value_type> patches; + value_type test_val = value_type(2).pow(253) + 11; + patches[std::make_pair(1, 2)] = value_type(2); + value_type sum = value_type(8).inversed(); + for (std::size_t i = 1; i < 17; i++) { + patches[std::make_pair(i, 0)] = sum = value_type(2).pow(16) * sum + (i != 16 ? 0 : 11); + } + assert(sum == test_val); + // For 17th row we have to also get 11 assigned. + patches[std::make_pair(16, 8)] = 3; + patches[std::make_pair(16, 7)] = 2; + test_range_check(test_val, patches); + + using field_type_2 = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_range_check(2, + {std::make_pair(std::make_pair(1, 14), 2), + std::make_pair(std::make_pair(1, 0 ), 2)}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_range_check_oops_wrong_chunks) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + using value_type = typename field_type::value_type; + + std::map, value_type> patches; + patches[std::make_pair(1, 0)] = 1024; + for (std::size_t i = 1; i < 15; i++) { + patches[std::make_pair(1, i)] = 0; + } + test_range_check(1024, patches); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/plonk/sqrt.cpp b/libs/blueprint/test/algebra/fields/plonk/sqrt.cpp new file mode 100644 index 000000000..fd79b3626 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/plonk/sqrt.cpp @@ -0,0 +1,119 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_fields_sqrt_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; +using namespace nil::blueprint; + +template +void test_sqrt(typename BlueprintFieldType::value_type input) { + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = components::sqrt; + + typename BlueprintFieldType::value_type expected_res = input.sqrt(); + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input)}; + + std::vector public_input = {input}; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, {0}, {0}); + + auto result_check = [&expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + assert(expected_res == var_value(assignment, real_res.output)); + }; + + test_component + (component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sqrt_test) { + using field_type = typename algebra::curves::pallas::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < random_tests_amount; i++) { + auto random_square = generate_random(); + random_square = random_square * random_square; + test_sqrt(random_square); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sqrt_nonresidue_test) { + using field_type = typename algebra::curves::vesta::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < random_tests_amount; i++) { + auto random_square = generate_random(); + while (!random_square.is_square()) { + random_square = generate_random(); + } + test_sqrt(random_square); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/r1cs/arithmetic.hpp b/libs/blueprint/test/algebra/fields/r1cs/arithmetic.hpp new file mode 100644 index 000000000..f5320bd0d --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/arithmetic.hpp @@ -0,0 +1,85 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_FP2_COMPONENT_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_FP2_COMPONENT_TEST_HPP + +#include + +using namespace nil::crypto3::zk; + +template class Fpk_variableT, + template class Fpk_mul_componentT> +blueprint test_field_element_mul(typename FieldType::value_type a_value, + typename FieldType::value_type b_value){ + using field_type = FieldType; + using element_component = Fpk_variableT; + using element_mul_component = Fpk_mul_componentT; + using base_field_type = typename field_type::base_field_type; + + blueprint bp; + + element_component A(bp, a_value); + element_component B(bp, b_value); + element_component result(bp); + + element_mul_component el_mul_instance(bp, A, B, result); + el_mul_instance.generate_gates(); + el_mul_instance.generate_assignments(); + + const typename field_type::value_type res = result.get_element(); + + BOOST_CHECK(bp.is_satisfied()); + BOOST_CHECK(res == (a_value * b_value)); + + return bp; +} + +template class Fpk_variableT, + template class Fpk_squared_componentT> +blueprint test_field_element_squared(typename FieldType::value_type a_value){ + using field_type = FieldType; + using element_component = Fpk_variableT; + using element_squared_component = Fpk_squared_componentT; + using base_field_type = typename field_type::base_field_type; + + blueprint bp; + + element_component A(bp, a_value); + element_component result(bp); + + element_squared_component el_squared_instance(bp, A, result); + el_squared_instance.generate_gates(); + el_squared_instance.generate_assignments(); + + const typename field_type::value_type res = result.get_element(); + + BOOST_CHECK(bp.is_satisfied()); + BOOST_CHECK(res == (a_value.squared())); + + return bp; +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_ELEMENT_FP2_COMPONENT_TEST_HPP diff --git a/libs/blueprint/test/algebra/fields/r1cs/exponentiation.cpp b/libs/blueprint/test/algebra/fields/r1cs/exponentiation.cpp new file mode 100644 index 000000000..738ae9911 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/exponentiation.cpp @@ -0,0 +1,98 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE exponentiation_components_test + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template class Fpk_variableT, template class Fpk_mul_componentT, + template class Fpk_sqr_componentT> +void test_exponentiation_component(const typename FpkT::integral_type &power) { + typedef typename FpkT::base_field_type FieldType; + + blueprint bp; + Fpk_variableT x(bp); + Fpk_variableT x_to_power(bp); + components::exponentiation_component + exp_component(bp, x, power, x_to_power); + exp_component.generate_gates(); + + for (std::size_t i = 0; i < 10; ++i) { + const typename FpkT::value_type x_val = random_element(); + x.generate_assignments(x_val); + exp_component.generate_assignments(); + const typename FpkT::value_type res = x_to_power.get_element(); + BOOST_CHECK(bp.is_satisfied()); + BOOST_CHECK(res == (x_val.pow(power))); + } + std::cout << "Number of constraints: " << bp.num_constraints() << std::endl; + std::cout << "Power: " << power << std::endl; +} + +BOOST_AUTO_TEST_SUITE(exponentiation_component_test_suite) + +BOOST_AUTO_TEST_CASE(exponentiation_component_mnt4_298_test_case) { + + std::cout << "Testing mnt4<298>: " << std::endl; + + test_exponentiation_component::pairing::fqk_type, components::element_fp4, + components::element_fp4_mul, components::element_fp4_squared>( + curves::mnt4<298>::pairing::final_exponent_last_chunk_abs_of_w0); + +} + +BOOST_AUTO_TEST_CASE(exponentiation_component_mnt6_298_test_case) { + + std::cout << "Testing mnt6<298>: " << std::endl; + + test_exponentiation_component::pairing::fqk_type, components::element_fp6_2over3, + components::element_fp6_2over3_mul, components::element_fp6_2over3_squared>( + curves::mnt6<298>::pairing::final_exponent_last_chunk_abs_of_w0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp2.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp2.cpp new file mode 100644 index 000000000..18f0a0937 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp2.cpp @@ -0,0 +1,95 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp2_test + +#include +#include + +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp2 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp2 mul component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp2 squared component test for MNT4-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp2 squared component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp2_verification.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp2_verification.cpp new file mode 100644 index 000000000..4965c204d --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp2_verification.cpp @@ -0,0 +1,112 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp2_test + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +#include "../../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp2 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component>(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp2 mul component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp2 squared component test for MNT4-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component>(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp2 squared component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp3.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp3.cpp new file mode 100644 index 000000000..4b0e6424e --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp3.cpp @@ -0,0 +1,95 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp3_test + +#include +#include + +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp3 mul component test for MNT6-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp3 mul component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp3 squared component test for MNT6-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp3 squared component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp3_verification.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp3_verification.cpp new file mode 100644 index 000000000..94921fcc9 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp3_verification.cpp @@ -0,0 +1,112 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp3_test + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +#include "../../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp3 mul component test for MNT6-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp3 mul component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::template g2_type<>::field_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp3 squared component test for MNT6-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp3 squared component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp4.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp4.cpp new file mode 100644 index 000000000..97890e04e --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp4.cpp @@ -0,0 +1,95 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp2_test + +#include +#include + +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp4 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp4 mul component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp4 squared component test for MNT4-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp4 squared component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp4_verification.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp4_verification.cpp new file mode 100644 index 000000000..43c8997c6 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp4_verification.cpp @@ -0,0 +1,112 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp2_test + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +#include "../../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp4 mul component test for MNT4-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp4 mul component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt4_case) { + using curve_type = typename curves::mnt4<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 10; + std::cout << "Starting element Fp4 squared component test for MNT4-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = + test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp4 squared component test for MNT4-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3.cpp new file mode 100644 index 000000000..92f70a68f --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3.cpp @@ -0,0 +1,94 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp3_test + +#include +#include + +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp6_2over3 mul component test for MNT6-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++){ + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = test_field_element_mul(a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp6_2over3 mul component test for MNT6-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 500; + std::cout << "Starting element Fp6_2over3 squared component test for MNT6-298 " << tries_quantity << " times ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++){ + typename field_type::value_type a_value = random_element(); + + blueprint bp = test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp6_2over3 squared component test for MNT6-298 finished, average time: " << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3_verification.cpp b/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3_verification.cpp new file mode 100644 index 000000000..7e26e7bc7 --- /dev/null +++ b/libs/blueprint/test/algebra/fields/r1cs/fp6_2over3_verification.cpp @@ -0,0 +1,114 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE element_fp3_test + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "arithmetic.hpp" + +#include "../../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(field_element_arithmetic_component_test_suite) + +BOOST_AUTO_TEST_CASE(field_element_mul_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 5; + std::cout << "Starting element Fp6_2over3 mul component test for MNT6-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + typename field_type::value_type b_value = random_element(); + + blueprint bp = + test_field_element_mul( + a_value, b_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp6_2over3 mul component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_CASE(field_element_squared_component_test_mnt6_case) { + using curve_type = typename curves::mnt6<298>; + using field_type = typename curve_type::gt_type; + using base_field_type = typename curve_type::base_field_type; + + std::size_t tries_quantity = 5; + std::cout << "Starting element Fp6_2over3 squared component test for MNT6-298 " << tries_quantity << " times ..." + << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < tries_quantity; i++) { + typename field_type::value_type a_value = random_element(); + + blueprint bp = test_field_element_squared(a_value); + + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Element Fp6_2over3 squared component test for MNT6-298 finished, average time: " + << elapsed.count() * 1e-9 / tries_quantity << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.cpp b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.cpp new file mode 100644 index 000000000..8d17abedd --- /dev/null +++ b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.cpp @@ -0,0 +1,62 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for gadgets for Miller loops. + + See weierstrass_miller_loop.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#define BOOST_TEST_MODULE weierstrass_miller_loop_components_test + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "weierstrass_miller_loop.hpp" + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(weierstrass_miller_loop_components_test_suite) + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt4_miller_loop_components_test) { + test_mnt_miller_loop>(); +} + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt6_miller_loop_components_test) { + test_mnt_miller_loop>(); +} + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt4_e_over_e_miller_loop_components_test) { + test_mnt_e_over_e_miller_loop>(); +} + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt6_e_over_e_miller_loop_components_test) { + test_mnt_e_over_e_miller_loop>(); +} + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt4_e_times_e_miller_loop_components_test) { + test_mnt_e_times_e_over_e_miller_loop>(); +} + +BOOST_AUTO_TEST_CASE(weierstrass_miller_loop_mnt6_e_times_e_miller_loop_components_test) { + test_mnt_e_times_e_over_e_miller_loop>(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.hpp b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.hpp new file mode 100644 index 000000000..aa637d3ab --- /dev/null +++ b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/miller_loop.hpp @@ -0,0 +1,255 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for components for Miller loops. + + See weierstrass_miller_loop.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_TEST_HPP + +#include + +#include + + +#include +#include + +#include +#include + +#include +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_mnt_miller_loop() { + + using curve_type = CurveType; + using pair_curve_type = typename curve_type::pairing::pair_curve_type; + using curve_pairing_policy = typename curve_type::pairing; + using other_curve_pairing_policy = typename pair_curve_type::pairing; + + using component_policy = components::detail::basic_pairing_component_policy; + + blueprint bp; + typename pair_curve_type::template g1_type<>::value_type P_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + components::element_g1 P(bp); + components::element_g2 Q(bp); + + components::g1_precomputation prec_P; + components::g2_precomputation prec_Q; + + components::precompute_G1_component compute_prec_P(bp, P, prec_P); + components::precompute_G2_component compute_prec_Q(bp, Q, prec_Q); + + typename component_policy::Fqk_variable_type result(bp); + components::mnt_miller_loop_component miller(bp, prec_P, prec_Q, result); + + compute_prec_P.generate_gates(); + + compute_prec_Q.generate_gates(); + + miller.generate_gates(); + + P.generate_assignments(P_val); + compute_prec_P.generate_assignments(); + Q.generate_assignments(Q_val); + compute_prec_Q.generate_assignments(); + miller.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P = + affine_ate_precompute_g1(P_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q = + affine_ate_precompute_g2(Q_val); + typename other_curve_pairing_policy::fqk_type::value_type native_result = + affine_ate_miller_loop(native_prec_P, native_prec_Q); + + BOOST_CHECK(result.get_element() == native_result); + std::cout << "number of constraints for Miller loop" << bp.num_constraints() << std::endl; +} + +template +void test_mnt_e_over_e_miller_loop() { + + using curve_type = CurveType; + using pair_curve_type = typename curve_type::pairing::pair_curve_type; + using curve_pairing_policy = typename curve_type::pairing; + using other_curve_pairing_policy = typename pair_curve_type::pairing; + + using component_policy = components::detail::basic_pairing_component_policy; + + blueprint bp; + typename pair_curve_type::template g1_type<>::value_type P1_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q1_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + typename pair_curve_type::template g1_type<>::value_type P2_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q2_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + components::element_g1 P1(bp); + components::element_g2 Q1(bp); + components::element_g1 P2(bp); + components::element_g2 Q2(bp); + + components::g1_precomputation prec_P1; + components::precompute_G1_component compute_prec_P1(bp, P1, prec_P1); + components::g1_precomputation prec_P2; + components::precompute_G1_component compute_prec_P2(bp, P2, prec_P2); + components::g2_precomputation prec_Q1; + components::precompute_G2_component compute_prec_Q1(bp, Q1, prec_Q1); + components::g2_precomputation prec_Q2; + components::precompute_G2_component compute_prec_Q2(bp, Q2, prec_Q2); + + typename component_policy::Fqk_variable_type result(bp); + components::mnt_e_over_e_miller_loop_component miller(bp, prec_P1, prec_Q1, prec_P2, prec_Q2, result); + + compute_prec_P1.generate_gates(); + compute_prec_P2.generate_gates(); + + compute_prec_Q1.generate_gates(); + compute_prec_Q2.generate_gates(); + + miller.generate_gates(); + + P1.generate_assignments(P1_val); + compute_prec_P1.generate_assignments(); + Q1.generate_assignments(Q1_val); + compute_prec_Q1.generate_assignments(); + P2.generate_assignments(P2_val); + compute_prec_P2.generate_assignments(); + Q2.generate_assignments(Q2_val); + compute_prec_Q2.generate_assignments(); + miller.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P1 = + affine_ate_precompute_g1(P1_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q1 = + affine_ate_precompute_g2(Q1_val); + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P2 = + affine_ate_precompute_g1(P2_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q2 = + affine_ate_precompute_g2(Q2_val); + typename other_curve_pairing_policy::fqk_type::value_type native_result = + (affine_ate_miller_loop(native_prec_P1, native_prec_Q1) * + affine_ate_miller_loop(native_prec_P2, native_prec_Q2).inversed()); + + BOOST_CHECK(result.get_element() == native_result); + std::cout << "number of constraints for e over e Miller loop " << bp.num_constraints() << std::endl; +} + +template +void test_mnt_e_times_e_over_e_miller_loop() { + + using curve_type = CurveType; + using pair_curve_type = typename curve_type::pairing::pair_curve_type; + using curve_pairing_policy = typename curve_type::pairing; + using other_curve_pairing_policy = typename pair_curve_type::pairing; + + using component_policy = components::detail::basic_pairing_component_policy; + + blueprint bp; + typename pair_curve_type::template g1_type<>::value_type P1_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q1_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + typename pair_curve_type::template g1_type<>::value_type P2_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q2_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + typename pair_curve_type::template g1_type<>::value_type P3_val = + random_element() * pair_curve_type::template g1_type<>::value_type::one(); + typename pair_curve_type::template g2_type<>::value_type Q3_val = + random_element() * pair_curve_type::template g2_type<>::value_type::one(); + + components::element_g1 P1(bp); + components::element_g2 Q1(bp); + components::element_g1 P2(bp); + components::element_g2 Q2(bp); + components::element_g1 P3(bp); + components::element_g2 Q3(bp); + + components::g1_precomputation prec_P1; + components::precompute_G1_component compute_prec_P1(bp, P1, prec_P1); + components::g1_precomputation prec_P2; + components::precompute_G1_component compute_prec_P2(bp, P2, prec_P2); + components::g1_precomputation prec_P3; + components::precompute_G1_component compute_prec_P3(bp, P3, prec_P3); + components::g2_precomputation prec_Q1; + components::precompute_G2_component compute_prec_Q1(bp, Q1, prec_Q1); + components::g2_precomputation prec_Q2; + components::precompute_G2_component compute_prec_Q2(bp, Q2, prec_Q2); + components::g2_precomputation prec_Q3; + components::precompute_G2_component compute_prec_Q3(bp, Q3, prec_Q3); + + typename component_policy::Fqk_variable_type result(bp); + components::mnt_e_times_e_over_e_miller_loop_component miller(bp, prec_P1, prec_Q1, prec_P2, prec_Q2, + prec_P3, prec_Q3, result); + + compute_prec_P1.generate_gates(); + compute_prec_P2.generate_gates(); + compute_prec_P3.generate_gates(); + + compute_prec_Q1.generate_gates(); + compute_prec_Q2.generate_gates(); + compute_prec_Q3.generate_gates(); + + miller.generate_gates(); + + P1.generate_assignments(P1_val); + compute_prec_P1.generate_assignments(); + Q1.generate_assignments(Q1_val); + compute_prec_Q1.generate_assignments(); + P2.generate_assignments(P2_val); + compute_prec_P2.generate_assignments(); + Q2.generate_assignments(Q2_val); + compute_prec_Q2.generate_assignments(); + P3.generate_assignments(P3_val); + compute_prec_P3.generate_assignments(); + Q3.generate_assignments(Q3_val); + compute_prec_Q3.generate_assignments(); + miller.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P1 = + affine_ate_precompute_g1(P1_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q1 = + affine_ate_precompute_g2(Q1_val); + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P2 = + affine_ate_precompute_g1(P2_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q2 = + affine_ate_precompute_g2(Q2_val); + typename other_curve_pairing_policy::affine_ate_g1_precomp native_prec_P3 = + affine_ate_precompute_g1(P3_val); + typename other_curve_pairing_policy::affine_ate_g2_precomp native_prec_Q3 = + affine_ate_precompute_g2(Q3_val); + typename other_curve_pairing_policy::fqk_type::value_type native_result = + (affine_ate_miller_loop(native_prec_P1, native_prec_Q1) * + affine_ate_miller_loop(native_prec_P2, native_prec_Q2) * + affine_ate_miller_loop(native_prec_P3, native_prec_Q3).inversed()); + + BOOST_CHECK(result.get_element() == native_result); + std::cout << "number of constraints for e times e over e Miller loop " << bp.num_constraints() << std::endl; +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_WEIERSTRASS_MILLER_LOOP_TEST_HPP diff --git a/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/precomputation.cpp b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/precomputation.cpp new file mode 100644 index 000000000..c3858d432 --- /dev/null +++ b/libs/blueprint/test/algebra/pairing/weierstrass/r1cs/precomputation.cpp @@ -0,0 +1,107 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE weierstrass_precomputation_components_test + +#include + +#include +#include + +#include +#include + +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_element_g1_precomp() { + blueprint bp; + typename CurveType::pairing::pair_curve_type::template g1_type<>::value_type g_val = + algebra::random_element() * + CurveType::pairing::pair_curve_type::template g1_type<>::value_type::one(); + + element_g1 g(bp); + g1_precomputation precomp; + precompute_G1_component do_precomp(bp, g, precomp); + do_precomp.generate_gates(); + + g.generate_assignments(g_val); + do_precomp.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename CurveType::pairing::g1_precomp const_precomp(bp, g_val); + + typename CurveType::pairing::pair_curve_type::pairing::affine_ate_g1_precomp native_precomp = + CurveType::pairing::pair_curve_type::affine_ate_precompute_g1(g_val); + BOOST_CHECK(precomp.PY_twist_squared->get_element() == native_precomp.PY_twist_squared); + BOOST_CHECK(const_precomp.PY_twist_squared->get_element() == native_precomp.PY_twist_squared); +} + +template +void test_element_g2_precomp() { + blueprint bp; + typename CurveType::pairing::pair_curve_type::template g2_type<>::value_type g_val = + algebra::random_element() * + CurveType::pairing::pair_curve_type::template g2_type<>::value_type::one(); + + element_g2 g(bp); + g2_precomputation precomp; + precompute_G2_component do_precomp(bp, g, precomp); + do_precomp.generate_gates(); + + g.generate_assignments(g_val); + do_precomp.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename CurveType::pairing::pair_curve_type::pairing::affine_ate_g2_precomp native_precomp = + CurveType::pairing::pair_curve_type::affine_ate_precompute_g2(g_val); + + BOOST_CHECK(precomp.coeffs.size() - 1 == + native_precomp.coeffs.size()); // the last precomp is unused, but remains for convenient programming + for (std::size_t i = 0; i < native_precomp.coeffs.size(); ++i) { + BOOST_CHECK(precomp.coeffs[i]->RX->get_element() == native_precomp.coeffs[i].old_RX); + BOOST_CHECK(precomp.coeffs[i]->RY->get_element() == native_precomp.coeffs[i].old_RY); + BOOST_CHECK(precomp.coeffs[i]->gamma->get_element() == native_precomp.coeffs[i].gamma); + BOOST_CHECK(precomp.coeffs[i]->gamma_X->get_element() == native_precomp.coeffs[i].gamma_X); + } + + std::cout << "number of constraints for G2 precomp: " << bp.num_constraints() << std::endl; +} + +BOOST_AUTO_TEST_SUITE(weierstrass_precomputation_components_test_suite) + +BOOST_AUTO_TEST_CASE(weierstrass_precomputation_components_test) { + + test_all_set_commitment_components>(); + test_all_set_commitment_components>(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/basic_components.cpp b/libs/blueprint/test/basic_components.cpp new file mode 100644 index 000000000..3f3c6e60f --- /dev/null +++ b/libs/blueprint/test/basic_components.cpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE basic_components_test + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_disjunction_component(size_t n) { + blueprint::blueprint bp; + blueprint::detail::blueprint_variable_vector inputs; + inputs.allocate(bp, n); + + blueprint::detail::blueprint_variable_vector output; + output.allocate(bp); + + nil::crypto3::blueprint::components::disjunction d(bp, inputs, output); + d.generate_gates(); + + for (std::size_t w = 0; w < 1ul << n; ++w) { + for (std::size_t j = 0; j < n; ++j) { + bp.val(inputs[j]) = typename FieldType::value_type((w & (1ul << j)) ? 1 : 0); + } + + d.generate_assignments(); + + BOOST_CHECK(bp.val(output) == (w ? FieldType::value_type::one() : FieldType::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + + bp.val(output) = (w ? FieldType::value_type::zero() : FieldType::value_type::one()); + BOOST_CHECK(!bp.is_satisfied()); + } +} + +template +void test_conjunction_component(size_t n) { + blueprint::blueprint bp; + blueprint::detail::blueprint_variable_vector inputs; + inputs.allocate(bp, n); + + blueprint::detail::blueprint_variable output; + output.allocate(bp); + + nil::crypto3::blueprint::components::conjunction c(bp, inputs, output); + c.generate_gates(); + + for (std::size_t w = 0; w < 1ul << n; ++w) { + for (std::size_t j = 0; j < n; ++j) { + bp.val(inputs[j]) = (w & (1ul << j)) ? FieldType::value_type::one() : FieldType::value_type::zero(); + } + + c.generate_assignments(); + + BOOST_CHECK(bp.val(output) == + (w == (1ul << n) - 1 ? FieldType::value_type::one() : FieldType::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + + bp.val(output) = (w == (1ul << n) - 1 ? FieldType::value_type::zero() : FieldType::value_type::one()); + BOOST_CHECK(!bp.is_satisfied()); + } +} + +template +void test_comparison_component(size_t n) { + blueprint::blueprint bp; + + blueprint::detail::blueprint_variable A, B, less, less_or_eq; + A.allocate(bp); + B.allocate(bp); + less.allocate(bp); + less_or_eq.allocate(bp); + + nil::crypto3::blueprint::components::comparison cmp(bp, n, A, B, less, less_or_eq); + cmp.generate_gates(); + + for (std::size_t a = 0; a < 1ul << n; ++a) { + for (std::size_t b = 0; b < 1ul << n; ++b) { + bp.val(A) = typename FieldType::value_type(a); + bp.val(B) = typename FieldType::value_type(b); + + cmp.generate_assignments(); + + BOOST_CHECK(bp.val(less) == (a < b ? FieldType::value_type::one() : FieldType::value_type::zero())); + BOOST_CHECK(bp.val(less_or_eq) == (a <= b ? FieldType::value_type::one() : FieldType::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + } + } +} + +template +void test_inner_product_component(size_t n) { + blueprint::blueprint bp; + blueprint::detail::blueprint_variable_vector A; + A.allocate(bp, n); + blueprint::detail::blueprint_variable_vector B; + B.allocate(bp, n); + + blueprint::detail::blueprint_variable result; + result.allocate(bp); + + nil::crypto3::blueprint::components::inner_product g(bp, A, B, result); + g.generate_gates(); + + for (std::size_t i = 0; i < 1ul << n; ++i) { + for (std::size_t j = 0; j < 1ul << n; ++j) { + std::size_t correct = 0; + for (std::size_t k = 0; k < n; ++k) { + bp.val(A[k]) = (i & (1ul << k) ? FieldType::value_type::one() : FieldType::value_type::zero()); + bp.val(B[k]) = (j & (1ul << k) ? FieldType::value_type::one() : FieldType::value_type::zero()); + correct += ((i & (1ul << k)) && (j & (1ul << k)) ? 1 : 0); + } + + g.generate_assignments(); + + BOOST_CHECK(bp.val(result) == typename FieldType::value_type(correct)); + BOOST_CHECK(bp.is_satisfied()); + + bp.val(result) = typename FieldType::value_type(100 * n + 19); + BOOST_CHECK(!bp.is_satisfied()); + } + } +} + +template +void test_loose_multiplexing_component(size_t n) { + blueprint::blueprint bp; + + blueprint::detail::blueprint_variable_vector arr; + arr.allocate(bp, 1ul << n); + blueprint::detail::blueprint_variable index, result, success_flag; + index.allocate(bp); + result.allocate(bp); + success_flag.allocate(bp); + + nil::crypto3::blueprint::components::loose_multiplexing g(bp, arr, index, result, success_flag); + g.generate_gates(); + + for (std::size_t i = 0; i < 1ul << n; ++i) { + bp.val(arr[i]) = typename FieldType::value_type((19 * i) % (1ul << n)); + } + + for (int idx = -1; idx <= (int)(1ul << n); ++idx) { + + bp.val(index) = typename FieldType::value_type(idx); + g.generate_assignments(); + + if (0 <= idx && idx <= (int)(1ul << n) - 1) { + BOOST_CHECK(bp.val(result) == typename FieldType::value_type((19 * idx) % (1ul << n))); + BOOST_CHECK(bp.val(success_flag) == FieldType::value_type::one()); + BOOST_CHECK(bp.is_satisfied()); + bp.val(result) -= FieldType::value_type::one(); + BOOST_CHECK(!bp.is_satisfied()); + } else { + BOOST_CHECK(bp.val(success_flag) == FieldType::value_type::zero()); + BOOST_CHECK(bp.is_satisfied()); + bp.val(success_flag) = FieldType::value_type::one(); + BOOST_CHECK(!bp.is_satisfied()); + } + } +} + +BOOST_AUTO_TEST_SUITE(basic_components_test_suite) + +BOOST_AUTO_TEST_CASE(basic_components_disjunction_test) { + std::cout << "Disjunction component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_disjunction_component>(10); + std::cout << "Started for mnt4<298>" << std::endl; + test_disjunction_component>(10); + std::cout << "Started for mnt6<298>" << std::endl; + test_disjunction_component>(10); +} + +BOOST_AUTO_TEST_CASE(basic_components_conjunction_test) { + std::cout << "Conjunction component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_conjunction_component>(10); + std::cout << "Started for mnt4<298>" << std::endl; + test_conjunction_component>(10); + std::cout << "Started for mnt6<298>" << std::endl; + test_conjunction_component>(10); +} + +BOOST_AUTO_TEST_CASE(basic_components_comparison_test) { + std::cout << "Comparison component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_comparison_component>(5); + std::cout << "Started for mnt4<298>" << std::endl; + test_comparison_component>(5); + std::cout << "Started for mnt6<298>" << std::endl; + test_comparison_component>(5); +} + +BOOST_AUTO_TEST_CASE(basic_components_inner_product_test) { + std::cout << "Inner product component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_inner_product_component>(5); + std::cout << "Started for mnt4<298>" << std::endl; + test_inner_product_component>(5); + std::cout << "Started for mnt6<298>" << std::endl; + test_inner_product_component>(5); +} + +BOOST_AUTO_TEST_CASE(basic_components_loose_multiplexing_test) { + std::cout << "Loose multiplexing component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_loose_multiplexing_component>(5); + std::cout << "Started for mnt4<298>" << std::endl; + test_loose_multiplexing_component>(5); + std::cout << "Started for mnt6<298>" << std::endl; + test_loose_multiplexing_component>(5); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/basic_components_r1cs_gg_ppzksnark.cpp b/libs/blueprint/test/basic_components_r1cs_gg_ppzksnark.cpp new file mode 100644 index 000000000..6b055151a --- /dev/null +++ b/libs/blueprint/test/basic_components_r1cs_gg_ppzksnark.cpp @@ -0,0 +1,199 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE basic_components_verification_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "verify_r1cs_scheme.hpp" + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_disjunction_component(std::size_t w) { + + using field_type = typename CurveType::scalar_field_type; + using curve_type = CurveType; + + std::size_t n = std::log2(w) + ((w > (1ul << std::size_t(std::log2(w)))) ? 1 : 0); + + blueprint::blueprint bp; + nil::crypto3::blueprint::detail::blueprint_variable output; + output.allocate(bp); + + bp.set_input_sizes(1); + + nil::crypto3::blueprint::detail::blueprint_variable_vector inputs; + inputs.allocate(bp, n); + + nil::crypto3::blueprint::components::disjunction d(bp, inputs, output); + d.generate_gates(); + + for (std::size_t j = 0; j < n; ++j) { + bp.val(inputs[j]) = typename field_type::value_type((w & (1ul << j)) ? 1 : 0); + } + + d.generate_assignments(); + + BOOST_CHECK(bp.val(output) == (w ? field_type::value_type::one() : field_type::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); +} + +template +void test_conjunction_component(std::size_t w) { + + using field_type = typename CurveType::scalar_field_type; + using curve_type = CurveType; + + std::size_t n = std::log2(w) + ((w > (1ul << std::size_t(std::log2(w)))) ? 1 : 0); + + blueprint::blueprint bp; + + nil::crypto3::blueprint::detail::blueprint_variable output; + output.allocate(bp); + + bp.set_input_sizes(1); + + nil::crypto3::blueprint::detail::blueprint_variable_vector inputs; + inputs.allocate(bp, n); + + nil::crypto3::blueprint::components::conjunction c(bp, inputs, output); + c.generate_gates(); + + for (std::size_t j = 0; j < n; ++j) { + bp.val(inputs[j]) = (w & (1ul << j)) ? field_type::value_type::one() : field_type::value_type::zero(); + } + + c.generate_assignments(); + + BOOST_CHECK(bp.val(output) == + (w == (1ul << n) - 1 ? field_type::value_type::one() : field_type::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); +} + +template +void test_comparison_component(std::size_t a, std::size_t b) { + + using field_type = typename CurveType::scalar_field_type; + using curve_type = CurveType; + + blueprint::blueprint bp; + + nil::crypto3::blueprint::detail::blueprint_variable A, B, less, less_or_eq; + A.allocate(bp); + B.allocate(bp); + less.allocate(bp); + less_or_eq.allocate(bp); + + bp.set_input_sizes(1); + std::size_t n = + std::log2(std::max(a, b)) + ((std::max(a, b) > (1ul << std::size_t(std::log2(std::max(a, b))))) ? 1 : 0); + + nil::crypto3::blueprint::components::comparison cmp(bp, n, A, B, less, less_or_eq); + cmp.generate_gates(); + + bp.val(A) = typename field_type::value_type(a); + bp.val(B) = typename field_type::value_type(b); + + cmp.generate_assignments(); + + BOOST_CHECK(bp.val(less) == (a < b ? field_type::value_type::one() : field_type::value_type::zero())); + BOOST_CHECK(bp.val(less_or_eq) == (a <= b ? field_type::value_type::one() : field_type::value_type::zero())); + BOOST_CHECK(bp.is_satisfied()); + + BOOST_CHECK(verify_component(bp)); +} + +BOOST_AUTO_TEST_SUITE(basic_components_test_suite) + +BOOST_AUTO_TEST_CASE(basic_components_disjunction_r1cs_gg_ppzksnark_test) { + std::cout << "Disjunction component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_disjunction_component>(10); + std::cout << "Started for mnt4<298>" << std::endl; + test_disjunction_component>(10); + std::cout << "Started for mnt6<298>" << std::endl; + test_disjunction_component>(10); +} + +BOOST_AUTO_TEST_CASE(basic_components_conjunction_r1cs_gg_ppzksnark_test) { + std::cout << "Conjunction component test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_conjunction_component>(10); + std::cout << "Started for mnt4<298>" << std::endl; + test_conjunction_component>(10); + std::cout << "Started for mnt6<298>" << std::endl; + test_conjunction_component>(10); +} + +BOOST_AUTO_TEST_CASE(basic_components_comparison_r1cs_gg_ppzksnark_test) { + std::cout << "Comparison component r1cs_gg_ppzksnark test started" << std::endl; + std::cout << "Started for bls12<381>" << std::endl; + test_comparison_component>(1, 4); + std::cout << "Started for mnt4<298>" << std::endl; + test_comparison_component>(1, 4); + std::cout << "Started for mnt6<298>" << std::endl; + test_comparison_component>(1, 4); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/component_batch.cpp b/libs/blueprint/test/component_batch.cpp new file mode 100644 index 000000000..bc7a314f6 --- /dev/null +++ b/libs/blueprint/test/component_batch.cpp @@ -0,0 +1,518 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_component_batch_test + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace nil::blueprint; +using namespace nil; + +template +struct compare_copy_constraints { + bool operator()(const crypto3::zk::snark::plonk_copy_constraint &lhs, + const crypto3::zk::snark::plonk_copy_constraint &rhs) const { + crypto3::zk::snark::plonk_copy_constraint norm_lhs = + lhs.first < lhs.second ? lhs : crypto3::zk::snark::plonk_copy_constraint(lhs.second, lhs.first); + crypto3::zk::snark::plonk_copy_constraint norm_rhs = + rhs.first < rhs.second ? rhs : crypto3::zk::snark::plonk_copy_constraint(rhs.second, rhs.first); + return norm_lhs.first < norm_rhs.first || (norm_lhs.first == norm_rhs.first && norm_lhs.second < norm_rhs.second); + } +}; + +template +bool compare_copy_constraint_vectors(const std::vector> &lhs, + const std::vector> &rhs) { + std::set, compare_copy_constraints> + lhs_set, rhs_set; + + for( const auto &c: lhs ) { + lhs_set.insert(c); + } + for( const auto &c: rhs ) { + rhs_set.insert(c); + } + + if (lhs_set.size() != rhs_set.size() ) { + return false; + } + return lhs_set == rhs_set; +} + +template +struct public_input_var_maker { + using var = crypto3::zk::snark::plonk_variable; + using assignment_type = assignment>; + assignment_type& assignment_table; + nil::crypto3::random::algebraic_engine generate_random; + boost::random::uniform_int_distribution bool_dist{0, 1}; + boost::random::mt19937 seed_seq{1444}; + std::size_t curr_idx = 0; + + public_input_var_maker(assignment_type& assignment_) : assignment_table(assignment_) { + generate_random.seed(seed_seq); + } + + var operator()() { + assignment_table.public_input(0, curr_idx) = generate_random(); + return var(0, curr_idx++, false, var::column_type::public_input); + } + + var binary_var() { + assignment_table.public_input(0, curr_idx) = bool_dist(seed_seq); + return var(0, curr_idx++, false, var::column_type::public_input); + } +}; + +BOOST_AUTO_TEST_SUITE(blueprint_component_batch_test_suite) + +BOOST_AUTO_TEST_CASE(component_batch_basic_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using copy_constraint_type = crypto3::zk::snark::plonk_copy_constraint; + + assignment_type assignment_table(14, 1, 0, 1); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + + using component_type = components::multiplication< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + std::size_t row = assignment_table.finalize_component_batches(circuit, 0); + BOOST_CHECK_EQUAL(row, 1); + BOOST_CHECK_EQUAL(circuit.gates().size(), 1); + const auto &gate = circuit.gates()[0]; + BOOST_CHECK_EQUAL(gate.constraints.size(), 4); + std::array expected_constraints = { + var(0, 0) * var(1, 0) - var(2, 0), + var(3, 0) * var(4, 0) - var(5, 0), + var(6, 0) * var(7, 0) - var(8, 0), + var(9, 0) * var(10, 0) - var(11, 0) + }; + for (std::size_t i = 0; i < gate.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate.constraints[i], expected_constraints[i]); + } + const std::vector expected_copy_constraints = { + {var(0, 0, false, var::column_type::public_input), var(0, 0, false, var::column_type::witness)}, + {var(1, 0, false, var::column_type::witness), var(0, 1, false, var::column_type::public_input)}, + {var(0, 2, false, var::column_type::public_input), var(3, 0, false, var::column_type::witness)}, + {var(4, 0, false, var::column_type::witness), var(0, 3, false, var::column_type::public_input)} + }; + BOOST_ASSERT(compare_copy_constraint_vectors(circuit.copy_constraints(), expected_copy_constraints)); + + // assignment_table.export_table(std::cout); + // circuit.export_circuit(std::cout); +} + +BOOST_AUTO_TEST_CASE(component_batch_continuation_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using copy_constraint_type = crypto3::zk::snark::plonk_copy_constraint; + + assignment_type assignment_table(15, 1, 0, 2); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + + using component_type = components::multiplication< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + auto first_result = assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + auto second_result = assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + auto third_result = assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + auto fourth_result = assignment_table.add_input_to_batch_assignment({first_result.output, second_result.output}); + using addition_type = components::addition< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + std::size_t row = 0; + addition_type add_component({0, 1, 2}, {}, {}); + auto addition_result = generate_assignments(add_component, assignment_table, {third_result.output, fourth_result.output}, row); + generate_circuit(add_component, circuit, assignment_table, {third_result.output, fourth_result.output}, row++); + auto fifth_result = assignment_table.add_input_to_batch_assignment({addition_result.output, public_input_var_maker()}); + generate_assignments(add_component, assignment_table, {addition_result.output, fifth_result.output}, row); + generate_circuit(add_component, circuit, assignment_table, {addition_result.output, fifth_result.output}, row++); + row = assignment_table.finalize_component_batches(circuit, row); + BOOST_CHECK_EQUAL(row, 4); + BOOST_CHECK_EQUAL(circuit.gates().size(), 2); + const auto &gate = circuit.gates()[1]; + BOOST_CHECK_EQUAL(gate.constraints.size(), 5); + std::array expected_constraints = { + var(0, 0) * var(1, 0) - var(2, 0), + var(3, 0) * var(4, 0) - var(5, 0), + var(6, 0) * var(7, 0) - var(8, 0), + var(9, 0) * var(10, 0) - var(11, 0), + var(12, 0) * var(13, 0) - var(14, 0) + }; + for (std::size_t i = 0; i < gate.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate.constraints[i], expected_constraints[i]); + } + const std::vector expected_copy_constraints = { + {var(11, 2, false, var::column_type::witness), var(0, 0, false, var::column_type::witness)}, + {var(1, 0, false, var::column_type::witness), var(2, 3, false, var::column_type::witness)}, + {var(2, 0, false, var::column_type::witness), var(0, 1, false, var::column_type::witness)}, + {var(1, 1, false, var::column_type::witness), var(14, 2, false, var::column_type::witness)}, + {var(0, 0, false, var::column_type::public_input), var(0, 2, false, var::column_type::witness)}, + {var(1, 2, false, var::column_type::witness), var(0, 1, false, var::column_type::public_input)}, + {var(0, 2, false, var::column_type::public_input), var(3, 2, false, var::column_type::witness)}, + {var(4, 2, false, var::column_type::witness), var(0, 3, false, var::column_type::public_input)}, + {var(0, 4, false, var::column_type::public_input), var(6, 2, false, var::column_type::witness)}, + {var(7, 2, false, var::column_type::witness), var(0, 5, false, var::column_type::public_input)}, + {var(0, 6, false, var::column_type::public_input), var(9, 2, false, var::column_type::witness)}, + {var(10, 2, false, var::column_type::witness), var(0, 7, false, var::column_type::public_input)}, + {var(2, 0, false, var::column_type::witness), var(12, 2, false, var::column_type::witness)}, + {var(13, 2, false, var::column_type::witness), var(0, 8, false, var::column_type::public_input)}, + {var(2, 2, false, var::column_type::witness), var(0, 3, false, var::column_type::witness)}, + {var(1, 3, false, var::column_type::witness), var(5, 2, false, var::column_type::witness)} + }; + + BOOST_ASSERT(compare_copy_constraint_vectors(circuit.copy_constraints(), expected_copy_constraints)); + + // assignment_table.export_table(std::cout); + // circuit.export_circuit(std::cout); +} + +BOOST_AUTO_TEST_CASE(component_batch_multibatch_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using copy_constraint_type = crypto3::zk::snark::plonk_copy_constraint; + + assignment_type assignment_table(15, 1, 0, 3); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + + using mul_component_type = components::multiplication< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + using add_component_type = components::addition< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + using div_or_zero_component_type = components::division_or_zero; + auto mul_result = assignment_table.add_input_to_batch_assignment( + {public_input_var_maker(), public_input_var_maker()}); + auto add_result = assignment_table.add_input_to_batch_assignment({mul_result.output, public_input_var_maker()}); + auto mul_result_2 = assignment_table.add_input_to_batch_assignment({add_result.output, mul_result.output}); + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + div_or_zero_component_type div_or_zero_component({0, 1, 2, 3, 4}, {}, {}); + var div_or_zero_var = public_input_var_maker(); + auto div_or_zero_res = generate_assignments( + div_or_zero_component, assignment_table, {mul_result_2.output, div_or_zero_var}, 0); + generate_circuit(div_or_zero_component, circuit, assignment_table, {mul_result_2.output, div_or_zero_var}, 0); + assignment_table.add_input_to_batch_assignment({div_or_zero_res.output, public_input_var_maker()}); + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + assignment_table.add_input_to_batch_assignment({add_result.output, mul_result.output}); + // duplicates, should not count! + for (std::size_t i = 0; i < 5; i++) { + assignment_table.add_input_to_batch_assignment({add_result.output, mul_result.output}); + } + // not duplicates, should count + for (std::size_t i = 0; i < 5; i++) { + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), public_input_var_maker()}); + } + std::size_t row = assignment_table.finalize_component_batches(circuit, 1); + BOOST_CHECK_EQUAL(row, 4); + + BOOST_CHECK_EQUAL(circuit.gates().size(), 3); + const auto &gate_1 = circuit.gates()[1]; + BOOST_CHECK_EQUAL(gate_1.constraints.size(), 5); + const std::array expected_constraints_mul = { + var(0, 0) * var(1, 0) - var(2, 0), + var(3, 0) * var(4, 0) - var(5, 0), + var(6, 0) * var(7, 0) - var(8, 0), + var(9, 0) * var(10, 0) - var(11, 0), + var(12, 0) * var(13, 0) - var(14, 0) + }; + const std::array expected_constraints_add = { + var(0, 0) + var(1, 0) - var(2, 0), + var(3, 0) + var(4, 0) - var(5, 0), + var(6, 0) + var(7, 0) - var(8, 0), + var(9, 0) + var(10, 0) - var(11, 0), + var(12, 0) + var(13, 0) - var(14, 0) + }; + if (gate_1.constraints[0] == var(0, 0) * var(1, 0) - var(2, 0)) { + for (std::size_t i = 0; i < gate_1.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate_1.constraints[i], expected_constraints_mul[i]); + } + } else { + for (std::size_t i = 0; i < gate_1.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate_1.constraints[i], expected_constraints_add[i]); + } + } + const auto &gate_2 = circuit.gates()[2]; + BOOST_CHECK_EQUAL(gate_1.constraints.size(), 5); + if (gate_2.constraints[0] == var(0, 0) * var(1, 0) - var(2, 0)) { + for (std::size_t i = 0; i < gate_2.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate_2.constraints[i], expected_constraints_mul[i]); + } + } else { + for (std::size_t i = 0; i < gate_2.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate_2.constraints[i], expected_constraints_add[i]); + } + } + BOOST_ASSERT((gate_1.constraints[0] == expected_constraints_mul[0] && + gate_2.constraints[0] == expected_constraints_add[0]) || + (gate_1.constraints[0] == expected_constraints_add[0] && + gate_2.constraints[0] == expected_constraints_mul[0])); + + const std::vector expected_copy_constraints = { + {var(14, 2, false, var::column_type::witness), var(0, 0, false, var::column_type::witness)}, + {var(1, 0, false, var::column_type::witness), var(0, 5, false, var::column_type::public_input)}, + {var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::witness)}, + {var(1, 1, false, var::column_type::witness), var(0, 1, false, var::column_type::public_input)}, + {var(0, 3, false, var::column_type::public_input), var(3, 1, false, var::column_type::witness)}, + {var(4, 1, false, var::column_type::witness), var(0, 4, false, var::column_type::public_input)}, + {var(0, 7, false, var::column_type::public_input), var(6, 1, false, var::column_type::witness)}, + {var(7, 1, false, var::column_type::witness), var(0, 8, false, var::column_type::public_input)}, + {var(0, 9, false, var::column_type::public_input), var(9, 1, false, var::column_type::witness)}, + {var(10, 1, false, var::column_type::witness), var(0, 10, false, var::column_type::public_input)}, + {var(0, 11, false, var::column_type::public_input), var(12, 1, false, var::column_type::witness)}, + {var(13, 1, false, var::column_type::witness), var(0, 12, false, var::column_type::public_input)}, + {var(0, 13, false, var::column_type::public_input), var(0, 2, false, var::column_type::witness)}, + {var(1, 2, false, var::column_type::witness), var(0, 14, false, var::column_type::public_input)}, + {var(0, 15, false, var::column_type::public_input), var(3, 2, false, var::column_type::witness)}, + {var(4, 2, false, var::column_type::witness), var(0, 16, false, var::column_type::public_input)}, + {var(0, 17, false, var::column_type::public_input), var(6, 2, false, var::column_type::witness)}, + {var(7, 2, false, var::column_type::witness), var(0, 18, false, var::column_type::public_input)}, + {var(2, 0, false, var::column_type::witness), var(9, 2, false, var::column_type::witness)}, + {var(10, 2, false, var::column_type::witness), var(0, 6, false, var::column_type::public_input)}, + {var(2, 3, false, var::column_type::witness), var(12, 2, false, var::column_type::witness)}, + {var(13, 2, false, var::column_type::witness), var(2, 1, false, var::column_type::witness)}, + {var(2, 1, false, var::column_type::witness), var(0, 3, false, var::column_type::witness)}, + {var(1, 3, false, var::column_type::witness), var(0, 2, false, var::column_type::public_input)}, + {var(2, 3, false, var::column_type::witness), var(3, 3, false, var::column_type::witness)}, + {var(4, 3, false, var::column_type::witness), var(2, 1, false, var::column_type::witness)} + }; + + BOOST_ASSERT(compare_copy_constraint_vectors(circuit.copy_constraints(), expected_copy_constraints)); + + // assignment_table.export_table(std::cout); + // circuit.export_circuit(std::cout); +} + +BOOST_AUTO_TEST_CASE(component_batch_const_batch_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using copy_constraint_type = crypto3::zk::snark::plonk_copy_constraint; + + assignment_type assignment_table(15, 1, 1, 3); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + + using multiplication_type = components::multiplication< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + using mul_by_constant_type = components::mul_by_constant; + + mul_by_constant_type mul_by_constant_component({0, 1, 2}, {0}, {}, 1444); + std::size_t row = 0; + var mul_by_constant_input = public_input_var_maker(); + auto mul_by_const_result = generate_assignments( + mul_by_constant_component, assignment_table, {mul_by_constant_input}, row); + generate_circuit(mul_by_constant_component, circuit, assignment_table, {mul_by_constant_input}, row++); + lookup_constraint_type lookup_constraint; + lookup_constraint.table_id = 0; + lookup_constraint.lookup_input.push_back(constraint_type({var(0, 1, true, var::column_type::constant)})); + std::size_t lookup_selector = circuit.add_lookup_gate(lookup_constraint); + assignment_table.enable_selector(lookup_selector, row++); + // filling the constants is required to resize the column + assignment_table.constant(0, row) = 1445; + assignment_table.enable_selector(lookup_selector, row++); + assignment_table.constant(0, row) = 1446; + auto mul_result = assignment_table.add_input_to_batch_assignment( + {assignment_table.add_batch_constant_variable(1), assignment_table.add_batch_constant_variable(2)}); + // have to check lookup functionality manually + assignment_table.add_input_to_batch_assignment({public_input_var_maker(), mul_result.output}); + assignment_table.add_input_to_batch_assignment({mul_by_const_result.output, public_input_var_maker()}); + assignment_table.finalize_component_batches(circuit, row); + assignment_table.finalize_constant_batches(circuit, 0); + + // duplicates; should not count! + for (std::size_t i = 0; i < 10; i++) { + assignment_table.add_batch_constant_variable(2); + } + + BOOST_ASSERT(assignment_table.constant(0, 0) == 1444); + BOOST_ASSERT(assignment_table.constant(0, 1) == 1); + BOOST_ASSERT(assignment_table.constant(0, 2) == 1445); + BOOST_ASSERT(assignment_table.constant(0, 3) == 1446); + BOOST_ASSERT(assignment_table.constant(0, 4) == 2); + BOOST_ASSERT(assignment_table.rows_amount() == 5); + + const std::vector expected_copy_constraints = { + {var(0, 0, false, var::column_type::public_input), var(0, 0, false, var::column_type::witness)}, + {var(0, 1, false, var::column_type::public_input), var(0, 3, false, var::column_type::witness)}, + {var(1, 3, false, var::column_type::witness), var(8, 3, false, var::column_type::witness)}, + {var(1, 0, false, var::column_type::witness), var(3, 3, false, var::column_type::witness)}, + {var(4, 3, false, var::column_type::witness), var(0, 2, false, var::column_type::public_input)}, + {var(0, 1, false, var::column_type::constant), var(6, 3, false, var::column_type::witness)}, + {var(7, 3, false, var::column_type::witness), var(0, 4, false, var::column_type::constant)} + }; + + BOOST_ASSERT(compare_copy_constraint_vectors(circuit.copy_constraints(), expected_copy_constraints)); + + // assignment_table.export_table(std::cout); + // circuit.export_circuit(std::cout); +} + +BOOST_AUTO_TEST_CASE(component_batch_params_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using copy_constraint_type = crypto3::zk::snark::plonk_copy_constraint; + + using swap_component_type = components::flexible_swap; + using input_type = typename swap_component_type::input_type; + + assignment_type assignment_table(15, 1, 1, 3); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + constexpr std::size_t size_small = 1; + constexpr std::size_t size_big = 2; + input_type input; + + input.inp = {public_input_var_maker.binary_var(), public_input_var_maker(), public_input_var_maker()}; + auto res_1 = assignment_table.add_input_to_batch_assignment(input); + + input.inp = {public_input_var_maker.binary_var(), public_input_var_maker(), public_input_var_maker()}; + auto res_2 = assignment_table.add_input_to_batch_assignment(input); + + input.inp = {public_input_var_maker.binary_var(), res_1.output[0], res_2.output[1]}; + auto res_3 = assignment_table.add_input_to_batch_assignment(input); + assignment_table.finalize_component_batches(circuit, 0); + + BOOST_CHECK_EQUAL(circuit.gates().size(), 1); + const auto &gate_1 = circuit.gates()[0]; + BOOST_CHECK_EQUAL(gate_1.constraints.size(), 9); + std::array expected_constraints = { + var(0, 0) * (var(0, 0) - 1), + var(3, 0) - (((0 - (var(0, 0) - 1)) * var(1, 0)) + var(0, 0) * var(2, 0)), + var(4, 0) - (((0 - (var(0, 0) - 1)) * var(2, 0)) + var(0, 0) * var(1, 0)), + var(5, 0) * (var(5, 0) - 1), + var(8, 0) - (((0 - (var(5, 0) - 1)) * var(6, 0)) + var(5, 0) * var(7, 0)), + var(9, 0) - (((0 - (var(5, 0) - 1)) * var(7, 0)) + var(5, 0) * var(6, 0)), + var(10, 0) * (var(10, 0) - 1), + var(13, 0) - (((0 - (var(10, 0) - 1)) * var(11, 0)) + var(10, 0) * var(12, 0)), + var(14, 0) - (((0 - (var(10, 0) - 1)) * var(12, 0)) + var(10, 0) * var(11, 0)) + }; + + for (std::size_t i = 0; i < gate_1.constraints.size(); ++i) { + BOOST_CHECK_EQUAL(gate_1.constraints[i], expected_constraints[i]); + } + + const std::vector expected_copy_constraints = { + {var(0, 0, false, var::column_type::public_input), var(0, 0, false, var::column_type::witness)}, + {var(0, 1, false, var::column_type::public_input), var(1, 0, false, var::column_type::witness)}, + {var(0, 2, false, var::column_type::public_input), var(2, 0, false, var::column_type::witness)}, + + {var(0, 3, false, var::column_type::public_input), var(5, 0, false, var::column_type::witness)}, + {var(0, 4, false, var::column_type::public_input), var(6, 0, false, var::column_type::witness)}, + {var(0, 5, false, var::column_type::public_input), var(7, 0, false, var::column_type::witness)}, + + {var(0, 6, false, var::column_type::public_input), var(10, 0, false, var::column_type::witness)}, + {var(3, 0, false, var::column_type::witness), var(11, 0, false, var::column_type::witness)}, + {var(9, 0, false, var::column_type::witness), var(12, 0, false, var::column_type::witness)} + }; + + BOOST_ASSERT(compare_copy_constraint_vectors(circuit.copy_constraints(), expected_copy_constraints)); + + // assignment_table.export_table(std::cout); + // circuit.export_circuit(std::cout); +} + +BOOST_AUTO_TEST_CASE(component_batch_generate_circuit_variant_basic_test) { + using curve_type = nil::crypto3::algebra::curves::vesta; + using field_type = typename curve_type::scalar_field_type; + + using assignment_type = assignment>; + using circuit_type = circuit>; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + assignment_type assignment_table(15, 1, 1, 3); + circuit_type circuit; + public_input_var_maker public_input_var_maker(assignment_table); + + using multiplication_type = components::multiplication< + ArithmetizationType, field_type, nil::blueprint::basic_non_native_policy>; + + typename multiplication_type::input_type input_1 = {public_input_var_maker(), public_input_var_maker()}; + typename multiplication_type::input_type input_2 = {public_input_var_maker(), public_input_var_maker()}; + auto res_1 = assignment_table.add_input_to_batch_circuit(input_1); + auto res_2 = assignment_table.add_input_to_batch_circuit(input_2); + BOOST_ASSERT(var_value(assignment_table, res_1.output) == 0); + BOOST_ASSERT(var_value(assignment_table, res_2.output) == 0); + res_1 = assignment_table.add_input_to_batch_assignment(input_1); + BOOST_ASSERT(var_value(assignment_table, res_1.output) == var_value(assignment_table, input_1.x) * var_value(assignment_table, input_1.y)); + BOOST_ASSERT(var_value(assignment_table, res_1.output) != 0); + BOOST_ASSERT(var_value(assignment_table, res_2.output) == 0); + res_2 = assignment_table.add_input_to_batch_assignment(input_2); + BOOST_ASSERT(var_value(assignment_table, res_2.output) == var_value(assignment_table, input_2.x) * var_value(assignment_table, input_2.y)); + BOOST_ASSERT(var_value(assignment_table, res_2.output) != 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/detail/huang_lu.cpp b/libs/blueprint/test/detail/huang_lu.cpp new file mode 100644 index 000000000..01bd74d9c --- /dev/null +++ b/libs/blueprint/test/detail/huang_lu.cpp @@ -0,0 +1,151 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_huang_lu_test + +#include + +#include + +#include +#include +#include +#include + +using namespace nil::blueprint::components::detail; + +BOOST_AUTO_TEST_SUITE(blueprint_huang_lu_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_huang_lu_basic) { + std::list> sizes = { + {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5} + }; + std::size_t agent_amount = 2; + + std::unordered_map assignment = huang_lu(sizes, agent_amount); + + BOOST_CHECK_EQUAL(assignment[0], 1); + BOOST_CHECK_EQUAL(assignment[1], 1); + BOOST_CHECK_EQUAL(assignment[2], 0); + BOOST_CHECK_EQUAL(assignment[3], 1); + BOOST_CHECK_EQUAL(assignment[4], 0); + + agent_amount = 3; + assignment = huang_lu(sizes, agent_amount); + BOOST_CHECK_EQUAL(assignment[0], 1); + BOOST_CHECK_EQUAL(assignment[1], 2); + BOOST_CHECK_EQUAL(assignment[2], 2); + BOOST_CHECK_EQUAL(assignment[3], 1); + BOOST_CHECK_EQUAL(assignment[4], 0); + + // sanity + agent_amount = 1; + assignment = huang_lu(sizes, agent_amount); + BOOST_CHECK_EQUAL(assignment[0], 0); + BOOST_CHECK_EQUAL(assignment[1], 0); + BOOST_CHECK_EQUAL(assignment[2], 0); + BOOST_CHECK_EQUAL(assignment[3], 0); + BOOST_CHECK_EQUAL(assignment[4], 0); +} + +std::pair exhaustive_value_search(std::vector &sizes, + std::vector &tasks, + std::size_t curr_task, + std::size_t branching_threshold, + bool result_found = false) { + if (curr_task < tasks.size()) { + for (std::size_t j = 0; j < sizes.size(); j++) { + if (sizes[j] + tasks[curr_task] <= branching_threshold) { + sizes[j] += tasks[curr_task]; + auto [value, act_result_found] = + exhaustive_value_search(sizes, tasks, curr_task + 1, branching_threshold, result_found); + if (value <= branching_threshold && act_result_found) { + branching_threshold = value; + result_found = true; + } + sizes[j] -= tasks[curr_task]; + } + } + } else if (curr_task == tasks.size()) { + result_found = true; + std::size_t new_branching_threshold = 0; + for (std::size_t i = 0; i < sizes.size(); i++) { + new_branching_threshold = std::max(new_branching_threshold, sizes[i]); + } + branching_threshold = new_branching_threshold; + } + return std::make_pair(branching_threshold, result_found); +} + +void test_against_exhaustive_search(std::list> &sizes, + std::size_t agent_amount) { + std::unordered_map assignment = huang_lu(sizes, agent_amount); + // Sanity check: all tasks are assigned + for (std::size_t i = 0; i < sizes.size(); i++) { + BOOST_ASSERT(assignment.find(i) != assignment.end()); + } + std::vector tasks(sizes.size(), 0); + for (auto [key, size] : sizes) { + tasks[key] = size; + } + std::vector sizes_for_exhaustive_search(agent_amount, 0); + for (auto [task, agent] : assignment) { + sizes_for_exhaustive_search[agent] += tasks[task]; + } + std::size_t value = 0; + for (std::size_t i = 0; i < agent_amount; i++) { + value = std::max(value, sizes_for_exhaustive_search[i]); + sizes_for_exhaustive_search[i] = 0; + } + auto [true_value, result_found] = exhaustive_value_search( + sizes_for_exhaustive_search, + tasks, + 0, + value); + BOOST_ASSERT(result_found); + BOOST_ASSERT(9 * value <= true_value * 11); +} + +// Reduce this if you want to make the instances bigger +// The testing data is basically calculated by brute force +// So on bigger instances exponetial growth will make the tests real long +// Another approach is to precompute test result values +// Might be worth it? +constexpr static const std::size_t random_tests_amount = 40; + +BOOST_AUTO_TEST_CASE(blueprint_huang_lu_bruteforce_small_instance) { + std::srand(1337); + for (std::size_t i = 0; i < random_tests_amount; i++) { + // Generate random amount of agents, tasks and their sizes + std::size_t agent_amount = std::rand() % 6 + 1; + std::size_t tasks_amount = std::rand() % 17 + 1; + std::list> sizes; + for (std::size_t j = 0; j < tasks_amount; j++) { + sizes.push_back({j, std::rand() % 100 + 1}); + } + test_against_exhaustive_search(sizes, agent_amount); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/gate_id.cpp b/libs/blueprint/test/gate_id.cpp new file mode 100644 index 000000000..46c3ae723 --- /dev/null +++ b/libs/blueprint/test/gate_id.cpp @@ -0,0 +1,179 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE gate_id_test + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::math; +using namespace nil::blueprint; + +BOOST_AUTO_TEST_SUITE(gate_id_tests_suite) + +BOOST_AUTO_TEST_CASE(gate_id_sanity_tests) { + using curve_type = algebra::curves::pallas; + using field_type = typename curve_type::base_field_type; + using value_type = typename field_type::value_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + using constraint_type = nil::crypto3::zk::snark::plonk_constraint; + using gate_type = nil::crypto3::zk::snark::plonk_gate; + using gate_id_type = nil::blueprint::gate_id; + + value_set &values = value_set::get_value_set(); + std::vector constraints; + constraints.reserve(3); + constraints.emplace_back( + var(0, -1, true, var::column_type::witness) * var(1, 3, true, var::column_type::witness) - + var(2, 0, true, var::column_type::witness)); + constraints.emplace_back( + var(0, -1, true, var::column_type::witness) * var(5, 1, true, var::column_type::witness) - + var(6, 0, true, var::column_type::witness)); + constraints.emplace_back( + var(0, -1, true, var::column_type::witness) * var(9, 2, true, var::column_type::witness) + + var(10, 0, true, var::column_type::witness)); + gate_type gate(0, constraints); + + gate_id_type id(gate); + BOOST_ASSERT(id == id); + BOOST_ASSERT(!(id < id)); + std::vector constraints_2; + constraints_2.emplace_back( + var(0, -1, true, var::column_type::witness) * var(1, 3, true, var::column_type::witness) - + var(2, 0, true, var::column_type::witness)); + gate_id_type id_2(gate_type(0, constraints_2)); + BOOST_ASSERT(id != id_2); + BOOST_ASSERT(id < id_2 || id_2 < id); + + std::vector constraints_3; + std::vector constraints_4; + constraints_3.emplace_back( + var(0, -1, true, var::column_type::witness) * var(0, 1, true, var::column_type::witness)); + constraints_3.emplace_back( + var(0, 0, true, var::column_type::witness)); + constraints_4.emplace_back( + var(0, -1, true, var::column_type::witness) * var(0, 0, true, var::column_type::witness)); + constraints_4.emplace_back( + var(0, 1, true, var::column_type::witness)); + gate_id_type id_3(constraints_3); + gate_id_type id_4(constraints_4); + BOOST_ASSERT(id_3 != id_4); + + value_type power_100 = values.get_power(100); + value_type power_101 = values.get_power(101); + BOOST_ASSERT(power_100 != power_101); + + // gate_id should be unordered -- order of constraints inside the gate should not matter + std::vector constraints_5; + auto first_constraint = + var(0, -1, true, var::column_type::witness) * var(1, 3, true, var::column_type::witness) - + var(2, 0, true, var::column_type::witness); + auto second_constraint = + var(0, -1, true, var::column_type::witness) * var(5, 1, true, var::column_type::witness) - + var(6, 0, true, var::column_type::witness); + constraints_5.push_back(first_constraint); + constraints_5.push_back(second_constraint); + std::vector constraints_6; + constraints_6.push_back(second_constraint); + constraints_6.push_back(first_constraint); + + gate_id_type id_5(constraints_5), + id_6(constraints_6); + BOOST_ASSERT(id_5 == id_6); + + std::vector constraints_7; + constraints_7.emplace_back(var(0, 0, true, var::column_type::witness)); + std::vector constraints_8; + constraints_8.emplace_back(var(0, 0, true, var::column_type::constant)); + gate_id_type id_7(constraints_7), + id_8(constraints_8); + BOOST_ASSERT(id_7 != id_8); +} + +BOOST_AUTO_TEST_CASE(lookup_gate_id_sanity_tests) { + using curve_type = algebra::curves::pallas; + using field_type = typename curve_type::scalar_field_type; + using value_type = typename field_type::value_type; + using var = typename nil::crypto3::zk::snark::plonk_variable; + using lookup_constraint_type = nil::crypto3::zk::snark::plonk_lookup_constraint; + using lookup_gate_type = nil::crypto3::zk::snark::plonk_lookup_gate; + using lookup_gate_id_type = nil::blueprint::lookup_gate_id; + + std::vector constraints_1, constraints_2; + + constraints_1.reserve(2); + lookup_constraint_type constraint_1, constraint_2; + constraint_1.table_id = 0; + constraint_1.lookup_input.emplace_back( + var(0, -1, true, var::column_type::witness) * var(1, 3, true, var::column_type::witness) - + var(2, 0, true, var::column_type::witness)); + constraint_1.lookup_input.emplace_back(var(1, 3, true, var::column_type::witness)); + constraints_1.push_back(constraint_1); + constraint_2 = constraint_1; + constraint_2.table_id = 1; + constraints_1.push_back(constraint_2); + + constraints_2.resize(2); + std::copy(constraints_1.rbegin(), constraints_1.rend(), constraints_2.begin()); + + // Order of constraints should not matter + lookup_gate_type gate_1(0, constraints_1), + gate_2(1, constraints_2); + lookup_gate_id_type id_1 = lookup_gate_id_type(gate_1), + id_2 = lookup_gate_id_type(gate_2); + BOOST_ASSERT(id_1 == id_2); + BOOST_ASSERT(!(id_1 < id_2) && !(id_2 < id_1)); + // Order of variables inside constraints should matter + auto var_1 = var(0, -1, true, var::column_type::witness), + var_2 = var(1, 3, true, var::column_type::witness); + lookup_constraint_type constraint_3({0, {var_1, var_2}}), + constraint_4({0, {var_2, var_1}}); + lookup_gate_id_type id_3(constraint_3), + id_4(constraint_4); + BOOST_ASSERT(id_3 != id_4); + // Table ids should matter + lookup_constraint_type constraint_5({0, {var_1, var_2}}), + constraint_6({1, {var_1, var_2}}); + lookup_gate_id_type id_5(constraint_5), + id_6(constraint_6); + BOOST_ASSERT(id_5 != id_6); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/plonk/decomposition.cpp b/libs/blueprint/test/hashes/plonk/decomposition.cpp new file mode 100644 index 000000000..536530c78 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/decomposition.cpp @@ -0,0 +1,181 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_decomposition_test + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_decomposition(std::vector public_input, + std::vector expected_res, + const bool expected_to_pass) { + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + + using AssignmentType = blueprint::assignment; + using component_type = blueprint::components::decomposition; + + //check computation + auto output = component_type::calculate({public_input[0], public_input[1]}); + for (std::size_t i = 0; i < output.size(); i++){ + assert(expected_res[i] == output[i]); + } + + std::array input_state_var = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {input_state_var}; + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + for (std::size_t i = 0; i < real_res.output.size(); i++){ + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + auto result_check_to_fail = [](AssignmentType &assignment, + typename component_type::result_type &real_res) { }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{},{}); + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check_to_fail, instance_input); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +std::vector calculate_decomposition(std::vector data_value) { + std::array data = { + typename FieldType::integral_type(data_value[0].data), + typename FieldType::integral_type(data_value[1].data)}; + std::array range_chunks; + std::size_t shift = 0; + + for (std::size_t i = 0; i < 8; i++) { + range_chunks[i] = (data[0] >> shift) & ((1 << 16) - 1); + range_chunks[i + 8] = (data[1] >> shift) & ((1 << 16) - 1); + shift += 16; + } + + std::array output; + + output[0] = range_chunks[7] * (1 << 16) + range_chunks[6]; + output[1] = range_chunks[5] * (1 << 16) + range_chunks[4]; + output[2] = range_chunks[3] * (1 << 16) + range_chunks[2]; + output[3] = range_chunks[1] * (1 << 16) + range_chunks[0]; + output[4] = range_chunks[15] * (1 << 16) + range_chunks[14]; + output[5] = range_chunks[13] * (1 << 16) + range_chunks[12]; + output[6] = range_chunks[11] * (1 << 16) + range_chunks[10]; + output[7] = range_chunks[9] * (1 << 16) + range_chunks[8]; + + std::vector output_value; + + for (std::size_t i = 0; i < output.size(); i++){ + output_value.push_back(typename FieldType::value_type(output[i])); + } + + return output_value; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_decomposition_test0) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + test_decomposition( + {0x8d741211e928fdd4d33a13970d0ce7f3_cppui_modular255, 0x92f209334030f9ec8fa8a025e987a5dd_cppui_modular255}, + calculate_decomposition({0x8d741211e928fdd4d33a13970d0ce7f3_cppui_modular255, 0x92f209334030f9ec8fa8a025e987a5dd_cppui_modular255}), + true); + + test_decomposition( + {0u, 0u}, + calculate_decomposition({0u, 0u}), + true); + + test_decomposition( + {0xffffffffffffffffffffffffffffffff_cppui_modular255, 0xffffffffffffffffffffffffffffffff_cppui_modular255}, + calculate_decomposition({0xffffffffffffffffffffffffffffffff_cppui_modular255, 0xffffffffffffffffffffffffffffffff_cppui_modular255}), + true); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_decomposition_must_fail) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + typename field_type::value_type bad = 0x100000000000000000000000000000000_cppui_modular255; + + test_decomposition( + {0u, bad}, + calculate_decomposition({0, bad}), + false); + + test_decomposition( + {bad, 0u}, + calculate_decomposition({bad, 0u}), + false); + + bad = 0x4000000000000000000000000000000000000000000000000000000000000000_cppui_modular255; + + test_decomposition( + {0u, bad}, + calculate_decomposition({0u, bad}), + false); + + test_decomposition( + {bad, 0u}, + calculate_decomposition({bad, 0u}), + false); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base4.cpp b/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base4.cpp new file mode 100644 index 000000000..8a51471d9 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base4.cpp @@ -0,0 +1,55 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha256_sha_table_generators_base4_test + +#include + +#include +#include + +#include +#include + +#include + +using namespace nil; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha256_reverse_base4_generation) { + using curve_type = crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + std::cerr << "Starting sha256 reverse base4 generation" << std::endl; + std::unordered_set, + nil::blueprint::components::detail::SumHash> output_set; + nil::blueprint::components::detail::generate_base4_reverse_table( + output_set, 32); + print_sha_table_to_stream(output_set, std::cout); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base7.cpp b/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base7.cpp new file mode 100644 index 000000000..92ac90178 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/detail/sha_table_generators_base7.cpp @@ -0,0 +1,54 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha256_sha_table_generators_base7_test + +#include + +#include +#include + +#include +#include + +#include + +using namespace nil; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha256_reverse_base7_generation) { + using curve_type = crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + using integral_type = typename BlueprintFieldType::integral_type; + using value_type = typename BlueprintFieldType::value_type; + std::cerr << "Starting sha256 reverse base7 generation" << std::endl; + std::unordered_set, + nil::blueprint::components::detail::SumHash> output_set; + nil::blueprint::components::detail::generate_base7_reverse_table(output_set, 32); + print_sha_table_to_stream(output_set, std::cout); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/plonk/poseidon.cpp b/libs/blueprint/test/hashes/plonk/poseidon.cpp new file mode 100644 index 000000000..78f3cbda7 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/poseidon.cpp @@ -0,0 +1,170 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_poseidon_test + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_poseidon(std::vector public_input, + std::vector expected_res){ + + using FieldType = BlueprintFieldType; + + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 11; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + using component_type = + blueprint::components::poseidon; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + using var = crypto3::zk::snark::plonk_variable; + constexpr std::size_t Lambda = 5; + + std::array input_state_var = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), var(0, 2, false, var::column_type::public_input)}; + typename component_type::input_type instance_input = {input_state_var}; + + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + for (std::uint32_t i = 0; i < component_type::state_size; i++){ + std::cout << "input[" << i << "] : " << public_input[i].data << "\n"; + } + #endif + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + for (std::uint32_t i = 0; i < component_type::state_size; i++){ + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "expected[" << i << "]: " << expected_res[i].data << "\n"; + std::cout << "real[" << i << "] : " << var_value(assignment, real_res.output_state[i]).data << "\n"; + #endif + assert(expected_res[i] == var_value(assignment, real_res.output_state[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},{},{0}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +template +std::vector calculate_expected_poseidon(const typename std::vector &a) { + using poseidon_policy = nil::crypto3::hashes::detail::mina_poseidon_policy; + using permutation_type = nil::crypto3::hashes::detail::poseidon_permutation; + using state_type = typename permutation_type::state_type; + + state_type state; + std::copy(a.begin(), a.end(), state.begin()); + permutation_type::permute(state); + + std::vector result(3); + std::copy(state.begin(), state.end(), result.begin()); + return result; +} + +template +void test_poseidon_specfic_data(){ + std::vector input = {0,1,1}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {0,0,0}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {1,2,3}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {-1,-1,-1}; + test_poseidon(input, calculate_expected_poseidon(input)); + + typename FieldType::value_type threeFFF = 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular256; + input = {threeFFF, threeFFF, threeFFF}; + test_poseidon(input, calculate_expected_poseidon(input)); +} + +template +void test_poseidon_random_data(){ + using generator_type = nil::crypto3::random::algebraic_engine; + generator_type g; + boost::random::mt19937 seed_seq; + g.seed(seed_seq); + std::vector input; + + for (std::size_t i = 0; i < RandomDataTestsAmount; i++) { + input = {g(), g(), g()}; + test_poseidon(input, calculate_expected_poseidon(input)); + } +} + +constexpr static const std::size_t random_data_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_poseidon_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +// BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_bls12) { +// using field_type = typename crypto3::algebra::fields::bls12_fr<381>; +// test_poseidon_specfic_data(); +// test_poseidon_random_data(); +// } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/plonk/sha256.cpp b/libs/blueprint/test/hashes/plonk/sha256.cpp new file mode 100644 index 000000000..f7bf68955 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/sha256.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha256_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_sha256(std::vector public_input, std::array expected_res){ + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 33; + constexpr std::size_t SelectorColumns = 50; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::sha256; + + std::array input_state_var = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{0},{}); + + typename component_type::input_type instance_input = {input_state_var}; + auto result_check = [expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + assert(var_value(assignment, real_res.output[0]) == expected_res[0] && var_value(assignment, real_res.output[1]) == expected_res[1]); + }; + + // check computation + auto output = component_type::calculate({public_input[0], public_input[1], public_input[2], public_input[3]}); + for (std::size_t i = 0; i < 2; i++) { + assert(expected_res[i] == output[i]); + } + + if constexpr (Stretched) { + using stretched_component_type = blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void test_sha256_with_stretching(std::vector public_input, + std::array expected_res) { + test_sha256(public_input, expected_res); + //test_sha256(public_input, expected_res); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha256_test0) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + typename BlueprintFieldType::value_type s = typename BlueprintFieldType::value_type(2).pow(126); + + test_sha256_with_stretching( + {s, s + 1, s + 2, s + 3}, + {0xf5790a69d0a3f69cb85d0b5a233405fb_cppui_modular255, 0xa47050b703fce590fd6585dd02b175f8_cppui_modular255}); + + test_sha256_with_stretching({ + 0xf5a5fd42d16a20302798ef6ed309979b_cppui_modular255, 0x43003d2320d9f0e8ea9831a92759fb4b_cppui_modular255, + 0xdb56114e00fdd4c1f85c892bf35ac9a8_cppui_modular255, 0x9289aaecb1ebd0a96cde606a748b5d71_cppui_modular255}, + {0x42b052541dce45557d83d34634a45a56_cppui_modular255, 0xd216d4375e5a9584f6445ce4e63324af_cppui_modular255}); + test_sha256_with_stretching({ + 0xc78009fdf07fc56a11f122370658a353_cppui_modular255, 0xaaa542ed63e44c4bc15ff4cd105ab33c_cppui_modular255, + 0x536d98837f2dd165a55d5eeae9148595_cppui_modular255, 0x4472d56f246df256bf3cae19352a123c_cppui_modular255}, + {0x69113382140943e8205d01244f562096_cppui_modular255, 0x4d5b92a1cb78bf9fe35ab0bbd2f1f8c2_cppui_modular255}); + test_sha256_with_stretching({ + 0x9efde052aa15429fae05bad4d0b1d7c6_cppui_modular255, 0x4da64d03d7a1854a588c2cb8430c0d30_cppui_modular255, + 0xd88ddfeed400a8755596b21942c1497e_cppui_modular255, 0x114c302e6118290f91e6772976041fa1_cppui_modular255}, + {0x60a7f836b0b42a41d74143c1ae465c25_cppui_modular255, 0xed04376190677ef7d589bd69bc4d79c8_cppui_modular255}); + test_sha256_with_stretching({ + 0x87eb0ddba57e35f6d286673802a4af59_cppui_modular255, 0x75e22506c7cf4c64bb6be5ee11527f2c_cppui_modular255, + 0x26846476fd5fc54a5d43385167c95144_cppui_modular255, 0xf2643f533cc85bb9d16b782f8d7db193_cppui_modular255}, + {0x841510f2de07868d707940400d618c9e_cppui_modular255, 0xeeb91d1bd77177f196a238e272cb9bc3_cppui_modular255}); + test_sha256_with_stretching({ + 0x506d86582d252405b840018792cad2bf_cppui_modular255, 0x1259f1ef5aa5f887e13cb2f0094f51e1_cppui_modular255, + 0xffff0ad7e659772f9534c195c815efc4_cppui_modular255, 0x14ef1e1daed4404c06385d11192e92b_cppui_modular255}, + {0x88b8aa87277a142cbe3d58e7a85ced04_cppui_modular255, 0x4fec5eb57f1828caf06b5fae9c8c67fd_cppui_modular255}); + + test_sha256_with_stretching( + {0xffffffffffffffff_cppui_modular64, 0xffffffffffffffff_cppui_modular64, 0xffffffffffffffff_cppui_modular64, 0xffffffffffffffff_cppui_modular64}, + {0xf58ac0f0665e3f1886f2eae35542987b_cppui_modular255, 0x9d61cc98e5d3ed2a5a9d8e3b9b7d9f2f_cppui_modular255}); + test_sha256_with_stretching( + {1, 1, 1, 1}, + {0x8e1caeb2418a07d7d88f710dccd882d5_cppui_modular255, 0xb5772c88ae5ca4442ccc46c4518a3d3b_cppui_modular255}); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/plonk/sha256_process.cpp b/libs/blueprint/test/hashes/plonk/sha256_process.cpp new file mode 100644 index 000000000..1a10cbe50 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/sha256_process.cpp @@ -0,0 +1,225 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha256_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha256_process) { + + using curve_type = crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 33; + constexpr std::size_t SelectorColumns = 50; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using AssignmentType = blueprint::assignment; + + using component_type = blueprint::components::sha256_process; + + typename BlueprintFieldType::value_type s = typename BlueprintFieldType::value_type(2).pow(29); + std::array public_input = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + s - 5, s + 5, s - 6, s + 6, s - 7, s + 7, s - 8, s + 8, + s - 9, s + 9, s + 10, s - 10, s + 11, s - 11, s + 12, s - 12}; + std::array input_state_var = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + std::array round_constant = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + + std::array input_words_var; + for (int i = 0; i < 16; i++) { + input_words_var[i] = var(0, 8 + i, false, var::column_type::public_input); + } + std::array message_schedule_array; + for (std::size_t i = 0; i < 16; i++) { + message_schedule_array[i] = typename BlueprintFieldType::integral_type(public_input[8 + i].data); + } + + for(std::size_t i = 16; i < 64; i ++){ + typename BlueprintFieldType::integral_type s0 = ((message_schedule_array[i - 15] >> 7)|((message_schedule_array[i - 15] << (32 - 7)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((message_schedule_array[i - 15] >> 18)|((message_schedule_array[i - 15] << (32 - 18)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) + ^ (message_schedule_array[i - 15] >> 3); + typename BlueprintFieldType::integral_type s1 = ((message_schedule_array[i - 2] >> 17)|((message_schedule_array[i - 2] << (32 - 17)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((message_schedule_array[i - 2] >> 19)|((message_schedule_array[i - 2] << (32 - 19)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) + ^ (message_schedule_array[i - 2] >> 10); + message_schedule_array[i] = (message_schedule_array[i - 16] + s0 + s1 + message_schedule_array[i - 7])% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data); + + } + typename ArithmetizationType::field_type::integral_type a = + typename ArithmetizationType::field_type::integral_type(public_input[0].data); + typename ArithmetizationType::field_type::integral_type b = + typename ArithmetizationType::field_type::integral_type(public_input[1].data); + typename ArithmetizationType::field_type::integral_type c = + typename ArithmetizationType::field_type::integral_type(public_input[2].data); + typename ArithmetizationType::field_type::integral_type d = + typename ArithmetizationType::field_type::integral_type(public_input[3].data); + typename ArithmetizationType::field_type::integral_type e = + typename ArithmetizationType::field_type::integral_type(public_input[4].data); + typename ArithmetizationType::field_type::integral_type f = + typename ArithmetizationType::field_type::integral_type(public_input[5].data); + typename ArithmetizationType::field_type::integral_type g = + typename ArithmetizationType::field_type::integral_type(public_input[6].data); + typename ArithmetizationType::field_type::integral_type h = + typename ArithmetizationType::field_type::integral_type(public_input[7].data); + for (std::size_t i = 0; i < 64; i++) { + typename BlueprintFieldType::integral_type S0 = + ((a >> 2) | ((a << (32 - 2)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((a >> 13) | ((a << (32 - 13)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((a >> 22) | ((a << (32 - 22)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))); + typename BlueprintFieldType::integral_type S1 = + ((e >> 6) | ((e << (32 - 6)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((e >> 11) | ((e << (32 - 11)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))) ^ + ((e >> 25) | ((e << (32 - 25)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(32) - 1).data))); + typename BlueprintFieldType::integral_type maj = (a & b) ^ (a & c) ^ (b & c); + typename BlueprintFieldType::integral_type ch = (e & f) ^ ((~e) & g); + + /*std::vector e_bits(32); + for (std::size_t j = 0; j < 32; j++) { + e_bits[32 - j - 1] = multiprecision::bit_test(e, j); + } + std::vector f_bits(32); + for (std::size_t j = 0; j < 32; j++) { + f_bits[32 - j - 1] = multiprecision::bit_test(f, j); + } + std::vector g_bits(32); + for (std::size_t j = 0; j < 32; j++) { + g_bits[32 - j - 1] = multiprecision::bit_test(g, j); + } + std::vector sizes = {32}; + std::size_t base = 7; + + std::array, 2> e_s = + component_type::split_and_sparse(e_bits, sizes, base); + + std::array, 2> f_s = + component_type::split_and_sparse(f_bits, sizes, base); + + std::array, 2> g_s = + component_type::split_and_sparse(g_bits, sizes, base);*/ + typename BlueprintFieldType::integral_type tmp1 = h + S1 + ch + round_constant[i] + message_schedule_array[i]; + typename BlueprintFieldType::integral_type tmp2 = S0 + maj; + h = g; + g = f; + f = e; + + e = (d + tmp1)% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data); + d = c; + c = b; + b = a; + a = (tmp1 + tmp2)% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data); + } + std::array result_state = {(a + typename ArithmetizationType::field_type::integral_type(public_input[0].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (b + typename ArithmetizationType::field_type::integral_type(public_input[1].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (c + typename ArithmetizationType::field_type::integral_type(public_input[2].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (d + typename ArithmetizationType::field_type::integral_type(public_input[3].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (e + typename ArithmetizationType::field_type::integral_type(public_input[4].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (f + typename ArithmetizationType::field_type::integral_type(public_input[5].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (g + typename ArithmetizationType::field_type::integral_type(public_input[6].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data), + (h + typename ArithmetizationType::field_type::integral_type(public_input[7].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(32).data)}; + auto result_check = [result_state](AssignmentType &assignment, + component_type::result_type &real_res) { + for (std::size_t i = 0; i < 8; i++) { + assert(result_state[i] == typename ArithmetizationType::field_type::integral_type( + var_value(assignment, real_res.output_state[i]).data)); + } + }; + typename component_type::input_type instance_input = {input_state_var, input_words_var}; + + // check computation + std::array input_state = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + std::array input_words = { + s - 5, s + 5, s - 6, s + 6, s - 7, s + 7, s - 8, s + 8, + s - 9, s + 9, s + 10, s - 10, s + 11, s - 11, s + 12, s - 12}; + auto output = component_type::calculate(input_state, input_words); + for (std::size_t i = 0; i < 8; i++) { + assert(result_state[i] == typename ArithmetizationType::field_type::integral_type(output[i].data)); + } + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{0},{}); + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + crypto3::test_empty_component( + component_instance, desc, public_input, result_check, instance_input); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/plonk/sha512.cpp b/libs/blueprint/test/hashes/plonk/sha512.cpp new file mode 100644 index 000000000..c882a11bc --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/sha512.cpp @@ -0,0 +1,159 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha512_test + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; +using namespace nil::crypto3; +using namespace nil::crypto3::accumulators; + +template +void test_sha512(std::vector public_input){ + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + using component_type = blueprint::components::sha512; + + using var = crypto3::zk::snark::plonk_variable; + + + std::array e_R_x = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array e_R_y = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + std::array pk_x = { + var(0, 8, false, var::column_type::public_input), var(0, 9, false, var::column_type::public_input), + var(0, 10, false, var::column_type::public_input), var(0, 11, false, var::column_type::public_input)}; + std::array pk_y = { + var(0, 12, false, var::column_type::public_input), var(0, 13, false, var::column_type::public_input), + var(0, 14, false, var::column_type::public_input), var(0, 15, false, var::column_type::public_input)}; + std::array M = { + var(0, 16, false, var::column_type::public_input), var(0, 17, false, var::column_type::public_input), + var(0, 18, false, var::column_type::public_input), var(0, 19, false, var::column_type::public_input)}; + typename component_type::input_type instance_input = {{e_R_x, e_R_y}, {pk_x, pk_y}, M}; + + auto result_check = [](AssignmentType &assignment, typename component_type::result_type &real_res) { + + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{0},{}); + + if constexpr (Stretched) { + using stretched_component_type = blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha512) { + using curve_type = crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + + using ed25519_type = crypto3::algebra::curves::ed25519; + + ed25519_type::template g1_type::value_type B = + ed25519_type::template g1_type::value_type::one(); + ed25519_type::template g1_type::value_type R = 2 * B; + ed25519_type::scalar_field_type::value_type b = crypto3::algebra::random_element(); + ed25519_type::template g1_type::value_type T = b * R; + + ed25519_type::base_field_type::integral_type Tx = ed25519_type::base_field_type::integral_type(T.X.data); + ed25519_type::base_field_type::integral_type Ty = ed25519_type::base_field_type::integral_type(T.Y.data); + ed25519_type::base_field_type::integral_type Rx = ed25519_type::base_field_type::integral_type(R.X.data); + ed25519_type::base_field_type::integral_type Ry = ed25519_type::base_field_type::integral_type(R.Y.data); + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + std::vector public_input = { + Tx & mask, + (Tx >> 66) & mask, + (Tx >> 132) & mask, + (Tx >> 198) & (mask >> 9), + Ty & mask, + (Ty >> 66) & mask, + (Ty >> 132) & mask, + (Ty >> 198) & (mask >> 9), + Rx & mask, + (Rx >> 66) & mask, + (Rx >> 132) & mask, + (Rx >> 198) & (mask >> 9), + Ry & mask, + (Ry >> 66) & mask, + (Ry >> 132) & mask, + (Ry >> 198) & (mask >> 9), + mask, + mask, + mask, + (mask >> 8) + }; + + test_sha512(public_input); + test_sha512(public_input); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/plonk/sha512_process.cpp b/libs/blueprint/test/hashes/plonk/sha512_process.cpp new file mode 100644 index 000000000..0e8e423a0 --- /dev/null +++ b/libs/blueprint/test/hashes/plonk/sha512_process.cpp @@ -0,0 +1,218 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_sha512_test + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sha512_process) { + + using curve_type = crypto3::algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using var = crypto3::zk::snark::plonk_variable; + using AssignmentType = blueprint::assignment; + + using component_type = blueprint::components::sha512_process; + + typename BlueprintFieldType::value_type s = typename BlueprintFieldType::value_type(2).pow(59); + std::array public_input = {0x6a09e667f3bcc908_cppui_modular64, + 0xbb67ae8584caa73b_cppui_modular64, + 0x3c6ef372fe94f82b_cppui_modular64, + 0xa54ff53a5f1d36f1_cppui_modular64, + 0x510e527fade682d1_cppui_modular64, + 0x9b05688c2b3e6c1f_cppui_modular64, + 0x1f83d9abfb41bd6b_cppui_modular64, + 0x5be0cd19137e2179_cppui_modular64, + s - 5, + s + 5, + s - 6, + s + 6, + s - 7, + s + 7, + s - 8, + s + 8, + s - 9, + s + 9, + s + 10, + s - 10, + s + 11, + s - 11, + s + 12, + s - 12}; + std::array input_state_var = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + std::array round_constant = { + 0x428a2f98d728ae22_cppui_modular64, 0x7137449123ef65cd_cppui_modular64, 0xb5c0fbcfec4d3b2f_cppui_modular64, 0xe9b5dba58189dbbc_cppui_modular64, + 0x3956c25bf348b538_cppui_modular64, 0x59f111f1b605d019_cppui_modular64, 0x923f82a4af194f9b_cppui_modular64, 0xab1c5ed5da6d8118_cppui_modular64, + 0xd807aa98a3030242_cppui_modular64, 0x12835b0145706fbe_cppui_modular64, 0x243185be4ee4b28c_cppui_modular64, 0x550c7dc3d5ffb4e2_cppui_modular64, + 0x72be5d74f27b896f_cppui_modular64, 0x80deb1fe3b1696b1_cppui_modular64, 0x9bdc06a725c71235_cppui_modular64, 0xc19bf174cf692694_cppui_modular64, + 0xe49b69c19ef14ad2_cppui_modular64, 0xefbe4786384f25e3_cppui_modular64, 0x0fc19dc68b8cd5b5_cppui_modular64, 0x240ca1cc77ac9c65_cppui_modular64, + 0x2de92c6f592b0275_cppui_modular64, 0x4a7484aa6ea6e483_cppui_modular64, 0x5cb0a9dcbd41fbd4_cppui_modular64, 0x76f988da831153b5_cppui_modular64, + 0x983e5152ee66dfab_cppui_modular64, 0xa831c66d2db43210_cppui_modular64, 0xb00327c898fb213f_cppui_modular64, 0xbf597fc7beef0ee4_cppui_modular64, + 0xc6e00bf33da88fc2_cppui_modular64, 0xd5a79147930aa725_cppui_modular64, 0x06ca6351e003826f_cppui_modular64, 0x142929670a0e6e70_cppui_modular64, + 0x27b70a8546d22ffc_cppui_modular64, 0x2e1b21385c26c926_cppui_modular64, 0x4d2c6dfc5ac42aed_cppui_modular64, 0x53380d139d95b3df_cppui_modular64, + 0x650a73548baf63de_cppui_modular64, 0x766a0abb3c77b2a8_cppui_modular64, 0x81c2c92e47edaee6_cppui_modular64, 0x92722c851482353b_cppui_modular64, + 0xa2bfe8a14cf10364_cppui_modular64, 0xa81a664bbc423001_cppui_modular64, 0xc24b8b70d0f89791_cppui_modular64, 0xc76c51a30654be30_cppui_modular64, + 0xd192e819d6ef5218_cppui_modular64, 0xd69906245565a910_cppui_modular64, 0xf40e35855771202a_cppui_modular64, 0x106aa07032bbd1b8_cppui_modular64, + 0x19a4c116b8d2d0c8_cppui_modular64, 0x1e376c085141ab53_cppui_modular64, 0x2748774cdf8eeb99_cppui_modular64, 0x34b0bcb5e19b48a8_cppui_modular64, + 0x391c0cb3c5c95a63_cppui_modular64, 0x4ed8aa4ae3418acb_cppui_modular64, 0x5b9cca4f7763e373_cppui_modular64, 0x682e6ff3d6b2b8a3_cppui_modular64, + 0x748f82ee5defb2fc_cppui_modular64, 0x78a5636f43172f60_cppui_modular64, 0x84c87814a1f0ab72_cppui_modular64, 0x8cc702081a6439ec_cppui_modular64, + 0x90befffa23631e28_cppui_modular64, 0xa4506cebde82bde9_cppui_modular64, 0xbef9a3f7b2c67915_cppui_modular64, 0xc67178f2e372532b_cppui_modular64, + 0xca273eceea26619c_cppui_modular64, 0xd186b8c721c0c207_cppui_modular64, 0xeada7dd6cde0eb1e_cppui_modular64, 0xf57d4f7fee6ed178_cppui_modular64, + 0x06f067aa72176fba_cppui_modular64, 0x0a637dc5a2c898a6_cppui_modular64, 0x113f9804bef90dae_cppui_modular64, 0x1b710b35131c471b_cppui_modular64, + 0x28db77f523047d84_cppui_modular64, 0x32caab7b40c72493_cppui_modular64, 0x3c9ebe0a15c9bebc_cppui_modular64, 0x431d67c49c100d4c_cppui_modular64, + 0x4cc5d4becb3e42b6_cppui_modular64, 0x597f299cfc657e2a_cppui_modular64, 0x5fcb6fab3ad6faec_cppui_modular64, 0x6c44198c4a475817_cppui_modular64}; + + std::array input_words_var; + for (int i = 0; i < 16; i++) { + input_words_var[i] = var(0, 8 + i, false, var::column_type::public_input); + } + std::array message_schedule_array; + for (std::size_t i = 0; i < 16; i++) { + message_schedule_array[i] = typename BlueprintFieldType::integral_type(public_input[8 + i].data); + } + for(std::size_t i = 16; i < 80; i ++){ + typename BlueprintFieldType::integral_type s0 = ((message_schedule_array[i - 15] >> 1)|((message_schedule_array[i - 15] << (64 - 1)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((message_schedule_array[i - 15] >> 8)|((message_schedule_array[i - 15] << (64 - 8)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) + ^ (message_schedule_array[i - 15] >> 7); + typename BlueprintFieldType::integral_type s1 = ((message_schedule_array[i - 2] >> 19)|((message_schedule_array[i - 2] << (64 - 19)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((message_schedule_array[i - 2] >> 61)|((message_schedule_array[i - 2] << (64 - 61)) + & typename BlueprintFieldType::integral_type((typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) + ^ (message_schedule_array[i - 2] >> 6); + message_schedule_array[i] = (message_schedule_array[i - 16] + s0 + s1 + message_schedule_array[i - 7])% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + } + typename ArithmetizationType::field_type::integral_type a = + typename ArithmetizationType::field_type::integral_type(public_input[0].data); + typename ArithmetizationType::field_type::integral_type b = + typename ArithmetizationType::field_type::integral_type(public_input[1].data); + typename ArithmetizationType::field_type::integral_type c = + typename ArithmetizationType::field_type::integral_type(public_input[2].data); + typename ArithmetizationType::field_type::integral_type d = + typename ArithmetizationType::field_type::integral_type(public_input[3].data); + typename ArithmetizationType::field_type::integral_type e = + typename ArithmetizationType::field_type::integral_type(public_input[4].data); + typename ArithmetizationType::field_type::integral_type f = + typename ArithmetizationType::field_type::integral_type(public_input[5].data); + typename ArithmetizationType::field_type::integral_type g = + typename ArithmetizationType::field_type::integral_type(public_input[6].data); + typename ArithmetizationType::field_type::integral_type h = + typename ArithmetizationType::field_type::integral_type(public_input[7].data); + for (std::size_t i = 0; i < 80; i++) { + typename BlueprintFieldType::integral_type S0 = + ((a >> 28) | ((a << (64 - 28)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((a >> 34) | ((a << (64 - 34)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((a >> 39) | ((a << (64 - 39)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))); + + typename BlueprintFieldType::integral_type S1 = + ((e >> 14) | ((e << (64 - 14)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((e >> 18) | ((e << (64 - 18)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))) ^ + ((e >> 41) | ((e << (64 - 41)) & typename BlueprintFieldType::integral_type( + (typename BlueprintFieldType::value_type(2).pow(64) - 1).data))); + + typename BlueprintFieldType::integral_type maj = (a & b) ^ (a & c) ^ (b & c); + typename BlueprintFieldType::integral_type ch = (e & f) ^ ((~e) & g); + typename BlueprintFieldType::integral_type tmp1 = h + S1 + ch + round_constant[i] + message_schedule_array[i]; + typename BlueprintFieldType::integral_type tmp2 = S0 + maj; + h = g; + g = f; + f = e; + e = (d + tmp1)% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + d = c; + c = b; + b = a; + a = (tmp1 + tmp2)% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data); + } + std::array result_state = {(a + typename ArithmetizationType::field_type::integral_type(public_input[0].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (b + typename ArithmetizationType::field_type::integral_type(public_input[1].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (c + typename ArithmetizationType::field_type::integral_type(public_input[2].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (d + typename ArithmetizationType::field_type::integral_type(public_input[3].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (e + typename ArithmetizationType::field_type::integral_type(public_input[4].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (f + typename ArithmetizationType::field_type::integral_type(public_input[5].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (g + typename ArithmetizationType::field_type::integral_type(public_input[6].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data), + (h + typename ArithmetizationType::field_type::integral_type(public_input[7].data))% + typename BlueprintFieldType::integral_type(typename BlueprintFieldType::value_type(2).pow(64).data)}; + auto result_check = [result_state](AssignmentType &assignment, + component_type::result_type &real_res) { + for (std::size_t i = 0; i < 8; i++) { + assert(result_state[i] == typename ArithmetizationType::field_type::integral_type(var_value(assignment, real_res.output_state[i]).data)); + } + }; + typename component_type::input_type instance_input = {input_state_var, input_words_var}; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{0},{}); + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/r1cs/knapsack.cpp b/libs/blueprint/test/hashes/r1cs/knapsack.cpp new file mode 100644 index 000000000..4e8be5805 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/knapsack.cpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE knapsack_component_test + +#include +#include + +#include +#include +#include +#include + +#include "knapsack.hpp" + +using namespace nil::crypto3::algebra; +using namespace nil::crypto3::zk; + +BOOST_AUTO_TEST_SUITE(knapsack_component_test_suite) + +BOOST_AUTO_TEST_CASE(knapsack_component_test_bls12_381_case) { + std::cout << "Starting Knapsack component test for BLS12-381 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + test_knapsack_crh_with_bit_out_component::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for BLS12-381 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_mnt4_case) { + std::cout << "Starting Knapsack component test for MNT4-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + test_knapsack_crh_with_bit_out_component::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for MNT4-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_mnt6_case) { + std::cout << "Starting Knapsack component test for MNT6-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + test_knapsack_crh_with_bit_out_component::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for MNT6-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_edwards_183_case) { + std::cout << "Starting Knapsack component test for Edwards-183 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + test_knapsack_crh_with_bit_out_component::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for Edwards-183 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/r1cs/knapsack.hpp b/libs/blueprint/test/hashes/r1cs/knapsack.hpp new file mode 100644 index 000000000..29158e263 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/knapsack.hpp @@ -0,0 +1,301 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_TEST_HPP + +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace nil::crypto3::algebra; +using namespace nil::crypto3::zk; + +template +blueprint test_knapsack_crh_with_bit_out_component() { + blueprint bp; + std::cout << "Warning! Blank test is running." << std::endl; + return bp; +} + +template +blueprint test_knapsack_crh_with_bit_out_component_internal(std::size_t dimension, + const std::vector &input_bits, + const std::vector &digest_bits) { + BOOST_CHECK(components::knapsack_dimension::dimension == dimension); + components::knapsack_crh_with_bit_out_component::sample_randomness(input_bits.size()); + blueprint bp; + + components::block_variable input_block(bp, input_bits.size()); + components::digest_variable output_digest( + bp, components::knapsack_crh_with_bit_out_component::get_digest_len()); + components::knapsack_crh_with_bit_out_component H(bp, input_bits.size(), input_block, output_digest); + + input_block.generate_assignments(input_bits); + H.generate_gates(); + H.generate_assignments(); + + BOOST_CHECK(output_digest.get_digest().size() == digest_bits.size()); + BOOST_CHECK(bp.is_satisfied()); + + const std::size_t num_constraints = bp.num_constraints(); + const std::size_t expected_constraints = + components::knapsack_crh_with_bit_out_component::expected_constraints(); + BOOST_CHECK(num_constraints == expected_constraints); + + return bp; +} + +///* The tests are autogenerated (see +// * generate_knapsack_tests.py) and contain hard-to-read constants. */ +// +// template<> +// blueprint test_knapsack_crh_with_bit_out_component() { +// typedef typename curves::bn128::scalar_field_type FieldType; +// const std::size_t dimension = components::knapsack_dimension::dimension; +// const std::vector input_bits = {1, 1, 0, 0, 1, 0, 1, 0, 0, 1}; +// std::vector digest_bits; + +// if (dimension == 1) { +// // hash_vector[0] = +// // 19358128397917746746715486768528331499472172224025066869640626465460783114989 +// digest_bits = {1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, +// 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, +// 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, +// 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, +// 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, +// 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, +// 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, +// 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1}; +// } else if (dimension == 3) { +// // hash_vector[0] = +// // 19358128397917746746715486768528331499472172224025066869640626465460783114989 hash_vector[1] +// // = 14647747576997998233659818696206913383172548767133711974605617840575181365754 +// // hash_vector[2] = 2920097934141708417756781671323464432263982766704831772622221878471527707999 +// digest_bits = { +// 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, +// 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, +// 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, +// 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, +// 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, +// 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, +// 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, +// 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, +// 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, +// 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, +// 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, +// 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, +// 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, +// 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, +// 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, +// 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, +// 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, +// 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, +// 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, +// 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, +// 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, +// 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0}; +// } else { +// std::cout << "unsupported dimension" << std::endl; +// BOOST_CHECK(false); +// } + +// return test_knapsack_crh_with_bit_out_component_internal(dimension, input_bits, digest_bits); +// } + +template<> +blueprint::scalar_field_type> test_knapsack_crh_with_bit_out_component::scalar_field_type>() { + typedef typename curves::edwards<183>::scalar_field_type FieldType; + const std::size_t dimension = components::knapsack_dimension::dimension; + const std::vector input_bits = {1, 1, 0, 0, 1, 0, 1, 0, 0, 1}; + std::vector digest_bits; + + if (dimension == 1) { + // hash_vector[0] = 212682788919191185746369136465846038795231156077120478 + digest_bits = {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, + 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0}; + } else if (dimension == 3) { + // hash_vector[0] = 212682788919191185746369136465846038795231156077120478 + // hash_vector[1] = 208444103178970253386051017880119245406612361624666932 + // hash_vector[2] = 753512267902403701181906991398452949644481965281690464 + digest_bits = {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, + 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, + 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0}; + } else { + std::cout << "unsupported dimension" << std::endl; + BOOST_CHECK(false); + } + + return test_knapsack_crh_with_bit_out_component_internal(dimension, input_bits, digest_bits); +} + +template<> +blueprint::scalar_field_type> test_knapsack_crh_with_bit_out_component::scalar_field_type>() { + typedef typename curves::mnt4<298>::scalar_field_type FieldType; + const std::size_t dimension = components::knapsack_dimension::dimension; + const std::vector input_bits = {1, 1, 0, 0, 1, 0, 1, 0, 0, 1}; + std::vector digest_bits; + + if (dimension == 1) { + // hash_vector[0] = + // 5849873898117023322885358421738220900336336792093854367505800858141298949423761399689551 + digest_bits = {1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, + 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0}; + } else if (dimension == 3) { + // hash_vector[0] = + // 5849873898117023322885358421738220900336336792093854367505800858141298949423761399689551 + // hash_vector[1] = + // 53446030978469113922159049491079907226345855403292835149508287198951741313094713251809734 + // hash_vector[2] = + // 40260485387428589838404886401807432179330886729322245141417568340931755675196614173996382 + digest_bits = { + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}; + } else { + std::cout << "unsupported dimension" << std::endl; + BOOST_CHECK(false); + } + + return test_knapsack_crh_with_bit_out_component_internal(dimension, input_bits, digest_bits); +} + +template<> +blueprint::scalar_field_type> test_knapsack_crh_with_bit_out_component::scalar_field_type>() { + typedef typename curves::mnt6<298>::scalar_field_type FieldType; + const std::size_t dimension = components::knapsack_dimension::dimension; + const std::vector input_bits = {1, 1, 0, 0, 1, 0, 1, 0, 0, 1}; + std::vector digest_bits; + + if (dimension == 1) { + // hash_vector[0] = + // 5849873898117023322885358421738220900336335412351434682931015184050067928329141552099663 + digest_bits = {1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, + 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, + 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0}; + } else if (dimension == 3) { + // hash_vector[0] = + // 5849873898117023322885358421738220900336335412351434682931015184050067928329141552099663 + // hash_vector[1] = + // 53446030978469113922159049491079907226345854023550415464933501524860510292000093404219846 + // hash_vector[2] = + // 40260485387428589838404886401807432179330884659708615614555389829794909143554684402611550 + digest_bits = { + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, + 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}; + } else { + std::cout << "unsupported dimension" << std::endl; + BOOST_CHECK(false); + } + + return test_knapsack_crh_with_bit_out_component_internal(dimension, input_bits, digest_bits); +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_KNAPSACK_COMPONENT_TEST_HPP diff --git a/libs/blueprint/test/hashes/r1cs/knapsack_verification.cpp b/libs/blueprint/test/hashes/r1cs/knapsack_verification.cpp new file mode 100644 index 000000000..57aa7ff2a --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/knapsack_verification.cpp @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE knapsack_verification_component_test + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +// #include +// #include +// #include +// #include + +#include + +#include "knapsack.hpp" +#include "../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3::algebra; +using namespace nil::crypto3::zk; + +BOOST_AUTO_TEST_SUITE(knapsack_component_test_suite) + +BOOST_AUTO_TEST_CASE(knapsack_component_test_bls12_381_case) { + using curve_type = curves::bls12<381>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting Knapsack component test for BLS12-381 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = test_knapsack_crh_with_bit_out_component(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for BLS12-381 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_mnt4_case) { + using curve_type = curves::mnt4<298>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting Knapsack component test for MNT4-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = test_knapsack_crh_with_bit_out_component(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for MNT4-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_mnt6_case) { + using curve_type = curves::mnt6<298>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting Knapsack component test for MNT6-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = test_knapsack_crh_with_bit_out_component(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for MNT6-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(knapsack_component_test_edwards_183_case) { + using curve_type = curves::edwards<183>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting Knapsack component test for Edwards-183 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = test_knapsack_crh_with_bit_out_component(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Knapsack component test for Edwards-183 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/r1cs/pedersen.cpp b/libs/blueprint/test/hashes/r1cs/pedersen.cpp new file mode 100644 index 000000000..111c1d172 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/pedersen.cpp @@ -0,0 +1,393 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_fixed_base_mul_zcash_component_test + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void print_field_element(std::ostream &os, const typename fields::detail::element_fp &e) { + std::cout << e.data << std::endl; +} + +/// hashing to point +template +void test_blueprint_variable_vector_component_constructor(const std::vector &in_bits, + const ExpectedType &expected) { + using field_type = typename HashComponent::field_type; + + // input as blueprint_variable_vector + blueprint bp, bp_manual; + nil::crypto3::zk::detail::blueprint_variable_vector scalar, scalar_manual; + scalar.allocate(bp, in_bits.size()); + scalar.fill_with_bits(bp, in_bits); + scalar_manual.allocate(bp_manual, in_bits.size()); + scalar_manual.fill_with_bits(bp_manual, in_bits); + + // Auto allocation of the result + HashComponent hash_comp(bp, scalar); + hash_comp.generate_assignments(); + hash_comp.generate_gates(); + BOOST_CHECK(expected.X == bp.lc_val(hash_comp.result.X)); + BOOST_CHECK(expected.Y == bp.lc_val(hash_comp.result.Y)); + BOOST_CHECK(bp.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_manual(bp_manual); + HashComponent hash_comp_manual(bp_manual, scalar_manual, result_manual); + hash_comp_manual.generate_assignments(); + hash_comp_manual.generate_gates(); + BOOST_CHECK(expected.X == bp_manual.lc_val(result_manual.X)); + BOOST_CHECK(expected.Y == bp_manual.lc_val(result_manual.Y)); + BOOST_CHECK(bp_manual.is_satisfied()); + + std::cout << "Input bits: " << in_bits.size() << std::endl; + std::cout << "To point constrains: " << bp.num_constraints() << std::endl; +} + +/// hashing to point +template +void test_block_variable_component_constructor(const std::vector &in_bits, const ExpectedType &expected) { + using field_type = typename HashComponent::field_type; + + // input as block_variable + blueprint bp, bp_manual; + components::block_variable in_block(bp, in_bits.size()), in_block_manual(bp_manual, in_bits.size()); + in_block.generate_assignments(in_bits); + in_block_manual.generate_assignments(in_bits); + + // Auto allocation of the result + HashComponent hash_comp(bp, in_block); + hash_comp.generate_assignments(); + hash_comp.generate_gates(); + BOOST_CHECK(expected.X == bp.lc_val(hash_comp.result.X)); + BOOST_CHECK(expected.Y == bp.lc_val(hash_comp.result.Y)); + BOOST_CHECK(bp.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_manual(bp_manual); + HashComponent hash_comp_manual(bp_manual, in_block_manual, result_manual); + hash_comp_manual.generate_assignments(); + hash_comp_manual.generate_gates(); + BOOST_CHECK(expected.X == bp_manual.lc_val(result_manual.X)); + BOOST_CHECK(expected.Y == bp_manual.lc_val(result_manual.Y)); + BOOST_CHECK(bp_manual.is_satisfied()); +} + +/// hashing to point +template +void test_block_variables_component_constructor(const std::vector &in_bits, const ExpectedType &expected) { + using field_type = typename HashComponent::field_type; + + // input as container of block_variable + blueprint bp, bp_manual; + std::size_t half_size = in_bits.size() / 2; + components::block_variable in_block_left(bp, half_size), in_block_right(bp, in_bits.size() - half_size), + in_block_manual_left(bp_manual, half_size), in_block_manual_right(bp_manual, in_bits.size() - half_size); + in_block_left.generate_assignments(std::vector(std::cbegin(in_bits), std::cbegin(in_bits) + half_size)); + in_block_right.generate_assignments(std::vector(std::cbegin(in_bits) + half_size, std::cend(in_bits))); + in_block_manual_left.generate_assignments( + std::vector(std::cbegin(in_bits), std::cbegin(in_bits) + half_size)); + in_block_manual_right.generate_assignments( + std::vector(std::cbegin(in_bits) + half_size, std::cend(in_bits))); + + // Auto allocation of the result + HashComponent hash_comp(bp, + std::vector { + in_block_left, + in_block_right, + }); + hash_comp.generate_assignments(); + hash_comp.generate_gates(); + BOOST_CHECK(expected.X == bp.lc_val(hash_comp.result.X)); + BOOST_CHECK(expected.Y == bp.lc_val(hash_comp.result.Y)); + BOOST_CHECK(bp.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_manual(bp_manual); + HashComponent hash_comp_manual(bp_manual, + std::vector { + in_block_manual_left, + in_block_manual_right, + }, + result_manual); + hash_comp_manual.generate_assignments(); + hash_comp_manual.generate_gates(); + BOOST_CHECK(expected.X == bp_manual.lc_val(result_manual.X)); + BOOST_CHECK(expected.Y == bp_manual.lc_val(result_manual.Y)); + BOOST_CHECK(bp_manual.is_satisfied()); +} + +/// hashing to bits +template +void test_blueprint_variable_vector_component_constructor(const std::vector &in_bits, + const std::vector &expected_bits) { + using field_type = typename HashComponent::field_type; + + // input as blueprint_variable_vector + blueprint bp_bits, bp_bits_manual; + nil::crypto3::zk::detail::blueprint_variable_vector scalar_bits, scalar_bits_manual; + scalar_bits.allocate(bp_bits, in_bits.size()); + scalar_bits.fill_with_bits(bp_bits, in_bits); + scalar_bits_manual.allocate(bp_bits_manual, in_bits.size()); + scalar_bits_manual.fill_with_bits(bp_bits_manual, in_bits); + + // Auto allocation of the result + HashComponent hash_comp_bits(bp_bits, scalar_bits); + hash_comp_bits.generate_assignments(); + hash_comp_bits.generate_gates(); + BOOST_CHECK(expected_bits == hash_comp_bits.result.get_digest()); + BOOST_CHECK(bp_bits.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_bits_manual(bp_bits_manual, field_type::value_bits); + HashComponent hash_comp_bits_manual(bp_bits_manual, scalar_bits_manual, result_bits_manual); + hash_comp_bits_manual.generate_assignments(); + hash_comp_bits_manual.generate_gates(); + BOOST_CHECK(expected_bits == result_bits_manual.get_digest()); + BOOST_CHECK(bp_bits_manual.is_satisfied()); + + std::cout << "Input bits: " << in_bits.size() << std::endl; + std::cout << "To bits: " << bp_bits.num_constraints() << std::endl; +} + +/// hashing to bits +template +void test_digest_variable_component_constructor(const std::vector &in_bits, + const std::vector &expected_bits) { + using field_type = typename HashComponent::field_type; + + // input as digest_variable + blueprint bp_bits, bp_bits_manual; + components::digest_variable in_block(bp_bits, in_bits.size()), + in_block_manual(bp_bits_manual, in_bits.size()); + in_block.generate_assignments(in_bits); + in_block_manual.generate_assignments(in_bits); + + // Auto allocation of the result + HashComponent hash_comp_bits(bp_bits, in_block); + hash_comp_bits.generate_assignments(); + hash_comp_bits.generate_gates(); + BOOST_CHECK(expected_bits == hash_comp_bits.result.get_digest()); + BOOST_CHECK(bp_bits.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_bits_manual(bp_bits_manual, field_type::value_bits); + HashComponent hash_comp_bits_manual(bp_bits_manual, in_block_manual, result_bits_manual); + hash_comp_bits_manual.generate_assignments(); + hash_comp_bits_manual.generate_gates(); + BOOST_CHECK(expected_bits == result_bits_manual.get_digest()); + BOOST_CHECK(bp_bits_manual.is_satisfied()); +} + +/// hashing to bits +template +void test_digest_variables_component_constructor(const std::vector &in_bits, + const std::vector &expected_bits) { + using field_type = typename HashComponent::field_type; + + // input as container of block_variable + blueprint bp_bits, bp_bits_manual; + std::size_t half_size = in_bits.size() / 2; + components::digest_variable in_block_left(bp_bits, half_size), + in_block_right(bp_bits, in_bits.size() - half_size), in_block_manual_left(bp_bits_manual, half_size), + in_block_manual_right(bp_bits_manual, in_bits.size() - half_size); + in_block_left.generate_assignments(std::vector(std::cbegin(in_bits), std::cbegin(in_bits) + half_size)); + in_block_right.generate_assignments(std::vector(std::cbegin(in_bits) + half_size, std::cend(in_bits))); + in_block_manual_left.generate_assignments( + std::vector(std::cbegin(in_bits), std::cbegin(in_bits) + half_size)); + in_block_manual_right.generate_assignments( + std::vector(std::cbegin(in_bits) + half_size, std::cend(in_bits))); + + // Auto allocation of the result + HashComponent hash_comp_bits(bp_bits, + std::vector { + in_block_left, + in_block_right, + }); + hash_comp_bits.generate_assignments(); + hash_comp_bits.generate_gates(); + BOOST_CHECK(expected_bits == hash_comp_bits.result.get_digest()); + BOOST_CHECK(bp_bits.is_satisfied()); + + // Manual allocation of the result + typename HashComponent::result_type result_bits_manual(bp_bits_manual, field_type::value_bits); + HashComponent hash_comp_bits_manual(bp_bits_manual, + std::vector { + in_block_manual_left, + in_block_manual_right, + }, + result_bits_manual); + hash_comp_bits_manual.generate_assignments(); + hash_comp_bits_manual.generate_gates(); + BOOST_CHECK(expected_bits == result_bits_manual.get_digest()); + BOOST_CHECK(bp_bits_manual.is_satisfied()); +} + +// TODO: extend tests (check verification of wrong values) +template, + typename HashComponent = components::pedersen> +void test_pedersen_default_params_component( + const std::vector &in_bits, + const typename HashToPointComponent::element_component::group_value_type &expected, + const std::vector &expected_bits) { + using field_type = typename HashToPointComponent::element_component::group_value_type::field_type; + + /// hashing to point + test_blueprint_variable_vector_component_constructor(in_bits, expected); + test_block_variable_component_constructor(in_bits, expected); + test_block_variables_component_constructor(in_bits, expected); + + /// hashing to bits + test_blueprint_variable_vector_component_constructor(in_bits, expected_bits); + test_digest_variable_component_constructor(in_bits, expected_bits); + test_digest_variables_component_constructor(in_bits, expected_bits); +} + +// TODO: extend tests, add checks of wrong values +BOOST_AUTO_TEST_SUITE(blueprint_pedersen_manual_test_suite) + +// test data generated by https://github.com/zcash-hackworks/zcash-test-vectors +BOOST_AUTO_TEST_CASE(pedersen_jubjub_sha256_default_params_test) { + using curve_type = curves::jubjub; + using field_type = typename curve_type::base_field_type; + using field_value_type = typename field_type::value_type; + using integral_type = typename field_type::integral_type; + + std::vector bits_to_hash = {0, 0, 0, 1, 1, 1}; + auto expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("3669431847238482802904025485408296241776002230868041345055738963615665974946")), + field_value_type( + integral_type("27924821127213629235056488929093463445821551452792195607066067950495472725010"))); + std::vector expected_bits = { + 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, + 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}; + test_pedersen_default_params_component(bits_to_hash, expected, expected_bits); + + // check work of internal padding + bits_to_hash = { + 0, 0, 0, 1, 1, + }; + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("36263379031273262448220672699212876513597479199804632409115456999776988098218")), + field_value_type( + integral_type("31510484483269042758896724536623472863781228578271767290815193389100113348921"))); + expected_bits = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1}; + test_pedersen_default_params_component(bits_to_hash, expected, expected_bits); + + bits_to_hash = std::vector {0, 0, 1}; + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("37613883148175089126541491300600635192159391899451195953263717773938227311808")), + field_value_type( + integral_type("52287259411977570791304693313354699485314647509298698724706688571292689216990"))); + expected_bits = {0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1}; + test_pedersen_default_params_component(bits_to_hash, expected, expected_bits); + + bits_to_hash = std::vector {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1}; + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("42176130776060636907007595971304534904965322197894055434176666599102076910022")), + field_value_type( + integral_type("41298132615767455442973386625334423316246314118050839847545855695501416927077"))); + expected_bits = {0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, + 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1}; + test_pedersen_default_params_component(bits_to_hash, expected, expected_bits); + + bits_to_hash.resize(3 * 63 * 20); + for (auto i = 0; i < bits_to_hash.size(); i++) { + bits_to_hash[i] = std::vector {0, 0, 1}[i % 3]; + } + expected = + typename curve_type::template g1_type::value_type( + field_value_type( + integral_type("16831926627213193043296678235139527332739870606672735560230973395062624230202")), + field_value_type( + integral_type("29758113761493087483326459667018939508613372210858382541334106957041082715241"))); + expected_bits = {0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, + 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0}; + test_pedersen_default_params_component(bits_to_hash, expected, expected_bits); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/hashes/r1cs/sha256.cpp b/libs/blueprint/test/hashes/r1cs/sha256.cpp new file mode 100644 index 000000000..dc1a30191 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/sha256.cpp @@ -0,0 +1,80 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE sha256_component_test + +#include + +#include + +#include +#include +#include +#include + +#include "sha256.hpp" + +using namespace nil::crypto3::algebra; +using namespace nil::crypto3; +using namespace nil::crypto3::zk; + +BOOST_AUTO_TEST_SUITE(sha2_256_component_test_suite) +BOOST_AUTO_TEST_CASE(sha256_component_test_bls12_381_case) { + std::cout << "Starting SHA256 component test for BLS12-381 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + sha2_two_to_one_bp::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA256 component test for BLS12-381 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_mnt4_case) { + std::cout << "Starting SHA256 component test for MNT4-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + sha2_two_to_one_bp::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA256 component test for MNT4-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_mnt6_case) { + std::cout << "Starting SHA256 component test for MNT6-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + sha2_two_to_one_bp::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA256 component test for MNT6-298 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_edwards_183_case) { + std::cout << "Starting SHA256 component test for Edwards-183 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + sha2_two_to_one_bp::scalar_field_type>(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA256 component test for Edwards-183 finished, time: " << elapsed.count() * 1e-9 << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/hashes/r1cs/sha256.hpp b/libs/blueprint/test/hashes/r1cs/sha256.hpp new file mode 100644 index 000000000..ed5ade0e3 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/sha256.hpp @@ -0,0 +1,94 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_TEST_HPP + +#include +#include +#include +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::algebra; +using namespace nil::crypto3::zk; + +template +blueprint sha2_two_to_one_bp() { + blueprint bp; + + components::digest_variable left(bp, hashes::sha2<256>::digest_bits); + components::digest_variable right(bp, hashes::sha2<256>::digest_bits); + components::digest_variable output(bp, hashes::sha2<256>::digest_bits); + + components::sha256_two_to_one_hash_component f(bp, left, right, output); + + f.generate_gates(); + std::cout << "Number of constraints for sha256_two_to_one_hash_component: " << bp.num_constraints() << std::endl; + + std::array array_a_intermediate; + std::array array_b_intermediate; + std::array array_c_intermediate; + + std::array array_a = {0x426bc2d8, 0x4dc86782, 0x81e8957a, 0x409ec148, + 0xe6cffbe8, 0xafe6ba4f, 0x9c6f1978, 0xdd7af7e9}; + std::array array_b = {0x038cce42, 0xabd366b8, 0x3ede7e00, 0x9130de53, + 0x72cdf73d, 0xee825114, 0x8cb48d1b, 0x9af68ad0}; + std::array array_c = {0xeffd0b7f, 0x1ccba116, 0x2ee816f7, 0x31c62b48, + 0x59305141, 0x990e5c0a, 0xce40d33d, 0x0b1167d1}; + + std::vector left_bv(hashes::sha2<256>::digest_bits), right_bv(hashes::sha2<256>::digest_bits), + hash_bv(hashes::sha2<256>::digest_bits); + + nil::crypto3::detail::pack( + array_a.begin(), array_a.end(), array_a_intermediate.begin()); + + nil::crypto3::detail::pack( + array_b.begin(), array_b.end(), array_b_intermediate.begin()); + + nil::crypto3::detail::pack( + array_c.begin(), array_c.end(), array_c_intermediate.begin()); + + nil::crypto3::detail::pack_to(array_a_intermediate, left_bv.begin()); + + nil::crypto3::detail::pack_to(array_b_intermediate, right_bv.begin()); + + nil::crypto3::detail::pack_to(array_c_intermediate, hash_bv.begin()); + + left.generate_assignments(left_bv); + + right.generate_assignments(right_bv); + + f.generate_assignments(); + output.generate_assignments(hash_bv); + + BOOST_CHECK(bp.is_satisfied()); + + return bp; +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_SHA256_COMPONENT_TEST_HPP diff --git a/libs/blueprint/test/hashes/r1cs/sha256_verification.cpp b/libs/blueprint/test/hashes/r1cs/sha256_verification.cpp new file mode 100644 index 000000000..cff5b8d59 --- /dev/null +++ b/libs/blueprint/test/hashes/r1cs/sha256_verification.cpp @@ -0,0 +1,130 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE sha256_verification_test + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +// #include +// #include +// #include +// #include + +#include "sha256.hpp" +#include "../verify_r1cs_scheme.hpp" + +using namespace nil::crypto3::algebra; +using namespace nil::crypto3::zk; + +BOOST_AUTO_TEST_SUITE(sha256_component_test_suite) + +BOOST_AUTO_TEST_CASE(sha256_component_test_bls12_381_case) { + using curve_type = curves::bls12<381>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting SHA-256 component verification test for BLS12-381 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = sha2_two_to_one_bp(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA-256 component verification test for BLS12-381 finished, time: " << elapsed.count() * 1e-9 + << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_mnt4_case) { + using curve_type = curves::mnt4<298>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting SHA-256 component verification test for MNT4-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = sha2_two_to_one_bp(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA-256 component verification test for MNT4-298 finished, time: " << elapsed.count() * 1e-9 + << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_mnt6_case) { + using curve_type = curves::mnt6<298>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting SHA-256 component verification test for MNT6-298 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = sha2_two_to_one_bp(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA-256 component verification test for MNT6-298 finished, time: " << elapsed.count() * 1e-9 + << std::endl; +} + +BOOST_AUTO_TEST_CASE(sha256_component_test_edwards_183_case) { + using curve_type = curves::edwards<183>; + using scalar_field_type = typename curve_type::scalar_field_type; + + std::cout << "Starting SHA-256 component verification test for Edwards-183 ..." << std::endl; + auto begin = std::chrono::high_resolution_clock::now(); + blueprint bp = sha2_two_to_one_bp(); + + BOOST_CHECK(verify_component(bp)); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "SHA-256 component verification test for Edwards-183 finished, time: " << elapsed.count() * 1e-9 + << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/manifest.cpp b/libs/blueprint/test/manifest.cpp new file mode 100644 index 000000000..b4e3350f4 --- /dev/null +++ b/libs/blueprint/test/manifest.cpp @@ -0,0 +1,359 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_manifest_test + +#include +#include + +#include +#include +#include + +#include + +using namespace nil::blueprint; + +BOOST_AUTO_TEST_SUITE(blueprint_manifest_test_suite) + +void test_manifest_range_intersect(std::int32_t start_1, std::int32_t end_1, std::uint32_t step_1, + std::int32_t start_2, std::int32_t end_2, std::uint32_t step_2) { + std::shared_ptr param_1 = std::make_shared(start_1, end_1, step_1); + std::shared_ptr param_2 = std::make_shared(start_2, end_2, step_2); + std::shared_ptr result = param_1->intersect(param_2); + + std::int32_t new_start, new_end, new_step; + if (step_1 == step_2) { + new_start = std::max(start_1, start_2); + new_end = std::min(end_1, end_2); + new_step = step_1; + if (new_start >= new_end) { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::UNSAT); + } else if (new_start == new_end - 1) { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::SINGLE_VALUE); + manifest_single_value_param* res = dynamic_cast(result.get()); + BOOST_ASSERT(res->value == std::size_t(new_start)); + } else { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::RANGE); + manifest_range_param* res = dynamic_cast(result.get()); + BOOST_ASSERT(res->start == new_start); + BOOST_ASSERT(res->finish == new_end); + BOOST_ASSERT(res->step == std::size_t(new_step)); + } + } else { + auto [gcd, m, n] = boost::integer::extended_euclidean(step_1, step_2); + if (start_1 % gcd != start_2 % gcd) { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::UNSAT); + } else { + new_step = step_1 * (step_2 / gcd); + std::uint32_t result_modulo_new_step = + (new_step + ((start_1 * int(step_2) * n + start_2 * int(step_1) * m) / gcd) % new_step) % new_step; + new_start = std::max(start_1, start_2); + new_start = new_start + (new_step + int(result_modulo_new_step - new_start) % new_step) % new_step; + new_end = std::min(end_1, end_2); + if (new_start >= new_end) { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::UNSAT); + } else if (new_start == new_end - 1) { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::SINGLE_VALUE); + manifest_single_value_param* res = dynamic_cast(result.get()); + BOOST_ASSERT(res->value == std::size_t(new_start)); + } else { + BOOST_ASSERT(get_manifest_param_type(result) == manifest_param::type::RANGE); + manifest_range_param* res = dynamic_cast(result.get()); + BOOST_ASSERT(res->start == new_start); + BOOST_ASSERT(res->finish == new_end); + BOOST_ASSERT(res->step == std::size_t(new_step)); + } + } + } +} + +BOOST_AUTO_TEST_CASE(test_manifest_range_intersection) { + test_manifest_range_intersect(1, 10, 2, 5, 120, 2); + test_manifest_range_intersect(1, 10, 2, 5, 15, 3); + test_manifest_range_intersect(20, 100, 14, 50, 150, 6); + test_manifest_range_intersect(21, 100, 14, 49, 150, 7); + test_manifest_range_intersect(123, 1000, 30, 49, 150, 17); + test_manifest_range_intersect(21, 91, 14, 49, 150, 7); + test_manifest_range_intersect(0, 10, 1, 3, 12, 3); + test_manifest_range_intersect(2, 3, 1, 2, 3, 1); +} + +void test_operator(std::shared_ptr param_1, + std::shared_ptr param_2, + std::shared_ptr expected_result, + const std::function(std::shared_ptr, + std::shared_ptr)> &oper) { + std::shared_ptr result = oper(param_1, param_2); + std::shared_ptr result_2 = oper(param_2, param_1); + BOOST_ASSERT(get_manifest_param_type(result) == get_manifest_param_type(expected_result)); + BOOST_ASSERT(get_manifest_param_type(result_2) == get_manifest_param_type(expected_result)); + auto type = get_manifest_param_type(expected_result); + if (type == manifest_param::type::UNSAT) { + return; + } else if (type == manifest_param::type::SINGLE_VALUE) { + auto value_1 = dynamic_cast(result.get())->value; + auto value_2 = dynamic_cast(expected_result.get())->value; + BOOST_ASSERT(value_1 == value_2); + auto value_3 = dynamic_cast(result_2.get())->value; + BOOST_ASSERT(value_3 == value_2); + } else if (type == manifest_param::type::SET) { + auto set_1 = dynamic_cast(result.get())->set; + auto set_2 = dynamic_cast(expected_result.get())->set; + BOOST_ASSERT(set_1 == set_2); + auto set_3 = dynamic_cast(result_2.get())->set; + BOOST_ASSERT(set_3 == set_2); + } else if (type == manifest_param::type::RANGE) { + auto range_1 = dynamic_cast(result.get()); + auto range_2 = dynamic_cast(expected_result.get()); + BOOST_ASSERT(*range_1 == *range_2); + auto range_3 = dynamic_cast(result_2.get()); + BOOST_ASSERT(*range_3 == *range_2); + } else { + BOOST_ASSERT(false); + } +} + +BOOST_AUTO_TEST_CASE(test_manifest_param_intersection) { + auto intersection_tester = [](std::shared_ptr param_1, + std::shared_ptr param_2) { + return param_1->intersect(param_2); + }; + std::shared_ptr param_1 = std::make_shared(5); + std::shared_ptr param_2 = std::make_shared(5); + std::shared_ptr result_1 = std::make_shared(5); + test_operator(param_1, param_2, result_1, intersection_tester); + + std::shared_ptr param_3 = std::make_shared(6); + std::shared_ptr result_2 = std::make_shared(); + test_operator(param_1, param_3, result_2, intersection_tester); + + std::shared_ptr param_4 = std::make_shared(0, 10, 1); + std::shared_ptr result_3 = std::make_shared(5); + test_operator(param_1, param_4, result_3, intersection_tester); + + std::shared_ptr param_5 = std::make_shared( + std::set{0, 2, 5, 120}); + std::shared_ptr result_4 = std::make_shared(5); + test_operator(param_1, param_5, result_4, intersection_tester); + + std::shared_ptr result_5 = std::make_shared( + std::set{0, 2, 5} + ); + test_operator(param_5, param_4, result_5, intersection_tester); +} + +BOOST_AUTO_TEST_CASE(test_manifest_param_merge_with) { + auto merge_tester = [](std::shared_ptr param_1, + std::shared_ptr param_2) { + return param_1->merge_with(param_2); + }; + + std::shared_ptr param_1 = std::make_shared(5); + std::shared_ptr param_2 = std::make_shared(11); + std::shared_ptr result_1 = std::make_shared(11); + test_operator(param_1, param_2, result_1, merge_tester); + + std::shared_ptr param_3 = std::make_shared(0, 10, 1); + std::shared_ptr result_2 = std::make_shared(5, 10, 1); + test_operator(param_3, param_1, result_2, merge_tester); + test_operator(param_2, param_3, param_2, merge_tester); + test_operator(result_2, param_3, result_2, merge_tester); + + std::shared_ptr param_4 = std::make_shared(10, 20, 2); + test_operator(param_4, param_3, param_4, merge_tester); + std::shared_ptr param_5 = std::make_shared( + std::set{0, 1, 2, 3, 4, 5, 91, 11}); + std::shared_ptr result_3 = std::make_shared( + std::set{11, 12, 14, 16, 18, 91}); + test_operator(param_5, param_4, result_3, merge_tester); + + std::shared_ptr param_6 = std::make_shared(9, 28, 3); + std::shared_ptr result_4 = std::make_shared(std::set{ + 10, 12, 14, 16, 18, 15, 18, 21, 24, 27 + }); + test_operator(param_6, param_4, result_4, merge_tester); +} + +BOOST_AUTO_TEST_CASE(test_manifest_iteration) { + manifest_single_value_param param(5); + std::size_t i = 0; + for (auto val : param) { + BOOST_ASSERT(val == 5); + ++i; + } + BOOST_ASSERT(i == 1); + + using manifest_set_param = manifest_set_param; + std::set expected_set = {0, 1, 2, 3, 4, 5, 91, 11}; + manifest_set_param param_set(expected_set); + + std::size_t j = 0; + for (auto val : param_set) { + BOOST_ASSERT(expected_set.find(val) != expected_set.end()); + ++j; + } + BOOST_ASSERT(j == expected_set.size()); + + using manifest_range_param = manifest_range_param; + manifest_range_param param_range(1, 16, 2); + std::set expected_range = {1, 3, 5, 7, 9, 11, 13, 15}; + std::size_t k = 0; + for (auto val : param_range) { + BOOST_ASSERT(expected_range.find(val) != expected_range.end()); + ++k; + } + BOOST_ASSERT(k == expected_range.size()); +} + +template +void test_table_operation(const std::map, TestType1> &test_table, + const std::function &operation) { + for (auto test_case : test_table) { + auto [type_1, type_2] = test_case.first; + auto expected_result = test_case.second; + auto result = operation(type_1, type_2); + BOOST_ASSERT(result == expected_result); + if constexpr (std::is_same_v) { + auto second_result = operation(type_2, type_1); + BOOST_ASSERT(second_result == expected_result); + } + } +} + +BOOST_AUTO_TEST_CASE(test_manifest_constant_type_intersection) { + compiler_manifest + has_constant(0, true), + has_no_constant(0, false); + std::map, manifest_constant_type> + intersection_test_table = { + {{manifest_constant_type::type::UNSAT, has_constant}, manifest_constant_type::type::UNSAT}, + {{manifest_constant_type::type::UNSAT, has_no_constant}, manifest_constant_type::type::UNSAT}, + {{manifest_constant_type::type::NONE, has_constant}, manifest_constant_type::type::NONE}, + {{manifest_constant_type::type::NONE, has_no_constant}, manifest_constant_type::type::NONE}, + {{manifest_constant_type::type::REQUIRED, has_constant}, manifest_constant_type::type::REQUIRED}, + {{manifest_constant_type::type::REQUIRED, has_no_constant}, manifest_constant_type::type::UNSAT}, + }; + std::function test_intersect + = [](const manifest_constant_type &type_1, const compiler_manifest &type_2) { + return type_1.intersect(type_2); + }; + + test_table_operation(intersection_test_table, test_intersect); +} + +BOOST_AUTO_TEST_CASE(test_manifest_constant_type_merge_with) { + std::map, manifest_constant_type> + merge_with_test_table = { + {{manifest_constant_type::type::UNSAT, manifest_constant_type::type::UNSAT}, manifest_constant_type::type::UNSAT}, + {{manifest_constant_type::type::UNSAT, manifest_constant_type::type::NONE}, manifest_constant_type::type::UNSAT}, + {{manifest_constant_type::type::UNSAT, manifest_constant_type::type::REQUIRED}, manifest_constant_type::type::UNSAT}, + {{manifest_constant_type::type::NONE, manifest_constant_type::type::NONE}, manifest_constant_type::type::NONE}, + {{manifest_constant_type::type::NONE, manifest_constant_type::type::REQUIRED}, manifest_constant_type::type::REQUIRED}, + {{manifest_constant_type::type::REQUIRED, manifest_constant_type::type::REQUIRED}, manifest_constant_type::type::REQUIRED}, + }; + std::function test_merge_with + = [](const manifest_constant_type &type_1, const manifest_constant_type &type_2) { + return type_1.merge_with(type_2); + }; + + test_table_operation(merge_with_test_table, test_merge_with); +} + +bool check_param_equality(const std::shared_ptr& param_1, + const std::shared_ptr& param_2) { + if (get_manifest_param_type(param_1) != get_manifest_param_type(param_2)) { + return false; + } + manifest_param::type type = get_manifest_param_type(param_1); + switch (type) { + case manifest_param::type::UNSAT: + return dynamic_cast(param_1.get())->operator==( + *dynamic_cast(param_2.get())); + case manifest_param::type::SINGLE_VALUE: + return dynamic_cast(param_1.get())->operator==( + *dynamic_cast(param_2.get())); + case manifest_param::type::RANGE: + return dynamic_cast(param_1.get())->operator==( + *dynamic_cast(param_2.get())); + case manifest_param::type::SET: + return dynamic_cast(param_1.get())->operator==( + *dynamic_cast(param_2.get())); + default: + return false; + } +} + +bool check_manifest_equality(const plonk_component_manifest& manifest_1, + const plonk_component_manifest& manifest_2) { + if (!check_param_equality(manifest_1.witness_amount, manifest_2.witness_amount)) { + return false; + } + if (manifest_1.constant_required != manifest_2.constant_required) { + return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(test_manifest_intersect) { + compiler_manifest comp_manifest_1(9, false); + plonk_component_manifest manifest_1( + std::make_shared(3, 12, 3), + manifest_constant_type::type::NONE); + plonk_component_manifest manifest_res_1 = comp_manifest_1.intersect(manifest_1); + plonk_component_manifest expected_res_1 = plonk_component_manifest( + std::make_shared(3, 10, 3), + manifest_constant_type::type::NONE); + BOOST_ASSERT(check_manifest_equality(manifest_res_1, expected_res_1)); + + plonk_component_manifest manifest_2( + std::make_shared(3, 12, 3), + manifest_constant_type::type::NONE); + plonk_component_manifest manifest_res_2 = comp_manifest_1.intersect(manifest_2); + plonk_component_manifest expected_res_2 = plonk_component_manifest( + std::make_shared(3, 10, 3), + manifest_constant_type::type::NONE); + BOOST_ASSERT(check_manifest_equality(manifest_res_2, expected_res_2)); + + plonk_component_manifest manifest_3( + std::make_shared(5), + manifest_constant_type::type::REQUIRED); + plonk_component_manifest manifest_res_3 = comp_manifest_1.intersect(manifest_3); + plonk_component_manifest expected_res_3 = plonk_component_manifest( + std::make_shared(5), + manifest_constant_type::type::UNSAT); + BOOST_ASSERT(check_manifest_equality(manifest_res_3, expected_res_3)); + + compiler_manifest comp_manifest_2(20, true); + plonk_component_manifest manifest_4( + std::make_shared(std::set{1, 2, 3, 11, 21, 22}), + manifest_constant_type::type::REQUIRED); + plonk_component_manifest manifest_res_4 = comp_manifest_2.intersect(manifest_4); + plonk_component_manifest expected_res_4 = plonk_component_manifest( + std::make_shared(std::set{1, 2, 3, 11}), + manifest_constant_type::type::REQUIRED); + BOOST_ASSERT(check_manifest_equality(manifest_res_4, expected_res_4)); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/merkle_tree_components.cpp b/libs/blueprint/test/merkle_tree_components.cpp new file mode 100644 index 000000000..fd3d1c5d5 --- /dev/null +++ b/libs/blueprint/test/merkle_tree_components.cpp @@ -0,0 +1,439 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE merkle_tree_components_test + +#include + +#include + +// TODO: fix sha256 component +// #include +#include + +// TODO: fix update component +// #include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +// template +// void test_merkle_tree_check_update_component() { +// /* prepare test */ +// const std::size_t digest_len = Hash::get_digest_len(); +// +// const std::size_t tree_depth = 16; +// std::vector prev_path(tree_depth); +// +// std::vector prev_load_hash(digest_len); +// std::generate(prev_load_hash.begin(), prev_load_hash.end(), [&]() { return std::rand() % 2; }); +// std::vector prev_store_hash(digest_len); +// std::generate(prev_store_hash.begin(), prev_store_hash.end(), [&]() { return std::rand() % 2; }); +// +// std::vector loaded_leaf = prev_load_hash; +// std::vector stored_leaf = prev_store_hash; +// +// std::vector address_bits; +// +// std::size_t address = 0; +// for (long level = tree_depth - 1; level >= 0; --level) { +// const bool computed_is_right = (std::rand() % 2); +// address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0); +// address_bits.push_back(computed_is_right); +// std::vector other(digest_len); +// std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); +// +// std::vector load_block = prev_load_hash; +// load_block.insert(computed_is_right ? load_block.begin() : load_block.end(), other.begin(), other.end()); +// std::vector store_block = prev_store_hash; +// store_block.insert(computed_is_right ? store_block.begin() : store_block.end(), other.begin(), other.end()); +// +// std::vector load_h = Hash::get_hash(load_block); +// std::vector store_h = Hash::get_hash(store_block); +// +// prev_path[level] = other; +// +// prev_load_hash = load_h; +// prev_store_hash = store_h; +// } +// +// std::vector load_root = prev_load_hash; +// std::vector store_root = prev_store_hash; +// +// /* execute the test */ +// components::blueprint bp; +// components::blueprint_variable_vector address_bits_va; +// address_bits_va.allocate(bp, tree_depth); +// components::digest_variable prev_leaf_digest(bp, digest_len); +// components::digest_variable prev_root_digest(bp, digest_len); +// components::merkle_authentication_path_variable prev_path_var(bp, tree_depth); +// components::digest_variable next_leaf_digest(bp, digest_len); +// components::digest_variable next_root_digest(bp, digest_len); +// components::merkle_authentication_path_variable next_path_var(bp, tree_depth); +// components::merkle_tree_check_update_components mls( +// bp, tree_depth, address_bits_va, prev_leaf_digest, prev_root_digest, prev_path_var, next_leaf_digest, +// next_root_digest, next_path_var, components::blueprint_variable(0)); +// +// prev_path_var.generate_gates(); +// mls.generate_gates(); +// +// address_bits_va.fill_with_bits(bp, address_bits); +// BOOST_REQUIRE(address_bits_va.get_field_element_from_bits(bp) == address); +// prev_leaf_digest.generate_assignments(loaded_leaf); +// prev_path_var.generate_assignments(address, prev_path); +// next_leaf_digest.generate_assignments(stored_leaf); +// address_bits_va.fill_with_bits(bp, address_bits); +// mls.generate_assignments(); +// +// /* make sure that update check will check for the right things */ +// prev_leaf_digest.generate_assignments(loaded_leaf); +// next_leaf_digest.generate_assignments(stored_leaf); +// prev_root_digest.generate_assignments(load_root); +// next_root_digest.generate_assignments(store_root); +// address_bits_va.fill_with_bits(bp, address_bits); +// BOOST_REQUIRE(bp.is_satisfied()); +// +// const std::size_t num_constraints = bp.num_constraints(); +// const std::size_t expected_constraints = +// components::merkle_tree_check_update_components::expected_constraints(tree_depth); +// BOOST_REQUIRE(num_constraints == expected_constraints); +// } + +// template +// void test_merkle_tree_check_read_component() { +// /* prepare test */ +// const std::size_t digest_len = Hash::get_digest_len(); +// const std::size_t tree_depth = 16; +// std::vector path(tree_depth); +// +// std::vector prev_hash(digest_len); +// std::generate(prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; }); +// std::vector leaf = prev_hash; +// +// std::vector address_bits; +// +// std::size_t address = 0; +// for (long level = tree_depth - 1; level >= 0; --level) { +// const bool computed_is_right = (std::rand() % 2); +// address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0); +// address_bits.push_back(computed_is_right); +// std::vector other(digest_len); +// std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); +// +// std::vector block = prev_hash; +// block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end()); +// std::vector h = Hash::get_hash(block); +// +// path[level] = other; +// +// prev_hash = h; +// } +// std::vector root = prev_hash; +// +// /* execute test */ +// components::blueprint bp; +// components::blueprint_variable_vector address_bits_va; +// address_bits_va.allocate(bp, tree_depth); +// components::digest_variable leaf_digest(bp, digest_len); +// components::digest_variable root_digest(bp, digest_len); +// components::merkle_authentication_path_variable path_var(bp, tree_depth); +// components::merkle_tree_check_read_component ml(bp, tree_depth, address_bits_va, leaf_digest, +// root_digest, path_var, +// components::blueprint_variable(0)); +// +// path_var.generate_gates(); +// ml.generate_gates(); +// +// address_bits_va.fill_with_bits(bp, address_bits); +// BOOST_REQUIRE(address_bits_va.get_field_element_from_bits(bp) == address); +// leaf_digest.generate_assignments(leaf); +// path_var.generate_assignments(address, path); +// ml.generate_assignments(); +// +// /* make sure that read checker didn't accidentally overwrite anything */ +// address_bits_va.fill_with_bits(bp, address_bits); +// leaf_digest.generate_assignments(leaf); +// root_digest.generate_assignments(root); +// BOOST_REQUIRE(bp.is_satisfied()); +// +// const std::size_t num_constraints = bp.num_constraints(); +// const std::size_t expected_constraints = +// components::merkle_tree_check_read_component::expected_constraints(tree_depth); +// BOOST_REQUIRE(num_constraints == expected_constraints); +// } + +// template +// void test_all_merkle_tree_components() { +// typedef typename CurveType::scalar_field_type scalar_field_type; +// +// // for now all CRH components are knapsack CRH's; can be easily extended +// // later to more expressive selector types. +// using crh_with_field_out_component = components::knapsack_crh_with_field_out_component; +// using crh_with_bit_out_component = components::knapsack_crh_with_bit_out_component; +// +// test_merkle_tree_check_read_component(); +// test_merkle_tree_check_read_component>(); +// +// test_merkle_tree_check_update_component(); +// test_merkle_tree_check_update_component>(); +// } + +template +std::vector calculate_pedersen_via_component(const std::vector &in_bits) { + using hash_component = components::pedersen; + using field_type = typename hash_component::field_type; + + components::blueprint bp_bits; + components::block_variable in_block(bp_bits, in_bits.size()); + in_block.generate_assignments(in_bits); + + hash_component hash_comp_bits(bp_bits, in_block); + hash_comp_bits.generate_assignments(); + hash_comp_bits.generate_gates(); + return hash_comp_bits.result.get_digest(); +} + +void test_jubjub_pedersen_merkle_tree_container_check_validate_component() { + using curve_type = curves::jubjub; + using bp_generator_hash_type = hashes::sha2<256>; + using hash_params = hashes::find_group_hash_default_params; + using hash_component = components::pedersen; + using field_type = typename hash_component::field_type; + constexpr std::size_t arity = 2; + using merkle_proof_component = components::merkle_proof; + using merkle_validate_component = components::merkle_proof_validate; + + /* prepare test */ + const std::size_t digest_len = hash_component::get_digest_len(); + const std::size_t tree_depth = 2; + const std::size_t leafs_number = nil::crypto3::detail::pow(arity, tree_depth); + + // TODO: remove copy from array to vector + std::vector> leafs; + std::vector> leafs_v; + for (std::size_t i = 0; i < leafs_number; ++i) { + std::array leaf; + std::generate(leaf.begin(), leaf.end(), [&]() { return std::rand() % 2; }); + leafs.emplace_back(leaf); + } + + std::size_t leaf_idx = 0; + typename merkle_proof_component::merkle_tree_container tree(leafs); + typename merkle_proof_component::merkle_proof_container proof(tree, leaf_idx); + BOOST_CHECK(proof.validate(leafs[leaf_idx])); + BOOST_CHECK(!proof.validate(leafs[(leaf_idx + 1) % leafs_number])); + for (std::size_t i = 0; i < leafs_number; ++i) { + leafs_v.emplace_back( + static_cast>(nil::crypto3::hash(leafs[i]))); + } + + /* execute test */ + components::blueprint bp; + components::blueprint_variable_vector address_bits_va; + address_bits_va.allocate(bp, tree_depth); + components::digest_variable leaf_digest(bp, digest_len); + components::digest_variable root_digest(bp, digest_len); + merkle_proof_component path_var(bp, tree_depth); + merkle_validate_component ml(bp, tree_depth, address_bits_va, leaf_digest, root_digest, path_var, + components::blueprint_variable(0)); + + path_var.generate_gates(); + ml.generate_gates(); + + leaf_digest.generate_assignments(leafs_v[leaf_idx]); + path_var.generate_assignments(proof); + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address); + BOOST_REQUIRE(address_bits_va.get_field_element_from_bits(bp) == path_var.address); + ml.generate_assignments(); + + /* make sure that read checker didn't accidentally overwrite anything */ + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address); + leaf_digest.generate_assignments(leafs_v[leaf_idx]); + /// Very important step, hidden error could appear without it. merkle_validate_component use + /// bit_vector_copy_component to copy computed root into root_digest, so without this step internal check of the + /// computed step will always be positive + root_digest.generate_assignments(merkle_proof_component::root(proof)); + BOOST_REQUIRE(bp.is_satisfied()); + + auto root_wrong = merkle_proof_component::root(proof); + root_wrong[0] = !root_wrong[0]; + // false negative test with wrong root + root_digest.generate_assignments(root_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // reset blueprint in the correct state + root_digest.generate_assignments(merkle_proof_component::root(proof)); + BOOST_REQUIRE(bp.is_satisfied()); + // false negative test with wrong leaf + auto leaf_digest_wrong = leafs_v[leaf_idx]; + leaf_digest_wrong[0] = !leaf_digest_wrong[0]; + leaf_digest.generate_assignments(leaf_digest_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // reset blueprint in the correct state + leaf_digest.generate_assignments(leafs_v[leaf_idx]); + BOOST_REQUIRE(bp.is_satisfied()); + // false negative test with wrong path + typename merkle_proof_component::merkle_proof_container proof_wrong(tree, (leaf_idx + 1) % leafs_number); + path_var.generate_assignments(proof_wrong); + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address); + BOOST_REQUIRE(!bp.is_satisfied()); +} + +void test_jubjub_pedersen_merkle_tree_check_validate_component() { + using curve_type = curves::jubjub; + using bp_generator_hash_type = hashes::sha2<256>; + using hash_params = hashes::find_group_hash_default_params; + using hash_component = components::pedersen; + using field_type = typename hash_component::field_type; + constexpr std::size_t arity = 2; + using merkle_proof_component = components::merkle_proof; + using merkle_validate_component = components::merkle_proof_validate; + + /* prepare test */ + const std::size_t digest_len = hash_component::get_digest_len(); + const std::size_t tree_depth = 16; + std::vector path(tree_depth); + + std::vector prev_hash(digest_len); + std::generate(prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; }); + std::vector leaf = prev_hash; + auto leaf_wrong = leaf; + leaf_wrong[0] = !leaf_wrong[0]; + + std::vector address_bits; + + std::size_t address = 0; + for (long level = tree_depth - 1; level >= 0; --level) { + const bool computed_is_right = (std::rand() % 2); + address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0); + address_bits.push_back(computed_is_right); + std::vector other(digest_len); + std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); + + std::vector block = prev_hash; + block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end()); + std::vector h = calculate_pedersen_via_component(block); + + path[level] = other; + + prev_hash = h; + } + std::vector root = prev_hash; + auto root_wrong = root; + root_wrong[0] = !root_wrong[0]; + auto path_wrong = path; + path_wrong[0][0] = !path_wrong[0][0]; + auto address_bits_wrong = address_bits; + address_bits_wrong[0] = !address_bits_wrong[0]; + + /* execute test */ + components::blueprint bp; + components::blueprint_variable_vector address_bits_va; + address_bits_va.allocate(bp, tree_depth); + components::digest_variable leaf_digest(bp, digest_len); + components::digest_variable root_digest(bp, digest_len); + merkle_proof_component path_var(bp, tree_depth); + merkle_validate_component ml(bp, tree_depth, address_bits_va, leaf_digest, root_digest, path_var, + components::blueprint_variable(0)); + + path_var.generate_gates(); + ml.generate_gates(); + + address_bits_va.fill_with_bits(bp, address_bits); + BOOST_REQUIRE(address_bits_va.field_element_from_bits(bp) == address); + leaf_digest.generate_assignments(leaf); + path_var.generate_assignments(address, path); + ml.generate_assignments(); + + /* make sure that read checker didn't accidentally overwrite anything */ + address_bits_va.fill_with_bits(bp, address_bits); + leaf_digest.generate_assignments(leaf); + /// Very important step, hidden error could appear without it. merkle_validate_component use + /// bit_vector_copy_component to copy computed root into root_digest, so without this step internal check of the + /// computed step will always be positive + root_digest.generate_assignments(root); + BOOST_REQUIRE(bp.is_satisfied()); + + // false negative test with wrong root + root_digest.generate_assignments(root_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // reset blueprint in the correct state + root_digest.generate_assignments(root); + BOOST_REQUIRE(bp.is_satisfied()); + // false negative test with wrong leaf + leaf_digest.generate_assignments(leaf_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // reset blueprint in the correct state + leaf_digest.generate_assignments(leaf); + BOOST_REQUIRE(bp.is_satisfied()); + // false negative test with wrong path + path_var.generate_assignments(address, path_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // reset blueprint in the correct state + path_var.generate_assignments(address, path); + BOOST_REQUIRE(bp.is_satisfied()); + // false negative test with wrong address + address_bits_va.fill_with_bits(bp, address_bits_wrong); + BOOST_REQUIRE(!bp.is_satisfied()); + + // const std::size_t num_constraints = bp.num_constraints(); + // const std::size_t expected_constraints = + // components::merkle_tree_check_read_component::expected_constraints(tree_depth); + // BOOST_REQUIRE(num_constraints == expected_constraints); +} + +BOOST_AUTO_TEST_SUITE(merkle_tree_components_test_suite) + +// BOOST_AUTO_TEST_CASE(merkle_tree_components_bls12_381_test) { +// +// test_all_merkle_tree_components>(); +// } +// +// BOOST_AUTO_TEST_CASE(merkle_tree_components_mnt4_test) { +// +// test_all_merkle_tree_components>(); +// } +// +// BOOST_AUTO_TEST_CASE(merkle_tree_components_mnt6_test) { +// +// test_all_merkle_tree_components>(); +// } + +BOOST_AUTO_TEST_CASE(merkle_tree_components_jubjub_pedersen_test) { + + test_jubjub_pedersen_merkle_tree_container_check_validate_component(); + // test_jubjub_pedersen_merkle_tree_check_validate_component(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/mock/mocked_components.cpp b/libs/blueprint/test/mock/mocked_components.cpp new file mode 100644 index 000000000..943376106 --- /dev/null +++ b/libs/blueprint/test/mock/mocked_components.cpp @@ -0,0 +1,748 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tablain +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_mock_tests + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "../test_plonk_component.hpp" + +using namespace nil; + +#define ARITHMETIZATION_GEN \ + constexpr std::size_t WitnessColumns = 3; \ + constexpr std::size_t PublicInputColumns = 1; \ + constexpr std::size_t ConstantColumns = 0; \ + constexpr std::size_t SelectorColumns = 2; \ + zk::snark::plonk_table_description desc( \ + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); \ + using ArithmetizationType = \ + crypto3::zk::snark::plonk_constraint_system; \ + using AssignmentType = blueprint::assignment; \ + using hash_type = crypto3::hashes::keccak_1600<256>; \ + constexpr std::size_t Lambda = 1; + +#define TEST_SMALL_UNSIGNED_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a, \ + const typename BlueprintFieldType::value_type &b, \ + const typename BlueprintFieldType::value_type &expected_result) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a, b}; \ + \ + auto result_check = [&expected_result](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_result == var_value(assignment, real_res.a)); \ + }; \ + \ + component_type component_instance({0u, 1u, 2u}, {}, {}); \ + typename component_type::result_type(component_instance, 10); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +TEST_SMALL_UNSIGNED_GEN(test_small_unsigned_add, unsigned_addition_component_small) +TEST_SMALL_UNSIGNED_GEN(test_small_unsigned_sub, unsigned_subtraction_component_small) +TEST_SMALL_UNSIGNED_GEN(test_small_unsigned_mul, unsigned_multiplication_component_small) +TEST_SMALL_UNSIGNED_GEN(test_small_unsigned_div, unsigned_division_component_small) +TEST_SMALL_UNSIGNED_GEN(test_small_unsigned_rem, unsigned_remainder_component_small) + +#define TEST_SMALL_SIGNED_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a_sign, \ + const typename BlueprintFieldType::value_type &a_mod, \ + const typename BlueprintFieldType::value_type &b_sign, \ + const typename BlueprintFieldType::value_type &b_mod, \ + const typename BlueprintFieldType::value_type &expected_result_sign, \ + const typename BlueprintFieldType::value_type &expected_result_mod) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + var(0, 2, false, var::column_type::public_input), \ + var(0, 3, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a_sign, a_mod, b_sign, b_mod}; \ + \ + auto result_check = [&expected_result_sign, &expected_result_mod](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_result_sign == var_value(assignment, real_res.value[0])); \ + BOOST_ASSERT(expected_result_mod == var_value(assignment, real_res.value[1])); \ + }; \ + \ + component_type component_instance({0u, 1u, 2u}, {}, {}); \ + typename component_type::result_type(component_instance, 10u); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +TEST_SMALL_SIGNED_GEN(test_small_signed_add, signed_addition_component_small) +TEST_SMALL_SIGNED_GEN(test_small_signed_sub, signed_subtraction_component_small) +TEST_SMALL_SIGNED_GEN(test_small_signed_mul, signed_multiplication_component_small) +TEST_SMALL_SIGNED_GEN(test_small_signed_div, signed_division_component_small) +TEST_SMALL_SIGNED_GEN(test_small_signed_rem, signed_remainder_component_small) + +static constexpr const int random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_mock_test_suite) + +#define OP_ADDITION(a, b) (a + b) +#define OP_SUBTRACTION(a, b) (a - b) +#define OP_MULTIPLICATION(a, b) (a * b) +#define OP_DIVISION(a, b) (a / b) +#define OP_REMAINDER(a, b) (a % b) +#define OP_LESS(a, b) (a < b) +#define OP_LESS_EQ(a, b) (a <= b) +#define OP_GREATER(a, b) (a > b) +#define OP_GREATER_EQ(a, b) (a >= b) + +#define OP_UNSIGNED_TEST_FUNC_GEN(NAME, TEST_NAME, OP, EX_1, EX_2, EX_3) \ + template \ + void NAME() { \ + using uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend>; \ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(uint_type(2), Size) - 1); \ + uint_type a = EX_1, b = EX_2; \ + TEST_NAME(a, b, EX_3); \ + for (std::size_t i = 0; i < random_tests_amount; i++) { \ + a = dist(seed_seq); \ + b = dist(seed_seq); \ + TEST_NAME(a, b, OP(a, b)); \ + } \ + } + +#define OP_SIGNED_TEST_FUNC_GEN(NAME, TEST_NAME, OP) \ + template \ + void NAME() { \ + /* cpp_int_type is the one that exists in boost, it's not used by our code.*/ \ + using cpp_int_type = \ + boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend>; \ + using cpp_uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend>; \ + using uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend>; \ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(cpp_uint_type(2), Size) - 1); \ + boost::random::uniform_int_distribution sign_dist(0, 1); \ + for (std::size_t i = 0; i < 4 * random_tests_amount; i++) { \ + cpp_int_type a = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); \ + cpp_int_type b = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); \ + uint_type a_modular = typename uint_type::backend_type(cpp_uint_type(boost::multiprecision::abs(a)).backend()); \ + uint_type b_modular = typename uint_type::backend_type(cpp_uint_type(boost::multiprecision::abs(b)).backend()); \ + cpp_int_type result = OP(a, b); \ + uint_type result_modular = typename uint_type::backend_type(cpp_uint_type(boost::multiprecision::abs(result)).backend()); \ + TEST_NAME( \ + a >= 0 ? 0 : 1, a_modular, \ + b >= 0 ? 0 : 1, b_modular, \ + result.sign() >= 0 ? 0 : 1, result_modular); \ + } \ + } + +OP_UNSIGNED_TEST_FUNC_GEN( + test_small_unsigned_addition, test_small_unsigned_add, OP_ADDITION, + 1, boost::multiprecision::pow(uint_type(2), Size) - 1, 0) +OP_UNSIGNED_TEST_FUNC_GEN( + test_small_unsigned_multiplication, test_small_unsigned_mul, OP_MULTIPLICATION, + 2, boost::multiprecision::pow(uint_type(2), Size - 1), 0) +OP_UNSIGNED_TEST_FUNC_GEN( + test_small_unsigned_subtraction, test_small_unsigned_sub, OP_SUBTRACTION, + 0, boost::multiprecision::pow(uint_type(2), Size) - 1, 1); +OP_UNSIGNED_TEST_FUNC_GEN( + test_small_unsigned_division, test_small_unsigned_div, OP_DIVISION, 1, 2, 0); +OP_UNSIGNED_TEST_FUNC_GEN( + test_small_unsigned_remainder, test_small_unsigned_rem, OP_REMAINDER, 30, 3, 0); + +OP_SIGNED_TEST_FUNC_GEN(test_small_signed_addition, test_small_signed_add, OP_ADDITION) +OP_SIGNED_TEST_FUNC_GEN(test_small_signed_subtraction, test_small_signed_sub, OP_SUBTRACTION) +OP_SIGNED_TEST_FUNC_GEN(test_small_signed_multiplication, test_small_signed_mul, OP_MULTIPLICATION) +OP_SIGNED_TEST_FUNC_GEN(test_small_signed_division, test_small_signed_div, OP_DIVISION) +OP_SIGNED_TEST_FUNC_GEN(test_small_signed_remainder, test_small_signed_rem, OP_REMAINDER) + +// seprate infrastructure for abs +template +void test_small_signed_abs( + const typename BlueprintFieldType::value_type &a_sign, + const typename BlueprintFieldType::value_type &a_mod, + const typename BlueprintFieldType::value_type &expected_sign, + const typename BlueprintFieldType::value_type &expected_mod) { + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + ARITHMETIZATION_GEN; + using component_type = + nil::blueprint::components::signed_abs_component_small; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + }; + + std::vector public_input = {a_sign, a_mod}; + + auto result_check = [&expected_sign, &expected_mod](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + BOOST_ASSERT(expected_sign == var_value(assignment, real_res.value[0])); + BOOST_ASSERT(expected_mod == var_value(assignment, real_res.value[1])); + }; + + component_type component_instance({0, 1, 2}, {}, {}); + typename component_type::result_type(component_instance, 10); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::NONE); +} + +template +void test_small_signed_absolute() { + using cpp_int_type = boost::multiprecision::number>; + using cpp_uint_type = boost::multiprecision::number>; + using uint_type = boost::multiprecision::number>; + + boost::random::mt19937 seed_seq; + boost::random::uniform_int_distribution + dist(0, boost::multiprecision::pow(cpp_uint_type(2), Size) - 1); + boost::random::uniform_int_distribution sign_dist(0, 1); + for (std::size_t i = 0; i < 4 * random_tests_amount; i++) { + cpp_int_type a = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); + uint_type a_modular = typename uint_type::backend_type(cpp_uint_type(boost::multiprecision::abs(a)).backend()); + test_small_signed_abs( + a >= 0 ? 0 : 1, a_modular, + 0, a_modular); + } +} + +template +void test_big_signed_abs( + const typename BlueprintFieldType::value_type &a_sign, + const typename BlueprintFieldType::value_type &a_first, + const typename BlueprintFieldType::value_type &a_second, + const typename BlueprintFieldType::value_type &expected_sign, + const typename BlueprintFieldType::value_type &expected_first, + const typename BlueprintFieldType::value_type &expected_second) { + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + ARITHMETIZATION_GEN; + using component_type = + nil::blueprint::components::signed_abs_component_big; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + }; + + std::vector public_input = {a_sign, a_first, a_second}; + + auto result_check = [&expected_sign, &expected_first, &expected_second](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + BOOST_ASSERT(expected_sign == var_value(assignment, real_res.value[0])); + BOOST_ASSERT(expected_first == var_value(assignment, real_res.value[1])); + BOOST_ASSERT(expected_second == var_value(assignment, real_res.value[2])); + }; + + component_type component_instance({0, 1, 2}, {}, {}); + typename component_type::result_type(component_instance, 10); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::NONE); +} + +template +void test_big_signed_absolute() { + using cpp_int_type = boost::multiprecision::number>; + using cpp_uint_type = boost::multiprecision::number>; + using uint_type = boost::multiprecision::number< + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; + + boost::random::mt19937 seed_seq; + boost::random::uniform_int_distribution + dist(0, boost::multiprecision::pow(cpp_uint_type(2), 256) - 1); + boost::random::uniform_int_distribution sign_dist(0, 1); + static const cpp_int_type top_mask = ((cpp_uint_type(1) << 128) - 1) << 128; + static const cpp_int_type bottom_mask = (cpp_uint_type(1) << 128) - 1; + for (std::size_t i = 0; i < 4 * random_tests_amount; i++) { + cpp_int_type a = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); + cpp_int_type a_first = (a & top_mask) >> 128, + a_second = a & bottom_mask; + uint_type a_first_modular = typename uint_type::backend_type(cpp_uint_type(a_first).backend()); + uint_type a_second_modular = typename uint_type::backend_type(cpp_uint_type(a_second).backend()); + test_big_signed_abs( + a >= 0 ? 0 : 1, a_first_modular, a_second_modular, + 0, a_first_modular, a_second_modular); + } +} + +#define TEST_BIG_UNSIGNED_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a_first, \ + const typename BlueprintFieldType::value_type &a_second, \ + const typename BlueprintFieldType::value_type &b_first, \ + const typename BlueprintFieldType::value_type &b_second, \ + const typename BlueprintFieldType::value_type &expected_first, \ + const typename BlueprintFieldType::value_type &expected_second) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + var(0, 2, false, var::column_type::public_input), \ + var(0, 3, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a_first, a_second, b_first, b_second}; \ + \ + auto result_check = [&expected_first, &expected_second](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_first == var_value(assignment, real_res.value[0])); \ + BOOST_ASSERT(expected_second == var_value(assignment, real_res.value[1])); \ + }; \ + \ + component_type component_instance({0, 1, 2}, {}, {}); \ + typename component_type::result_type(component_instance, 10); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +#define TEST_BIG_UNSIGNED_BOOL_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a_first, \ + const typename BlueprintFieldType::value_type &a_second, \ + const typename BlueprintFieldType::value_type &b_first, \ + const typename BlueprintFieldType::value_type &b_second, \ + const typename BlueprintFieldType::value_type &expected_result) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + var(0, 2, false, var::column_type::public_input), \ + var(0, 3, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a_first, a_second, b_first, b_second}; \ + \ + auto result_check = [&expected_result](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_result == var_value(assignment, real_res.a)); \ + }; \ + \ + component_type component_instance({0, 1, 2}, {}, {}); \ + typename component_type::result_type(component_instance, 10); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +TEST_BIG_UNSIGNED_GEN(test_big_unsigned_add, unsigned_addition_component_big) +TEST_BIG_UNSIGNED_GEN(test_big_unsigned_sub, unsigned_subtraction_component_big) +TEST_BIG_UNSIGNED_GEN(test_big_unsigned_mul, unsigned_multiplication_component_big) +TEST_BIG_UNSIGNED_GEN(test_big_unsigned_div, unsigned_division_component_big) +TEST_BIG_UNSIGNED_GEN(test_big_unsigned_rem, unsigned_remainder_component_big) + +TEST_BIG_UNSIGNED_BOOL_GEN(test_big_unsigned_less, unsinged_less_component_big) +TEST_BIG_UNSIGNED_BOOL_GEN(test_big_unsigned_less_eq, unsinged_less_equal_component_big) +TEST_BIG_UNSIGNED_BOOL_GEN(test_big_unsigned_greater, unsinged_greater_component_big) +TEST_BIG_UNSIGNED_BOOL_GEN(test_big_unsigned_greater_eq, unsinged_greater_equal_component_big) + +#define TEST_BIG_SIGNED_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a_sign, \ + const typename BlueprintFieldType::value_type &a_first, \ + const typename BlueprintFieldType::value_type &a_second, \ + const typename BlueprintFieldType::value_type &b_sign, \ + const typename BlueprintFieldType::value_type &b_first, \ + const typename BlueprintFieldType::value_type &b_second, \ + const typename BlueprintFieldType::value_type &expected_sign, \ + const typename BlueprintFieldType::value_type &expected_first, \ + const typename BlueprintFieldType::value_type &expected_second) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + var(0, 2, false, var::column_type::public_input), \ + var(0, 3, false, var::column_type::public_input), \ + var(0, 4, false, var::column_type::public_input), \ + var(0, 5, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a_sign, a_first, a_second, b_sign, b_first, b_second}; \ + \ + auto result_check = [&expected_sign, &expected_first, &expected_second](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_sign == var_value(assignment, real_res.value[0])); \ + BOOST_ASSERT(expected_first == var_value(assignment, real_res.value[1])); \ + BOOST_ASSERT(expected_second == var_value(assignment, real_res.value[2])); \ + }; \ + \ + component_type component_instance({0, 1, 2}, {}, {}); \ + typename component_type::result_type(component_instance, 10); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +TEST_BIG_SIGNED_GEN(test_big_signed_add, signed_addition_component_big) +TEST_BIG_SIGNED_GEN(test_big_signed_sub, signed_subtraction_component_big) +TEST_BIG_SIGNED_GEN(test_big_signed_mul, signed_multiplication_component_big) +TEST_BIG_SIGNED_GEN(test_big_signed_div, signed_division_component_big) +TEST_BIG_SIGNED_GEN(test_big_signed_rem, signed_remainder_component_big) + +#define TEST_BIG_BOOL_SIGNED_GEN(FUNC_NAME, COMPONENT_NAME) \ + template \ + void FUNC_NAME ( \ + const typename BlueprintFieldType::value_type &a_sign, \ + const typename BlueprintFieldType::value_type &a_first, \ + const typename BlueprintFieldType::value_type &a_second, \ + const typename BlueprintFieldType::value_type &b_sign, \ + const typename BlueprintFieldType::value_type &b_first, \ + const typename BlueprintFieldType::value_type &b_second, \ + const typename BlueprintFieldType::value_type &expected_result) { \ + \ + using value_type = typename BlueprintFieldType::value_type; \ + using var = crypto3::zk::snark::plonk_variable; \ + ARITHMETIZATION_GEN; \ + using component_type = \ + nil::blueprint::components::COMPONENT_NAME; \ + \ + typename component_type::input_type instance_input = { \ + var(0, 0, false, var::column_type::public_input), \ + var(0, 1, false, var::column_type::public_input), \ + var(0, 2, false, var::column_type::public_input), \ + var(0, 3, false, var::column_type::public_input), \ + var(0, 4, false, var::column_type::public_input), \ + var(0, 5, false, var::column_type::public_input), \ + }; \ + \ + std::vector public_input = {a_sign, a_first, a_second, b_sign, b_first, b_second}; \ + \ + auto result_check = [&expected_result](AssignmentType &assignment, \ + typename component_type::result_type &real_res) { \ + \ + BOOST_ASSERT(expected_result == var_value(assignment, real_res.a)); \ + }; \ + \ + component_type component_instance({0, 1, 2}, {}, {}); \ + typename component_type::result_type(component_instance, 10); \ + \ + crypto3::test_component( \ + component_instance, desc, public_input, result_check, instance_input, \ + nil::blueprint::connectedness_check_type::type::NONE); \ + } + +TEST_BIG_BOOL_SIGNED_GEN(test_big_signed_less, signed_less_component_big) +TEST_BIG_BOOL_SIGNED_GEN(test_big_signed_less_eq, signed_less_equal_component_big) +TEST_BIG_BOOL_SIGNED_GEN(test_big_signed_greater, signed_greater_component_big) +TEST_BIG_BOOL_SIGNED_GEN(test_big_signed_greater_eq, signed_greater_equal_component_big) + +// TODO(martun): pallas is actually 255 bits, when moving 256-bit number into that field we are losing a bit. So far that does not result to an error, but better re-check. +#define OP_UNSIGNED_BIG_TEST_FUNC_GEN(NAME, TEST_NAME, OP) \ + template \ + void NAME() { \ + using uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; \ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(uint_type(2), 256) - 1); \ + static const uint_type top_mask = ((uint_type(1) << 128) - 1) << 128; \ + static const uint_type bottom_mask = (uint_type(1) << 128) - 1; \ + for (std::size_t i = 0; i < random_tests_amount; i++) { \ + uint_type a = dist(seed_seq), \ + b = dist(seed_seq); \ + uint_type expected_result = OP(a, b); \ + uint_type expected_first = (expected_result & top_mask) >> 128, \ + expected_second = expected_result & bottom_mask; \ + TEST_NAME((a & top_mask) >> 128, a & bottom_mask, \ + (b & top_mask) >> 128, b & bottom_mask, \ + expected_first, expected_second); \ + } \ + } + +#define OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN(NAME, TEST_NAME, OP) \ + template \ + void NAME() { \ + using uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; \ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(uint_type(2), 256) - 1); \ + static const uint_type top_mask = ((uint_type(1) << 128) - 1) << 128; \ + static const uint_type bottom_mask = (uint_type(1) << 128) - 1; \ + for (std::size_t i = 0; i < random_tests_amount; i++) { \ + uint_type a = dist(seed_seq), \ + b = dist(seed_seq); \ + bool expected_result = OP(a, b); \ + TEST_NAME((a & top_mask) >> 128, a & bottom_mask, \ + (b & top_mask) >> 128, b & bottom_mask, \ + expected_result); \ + } \ + } + +OP_UNSIGNED_BIG_TEST_FUNC_GEN(test_big_unsigned_addition, test_big_unsigned_add, OP_ADDITION) +OP_UNSIGNED_BIG_TEST_FUNC_GEN(test_big_unsigned_subtraction, test_big_unsigned_sub, OP_SUBTRACTION) +OP_UNSIGNED_BIG_TEST_FUNC_GEN(test_big_unsigned_multiplication, test_big_unsigned_mul, OP_MULTIPLICATION) +OP_UNSIGNED_BIG_TEST_FUNC_GEN(test_big_unsigned_division, test_big_unsigned_div, OP_DIVISION) +OP_UNSIGNED_BIG_TEST_FUNC_GEN(test_big_unsigned_remainder, test_big_unsigned_rem, OP_REMAINDER) + +OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_unsigned_less_than, test_big_unsigned_less, OP_LESS) +OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_unsigned_less_than_eq, test_big_unsigned_less_eq, OP_LESS_EQ) +OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_unsigned_greater_than, test_big_unsigned_greater, OP_GREATER) +OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_unsigned_greater_than_eq, test_big_unsigned_greater_eq, OP_GREATER_EQ) + + +#define OP_SIGNED_BIG_TEST_FUNC_GEN(NAME, TEST_NAME, OP) \ + template \ + void NAME() { \ + using cpp_int_type = boost::multiprecision::number>; \ + using cpp_uint_type = boost::multiprecision::number>; \ + using uint_type = boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; \ +\ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(cpp_uint_type(2), 256) - 1); \ + boost::random::uniform_int_distribution sign_dist(0, 1); \ + static const cpp_int_type top_mask = ((cpp_int_type(1) << 128) - 1) << 128; \ + static const cpp_int_type bottom_mask = (cpp_int_type(1) << 128) - 1; \ + for (std::size_t i = 0; i < 4 * random_tests_amount; i++) { \ + cpp_int_type a = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1), \ + b = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); \ + cpp_int_type expected_result = OP(a, b); \ + cpp_int_type expected_first = (expected_result & top_mask) >> 128, \ + expected_second = expected_result & bottom_mask; \ + cpp_int_type a_top = (a & top_mask) >> 128; \ + cpp_int_type a_bottom = a & bottom_mask; \ + cpp_int_type b_top = (b & top_mask) >> 128; \ + cpp_int_type b_bottom = b & bottom_mask; \ +\ + uint_type expected_first_modular = typename uint_type::backend_type(cpp_uint_type(expected_first).backend()); \ + uint_type expected_second_modular = typename uint_type::backend_type(cpp_uint_type(expected_second).backend()); \ + uint_type a_top_modular = typename uint_type::backend_type(cpp_uint_type(a_top).backend()); \ + uint_type a_bottom_modular = typename uint_type::backend_type(cpp_uint_type(a_bottom).backend()); \ + uint_type b_top_modular = typename uint_type::backend_type(cpp_uint_type(b_top).backend()); \ + uint_type b_bottom_modular = typename uint_type::backend_type(cpp_uint_type(b_bottom).backend()); \ +\ + TEST_NAME(a.sign() >= 0 ? 0 : 1, a_top_modular, a_bottom_modular, \ + b.sign() >= 0 ? 0 : 1, b_top_modular, b_bottom_modular, \ + expected_result.sign() >= 0 ? 0 : 1, expected_first_modular, expected_second_modular); \ + } \ + } + +#define OP_SIGNED_BIG_BOOL_TEST_FUNC_GEN(NAME, TEST_NAME, OP) \ + template \ + void NAME() { \ + using int_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; \ + using uint_type = \ + boost::multiprecision::number< \ + boost::multiprecision::backends::cpp_int_modular_backend<256u>>; \ + boost::random::mt19937 seed_seq; \ + boost::random::uniform_int_distribution \ + dist(0, boost::multiprecision::pow(uint_type(2), 256) - 1); \ + boost::random::uniform_int_distribution sign_dist(0, 1); \ + static const int_type top_mask = ((int_type(1) << 128) - 1) << 128; \ + static const int_type bottom_mask = (int_type(1) << 128) - 1; \ + for (std::size_t i = 0; i < 4 * random_tests_amount; i++) { \ + int_type a = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1), \ + b = dist(seed_seq) * (sign_dist(seed_seq) ? 1 : -1); \ + TEST_NAME(a.sign() >= 0 ? 0 : 1, (a & top_mask) >> 128, a & bottom_mask, \ + b.sign() >= 0 ? 0 : 1, (b & top_mask) >> 128, b & bottom_mask, \ + OP(a, b)); \ + } \ + } + +OP_SIGNED_BIG_TEST_FUNC_GEN(test_big_signed_addition, test_big_signed_add, OP_ADDITION) +OP_SIGNED_BIG_TEST_FUNC_GEN(test_big_signed_subtraction, test_big_signed_sub, OP_SUBTRACTION) +OP_SIGNED_BIG_TEST_FUNC_GEN(test_big_signed_multiplication, test_big_signed_mul, OP_MULTIPLICATION) +OP_SIGNED_BIG_TEST_FUNC_GEN(test_big_signed_division, test_big_signed_div, OP_DIVISION) +OP_SIGNED_BIG_TEST_FUNC_GEN(test_big_signed_remainder, test_big_signed_rem, OP_REMAINDER) + +OP_SIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_signed_less_than, test_big_signed_less, OP_LESS) +OP_SIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_signed_less_than_eq, test_big_signed_less_eq, OP_LESS_EQ) +OP_SIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_signed_greater_than, test_big_signed_greater, OP_GREATER) +OP_SIGNED_BIG_BOOL_TEST_FUNC_GEN(test_big_signed_greater_than_eq, test_big_signed_greater_eq, OP_GREATER_EQ) + +#define OP_TEST(TEST_NAME) \ + BOOST_AUTO_TEST_CASE(blueprint_ ## TEST_NAME ## _mock) { \ + using field_type = crypto3::algebra::curves::pallas::base_field_type; \ + TEST_NAME(); \ + TEST_NAME(); \ + TEST_NAME(); \ + TEST_NAME(); \ + TEST_NAME(); \ + } + +#define OP_BIG_TEST(TEST_NAME) \ + BOOST_AUTO_TEST_CASE(blueprint_ ## TEST_NAME ## _mock) { \ + using field_type = crypto3::algebra::curves::pallas::base_field_type; \ + TEST_NAME(); \ + } + +OP_TEST(test_small_unsigned_addition); +OP_TEST(test_small_unsigned_multiplication); +OP_TEST(test_small_unsigned_subtraction); +OP_TEST(test_small_unsigned_division); +OP_TEST(test_small_unsigned_remainder); + +OP_TEST(test_small_signed_addition); +OP_TEST(test_small_signed_subtraction); +OP_TEST(test_small_signed_multiplication); +OP_TEST(test_small_signed_division); +OP_TEST(test_small_signed_remainder); + +OP_TEST(test_small_signed_absolute); + +OP_BIG_TEST(test_big_unsigned_addition); +OP_BIG_TEST(test_big_unsigned_subtraction); +OP_BIG_TEST(test_big_unsigned_multiplication); +OP_BIG_TEST(test_big_unsigned_division); +OP_BIG_TEST(test_big_unsigned_remainder); + +OP_BIG_TEST(test_big_unsigned_less_than); +OP_BIG_TEST(test_big_unsigned_less_than_eq); +OP_BIG_TEST(test_big_unsigned_greater_than); +OP_BIG_TEST(test_big_unsigned_greater_than_eq); + +OP_BIG_TEST(test_big_signed_addition); +OP_BIG_TEST(test_big_signed_subtraction); +OP_BIG_TEST(test_big_signed_multiplication); +OP_BIG_TEST(test_big_signed_division); +OP_BIG_TEST(test_big_signed_remainder); + +OP_BIG_TEST(test_big_signed_less_than); +OP_BIG_TEST(test_big_signed_less_than_eq); +OP_BIG_TEST(test_big_signed_greater_than); +OP_BIG_TEST(test_big_signed_greater_than_eq); + +OP_BIG_TEST(test_big_signed_absolute); + +#undef ARITHMETIZATION_GEN +#undef OP_TEST +#undef OP_BIG_TEST + +#undef OP_ADDITION +#undef OP_SUBTRACTION +#undef OP_MULTIPLICATION +#undef OP_DIVISION +#undef OP_REMAINDER +#undef OP_LESS +#undef OP_LESS_EQ +#undef OP_GREATER +#undef OP_GREATER_EQ + +#undef OP_UNSIGNED_TEST_FUNC_GEN +#undef OP_SIGNED_TEST_FUNC_GEN +#undef OP_UNSIGNED_BIG_TEST_FUNC_GEN +#undef OP_UNSIGNED_BIG_BOOL_TEST_FUNC_GEN +#undef OP_SIGNED_BIG_TEST_FUNC_GEN + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/non_native/plonk/add_mul_zkllvm_compatible.cpp b/libs/blueprint/test/non_native/plonk/add_mul_zkllvm_compatible.cpp new file mode 100644 index 000000000..5d08096a7 --- /dev/null +++ b/libs/blueprint/test/non_native/plonk/add_mul_zkllvm_compatible.cpp @@ -0,0 +1,485 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_non_native_plonk_add_mul_zkllvm_compatible_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_mul(typename CurveType::base_field_type::value_type b_val, + typename Ed25519Type::template g1_type::value_type T){ + + using BlueprintFieldType = typename CurveType::base_field_type; + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 6; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + using foreign_integral_type = typename Ed25519Type::base_field_type::integral_type; + using value_type = typename BlueprintFieldType::value_type; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::variable_base_multiplication< + ArithmetizationType, + CurveType, + Ed25519Type, + nil::blueprint::basic_non_native_policy>; + + std::array input_var_Xa = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_Xb = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + var b = var(0, 8, false, var::column_type::public_input); + + typename component_type::input_type instance_input = { + {input_var_Xa, input_var_Xb}, b}; + + typename Ed25519Type::template g1_type::value_type P = T * b_val; + + foreign_integral_type Tx = foreign_integral_type(T.X.data); + foreign_integral_type Ty = foreign_integral_type(T.Y.data); + foreign_integral_type Px = foreign_integral_type(P.X.data); + foreign_integral_type Py = foreign_integral_type(P.Y.data); + foreign_integral_type base = 1; + foreign_integral_type mask = (base << 66) - 1; + + if (P == Ed25519Type::template g1_type::value_type::zero()) { + Py = 1; + } + if (T == Ed25519Type::template g1_type::value_type::zero()) { + Ty = 1; + } + + std::vector public_input = { + Tx & mask, (Tx >> 66) & mask, (Tx >> 132) & mask, (Tx >> 198) & mask, + Ty & mask, (Ty >> 66) & mask, (Ty >> 132) & mask, (Ty >> 198) & mask, + b_val}; + + auto result_check = [Px, Py, Tx, Ty, b_val](AssignmentType &assignment, typename component_type::result_type &real_res) { + foreign_integral_type base = 1; + foreign_integral_type mask = (base << 66) - 1; + for (std::size_t i = 0; i < 4; i++) { + if ((value_type((Px >> 66 * i) & mask) != var_value(assignment, real_res.output.x[i])) || + (value_type((Py >> 66 * i) & mask) != var_value(assignment, real_res.output.y[i]))) { + std::cerr << "test_mul failed! Point(hex form):\n"; + std::cerr << std::hex << Tx << std::dec << '\n'; + std::cerr << std::hex << Ty << std::dec << '\n'; + std::cerr << "Scalar(hex form):\n"; + std::cerr << std::hex << b_val.data << std::dec << '\n'<< std::endl; + } + BOOST_CHECK_EQUAL(value_type((Px >> 66 * i) & mask), var_value(assignment, real_res.output.x[i])); + BOOST_CHECK_EQUAL(value_type((Py >> 66 * i) & mask), var_value(assignment, real_res.output.y[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + if constexpr (Stretched) { + using stretched_component_type = nil::blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + // 253 is the default bits_amount + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, 253, blueprint::components::bit_composition_mode::MSB); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, 253, blueprint::components::bit_composition_mode::MSB); + } +} + +template +void test_mul_per_bit(){ + using ed25519_type = crypto3::algebra::curves::ed25519; + using BlueprintFieldType = typename CurveType::base_field_type; + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 6; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::variable_base_multiplication_per_bit< + ArithmetizationType, + CurveType, + ed25519_type, + nil::blueprint::basic_non_native_policy>; + std::array input_var_Xa = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_Xb = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + std::array input_var_Ya = { + var(0, 8, false, var::column_type::public_input), var(0, 9, false, var::column_type::public_input), + var(0, 10, false, var::column_type::public_input), var(0, 11, false, var::column_type::public_input)}; + std::array input_var_Yb = { + var(0, 12, false, var::column_type::public_input), var(0, 13, false, var::column_type::public_input), + var(0, 14, false, var::column_type::public_input), var(0, 15, false, var::column_type::public_input)}; + + var b = var(0, 16, false, var::column_type::public_input); + + typename component_type::input_type instance_input + = { + {input_var_Xa, input_var_Xb}, + {input_var_Ya, input_var_Yb}, + b}; + + ed25519_type::template g1_type::value_type T = crypto3::algebra::random_element>(); + ed25519_type::template g1_type::value_type R = crypto3::algebra::random_element>(); + typename BlueprintFieldType::value_type b_val = 1; + + ed25519_type::template g1_type::value_type bool_res = T * b_val; + ed25519_type::template g1_type::value_type doub_res = R + R; + ed25519_type::template g1_type::value_type P = bool_res + doub_res; + + ed25519_type::base_field_type::integral_type Tx = ed25519_type::base_field_type::integral_type(T.X.data); + ed25519_type::base_field_type::integral_type Ty = ed25519_type::base_field_type::integral_type(T.Y.data); + ed25519_type::base_field_type::integral_type Rx = ed25519_type::base_field_type::integral_type(R.X.data); + ed25519_type::base_field_type::integral_type Ry = ed25519_type::base_field_type::integral_type(R.Y.data); + ed25519_type::base_field_type::integral_type Px = ed25519_type::base_field_type::integral_type(P.X.data); + ed25519_type::base_field_type::integral_type Py = ed25519_type::base_field_type::integral_type(P.Y.data); + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + + std::vector public_input = { + Tx & mask, (Tx >> 66) & mask, (Tx >> 132) & mask, (Tx >> 198) & mask, + Ty & mask, (Ty >> 66) & mask, (Ty >> 132) & mask, (Ty >> 198) & mask, + Rx & mask, (Rx >> 66) & mask, (Rx >> 132) & mask, (Rx >> 198) & mask, + Ry & mask, (Ry >> 66) & mask, (Ry >> 132) & mask, (Ry >> 198) & mask, + b_val}; + + auto result_check = [Px, Py](AssignmentType &assignment, typename component_type::result_type &real_res) { + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + for (std::size_t i = 0; i < 4; i++) { + assert(typename BlueprintFieldType::value_type((Py >> 66 * i) & mask) + == var_value(assignment, real_res.output.y[i])); + assert(typename BlueprintFieldType::value_type((Py >> 66 * i) & mask) + == var_value(assignment, real_res.output.y[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + if constexpr (Stretched) { + using stretched_component_type = nil::blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void test_doubling() { + using ed25519_type = crypto3::algebra::curves::ed25519; + using BlueprintFieldType = typename CurveType::base_field_type; + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 6; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::doubling>; + + std::array input_var_Xa = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_Xb = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {{input_var_Xa, input_var_Xb}}; + + ed25519_type::template g1_type::value_type T = crypto3::algebra::random_element>(); + ed25519_type::template g1_type::value_type P = T + T; + + ed25519_type::base_field_type::integral_type Tx = ed25519_type::base_field_type::integral_type(T.X.data); + ed25519_type::base_field_type::integral_type Ty = ed25519_type::base_field_type::integral_type(T.Y.data); + ed25519_type::base_field_type::integral_type Px = ed25519_type::base_field_type::integral_type(P.X.data); + ed25519_type::base_field_type::integral_type Py = ed25519_type::base_field_type::integral_type(P.Y.data); + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + + std::vector public_input = { + Tx & mask, (Tx >> 66) & mask, (Tx >> 132) & mask, (Tx >> 198) & mask, + Ty & mask, (Ty >> 66) & mask, (Ty >> 132) & mask, (Ty >> 198) & mask}; + + auto result_check = [Px, Py](AssignmentType &assignment, typename component_type::result_type &real_res) { + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + for (std::size_t i = 0; i < 4; i++) { + assert(typename BlueprintFieldType::value_type((Px >> 66 * i) & mask) == + var_value(assignment, real_res.output.x[i])); + assert(typename BlueprintFieldType::value_type((Py >> 66 * i) & mask) == + var_value(assignment, real_res.output.y[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + if constexpr (Stretched) { + using stretched_component_type = nil::blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void test_complete_addition(){ + using ed25519_type = crypto3::algebra::curves::ed25519; + using BlueprintFieldType = typename CurveType::base_field_type; + constexpr std::size_t WitnessColumns = 9 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 6; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::complete_addition>; + + std::array input_var_Xa = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array input_var_Xb = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + std::array input_var_Ya = { + var(0, 8, false, var::column_type::public_input), var(0, 9, false, var::column_type::public_input), + var(0, 10, false, var::column_type::public_input), var(0, 11, false, var::column_type::public_input)}; + std::array input_var_Yb = { + var(0, 12, false, var::column_type::public_input), var(0, 13, false, var::column_type::public_input), + var(0, 14, false, var::column_type::public_input), var(0, 15, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {{input_var_Xa, input_var_Xb}, {input_var_Ya, input_var_Yb}}; + + ed25519_type::template g1_type::value_type T = crypto3::algebra::random_element>(); + ed25519_type::template g1_type::value_type R = crypto3::algebra::random_element>(); + ed25519_type::template g1_type::value_type P = T + R; + + ed25519_type::base_field_type::integral_type Tx = ed25519_type::base_field_type::integral_type(T.X.data); + ed25519_type::base_field_type::integral_type Ty = ed25519_type::base_field_type::integral_type(T.Y.data); + ed25519_type::base_field_type::integral_type Rx = ed25519_type::base_field_type::integral_type(R.X.data); + ed25519_type::base_field_type::integral_type Ry = ed25519_type::base_field_type::integral_type(R.Y.data); + ed25519_type::base_field_type::integral_type Px = ed25519_type::base_field_type::integral_type(P.X.data); + ed25519_type::base_field_type::integral_type Py = ed25519_type::base_field_type::integral_type(P.Y.data); + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + + std::vector public_input = { + Tx & mask, (Tx >> 66) & mask, (Tx >> 132) & mask, (Tx >> 198) & mask, + Ty & mask, (Ty >> 66) & mask, (Ty >> 132) & mask, (Ty >> 198) & mask, + Rx & mask, (Rx >> 66) & mask, (Rx >> 132) & mask, (Rx >> 198) & mask, + Ry & mask, (Ry >> 66) & mask, (Ry >> 132) & mask, (Ry >> 198) & mask}; + + auto result_check = [Px, Py](AssignmentType &assignment, typename component_type::result_type &real_res) { + typename ed25519_type::base_field_type::integral_type base = 1; + typename ed25519_type::base_field_type::integral_type mask = (base << 66) - 1; + for (std::size_t i = 0; i < 4; i++) { + assert(typename BlueprintFieldType::value_type((Px >> 66 * i) & mask) == + var_value(assignment, real_res.output.x[i])); + assert(typename BlueprintFieldType::value_type((Py >> 66 * i) & mask) == + var_value(assignment, real_res.output.y[i])); + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {0}, {}); + + if constexpr (Stretched) { + using stretched_component_type = nil::blueprint::components::component_stretcher< + BlueprintFieldType, + component_type>; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + crypto3::test_component( + stretched_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +constexpr static const std::size_t random_tests_amount = 1; + +BOOST_AUTO_TEST_CASE(blueprint_non_native_complete_addition) { + for (std::size_t i = 0; i < random_tests_amount; i++) { + test_complete_addition(); + test_complete_addition(); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_doubling) { + for (std::size_t i = 0; i < random_tests_amount; i++) { + test_doubling(); + test_doubling(); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_mul_per_bit) { + for (std::size_t i = 0; i < random_tests_amount; i++) { + test_mul_per_bit(); + test_mul_per_bit(); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_mul_1) { + //auto start = std::chrono::high_resolution_clock::now(); + + using CurveType = typename crypto3::algebra::curves::pallas; + using Ed25519Type = typename crypto3::algebra::curves::ed25519; + + typename CurveType::base_field_type::integral_type scal_integral; + typename CurveType::base_field_type::value_type scal_rand; + typename CurveType::base_field_type::value_type scal_max = + 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular256; + typename CurveType::base_field_type::value_type scal_zero = 0; + + typename Ed25519Type::template g1_type + ::value_type point_zero = Ed25519Type::template g1_type + ::value_type::zero(); + + + nil::crypto3::random::algebraic_engine< + Ed25519Type::template g1_type> + random_point_generator; + boost::random::mt19937 seed_seq_1; + random_point_generator.seed(seed_seq_1); + + nil::crypto3::random::algebraic_engine random_scalar_generator; + boost::random::mt19937 seed_seq_2; + random_scalar_generator.seed(seed_seq_2); + + // We need 2 conversions here, first convert modular type to integral type of the same size, then convert and integral + // type of one size to integral type of another size. + scal_integral = typename CurveType::base_field_type::integral_type(Ed25519Type::scalar_field_type::integral_type(random_scalar_generator().data)); + scal_rand = typename CurveType::base_field_type::value_type (scal_integral); + + test_mul(scal_zero, point_zero); + test_mul(scal_zero, point_zero); + test_mul(scal_max, point_zero); + test_mul(scal_max, point_zero); + test_mul(scal_rand, point_zero); + test_mul(scal_rand, point_zero); + test_mul(scal_zero, random_point_generator()); + test_mul(scal_zero, random_point_generator()); + test_mul(scal_max, random_point_generator()); + test_mul(scal_max, random_point_generator()); + + for (std::size_t i = 0; i < random_tests_amount; i++) { + // We need 2 conversions here, first convert modular type to integral type of the same size, then convert and integral + // type of one size to integral type of another size. + scal_integral = typename CurveType::base_field_type::integral_type(Ed25519Type::scalar_field_type::integral_type((random_scalar_generator()).data)); + scal_rand = typename CurveType::base_field_type::value_type (scal_integral); + + test_mul(scal_rand, random_point_generator()); + test_mul(scal_rand, random_point_generator()); + } + //auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + //std::cout << "blueprint_non_native_mul_1 test duration: " << duration.count() << " ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/non_native/plonk/bool_scalar_multiplication.cpp b/libs/blueprint/test/non_native/plonk/bool_scalar_multiplication.cpp new file mode 100644 index 000000000..07b361108 --- /dev/null +++ b/libs/blueprint/test/non_native/plonk/bool_scalar_multiplication.cpp @@ -0,0 +1,220 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_bool_scalar_multiplication_test + +#include + +#include +#include + +#include +#include +#include +#include <../test/algebra/fields/plonk/non_native/chop_and_glue_non_native.hpp> + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void profiling_inside_result_check(const std::vector &public_input, + std::vector expected_res, + AssignmentType &assignment, typename ComponentType::result_type &real_res) { + +} + +template +void test_bool_scalar_multiplication(const std::vector &public_input, + const std::vector &expected_res, + const bool expected_to_pass) { + + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = + blueprint::components::bool_scalar_multiplication>; + + std::array T_x = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + std::array T_y = { + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + typename component_type::input_type instance_input = {{T_x, T_y}, var(0, 8, false, var::column_type::public_input)}; + + auto result_check = [&expected_res, &expected_to_pass](AssignmentType &assignment, + typename component_type::result_type &real_res) { +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::array x, y, expected_x, expected_y, real_x, real_y; + for (std::size_t i = 0; i < 4; i++) { + x[i] = public_input[i]; + y[i] = public_input[i + 4]; + expected_x[i] = expected_res[i]; + expected_y[i] = expected_res[i + 4]; + real_x[i] = var_value(assignment, real_res.output.x[i]); + real_y[i] = var_value(assignment, real_res.output.y[i]); + } + + std::cout << std::hex; + + std::cout << "_____________________________________________________________________________________________________" + "____________________________________________\n"; + std::cout << "input x: "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << x[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(x).data << ")\n"; + + std::cout << " y: "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << y[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(y).data << ")\n"; + + std::cout << " bool: " << public_input[8].data << "\n"; + + std::cout << "expected: "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << expected_x[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(expected_x).data << ")\n"; + std::cout << " "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << expected_y[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(expected_y).data << ")\n"; + + std::cout << "real : "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << real_x[3 - i].data << " "; + } + std::cout << "(" << glue_non_native(real_x).data << ")\n"; + std::cout << " "; + for (std::size_t i = 0; i < 4; i++) { + std::cout << real_y[3 - i].data << " "; + } + // std::cout << "(" << glue_non_native(real_y).data << ")" << std::endl; +#endif + if (expected_to_pass) { + for (std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output.x[i])); + assert(expected_res[i + 4] == var_value(assignment, real_res.output.y[i])); + } + } + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {}, {}); + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void test_bool_scalar_multiplication_usable( + typename NonNativeCurveType::template g1_type::value_type point, + typename FieldType::value_type scalar_bool, + const bool expected_to_pass) { + + std::vector public_input = + create_public_input( + chop_non_native(point.X), + chop_non_native(point.Y)); + + std::vector expected_res; + if (scalar_bool == 1) { + expected_res = public_input; + } else { + expected_res = {0, 0, 0, 0, 1, 0, 0, 0}; + } + public_input.push_back(scalar_bool); + + test_bool_scalar_multiplication(public_input, expected_res, expected_to_pass); +} + +constexpr static const std::size_t random_tests_amount = 3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bool_scalar_mul_test1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using non_native_curve_type = crypto3::algebra::curves::ed25519; + + nil::crypto3::random::algebraic_engine< + crypto3::algebra::curves::ed25519::template g1_type> + rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + test_bool_scalar_multiplication_usable({0, 1}, 1, true); + test_bool_scalar_multiplication_usable({0, 1}, 0, true); + + for (std::size_t i = 0; i < random_tests_amount; i++) { + test_bool_scalar_multiplication_usable(rand(), 1, true); + test_bool_scalar_multiplication_usable(rand(), 0, true); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_bool_scalar_mul_must_fail) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using non_native_curve_type = crypto3::algebra::curves::ed25519; + + nil::crypto3::random::algebraic_engine< + crypto3::algebra::curves::ed25519::template g1_type> + rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + test_bool_scalar_multiplication_usable({0, 1}, 2, false); + test_bool_scalar_multiplication_usable({0, 1}, 10, false); + test_bool_scalar_multiplication_usable({0, 1}, -1, false); + test_bool_scalar_multiplication_usable(rand(), 2, false); + test_bool_scalar_multiplication_usable(rand(), 10, false); + test_bool_scalar_multiplication_usable(rand(), -1, false); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/non_native/plonk/scalar_non_native_range.cpp b/libs/blueprint/test/non_native/plonk/scalar_non_native_range.cpp new file mode 100644 index 000000000..9ec1de162 --- /dev/null +++ b/libs/blueprint/test/non_native/plonk/scalar_non_native_range.cpp @@ -0,0 +1,151 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_non_native_scalar_range_test + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_scalar_non_native_range(std::vector public_input, + const bool expected_to_pass) { + + using ed25519_type = crypto3::algebra::curves::ed25519; + constexpr std::size_t WitnessColumns = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc = { + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns}; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::scalar_non_native_range; + + typename component_type::input_type instance_input = {var(0, 0, false, var::column_type::public_input)}; + + auto result_check = [public_input](AssignmentType &assignment, typename component_type::result_type &real_res) { +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << std::hex + << "________________________________________________________________________________________\ninput: " + << public_input[0].data << std::endl; +#endif + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8}, {}, {}); + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_non_native_scalar_range_test0) { + test_scalar_non_native_range({45524}, true); +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_scalar_range_test1) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + typename field_type::integral_type ed25519_scalar_modulus = + 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular255; + typename field_type::value_type ones = 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular255; + + test_scalar_non_native_range({typename field_type::value_type(ed25519_scalar_modulus - 1)}, true); + + test_scalar_non_native_range({typename field_type::value_type(ones)}, true); + + test_scalar_non_native_range({1}, true); + + test_scalar_non_native_range({0}, true); + + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + typename field_type::value_type r; + typename field_type::integral_type r_integral; + + for (std::size_t i = 0; i < random_tests_amount; i++) { + r = rand(); + r_integral = typename field_type::integral_type(r.data); + r_integral = r_integral % ed25519_scalar_modulus; + r = typename field_type::value_type(r_integral); + test_scalar_non_native_range({r}, true); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_non_native_scalar_range_test_must_fail) { + using field_type = crypto3::algebra::curves::pallas::base_field_type; + + nil::crypto3::random::algebraic_engine rand; + boost::random::mt19937 seed_seq; + rand.seed(seed_seq); + + typename field_type::integral_type ed25519_scalar_modulus = + 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed_cppui_modular255; + typename field_type::extended_integral_type pow_bits = 1u; + pow_bits <<= field_type::modulus_bits; + typename field_type::integral_type ed25519_scalar_overage = pow_bits - field_type::extended_integral_type( + ed25519_scalar_modulus) - 1; + + typename field_type::integral_type overage; + + for (std::size_t i = 0; i < random_tests_amount; i++) { + overage = (typename field_type::integral_type(rand().data)) % ed25519_scalar_overage; + // Test with numbers larger than modulus must fail. + test_scalar_non_native_range( + {typename field_type::value_type(ed25519_scalar_modulus + overage)}, false); // false positive + } + test_scalar_non_native_range({ed25519_scalar_modulus + 1u}, false); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/private_input.cpp b/libs/blueprint/test/private_input.cpp new file mode 100644 index 000000000..a93930f93 --- /dev/null +++ b/libs/blueprint/test/private_input.cpp @@ -0,0 +1,131 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_private_input_test + +#include + +#include + +#include +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil; + +template +void test_add(const typename FieldType::value_type &a, const typename FieldType::value_type &b) { + using BlueprintFieldType = FieldType; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::addition>; + + const std::size_t private_index = AssignmentType::private_storage_index; + + typename component_type::input_type instance_input = { + var(private_index, 0, false, var::column_type::public_input), + var(private_index, 1, false, var::column_type::public_input) + }; + + typename BlueprintFieldType::value_type expected_res = a + b; + + std::vector public_input = {a, b}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "add test: " << "\n"; + std::cout << "input : " << public_input[0].data << " " << public_input[1].data << "\n"; + std::cout << "expected: " << expected_res.data << "\n"; + std::cout << "real : " << var_value(assignment, real_res.output).data << "\n\n"; + #endif + assert(expected_res == var_value(assignment, real_res.output)); + }; + + component_type component_instance({0, 1, 2}, {}, {}); + + nil::crypto3::test_component_private_input< + component_type, BlueprintFieldType, hash_type, Lambda> + (component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_CASE(blueprint_plonk_private_input_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = typename field_type::value_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for (std::size_t i = 0; i < random_tests_amount; i++) { + value_type a = generate_random(); + value_type b = generate_random(); + test_add(a, b); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_private_input_copy_constraints) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using AssignmentType = nil::blueprint::assignment>; + + using var = crypto3::zk::snark::plonk_variable; + + const std::size_t private_index = AssignmentType::private_storage_index; + blueprint::circuit> bp; + + auto private_1 = var(private_index, 0, false, var::column_type::public_input), + private_2 = var(private_index, 1, false, var::column_type::public_input), + public_1 = var(0, 0, false, var::column_type::public_input), + public_2 = var(0, 1, false, var::column_type::public_input); + + bp.add_copy_constraint({private_1, private_2}); + BOOST_ASSERT(bp.copy_constraints().size() == 0); + + bp.add_copy_constraint({private_1, public_1}); + BOOST_ASSERT(bp.copy_constraints().size() == 0); + + bp.add_copy_constraint({public_2, private_2}); + BOOST_ASSERT(bp.copy_constraints().size() == 0); + + bp.add_copy_constraint({public_1, public_2}); + BOOST_ASSERT(bp.copy_constraints().size() == 1); +} diff --git a/libs/blueprint/test/proxy.cpp b/libs/blueprint/test/proxy.cpp new file mode 100644 index 000000000..8d0f62a08 --- /dev/null +++ b/libs/blueprint/test/proxy.cpp @@ -0,0 +1,393 @@ +// Copyright (c) 2023 Aleksei Kokoshnikov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_proxy_test + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace nil::blueprint; + +BOOST_AUTO_TEST_CASE(blueprint_circuit_proxy_gates_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = nil::crypto3::zk::snark::plonk_variable; + + auto bp_ptr = std::make_shared>(); + std::vector> circuits; + circuits.emplace_back(bp_ptr, 0); + circuits.emplace_back(bp_ptr, 1); + + circuits[0].add_gate({0, {var(0, 0, true, var::column_type::witness)}}); + circuits[0].add_gate({1, {var(0, 1, true, var::column_type::witness)}}); + circuits[1].add_gate({2, {var(0, 2, true, var::column_type::witness)}}); + + BOOST_ASSERT(circuits[0].get_next_selector_index() == circuits[1].get_next_selector_index()); + BOOST_ASSERT(circuits[0].get_next_selector_index() == 3); + + BOOST_ASSERT(circuits[0].num_gates() == circuits[1].num_gates()); + BOOST_ASSERT(circuits[0].num_gates() == 3); + + std::set used_gates_0 = {0, 1}; + BOOST_ASSERT(circuits[0].get_used_gates() == used_gates_0); + std::set used_gates_1 = {2}; + BOOST_ASSERT(circuits[1].get_used_gates() == used_gates_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_circuit_proxy_lookup_gates_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = nil::crypto3::zk::snark::plonk_variable; + + auto bp_ptr = std::make_shared>(); + std::vector> circuits; + circuits.emplace_back(bp_ptr, 0); + circuits.emplace_back(bp_ptr, 1); + + circuits[0].add_lookup_gate({0, {var(0, 0, true, var::column_type::constant)}}); + circuits[0].add_lookup_gate({1, {var(1, 0, true, var::column_type::constant)}}); + circuits[1].add_lookup_gate({2, {var(0, 1, true, var::column_type::constant)}}); + + BOOST_ASSERT(circuits[0].get_next_selector_index() == circuits[1].get_next_selector_index()); + BOOST_ASSERT(circuits[0].get_next_selector_index() == 3); + + BOOST_ASSERT(circuits[0].num_lookup_gates() == circuits[1].num_lookup_gates()); + BOOST_ASSERT(circuits[0].num_lookup_gates() == 3); + + std::set used_lookup_gates_0 = {0, 1}; + BOOST_ASSERT(circuits[0].get_used_lookup_gates() == used_lookup_gates_0); + std::set used_lookup_gates_1 = {2}; + BOOST_ASSERT(circuits[1].get_used_lookup_gates() == used_lookup_gates_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_circuit_proxy_copy_constraints_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = nil::crypto3::zk::snark::plonk_variable; + + auto bp_ptr = std::make_shared>(); + std::vector> circuits; + circuits.emplace_back(bp_ptr, 0); + circuits.emplace_back(bp_ptr, 1); + + circuits[0].add_copy_constraint({var(0, 0, false, var::column_type::witness), var(0, -1, false, var::column_type::witness)}); + circuits[0].add_copy_constraint({var(0, 1, false, var::column_type::witness), var(0, -1, false, var::column_type::witness)}); + circuits[1].add_copy_constraint({var(0, 2, false, var::column_type::witness), var(0, -1, false, var::column_type::witness)}); + + std::set used_copy_constraints_0 = {0, 1}; + BOOST_ASSERT(circuits[0].get_used_copy_constraints() == used_copy_constraints_0); + std::set used_copy_constraints_1 = {2}; + BOOST_ASSERT(circuits[1].get_used_copy_constraints() == used_copy_constraints_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_circuit_proxy_lookup_tables_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto bp_ptr = std::make_shared>(); + std::vector> circuits; + circuits.emplace_back(bp_ptr, 0); + circuits.emplace_back(bp_ptr, 1); + + const std::string lookup_tbale_name_0 = "sha256_sparse_base4/full"; + const std::string lookup_tbale_name_1 = "sha256_sparse_base4/first_column"; + const std::string lookup_tbale_name_2 = "sha256_reverse_sparse_base4/full"; + + circuits[0].reserve_table(lookup_tbale_name_0); + circuits[0].reserve_table(lookup_tbale_name_1); + circuits[1].reserve_table(lookup_tbale_name_2); + + std::set used_lookup_tables_0 = {0, 1}; + BOOST_ASSERT(circuits[0].get_used_lookup_tables() == used_lookup_tables_0); + std::set used_lookup_tables_1 = {2}; + BOOST_ASSERT(circuits[1].get_used_lookup_tables() == used_lookup_tables_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_selector_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto assignment_ptr = std::make_shared>(desc); + std::vector> assignments; + assignments.emplace_back(assignment_ptr, 0); + assignments.emplace_back(assignment_ptr, 1); + + // add selectors + assignments[0].selector(0, 0); + assignments[0].selector(0, 1); + assignments[1].selector(1, 2); + + BOOST_ASSERT(assignments[0].rows_amount() == assignments[1].rows_amount()); + BOOST_ASSERT(assignments[0].rows_amount() == 3); + + BOOST_ASSERT(assignments[0].selector_column_size(0) == assignments[1].selector_column_size(0)); + BOOST_ASSERT(assignments[0].selector_column_size(0) == 2); + BOOST_ASSERT(assignments[0].selector_column_size(1) == assignments[1].selector_column_size(1)); + BOOST_ASSERT(assignments[0].selector_column_size(1) == 3); + + std::set used_rows_0 = {0, 1}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + std::set used_rows_1 = {2}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); + + // enable selectors + assignments[0].enable_selector(2, 3, 5, 2); // selectors in col 2 rows 3 and 5 + assignments[1].enable_selector(3, 6, 7); // selectors in col 3 rows 6 and 7 + + BOOST_ASSERT(assignments[0].rows_amount() == assignments[1].rows_amount()); + BOOST_ASSERT(assignments[0].rows_amount() == 8); + + BOOST_ASSERT(assignments[0].selector_column_size(2) == assignments[1].selector_column_size(2)); + BOOST_ASSERT(assignments[0].selector_column_size(2) == 6); + BOOST_ASSERT(assignments[0].selector_column_size(3) == assignments[1].selector_column_size(3)); + BOOST_ASSERT(assignments[0].selector_column_size(3) == 8); + + used_rows_0 = {0, 1, 3, 5}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + used_rows_1 = {2, 6, 7}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_shared_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto assignment_ptr = std::make_shared>(desc); + std::vector> assignments; + assignments.emplace_back(assignment_ptr, 0); + assignments.emplace_back(assignment_ptr, 1); + + // add shareds + assignments[0].shared(0, 0); + assignments[0].shared(0, 1); + assignments[1].shared(0, 2); + + BOOST_ASSERT(assignments[0].shared_column_size(0) == assignments[1].shared_column_size(0)); + BOOST_ASSERT(assignments[0].shared_column_size(0) == 3); + + std::set used_rows_0 = {}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + std::set used_rows_1 = {}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_witness_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto assignment_ptr = std::make_shared>(desc); + std::vector> assignments; + assignments.emplace_back(assignment_ptr, 0); + assignments.emplace_back(assignment_ptr, 1); + + // add witness + assignments[0].witness(0, 0); + assignments[0].witness(0, 1); + assignments[1].witness(1, 2); + + BOOST_ASSERT(assignments[0].rows_amount() == assignments[1].rows_amount()); + BOOST_ASSERT(assignments[0].rows_amount() == 3); + + BOOST_ASSERT(assignments[0].allocated_rows() == assignments[1].allocated_rows()); + BOOST_ASSERT(assignments[0].allocated_rows() == 3); + + BOOST_ASSERT(assignments[0].witness_column_size(0) == assignments[1].witness_column_size(0)); + BOOST_ASSERT(assignments[0].witness_column_size(0) == 2); + BOOST_ASSERT(assignments[0].witness_column_size(1) == assignments[1].witness_column_size(1)); + BOOST_ASSERT(assignments[0].witness_column_size(1) == 3); + + std::set used_rows_0 = {0, 1}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + std::set used_rows_1 = {2}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_constant_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto assignment_ptr = std::make_shared>(desc); + std::vector> assignments; + assignments.emplace_back(assignment_ptr, 0); + assignments.emplace_back(assignment_ptr, 1); + + // add constants + assignments[0].constant(0, 0); + assignments[0].constant(0, 1); + assignments[1].constant(1, 2); + + BOOST_ASSERT(assignments[0].rows_amount() == assignments[1].rows_amount()); + BOOST_ASSERT(assignments[0].rows_amount() == 3); + + BOOST_ASSERT(assignments[0].allocated_rows() == assignments[1].allocated_rows()); + BOOST_ASSERT(assignments[0].allocated_rows() == 3); + + BOOST_ASSERT(assignments[0].constant_column_size(0) == assignments[1].constant_column_size(0)); + BOOST_ASSERT(assignments[0].constant_column_size(0) == 2); + BOOST_ASSERT(assignments[0].constant_column_size(1) == assignments[1].constant_column_size(1)); + BOOST_ASSERT(assignments[0].constant_column_size(1) == 3); + + std::set used_rows_0 = {0, 1}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + std::set used_rows_1 = {2}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_public_input_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto assignment_ptr = std::make_shared>(desc); + std::vector> assignments; + assignments.emplace_back(assignment_ptr, 0); + assignments.emplace_back(assignment_ptr, 1); + + // add public_inputs + assignments[0].public_input(0, 0); + assignments[0].public_input(0, 1); + assignments[1].public_input(0, 2); + + BOOST_ASSERT(assignments[0].rows_amount() == assignments[1].rows_amount()); + BOOST_ASSERT(assignments[0].rows_amount() == 3); + + + BOOST_ASSERT(assignments[0].public_input_column_size(0) == assignments[1].public_input_column_size(0)); + BOOST_ASSERT(assignments[0].public_input_column_size(0) == 3); + + std::set used_rows_0 = {}; + BOOST_ASSERT(assignments[0].get_used_rows() == used_rows_0); + std::set used_rows_1 = {}; + BOOST_ASSERT(assignments[1].get_used_rows() == used_rows_1); +} + +BOOST_AUTO_TEST_CASE(blueprint_assignment_proxy_save_shared_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using var = nil::crypto3::zk::snark::plonk_variable; + + auto assignment_ptr = std::make_shared>(desc); + assignment_proxy assignment(assignment_ptr, 0); + + const std::vector v = {var(0, 0, true, var::column_type::witness), var(0, 1, true, var::column_type::witness)}; + assignment.witness(0, 0) = 1; + assignment.witness(0, 1) = 2; + const auto res = save_shared_var(assignment, v); + + BOOST_ASSERT(res.size() == 2); + BOOST_ASSERT(res[0].index == 1); + BOOST_ASSERT(res[1].index == 1); + BOOST_ASSERT(res[0].rotation == 0); + BOOST_ASSERT(res[1].rotation == 1); + BOOST_ASSERT(res[0].type == var::column_type::public_input); + BOOST_ASSERT(res[1].type == var::column_type::public_input); + BOOST_ASSERT(var_value(assignment, res[0]) == 1); + BOOST_ASSERT(var_value(assignment, res[1]) == 2); + + BOOST_ASSERT(assignment.shared_column_size(0) == 2); +} + +BOOST_AUTO_TEST_CASE(blueprint_proxy_call_pack_lookup_tables_test) { + using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 5; + constexpr std::size_t SelectorColumns = 35; + + nil::crypto3::zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + + auto bp_ptr = std::make_shared>(); + circuit_proxy bp(bp_ptr, 0); + auto assignment_ptr = std::make_shared>(desc); + assignment_proxy assignment(assignment_ptr, 0); + + std::vector lookup_columns_indices = {0, 1, 2, 3, 4}; + std::size_t usable_rows_amount = assignment.allocated_rows(); + std::uint32_t min_selector_index = 5; + bp.reserve_table("binary_xor_table/full"); + + nil::crypto3::zk::snark::pack_lookup_tables_horizontal( + bp.get_reserved_indices(), + bp.get_reserved_tables(), + bp.get_reserved_dynamic_tables(), + bp.get(), assignment.get(), lookup_columns_indices, min_selector_index, + usable_rows_amount); + + std::set lookup_constant_cols = {0, 1, 2, 3, 4}; + BOOST_ASSERT(assignment.get_lookup_constant_cols() == lookup_constant_cols); + std::set lookup_selector_cols = {min_selector_index}; + BOOST_ASSERT(assignment.get_lookup_selector_cols() == lookup_selector_cols); +} \ No newline at end of file diff --git a/libs/blueprint/test/r1cs_examples.hpp b/libs/blueprint/test/r1cs_examples.hpp new file mode 100644 index 000000000..675d8e8c3 --- /dev/null +++ b/libs/blueprint/test/r1cs_examples.hpp @@ -0,0 +1,216 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for a R1CS example, as well as functions to sample +// R1CS examples with prescribed parameters (according to some distribution). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_EXAMPLES_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_EXAMPLES_TEST_HPP + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace snark { + + using namespace nil::crypto3::algebra; + + /** + * A R1CS example comprises a R1CS constraint system, R1CS input, and R1CS witness. + */ + template + struct r1cs_example { + snark::r1cs_constraint_system constraint_system; + snark::r1cs_primary_input primary_input; + snark::r1cs_auxiliary_input auxiliary_input; + + r1cs_example() = default; + r1cs_example(const r1cs_example &other) = default; + r1cs_example(const snark::r1cs_constraint_system &constraint_system, + const snark::r1cs_primary_input &primary_input, + const snark::r1cs_auxiliary_input &auxiliary_input) : + constraint_system(constraint_system), + primary_input(primary_input), auxiliary_input(auxiliary_input) {}; + r1cs_example(snark::r1cs_constraint_system &&constraint_system, + snark::r1cs_primary_input &&primary_input, + snark::r1cs_auxiliary_input &&auxiliary_input) : + constraint_system(std::move(constraint_system)), + primary_input(std::move(primary_input)), auxiliary_input(std::move(auxiliary_input)) {}; + }; + + /** + * Generate a R1CS example such that: + * - the number of constraints of the R1CS constraint system is num_constraints; + * - the number of variables of the R1CS constraint system is (approximately) num_constraints; + * - the number of inputs of the R1CS constraint system is num_inputs; + * - the R1CS input consists of ``full'' field elements (typically require the whole log|Field| bits to + * represent). + */ + template + r1cs_example generate_r1cs_example_with_field_input(std::size_t num_constraints, + std::size_t num_inputs) { + + BOOST_CHECK(num_inputs <= num_constraints + 2); + + snark::r1cs_constraint_system cs; + cs.primary_input_size = num_inputs; + cs.auxiliary_input_size = 2 + num_constraints - num_inputs; // TODO: explain this + + snark::r1cs_variable_assignment full_variable_assignment; + typename FieldType::value_type a = algebra::random_element(); + typename FieldType::value_type b = algebra::random_element(); + full_variable_assignment.push_back(a); + full_variable_assignment.push_back(b); + + for (std::size_t i = 0; i < num_constraints - 1; ++i) { + linear_combination A, B, C; + + if (i % 2) { + // a * b = c + A.add_term(i + 1, 1); + B.add_term(i + 2, 1); + C.add_term(i + 3, 1); + typename FieldType::value_type tmp = a * b; + full_variable_assignment.push_back(tmp); + a = b; + b = tmp; + } else { + // a + b = c + B.add_term(0, 1); + A.add_term(i + 1, 1); + A.add_term(i + 2, 1); + C.add_term(i + 3, 1); + typename FieldType::value_type tmp = a + b; + full_variable_assignment.push_back(tmp); + a = b; + b = tmp; + } + + cs.add_constraint(snark::r1cs_constraint(A, B, C)); + } + + linear_combination A, B, C; + typename FieldType::value_type fin = FieldType::value_type::zero(); + for (std::size_t i = 1; i < cs.num_variables(); ++i) { + A.add_term(i, 1); + B.add_term(i, 1); + fin = fin + full_variable_assignment[i - 1]; + } + C.add_term(cs.num_variables(), 1); + cs.add_constraint(snark::r1cs_constraint(A, B, C)); + full_variable_assignment.push_back(fin.squared()); + + /* split variable assignment */ + snark::r1cs_primary_input primary_input(full_variable_assignment.begin(), + full_variable_assignment.begin() + num_inputs); + snark::r1cs_primary_input auxiliary_input(full_variable_assignment.begin() + num_inputs, + full_variable_assignment.end()); + + /* sanity checks */ + BOOST_CHECK(cs.num_variables() == full_variable_assignment.size()); + BOOST_CHECK(cs.num_variables() >= num_inputs); + BOOST_CHECK(cs.num_inputs() == num_inputs); + BOOST_CHECK(cs.num_constraints() == num_constraints); + BOOST_CHECK(cs.is_satisfied(primary_input, auxiliary_input)); + + r1cs_example re(std::move(cs), std::move(primary_input), std::move(auxiliary_input)); + + return re; + } + + /** + * Generate a R1CS example such that: + * - the number of constraints of the R1CS constraint system is num_constraints; + * - the number of variables of the R1CS constraint system is (approximately) num_constraints; + * - the number of inputs of the R1CS constraint system is num_inputs; + * - the R1CS input consists of binary values (as opposed to ``full'' field elements). + */ + template + r1cs_example generate_r1cs_example_with_binary_input(std::size_t num_constraints, + std::size_t num_inputs) { + BOOST_CHECK(num_inputs >= 1); + + snark::r1cs_constraint_system cs; + cs.primary_input_size = num_inputs; + cs.auxiliary_input_size = num_constraints; /* we will add one auxiliary variable per constraint */ + + snark::r1cs_variable_assignment full_variable_assignment; + for (std::size_t i = 0; i < num_inputs; ++i) { + full_variable_assignment.push_back(typename FieldType::value_type(std::rand() % 2)); + } + + std::size_t lastvar = num_inputs - 1; + for (std::size_t i = 0; i < num_constraints; ++i) { + ++lastvar; + const std::size_t u = (i == 0 ? std::rand() % num_inputs : std::rand() % i); + const std::size_t v = (i == 0 ? std::rand() % num_inputs : std::rand() % i); + + /* chose two random bits and XOR them together: + res = u + v - 2 * u * v + 2 * u * v = u + v - res + */ + linear_combination A, B, C; + A.add_term(u + 1, 2); + B.add_term(v + 1, 1); + if (u == v) { + C.add_term(u + 1, 2); + } else { + C.add_term(u + 1, 1); + C.add_term(v + 1, 1); + } + C.add_term(lastvar + 1, -FieldType::value_type::one()); + + cs.add_constraint(snark::r1cs_constraint(A, B, C)); + full_variable_assignment.push_back(full_variable_assignment[u] + full_variable_assignment[v] - + full_variable_assignment[u] * full_variable_assignment[v] - + full_variable_assignment[u] * full_variable_assignment[v]); + } + + /* split variable assignment */ + snark::r1cs_primary_input primary_input(full_variable_assignment.begin(), + full_variable_assignment.begin() + num_inputs); + snark::r1cs_primary_input auxiliary_input(full_variable_assignment.begin() + num_inputs, + full_variable_assignment.end()); + + /* sanity checks */ + BOOST_CHECK(cs.num_variables() == full_variable_assignment.size()); + BOOST_CHECK(cs.num_variables() >= num_inputs); + BOOST_CHECK(cs.num_inputs() == num_inputs); + BOOST_CHECK(cs.num_constraints() == num_constraints); + BOOST_CHECK(cs.is_satisfied(primary_input, auxiliary_input)); + + r1cs_example re = + r1cs_example(std::move(cs), std::move(primary_input), std::move(auxiliary_input)); + return re; + } + } // namespace snark + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_R1CS_EXAMPLES_TEST_HPP diff --git a/libs/blueprint/test/routing/r1cs/as_waksman.cpp b/libs/blueprint/test/routing/r1cs/as_waksman.cpp new file mode 100644 index 000000000..0f17b234f --- /dev/null +++ b/libs/blueprint/test/routing/r1cs/as_waksman.cpp @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE as_waksman_components_test + +#include + +#include +#include +#include +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size) { + blueprint bp; + integer_permutation permutation(num_packets); + permutation.random_shuffle(); + + std::vector> randbits(num_packets), outbits(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + randbits[packet_idx].allocate(bp, packet_size); + outbits[packet_idx].allocate(bp, packet_size); + + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + bp.val(randbits[packet_idx][bit_idx]) = + (rand() % 2) ? FieldType::value_type::zero() : FieldType::value_type::zero(); + } + } + as_waksman_routing_component r(bp, num_packets, randbits, outbits); + r.generate_gates(); + + r.generate_assignments(permutation); + + BOOST_CHECK(bp.is_satisfied()); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + BOOST_CHECK(bp.val(outbits[permutation.get(packet_idx)][bit_idx]) == bp.val(randbits[packet_idx][bit_idx])); + } + } + + bp.val(components::blueprint_variable(10)) = typename FieldType::value_type(12345); + BOOST_CHECK(!bp.is_satisfied()); +} + +BOOST_AUTO_TEST_SUITE(as_waksman_components_test_suite) + +BOOST_AUTO_TEST_CASE(as_waksman_components_test) { +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/routing/r1cs/benes.cpp b/libs/blueprint/test/routing/r1cs/benes.cpp new file mode 100644 index 000000000..85b9143fa --- /dev/null +++ b/libs/blueprint/test/routing/r1cs/benes.cpp @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE benes_components_test + +#include + +#include +#include +#include +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_benes_routing_component(const std::size_t num_packets, const std::size_t packet_size) { + const std::size_t dimension = static_cast(std::ceil(std::log2(num_packets))); + assert(num_packets == 1ul << dimension); + + blueprint bp; + integer_permutation permutation(num_packets); + permutation.random_shuffle(); + + std::vector> randbits(num_packets), outbits(num_packets); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + randbits[packet_idx].allocate(bp, packet_size); + outbits[packet_idx].allocate(bp, packet_size); + + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + bp.val(randbits[packet_idx][bit_idx]) = + (rand() % 2) ? FieldType::value_type::zero() : FieldType::value_type::zero(); + } + } + + benes_routing_component r(bp, num_packets, randbits, outbits, num_packets); + r.generate_gates(); + r.generate_assignments(permutation); + + assert(bp.is_satisfied()); + for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) { + assert(bp.val(outbits[permutation.get(packet_idx)][bit_idx]) == bp.val(randbits[packet_idx][bit_idx])); + } + } + + bp.val(blueprint_variable(10)) = typename FieldType::value_type(12345); + assert(!bp.is_satisfied()); +} + +BOOST_AUTO_TEST_SUITE(benes_components_test_suite) + +BOOST_AUTO_TEST_CASE(benes_components_test) { +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/routing_algorithms/routing_algorithms.cpp b/libs/blueprint/test/routing_algorithms/routing_algorithms.cpp new file mode 100644 index 000000000..e2678c4d3 --- /dev/null +++ b/libs/blueprint/test/routing_algorithms/routing_algorithms.cpp @@ -0,0 +1,76 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Functions to test the algorithms that route on Benes and AS-Waksman networks. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE routing_algorithms_test + +#include + +#include + +#include +#include + +using namespace nil::crypto3::zk; + +/** + * Test Benes network routing for all permutations on 2^static_cast(std::ceil(std::log2(N))) elements. + */ +void test_benes(const std::size_t N) { + integer_permutation permutation(1ul << static_cast(std::ceil(std::log2(N)))); + + do { + const benes_routing routing = get_benes_routing(permutation); + assert(valid_benes_routing(permutation, routing)); + } while (permutation.next_permutation()); +} + +/** + * Test AS-Waksman network routing for all permutations on N elements. + */ +void test_as_waksman(const std::size_t N) { + integer_permutation permutation(N); + + do { + const as_waksman_routing routing = get_as_waksman_routing(permutation); + assert(valid_as_waksman_routing(permutation, routing)); + } while (permutation.next_permutation()); +} + +BOOST_AUTO_TEST_SUITE(routing_algorithms_test_suite) + +BOOST_AUTO_TEST_CASE(routing_algorithms_test) { + std::size_t bn_size = 8; + printf("* for all permutations on %zu elements\n", bn_size); + test_benes(bn_size); + + std::size_t asw_max_size = 9; + for (std::size_t i = 2; i <= asw_max_size; ++i) { + test_as_waksman(i); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/routing_algorithms/test_routing_algorithms.py b/libs/blueprint/test/routing_algorithms/test_routing_algorithms.py new file mode 100644 index 000000000..731a09689 --- /dev/null +++ b/libs/blueprint/test/routing_algorithms/test_routing_algorithms.py @@ -0,0 +1,425 @@ +#!/usr/bin/env python + +from __future__ import division + +import itertools +import math +import random +import time +from collections import defaultdict + + +def top_height(sz): + """Returns the height of the top part of size `sz' AS-Waksman network.""" + return sz // 2 + + +def bottom_height(sz): + """Returns the height of the bottom part of size `sz' AS-Waksman + network.""" + return sz - top_height(sz) + + +def switch_output(base, pos, sz, top): + """The recursive AS-Waksman construction AS-Waksman(sz) places two + lines of floor(sz/2) switches each and connects the outputs of + AS-Waksman(floor(sz/2)) and AS-Waksman(ceil(sz/2)) in between + them. + + Return the output wire of left-hand side switch `pos'(relative to + the base level `base' in the recursive call) in size `sz' + AS-Waksman network. + + If `top' = True, return the top wire, otherwise return bottom + wire. """ + relpos = pos - base + assert relpos % 2 == 0 and relpos + 1 < sz + if top: + return base + (relpos // 2) + else: + return base + top_height(sz) + (relpos // 2) + + +def switch_input(base, pos, sz, top): + """This function is symmetric to switch_output(base, pos, sz, top), + but returns the input wire of the right-hand side switch (rather than + the output wire of the left-hand side switch).""" + # because of symmetry this coincides with switch_output + return switch_output(base, pos, sz, top) + + +def width(sz): + """Returns width of size `sz' AS-Waksman network. For example, width(2) = + 1, width(3) = 3, width(4) = 3.""" + return 2 * int(math.ceil(math.log(sz, 2))) - 1 + + +def construct_as_waksman_topology(n): + """Returns a pair (neighbors, switches) describing the topology of + AS-Waksman network of size n. + + neigbhors[i][j] lists the possible locations where a wire, at + position j before going through the i-th column, could be routed to + after passing through the column. neighbors[i][j] is a length 1 + list for straight wires and length 2 list for switches, where the + first element denotes the destination when the switch is operated + in "straight" mode and the second element denotes the destination + for the "cross" mode. + + switches[i] is the dictionary, whose keys are all positions of the + switches at the i-th column and keys are switch settings. This + function only returns the topology, so switch settings are all set + to be None.""" + + assert n > 1 + w = width(n) + + neighbors = [{} for i in xrange(w)] + switches = [{} for i in xrange(w)] + + def construct_as_waksman_topology_inner(left, right, lo, hi, rhs_dests): + """Construct AS-Waksman subnetwork occupying switch columns [left, + left+1, ..., right] that will route left-hand side inputs [lo, + lo+1, ..., hi] to right-hand side destinations rhs_dests[0], + rhs_dests[1], ..., rhs_dests[hi-lo+1]. (That is, rhs_dests are + 0-indexed w.r.t. base of lo.) + + This function will fill out neighbors[left], + neighbors[right-1] and add switches in columns switches[left], + switches[right].""" + if left > right: + return + + sz = (hi - lo + 1) + assert len(rhs_dests) == sz + + assert (right - left + 1) >= width(sz) + + if right - left + 1 > width(sz): + # If there is more space for the routing network than + # required, just add straight edges. This also takes care + # of size 1 routing network base case. + for i in xrange(lo, hi + 1): + neighbors[left][i] = [i] + neighbors[right][i] = [rhs_dests[i - lo]] + # Recurse to construct the corresponding subnetwork. + construct_as_waksman_topology_inner(left + 1, right - 1, lo, hi, range(lo, hi + 1)) + elif sz == 2: + # Non-trivial base case: routing a 2-element permutation. + neighbors[left][lo] = [rhs_dests[0], rhs_dests[1]] + neighbors[left][hi] = [rhs_dests[1], rhs_dests[0]] + switches[left][lo] = None + else: + # Networks of size sz > 2 are handled by adding two lines + # of switches alongside the network and recursing. + new_rhs_dests = [None] * sz + + # This adds floor(sz/2) switches alongside the network. As + # per AS-Waksman construction one of the switches in the even + # case can be eliminated (i.e. set to be constant); this + # will be handled later. + for i in xrange(lo, hi, 2): + switches[left][i] = None + switches[right][i] = None + + neighbors[left][i] = [switch_output(lo, i, sz, True), switch_output(lo, i, sz, False)] + neighbors[left][i + 1] = [switch_output(lo, i, sz, False), switch_output(lo, i, sz, True)] + + new_rhs_dests[switch_input(lo, i, sz, True) - lo] = i + new_rhs_dests[switch_input(lo, i, sz, False) - lo] = i + 1 + + neighbors[right][i] = [rhs_dests[i - lo], rhs_dests[i + 1 - lo]] + neighbors[right][i + 1] = [rhs_dests[i + 1 - lo], rhs_dests[i - lo]] + + if sz % 2 == 1: + # Odd special case: the last wire is not connected to + # any of the switches and just routed straight. + neighbors[left][hi] = [hi] + neighbors[right][hi] = [rhs_dests[hi - lo]] + new_rhs_dests[hi - lo] = hi + else: + # Even special case: fix the bottom-most LHS switch to + # a constant "straight" setting. + neighbors[left][hi - 1] = [switch_output(lo, hi - 1, sz, True)] + neighbors[left][hi] == [switch_output(lo, hi - 1, sz, False)] + + d = top_height(sz) + construct_as_waksman_topology_inner(left + 1, right - 1, lo, lo + d - 1, new_rhs_dests[:d]) + construct_as_waksman_topology_inner(left + 1, right - 1, lo + d, hi, new_rhs_dests[d:]) + + construct_as_waksman_topology_inner(0, w - 1, 0, n - 1, range(n)) + return (neighbors, switches) + + +def switch_position_from_wire_position(base, global_pos): + """Each switch occupies two wire positions (pos, pos+1); given a wire + position (plus, a base for offsetting the switch within subnetwork + that created it), this function returns the "canonical" position for + the switch, that is, the "upper" position global_pos. + + global_pos is assumed to be input position for the LHS switches + and output position for the RHS switches.""" + return ((global_pos - base) & ~1) + base + + +def get_switch_value_from_top_bottom_decision(base, global_pos, top): + """Return a switch value that makes switch s = + switch_position_from_wire_position(base, global_pos) to route the + wire global_pos via the top (if top = True), resp., bottom (if top + = False) subnetwork. + + global_pos is assumed to be input position for the LHS switches + and output position for the RHS switches.""" + s = switch_position_from_wire_position(base, global_pos) + return (s == global_pos) ^ top + + +def get_top_bottom_decision_from_switch_value(base, global_pos, val): + """Returns True if the switch s = + switch_position_from_wire_position(base, global_pos) when set to + "straight" (if val = True), resp., "cross" (if val = False), + routes the wire global_pos via the top subnetwork. + + global_pos is assumed to be input position for the LHS switches + and output position for the RHS switches.""" + s = switch_position_from_wire_position(base, global_pos) + return (s == global_pos) ^ val + + +def other_output_position(base, global_pos): + """Given an output position of a RHS switch, calculate and return the + output position of the other wire also connected to this switch.""" + switch = switch_position_from_wire_position(base, global_pos) + return (1 - (global_pos - switch)) + switch + + +def other_input_position(base, global_pos): + """Given an input position of a LHS switch, calculate and return the + output position of the other wire also connected to this switch.""" + # Exploiting symmetry here, this is the same as the output + # position for the corresponding RHS switch. + return other_output_position(base, global_pos) + + +def route_as_waksman(n, network, pi): + """Return AS-Waksman switch settings that implement the given + permutation.""" + assert n > 1 + w = width(n) + neighbors, switches = network + + piinv = [None for i in xrange(n)] + for i in xrange(n): + piinv[pi[i]] = i + + def route_as_waksman_inner(left, right, lo, hi, pi, piinv): + """Get AS-Waksman switch settings for the subnetwork occupying switch + columns [left, left+1, ..., right] that will route left-hand + side inputs [lo, lo+1, ..., hi] to right-hand side + destinations pi[lo], pi[lo+1], ... pi[hi].""" + if left > right: + return + + sz = (hi - lo + 1) + assert (right - left + 1) >= width(sz) + + if right - left + 1 > width(sz): + # If there is more space for the routing network than + # required, then the topology for this subnetwork includes + # straight edges along its sides and no switches, so we + # just recurse. + route_as_waksman_inner(left + 1, right - 1, lo, hi, pi, piinv) + elif sz == 2: + # Non-trivial base case: switch settings for a 2-element permutation. + assert set([pi[lo], pi[lo + 1]]) == set([lo, lo + 1]) + switches[left][lo] = (pi[lo] != lo) + else: + newpi = defaultdict(lambda: None) + newpiinv = defaultdict(lambda: None) + # Our algorithm will first assign a setting for a LHS + # switch, route its target to RHS, which will enforce a + # RHS switch setting. Then, we back-route the RHS value + # back to LHS. If this enforces a LHS switch setting, then + # forward-route that, otherwise we will select the next + # value from LHS to route. + lhs_routed = defaultdict(lambda: False) + + if sz % 2 == 1: + # If size is odd we first deal with the bottom-most + # straight wire, which is not connected to any of the + # switches at this level of recursion and just passed + # into the lower subnetwork. + if pi[hi] == hi: + # Easy case: it is routed directly to the + # bottom-most wire on RHS, so no switches need to + # be touched. + newpi[hi] = hi + newpiinv[hi] = hi + to_route = hi - 1 + route_left = True + else: + # Other case: the straight wire is routed to a + # switch on RHS, so route the other value from + # that switch using the lower subnetwork. + rhs_switch = switch_position_from_wire_position(lo, pi[hi]) + rhs_switch_val = get_switch_value_from_top_bottom_decision(lo, pi[hi], False) + switches[right][rhs_switch] = rhs_switch_val + tprime = switch_input(lo, rhs_switch, sz, False) + newpi[hi] = tprime + newpiinv[tprime] = hi + + to_route = other_output_position(lo, pi[hi]) + route_left = False + + lhs_routed[hi] = True + max_unrouted = hi - 1 + else: + # If n is even, then the bottom-most switch (one + # freely set in Benes construction) is fixed to a + # constant straight setting. So we route wire hi + # accordingly. + switches[left][hi - 1] = False + to_route = hi + route_left = True + max_unrouted = hi + + while True: + # We maintain invariant that wire `to_route' on LHS + # (if route_left = True), resp., rhs (if route_left = + # False) can be routed. + if route_left: + # If switch value hasn't been assigned, assign it arbitrarily + lhs_switch = switch_position_from_wire_position(lo, to_route) + if switches[left][lhs_switch] is None: + switches[left][lhs_switch] = False + lhs_switch_val = switches[left][lhs_switch] + use_top = get_top_bottom_decision_from_switch_value(lo, to_route, lhs_switch_val) + + t = switch_output(lo, lhs_switch, sz, use_top) + if pi[to_route] == hi: + # We have routed to the straight wire for the + # odd case, so back-route from it. + newpi[t] = hi + newpiinv[hi] = t + + lhs_routed[to_route] = True + to_route = max_unrouted + route_left = True + else: + rhs_switch = switch_position_from_wire_position(lo, pi[to_route]) + # We know that the corresponding switch on RHS + # cannot be set, so set it according to our + # incoming wire. + assert switches[right][rhs_switch] is None + + switches[right][rhs_switch] = get_switch_value_from_top_bottom_decision(lo, pi[to_route], + use_top) + tprime = switch_input(lo, rhs_switch, sz, use_top) + newpi[t] = tprime + newpiinv[tprime] = t + + lhs_routed[to_route] = True + to_route = other_output_position(lo, pi[to_route]) + route_left = False + else: + # We have arrived on RHS side, so our switch + # setting is fixed. We will just back-route from + # that. + rhs_switch = switch_position_from_wire_position(lo, to_route) + lhs_switch = switch_position_from_wire_position(lo, piinv[to_route]) + + assert switches[right][rhs_switch] is not None + rhs_switch_val = switches[right][rhs_switch] + use_top = get_top_bottom_decision_from_switch_value(lo, to_route, rhs_switch_val) + lhs_switch_val = get_switch_value_from_top_bottom_decision(lo, piinv[to_route], use_top) + + # The value on LHS is either the same or unset + assert switches[left][lhs_switch] in [None, lhs_switch_val] + + switches[left][lhs_switch] = lhs_switch_val + t = switch_input(lo, rhs_switch, sz, use_top) + tprime = switch_output(lo, lhs_switch, sz, use_top) + newpi[tprime] = t + newpiinv[t] = tprime + + lhs_routed[piinv[to_route]] = True + to_route = other_input_position(lo, piinv[to_route]) + route_left = True + + # If the next item to be routed hasn't been routed + # before, then try routing it. + if not route_left or not lhs_routed[to_route]: + continue + + # Otherwise just find the next unrouted item. + while max_unrouted >= lo and lhs_routed[max_unrouted]: + max_unrouted -= 1 + + if max_unrouted < lo: + # All routed + break + else: + to_route = max_unrouted + route_left = True + + d = top_height(sz) + route_as_waksman_inner(left + 1, right - 1, lo, lo + d - 1, newpi, newpiinv) + route_as_waksman_inner(left + 1, right - 1, lo + d, hi, newpi, newpiinv) + + route_as_waksman_inner(0, w - 1, 0, n - 1, pi, piinv) + + +def check_as_waksman_routing(network, pi): + assert n > 1 + w = width(n) + neighbors, switches = network + + piinv = [None for i in xrange(n)] + for i in xrange(n): + piinv[pi[i]] = i + curperm = range(n) + for i in xrange(w): + nextperm = [None] * n + for j in xrange(n): + assert len(neighbors[i][j]) in [1, 2] + + if len(neighbors[i][j]) == 1: + nextperm[neighbors[i][j][0]] = curperm[j] + else: + assert (j in switches[i]) ^ ((j - 1) in switches[i]) + switchval = switches[i][j] if j in switches[i] else switches[i][j - 1] + nextperm[neighbors[i][j][1 if switchval else 0]] = curperm[j] + curperm = nextperm + + return curperm == piinv + + +def test_routing_of_all_permutations(n): + for pi in itertools.permutations(range(n)): + print + n, pi + network = construct_as_waksman_topology(n) + route_as_waksman(n, network, pi) + assert check_as_waksman_routing(network, pi) + + +def profile_routing_algorithm_speed(k_min, k_max): + prev_t = None + for k in xrange(k_min, k_max + 1): + n = 2 ** k + pi = range(n) + random.shuffle(pi) + network = construct_network(n) + t = time.time() + route(n, network, pi) + t = time.time() - t + assert check_as_waksman_routing(network, pi) + print + n, t, (t / prev_t if prev_t else "-"), t / (n * k) + prev_t = t + + +if __name__ == '__main__': + for n in xrange(2, 9): + test_routing_of_all_permutations(n) + # profile_routing_algorithm_speed(2, 16) diff --git a/libs/blueprint/test/set_commitment_component.cpp b/libs/blueprint/test/set_commitment_component.cpp new file mode 100644 index 000000000..02c84d40f --- /dev/null +++ b/libs/blueprint/test/set_commitment_component.cpp @@ -0,0 +1,118 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE set_commitment_component_test + +#include + +#include +#include +#include + +#include +#include + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +void test_set_commitment_component() { + + const std::size_t digest_len = HashT::digest_bits; + const std::size_t max_set_size = 16; + const std::size_t value_size = (HashT::block_bits > 0 ? HashT::block_bits : 10); + + set_commitment_accumulator accumulator(max_set_size, value_size); + + std::vector set_elems; + for (std::size_t i = 0; i < max_set_size; ++i) { + algebra::bit_vector elem(value_size); + std::generate(elem.begin(), elem.end(), [&]() { return std::rand() % 2; }); + set_elems.emplace_back(elem); + accumulator.add(elem); + BOOST_CHECK(accumulator.is_in_set(elem)); + } + + blueprint bp; + components::blueprint_variable_array element_bits; + element_bits.allocate(bp, value_size); + set_commitment_variable root_digest(bp, digest_len); + + bp_variable check_succesful; + check_succesful.allocate(bp); + + set_membership_proof_variable proof(bp, max_set_size); + + set_commitment_component sc(bp, max_set_size, element_bits, root_digest, proof, check_succesful); + sc.generate_gates(); + + /* test all elements from set */ + for (std::size_t i = 0; i < max_set_size; ++i) { + element_bits.fill_with_bits(bp, set_elems[i]); + bp.val(check_succesful) = FieldT::one(); + proof.generate_assignments(accumulator.get_membership_proof(set_elems[i])); + sc.generate_assignments(); + root_digest.generate_assignments(accumulator.get_commitment()); + BOOST_CHECK(bp.is_satisfied()); + } + std::cout << "membership tests OK" << std::endl; + + /* test an element not in set */ + for (std::size_t i = 0; i < value_size; ++i) { + bp.val(element_bits[i]) = FieldT(std::rand() % 2); + } + + bp.val(check_succesful) = FieldT::zero(); /* do not require the check result to be successful */ + proof.generate_assignments(accumulator.get_membership_proof(set_elems[0])); /* try it with invalid proof */ + sc.generate_assignments(); + root_digest.generate_assignments(accumulator.get_commitment()); + BOOST_CHECK(bp.is_satisfied()); + + bp.val(check_succesful) = FieldT::one(); /* now require the check result to be succesful */ + proof.generate_assignments(accumulator.get_membership_proof(set_elems[0])); /* try it with invalid proof */ + sc.generate_assignments(); + root_digest.generate_assignments(accumulator.get_commitment()); + BOOST_CHECK(!bp.is_satisfied()); /* the blueprint should be unsatisfied */ + std::cout << "non-membership test OK" << std::endl; +} + +template +void test_all_set_commitment_components() { + typedef typename CurveType::scalar_field_type scalar_field_type; + + // for now all CRH components are knapsack CRH's; can be easily extended + // later to more expressive selector types. + using crh_with_field_out_component = knapsack_crh_with_field_out_component; + using crh_with_bit_out_component = knapsack_crh_with_bit_out_component; + + test_set_commitment_component(); + test_set_commitment_component>(); +} + +int main(void) { + test_all_set_commitment_components>(); + test_all_set_commitment_components>(); + test_all_set_commitment_components>(); +} diff --git a/libs/blueprint/test/test_plonk_component.hpp b/libs/blueprint/test/test_plonk_component.hpp new file mode 100644 index 000000000..51c25c144 --- /dev/null +++ b/libs/blueprint/test/test_plonk_component.hpp @@ -0,0 +1,599 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Nikita Kaskov +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK unified addition component. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include +#include +#include +#include + +#include + +// #include "profiling_plonk_circuit.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + template + std::tuple< + nil::crypto3::zk::snark::plonk_table_description, + circuit>, + assignment>> + generate_empty_assignments(); + } + } + + namespace crypto3 { + inline std::vector generate_random_step_list(const std::size_t r, const std::size_t max_step) { + using dist_type = std::uniform_int_distribution; + static std::random_device random_engine; + + std::vector step_list; + std::size_t steps_sum = 0; + while (steps_sum != r) { + if (r - steps_sum <= max_step) { + while (r - steps_sum != 1) { + step_list.emplace_back(r - steps_sum - 1); + steps_sum += step_list.back(); + } + step_list.emplace_back(1); + steps_sum += step_list.back(); + } else { + step_list.emplace_back(dist_type(1, max_step)(random_engine)); + steps_sum += step_list.back(); + } + } + return step_list; + } + + template + class plonk_test_assigner { + public: + virtual typename ComponentType::result_type operator()( + const ComponentType&, + nil::blueprint::assignment>&, + const typename ComponentType::input_type&, + const std::uint32_t) const = 0; + }; + + template + class plonk_test_default_assigner : + public plonk_test_assigner { + public: + typename ComponentType::result_type operator()( + const ComponentType &component, + nil::blueprint::assignment> &assignment, + const typename ComponentType::input_type &instance_input, + const std::uint32_t start_row_index) const override { + + return blueprint::components::generate_assignments( + component, assignment, instance_input, start_row_index); + } + }; + + template + class plonk_test_custom_assigner : + public plonk_test_assigner { + + using assigner_type = + std::function>&, + const typename ComponentType::input_type&, + const std::uint32_t)>; + assigner_type assigner; + public: + plonk_test_custom_assigner(assigner_type assigner) : assigner(assigner) {}; + + typename ComponentType::result_type operator()( + const ComponentType &component, + nil::blueprint::assignment> &assignment, + const typename ComponentType::input_type &instance_input, + const std::uint32_t start_row_index) const override { + + return this->assigner(component, assignment, instance_input, start_row_index); + } + }; + + template< + typename ComponentType, typename BlueprintFieldType, typename Hash, + std::size_t Lambda, typename PublicInputContainerType, typename FunctorResultCheck, bool PrivateInput, + typename... ComponentStaticInfoArgs> + auto prepare_component(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, + const FunctorResultCheck &result_check, + const plonk_test_assigner &assigner, + typename ComponentType::input_type instance_input, + bool expected_to_pass, + blueprint::connectedness_check_type connectedness_check, + ComponentStaticInfoArgs... component_static_info_args) { + using component_type = ComponentType; + blueprint::circuit> bp; + blueprint::assignment> assignment(desc); + + if constexpr( nil::blueprint::use_lookups() ){ + auto lookup_tables = component_instance.component_lookup_tables(); + for(auto &[k,v]:lookup_tables){ + if( v == 1 ) + bp.reserve_dynamic_table(k); + else + bp.reserve_table(k); + } + }; + + static boost::random::mt19937 gen; + static boost::random::uniform_int_distribution<> dist(0, 100); + std::size_t start_row = 0; //dist(gen); + // resize to ensure that if the component is empty by default (e.g. a component which only uses batching) + if (start_row != 0) { + assignment.witness(0, start_row - 1) = 0u; + } + + if constexpr (PrivateInput) { + for (std::size_t i = 0; i < public_input.size(); i++) { + assignment.private_storage(i) = public_input[i]; + } + } else { + for (std::size_t i = 0; i < public_input.size(); i++) { + assignment.public_input(0, i) = public_input[i]; + } + } + + blueprint::components::generate_circuit( + component_instance, bp, assignment, instance_input, start_row); + auto component_result = boost::get( + assigner(component_instance, assignment, instance_input, start_row)); + + // Stretched components do not have a manifest, as they are dynamically generated. + if constexpr (!blueprint::components::is_component_stretcher< + BlueprintFieldType, ComponentType>::value) { + BOOST_ASSERT_MSG(bp.num_gates() + bp.num_lookup_gates() == + component_type::get_gate_manifest(component_instance.witness_amount(), + component_static_info_args...).get_gates_amount(), + "Component total gates amount does not match actual gates amount."); + } + + if (start_row + component_instance.rows_amount >= public_input.size()) { + BOOST_ASSERT_MSG(assignment.rows_amount() - start_row == component_instance.rows_amount, + "Component rows amount does not match actual rows amount."); + // Stretched components do not have a manifest, as they are dynamically generated. + if constexpr (!blueprint::components::is_component_stretcher< + BlueprintFieldType, ComponentType>::value) { + BOOST_ASSERT_MSG(assignment.rows_amount() - start_row == + component_type::get_rows_amount(component_instance.witness_amount(), + component_static_info_args...), + "Static component rows amount does not match actual rows amount."); + } + } + + const std::size_t rows_after_component_batching = + assignment.finalize_component_batches(bp, start_row + component_instance.rows_amount); + const std::size_t rows_after_const_batching = + assignment.finalize_constant_batches(bp, 0, std::max(start_row, 1)); + const std::size_t rows_after_batching = std::max(rows_after_component_batching, rows_after_const_batching); + for (auto variable : component_result.all_vars()) { + if (assignment.get_batch_variable_map().count(variable)) { + variable.get() = assignment.get_batch_variable_map().at(variable); + } + } + + result_check(assignment, component_result); + + if constexpr (!PrivateInput) { + bool is_connected = check_connectedness( + assignment, + bp, + instance_input.all_vars(), + component_result.all_vars(), start_row, rows_after_batching - start_row, + connectedness_check); + if (connectedness_check.t == blueprint::connectedness_check_type::type::NONE) { + std::cout << "WARNING: Connectedness check is disabled." << std::endl; + } + + // Uncomment the following if you want to output a visual representation of the connectedness graph. + // I recommend turning off the starting row randomization + // If the whole of public_input isn't shown, increase the end row + + // auto zones = blueprint::detail::generate_connectedness_zones( + // assignment, bp, instance_input.all_vars(), start_row, rows_after_batching - start_row); + // blueprint::detail::export_connectedness_zones( + // zones, assignment, instance_input.all_vars(), start_row, rows_after_batching - start_row, std::cout); + + // BOOST_ASSERT_MSG(is_connected, + // "Component disconnected! See comment above this assert for a way to output a visual representation of the connectedness graph."); + } + desc.usable_rows_amount = assignment.rows_amount(); + + if constexpr (nil::blueprint::use_lookups()) { + // Components with lookups may use constant columns. + // But now all constants are placed in the first column. + // So we reserve the first column for non-lookup constants. + // Rather universal for testing + // We may start from zero if component doesn't use ordinary constants. + std::vector lookup_columns_indices; + for(std::size_t i = 1; i < assignment.constants_amount(); i++) { + lookup_columns_indices.push_back(i); + } + + std::size_t cur_selector_id = 0; + for(const auto &gate: bp.gates()){ + cur_selector_id = std::max(cur_selector_id, gate.selector_index); + } + for(const auto &lookup_gate: bp.lookup_gates()){ + cur_selector_id = std::max(cur_selector_id, lookup_gate.tag_index); + } + cur_selector_id++; + desc.usable_rows_amount = zk::snark::pack_lookup_tables_horizontal( + bp.get_reserved_indices(), + bp.get_reserved_tables(), + bp.get_reserved_dynamic_tables(), + bp, assignment, lookup_columns_indices, cur_selector_id, + desc.usable_rows_amount, + 500000 + ); + } + desc.rows_amount = zk::snark::basic_padding(assignment); + +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "Usable rows: " << desc.usable_rows_amount << std::endl; + std::cout << "Padded rows: " << desc.rows_amount << std::endl; + + profiling(assignment); +#endif + // assignment.export_table(std::cout); + // bp.export_circuit(std::cout); + + assert(blueprint::is_satisfied(bp, assignment) == expected_to_pass); + return std::make_tuple(desc, bp, assignment); + } + + template< + typename ComponentType, typename BlueprintFieldType, typename Hash, + std::size_t Lambda, typename PublicInputContainerType, typename FunctorResultCheck, bool PrivateInput, + typename... ComponentStaticInfoArgs> + auto prepare_empty_component(ComponentType component_instance, + const zk::snark::plonk_table_description &desc, + const PublicInputContainerType &public_input, + const FunctorResultCheck &result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check, + ComponentStaticInfoArgs... component_static_info_args) { + using component_type = ComponentType; + + blueprint::circuit> bp; + blueprint::assignment> assignment(desc); + + static boost::random::mt19937 gen; + static boost::random::uniform_int_distribution<> dist(0, 100); + std::size_t start_row = dist(gen); + + if constexpr (PrivateInput) { + for (std::size_t i = 0; i < public_input.size(); i++) { + assignment.private_storage(i) = public_input[i]; + } + } else { + for (std::size_t i = 0; i < public_input.size(); i++) { + assignment.public_input(0, i) = public_input[i]; + } + } + + auto component_result = boost::get( + blueprint::components::generate_empty_assignments( + component_instance, assignment, instance_input, start_row)); + // assignment.export_table(std::cout); + // bp.export_circuit(std::cout); + result_check(assignment, component_result); + + BOOST_ASSERT(bp.num_gates() == 0); + BOOST_ASSERT(bp.num_lookup_gates() == 0); + + return std::make_tuple(desc, bp, assignment); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_empty_component(ComponentType component_instance, + zk::snark::plonk_table_description input_desc, + const PublicInputContainerType &public_input, + FunctorResultCheck result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + auto [desc, bp, assignments] = + prepare_empty_component + (component_instance, input_desc, public_input, result_check, instance_input, + connectedness_check, component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_inner(ComponentType component_instance, + zk::snark::plonk_table_description input_desc, + const PublicInputContainerType &public_input, + const FunctorResultCheck &result_check, + const plonk_test_assigner + &assigner, + const typename ComponentType::input_type &instance_input, + bool expected_to_pass, + blueprint::connectedness_check_type connectedness_check, + ComponentStaticInfoArgs... component_static_info_args) { + auto [desc, bp, assignments] = + prepare_component + (component_instance, input_desc, public_input, result_check, assigner, instance_input, + expected_to_pass, connectedness_check, component_static_info_args...); + +// How to define it from crypto3 cmake? +//#define BLUEPRINT_PLACEHOLDER_PROOF_GEN_ENABLED +#ifdef BLUEPRINT_PLACEHOLDER_PROOF_GEN_ENABLED + using circuit_params = typename nil::crypto3::zk::snark::placeholder_circuit_params; + using lpc_params_type = typename nil::crypto3::zk::commitments::list_polynomial_commitment_params< + Hash, Hash, 2 + >; + + using commitment_type = typename nil::crypto3::zk::commitments::list_polynomial_commitment; + using commitment_scheme_type = typename nil::crypto3::zk::commitments::lpc_commitment_scheme; + using placeholder_params_type = typename nil::crypto3::zk::snark::placeholder_params; + + using fri_type = typename commitment_type::fri_type; + + std::size_t table_rows_log = std::ceil(std::log2(desc.rows_amount)); + + typename fri_type::params_type fri_params(1,table_rows_log, Lambda, 2); + commitment_scheme_type lpc_scheme(fri_params); + + typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type + preprocessed_public_data = nil::crypto3::zk::snark::placeholder_public_preprocessor::process( + bp, assignments.public_table(), desc, lpc_scheme + ); + + typename nil::crypto3::zk::snark::placeholder_private_preprocessor::preprocessed_data_type + preprocessed_private_data = nil::crypto3::zk::snark::placeholder_private_preprocessor::process( + bp, assignments.private_table(), desc + ); + + auto proof = nil::crypto3::zk::snark::placeholder_prover::process( + preprocessed_public_data, preprocessed_private_data, desc, bp, lpc_scheme + ); + + bool verifier_res = nil::crypto3::zk::snark::placeholder_verifier::process( + preprocessed_public_data.common_data, proof, desc, bp, lpc_scheme + ); + + if (expected_to_pass) { + BOOST_ASSERT(verifier_res); + } + else { + BOOST_ASSERT(!verifier_res); + } +#endif + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, + FunctorResultCheck result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + return test_component_inner( + component_instance, desc, public_input, result_check, + plonk_test_default_assigner(), + instance_input, true, connectedness_check, component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_to_fail(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, + FunctorResultCheck result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + return test_component_inner( + component_instance, desc, public_input, result_check, + plonk_test_default_assigner(), + instance_input, false, connectedness_check, component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_custom_assignments(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, FunctorResultCheck result_check, + const plonk_test_custom_assigner &custom_assigner, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + + return test_component_inner + (component_instance, desc, public_input, result_check, custom_assigner, + instance_input, true, connectedness_check, component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_to_fail_custom_assignments(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, FunctorResultCheck result_check, + const plonk_test_custom_assigner &custom_assigner, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + + return test_component_inner + (component_instance, desc, public_input, result_check, custom_assigner, + instance_input, false, connectedness_check,component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_private_input(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, FunctorResultCheck result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + + return test_component_inner + (component_instance, desc, public_input, result_check, + plonk_test_default_assigner(), + instance_input, true, connectedness_check, component_static_info_args...); + } + + template + typename std::enable_if< + std::is_same::value_type>::value>::type + test_component_to_fail_private_input(ComponentType component_instance, + zk::snark::plonk_table_description desc, + const PublicInputContainerType &public_input, FunctorResultCheck result_check, + typename ComponentType::input_type instance_input, + blueprint::connectedness_check_type connectedness_check = + blueprint::connectedness_check_type::type::STRONG, + ComponentStaticInfoArgs... component_static_info_args) { + + return test_component_inner + (component_instance, desc, public_input, result_check, + plonk_test_default_assigner(), + instance_input, false, connectedness_check, component_static_info_args...); + } + + /* + Most of the time while testing we do not want to generate an entire set of assignments from scratch. + This function wraps the generate_assignments call for the component, and patches the passed + coordinate/value pairs into the result. + */ + template + std::function>&, + const typename ComponentType::input_type&, + const std::uint32_t)> + generate_patched_assignments( + const std::map, typename BlueprintFieldType::value_type> + &patches) { + + return [&patches] + (const ComponentType &component, + nil::blueprint::assignment> &assignment, + const typename ComponentType::input_type &instance_input, + const std::uint32_t start_row_index) { + typename ComponentType::result_type result = + blueprint::components::generate_assignments( + component, assignment, instance_input, start_row_index); + + for (const auto &patch : patches) { + assignment.witness(component.W(patch.first.second), patch.first.first + start_row_index) = + patch.second; + } + + return result; + }; + } + } // namespace crypto3 +} // namespace nil diff --git a/libs/blueprint/test/utils/connectedness_check.cpp b/libs/blueprint/test/utils/connectedness_check.cpp new file mode 100644 index 000000000..f6e831ee7 --- /dev/null +++ b/libs/blueprint/test/utils/connectedness_check.cpp @@ -0,0 +1,215 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include +#include +#define BOOST_TEST_MODULE gate_merger_test + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace nil::blueprint; +using namespace nil::crypto3; +using nil::blueprint::check_connectedness; +using nil::blueprint::connectedness_check_type; + +//constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(connectedness_check_test_suite) + +BOOST_AUTO_TEST_CASE(connectedness_check_sanity_tests) { + using field_type = algebra::curves::pallas::scalar_field_type; + using value_type = typename field_type::value_type; + constexpr std::size_t WitnessesAmount = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using var = zk::snark::plonk_variable; + + assignment> assignment( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + circuit> bp; + + const std::size_t start_row_index = 4; + + std::vector public_input = {var(0, 0, false, var::column_type::public_input)}; + std::vector> reference_public_input = {public_input[0]}; + std::vector output_variables = {var(4, start_row_index, false, var::column_type::witness)}; + std::vector> reference_output_variables = {output_variables[0]}; + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + + bp.add_copy_constraint({public_input[0], output_variables[0]}); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + + public_input.push_back(var(0, 1, false, var::column_type::public_input)); + reference_public_input = {public_input[0], public_input[1]}; + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + + const std::size_t intermediate_var_index = 5; + var intermediate_var = var(intermediate_var_index, start_row_index, false, var::column_type::witness); + bp.add_copy_constraint({public_input[1], intermediate_var}); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + + bp.add_copy_constraint({intermediate_var, output_variables[0]}); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + + var another_intermediate_var = var(0, start_row_index + 2, false, var::column_type::constant); + output_variables.push_back(another_intermediate_var); + reference_output_variables = {output_variables[0], output_variables[1]}; + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 3, + connectedness_check_type::type::STRONG)); + + std::size_t selector_idx = bp.add_gate({ + var(intermediate_var_index, -1, true, var::column_type::witness), + var(0, +1, true, var::column_type::constant), + var(0, -1, true, var::column_type::witness), + var(1, -1, true, var::column_type::witness), + var(2, -1, true, var::column_type::witness)}); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 3, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 3, + connectedness_check_type::type::WEAK)); + + assignment.enable_selector(selector_idx, start_row_index + 1); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 3, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 3, + connectedness_check_type::type::WEAK)); + + var lookup_test_var = var(0, start_row_index + 3, false, var::column_type::constant); + output_variables.push_back(lookup_test_var); + reference_output_variables = {output_variables[0], output_variables[1], output_variables[2]}; + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::WEAK)); + + std::size_t lookup_selector_idx = bp.add_lookup_gate( + {{0, {var(0, -1, true, var::column_type::constant)}}, + {1, {var(0, 0, true, var::column_type::constant)}}}); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::WEAK)); + + assignment.enable_selector(lookup_selector_idx, start_row_index + 3); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::WEAK)); + public_input.push_back(var(0, 2, false, var::column_type::public_input)); + reference_public_input = {public_input[0], public_input[1], public_input[2]}; + output_variables.push_back(var(0, 2, false, var::column_type::public_input)); + reference_output_variables = {output_variables[0], output_variables[1], output_variables[2], output_variables[3]}; + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 4, + connectedness_check_type::type::WEAK)); +} + +BOOST_AUTO_TEST_CASE(connectedness_check_island_tests) { + using field_type = algebra::curves::pallas::scalar_field_type; + using value_type = typename field_type::value_type; + constexpr std::size_t WitnessesAmount = 9; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using var = zk::snark::plonk_variable; + + assignment> assignment( + WitnessesAmount, PublicInputColumns, ConstantColumns, SelectorColumns); + circuit> bp; + + const std::size_t start_row_index = 4; + + std::vector public_input = {var(0, 0, false, var::column_type::public_input)}; + std::vector> reference_public_input = {public_input[0]}; + std::vector output_variables = {var(4, start_row_index, false, var::column_type::witness)}; + std::vector> reference_output_variables = {output_variables[0]}; + var intermediate_var = var(5, start_row_index, false, var::column_type::witness); + bp.add_copy_constraint({public_input[0], intermediate_var}); + bp.add_gate({ + var(4, 0, true, var::column_type::witness) * var(5, 0, true, var::column_type::witness)}); + assignment.enable_selector(0, start_row_index); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::WEAK)); + bp.add_gate({var(6, 0, true, var::column_type::witness)}); + assignment.enable_selector(1, start_row_index); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 1, + connectedness_check_type( + connectedness_check_type::type::STRONG, + connectedness_check_type::island_type::NONE))); + bp.add_copy_constraint({intermediate_var, var(6, start_row_index, false, var::column_type::witness)}); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 2, + connectedness_check_type::type::STRONG)); + bp.add_lookup_gate({{0, {var(0, 1, true, var::column_type::constant)}}}); + assignment.enable_selector(2, start_row_index); + BOOST_ASSERT(!check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 2, + connectedness_check_type::type::STRONG)); + BOOST_ASSERT(check_connectedness(assignment, bp, reference_public_input, reference_output_variables, start_row_index, 2, + connectedness_check_type( + connectedness_check_type::type::STRONG, + connectedness_check_type::island_type::NONE))); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/additions.cpp b/libs/blueprint/test/verifiers/flexible/additions.cpp new file mode 100644 index 000000000..2e63a01c4 --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/additions.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_placeholder_flexible_additions_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_flexible_addition( + const std::vector> &array +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::flexible_additions; + + typename component_type::input_type instance_input; + instance_input.arr.reserve(ArraySize); + for (std::size_t i = 0; i < ArraySize; i++) { + instance_input.arr.emplace_back(std::make_pair( + var(0, 2*i, false, var::column_type::public_input), + var(0, 2*i+1, false, var::column_type::public_input) + )); + } + + std::vector public_input; + for (std::size_t i = 0; i < ArraySize; i++) { + public_input.push_back(array[i].first); + public_input.push_back(array[i].second); + } + + auto result_check = [&array](AssignmentType &assignment, + typename component_type::result_type &real_res) { + BOOST_ASSERT(real_res.output.size() == ArraySize); + for (std::size_t i = 0; i < ArraySize; i++) { + if(var_value(assignment, real_res.output[i]) != (array[i].first + array[i].second)){ + std::cout << "Block " << i << ": var = " << real_res.output[i] << " values = " << var_value(assignment, real_res.output[i]) << " != " + << (array[i].first + array[i].second) << std::endl; + } + BOOST_ASSERT(var_value(assignment, real_res.output[i]) == (array[i].first + array[i].second)); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, ArraySize); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, ArraySize); +} + +template +void flexible_addition_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_flexible_addition( + {{generate_random(), generate_random()},{generate_random(), generate_random()},{generate_random(), generate_random()}} + ); + test_flexible_addition( + {{generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}} + ); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + flexible_addition_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + flexible_addition_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + flexible_addition_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/constant_pow.cpp b/libs/blueprint/test/verifiers/flexible/constant_pow.cpp new file mode 100644 index 000000000..332ce2290 --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/constant_pow.cpp @@ -0,0 +1,126 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_placeholder_flexible_constant_pow_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_flexible_constant_pow( + const typename BlueprintFieldType::value_type x, const typename BlueprintFieldType::integral_type pow +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 20; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::flexible_constant_pow; + + typename component_type::input_type instance_input; + instance_input.x = var(0, 0, false, var::column_type::public_input); + + std::vector public_input = {x}; + + auto result_check = [&x, &pow](AssignmentType &assignment, + typename component_type::result_type &real_res) { + BOOST_ASSERT(var_value(assignment, real_res.y) == x.pow(pow)); + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, pow); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, pow); +} + +template +void flexible_constant_pow_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + +// test_flexible_constant_pow(2, 3); +// test_flexible_constant_pow(2, 5); + test_flexible_constant_pow(2, 20); + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_flexible_constant_pow(generate_random(), (BlueprintFieldType::modulus - 1)/ 4294967296); +// test_flexible_constant_pow(generate_random(), 3); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + flexible_constant_pow_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + flexible_constant_pow_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + flexible_constant_pow_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/multiplications.cpp b/libs/blueprint/test/verifiers/flexible/multiplications.cpp new file mode 100644 index 000000000..a90d2c47b --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/multiplications.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_placeholder_flexible_multiplications_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_flexible_multiplication( + const std::vector> &array +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::flexible_multiplications; + + typename component_type::input_type instance_input; + instance_input.arr.reserve(ArraySize); + for (std::size_t i = 0; i < ArraySize; i++) { + instance_input.arr.emplace_back(std::make_pair( + var(0, 2*i, false, var::column_type::public_input), + var(0, 2*i+1, false, var::column_type::public_input) + )); + } + + std::vector public_input; + for (std::size_t i = 0; i < ArraySize; i++) { + public_input.push_back(array[i].first); + public_input.push_back(array[i].second); + } + + auto result_check = [&array](AssignmentType &assignment, + typename component_type::result_type &real_res) { + BOOST_ASSERT(real_res.output.size() == ArraySize); + for (std::size_t i = 0; i < ArraySize; i++) { + if(var_value(assignment, real_res.output[i]) != (array[i].first * array[i].second)){ + std::cout << "Block " << i << ": var = " << real_res.output[i] << " values = " << var_value(assignment, real_res.output[i]) << " != " + << (array[i].first * array[i].second) << std::endl; + } + BOOST_ASSERT(var_value(assignment, real_res.output[i]) == (array[i].first * array[i].second)); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, ArraySize); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, ArraySize); +} + +template +void flexible_multiplication_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_flexible_multiplication( + {{generate_random(), generate_random()},{generate_random(), generate_random()},{generate_random(), generate_random()}} + ); + test_flexible_multiplication( + {{generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}, + {generate_random(), generate_random()}, {generate_random(), generate_random()}} + ); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + flexible_multiplication_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + flexible_multiplication_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + flexible_multiplication_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/poseidon.cpp b/libs/blueprint/test/verifiers/flexible/poseidon.cpp new file mode 100644 index 000000000..898029e8a --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/poseidon.cpp @@ -0,0 +1,205 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_flexible_poseidon_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_poseidon(std::vector public_input, + std::vector expected_res){ + + using FieldType = BlueprintFieldType; + + constexpr std::size_t WitnessColumns = Witnesses; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 11; + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + using component_type = + blueprint::components::flexible_poseidon; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + using var = crypto3::zk::snark::plonk_variable; + constexpr std::size_t Lambda = 5; + + std::array input_state_var = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), var(0, 2, false, var::column_type::public_input)}; + typename component_type::input_type instance_input = {input_state_var}; + + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + for (std::uint32_t i = 0; i < component_type::state_size; i++){ + std::cout << "input[" << i << "] : " << public_input[i].data << "\n"; + } + #endif + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + for (std::uint32_t i = 0; i < component_type::state_size; i++){ + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "expected[" << i << "]: " << expected_res[i].data << "\n"; + std::cout << "real[" << i << "] : " << var_value(assignment, real_res.output_state[i]).data << "\n"; + #endif + assert(expected_res[i] == var_value(assignment, real_res.output_state[i])); + } + }; + + std::array witnesses; + for( std::size_t i = 0; i < Witnesses; i++){ + witnesses[i] = i; + } + component_type component_instance(witnesses,std::array{0}, std::array{0}); + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + blueprint::connectedness_check_type::type::STRONG + ); +} + +template +std::vector calculate_expected_poseidon(const typename std::vector &a) { + using poseidon_policy = nil::crypto3::hashes::detail::mina_poseidon_policy; + using permutation_type = nil::crypto3::hashes::detail::poseidon_permutation; + using state_type = typename permutation_type::state_type; + + state_type state; + std::copy(a.begin(), a.end(), state.begin()); + permutation_type::permute(state); + + std::vector result(3); + std::copy(state.begin(), state.end(), result.begin()); + return result; +} + +template +void test_poseidon_specfic_data(){ + std::vector input = {0,1,1}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {0,0,0}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {1,2,3}; + test_poseidon(input, calculate_expected_poseidon(input)); + + input = {-1,-1,-1}; + test_poseidon(input, calculate_expected_poseidon(input)); + + typename FieldType::value_type threeFFF = 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular256; + input = {threeFFF, threeFFF, threeFFF}; + test_poseidon(input, calculate_expected_poseidon(input)); +} + +template +void test_poseidon_random_data(){ + using generator_type = nil::crypto3::random::algebraic_engine; + generator_type g; + boost::random::mt19937 seed_seq; + g.seed(seed_seq); + std::vector input; + + for (std::size_t i = 0; i < RandomDataTestsAmount; i++) { + input = {g(), g(), g()}; + test_poseidon(input, calculate_expected_poseidon(input)); + } +} + +constexpr static const std::size_t random_data_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_poseidon_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas_21) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas_42) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas_45) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas_84) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_pallas_168) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_poseidon_specfic_data(); + test_poseidon_random_data(); +} + +// BOOST_AUTO_TEST_CASE(blueprint_plonk_poseidon_test_bls12) { +// using field_type = typename crypto3::algebra::fields::bls12_fr<381>; +// test_poseidon_specfic_data(); +// test_poseidon_random_data(); +// } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/pow_factor.cpp b/libs/blueprint/test/verifiers/flexible/pow_factor.cpp new file mode 100644 index 000000000..8df5137b3 --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/pow_factor.cpp @@ -0,0 +1,140 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_plonk_pow_factor_test + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_pow_factor( + const std::vector &coefficients, + const typename BlueprintFieldType::value_type &theta, + std::size_t power){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::pow_factor; + + BOOST_ASSERT(coefficients.size() == power + 1); + + typename component_type::input_type instance_input; + instance_input.theta = var(0, 0, false, var::column_type::public_input); + instance_input.coefficients.reserve(power + 1); + for (std::size_t i = 0; i < power + 1; i++) { + instance_input.coefficients.emplace_back(var(0, i + 1, false, var::column_type::public_input)); + } + + std::vector public_input = {theta}; + std::copy(coefficients.begin(), coefficients.end(), std::back_inserter(public_input)); + BOOST_ASSERT(public_input.size() == power + 2); + + auto result_check = [power, &theta, &coefficients](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + value_type poly_value = coefficients[0]; + for (std::size_t i = 1; i < power + 1; i++) { + poly_value = poly_value * theta + coefficients[i]; + } + BOOST_ASSERT(var_value(assignment, real_res.output) == poly_value); + }; + + std::array witnesses; + std::iota(witnesses.begin(), witnesses.end(), 0); + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, power); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, power); +} + +template +void pow_factor_tests() { + static boost::random::mt19937 seed_seq(1444); + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + static boost::random::uniform_int_distribution<> power_dist(1, 400); + using value_type = typename BlueprintFieldType::value_type; + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + const std::size_t power = power_dist(seed_seq); + std::vector coefficients; + coefficients.reserve(power + 1); + for (std::size_t i = 0; i < power + 1; i++) { + coefficients.emplace_back(generate_random()); + } + value_type theta = generate_random(); + test_pow_factor(coefficients, theta, power); + } + // zero-padding case checked separately + const std::size_t power = WitnessAmount == 10 ? + 8 : + 8 + (WitnessAmount - 10) / 8 * 7; + std::vector coefficients; + coefficients.reserve(power + 1); + for (std::size_t i = 0; i < power + 1; i++) { + coefficients.emplace_back(generate_random()); + } + value_type theta = generate_random(); + test_pow_factor(coefficients, theta, power); +} + +constexpr static const std::size_t random_tests_amount = 20; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + pow_factor_tests(); + pow_factor_tests(); + pow_factor_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/flexible/swap.cpp b/libs/blueprint/test/verifiers/flexible/swap.cpp new file mode 100644 index 000000000..4bfb64cbe --- /dev/null +++ b/libs/blueprint/test/verifiers/flexible/swap.cpp @@ -0,0 +1,134 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_placeholder_flexible_swap_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_flexible_swap( + const std::array &input +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::flexible_swap; + + typename component_type::input_type instance_input; + instance_input.inp[0] = var(0, 0, false, var::column_type::public_input); + instance_input.inp[1] = var(0, 1, false, var::column_type::public_input); + instance_input.inp[2] = var(0, 2, false, var::column_type::public_input); + + std::vector public_input; + public_input.push_back(input[0]); + public_input.push_back(input[1]); + public_input.push_back(input[2]); + + auto result_check = [&public_input](AssignmentType &assignment, + typename component_type::result_type &real_res + ) { + if( public_input[0] == 0){ + BOOST_ASSERT(var_value(assignment, real_res.output[0]) == public_input[1]); + BOOST_ASSERT(var_value(assignment, real_res.output[1]) == public_input[2]); + } else { + BOOST_ASSERT(var_value(assignment, real_res.output[0]) == public_input[2]); + BOOST_ASSERT(var_value(assignment, real_res.output[1]) == public_input[1]); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG); +} + +template +void flexible_swap_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_flexible_swap({t_dist(seed_seq), generate_random(), generate_random()}); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + flexible_swap_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + flexible_swap_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + flexible_swap_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/base_field.cpp b/libs/blueprint/test/verifiers/kimchi/base_field.cpp new file mode 100644 index 000000000..cc7f92ede --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/base_field.cpp @@ -0,0 +1,320 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_base_field_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include +//#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_plonk_component.hpp" +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_base_field_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_base_field_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 25; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + using var_ec_point = typename zk::components::var_ec_point; + constexpr std::size_t Lambda = 40; + constexpr static const std::size_t batch_size = 1; + constexpr static const std::size_t lr_rounds = 1; + constexpr static const std::size_t comm_size = 1; + // constexpr static const std::size_t n_2 = ceil(log2(n)); + // constexpr static const std::size_t padding = (1 << n_2) - n; + + constexpr static std::size_t public_input_size = 1; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 1; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = zk::components::base_field; + + using commitment_type = + typename zk::components::kimchi_commitment_type; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + using var = zk::snark::plonk_variable; + + using binding = typename zk::components::binding; + + using verifier_index_type = zk::components::kimchi_verifier_index_base; + + using proof_type = zk::components::kimchi_proof_base; + + using kimchi_constants = zk::components::kimchi_inner_constants; + + // zk::snark::pickles_proof kimchi_proof = test_proof(); + + std::vector public_input; + std::vector shifted_var; + std::vector unshifted_var; + for (std::size_t i = 0; i < 14; i++) { + curve_type::template g1_type::value_type shifted = + algebra::random_element>(); + + public_input.push_back(shifted.X); + public_input.push_back(shifted.Y); + + shifted_var.push_back({var(0, i * 4, false, var::column_type::public_input), + var(0, i * 4 + 1, false, var::column_type::public_input)}); + + curve_type::template g1_type::value_type unshifted = + algebra::random_element>(); + + public_input.push_back(unshifted.X); + public_input.push_back(unshifted.Y); + + unshifted_var.push_back({var(0, i * 4 + 2, false, var::column_type::public_input), + var(0, i * 4 + 3, false, var::column_type::public_input)}); + } + std::array witness_comm; + for (std::size_t i = 0; i < witness_columns; i++) { + witness_comm[i] = {{unshifted_var[0]}}; + } + + std::array sigma_comm; + for (std::size_t i = 0; i < perm_size; i++) { + witness_comm[i] = {{unshifted_var[1]}}; + } + std::array + coefficient_comm; + for (std::size_t i = 0; i < coefficient_comm.size(); i++) { + coefficient_comm[i] = {{unshifted_var[2]}}; + } + std::vector oracles_poly_comm = { + {{unshifted_var[3]}}}; // to-do: get in the component from oracles + commitment_type lookup_runtime_comm = {{unshifted_var[4]}}; + commitment_type table_comm = {{unshifted_var[5]}}; + std::vector lookup_sorted_comm {{{unshifted_var[6]}}}; + std::vector lookup_selectors_comm = {{{unshifted_var[7]}}}; + std::vector selectors_comm = {{{unshifted_var[8]}}}; + commitment_type lookup_agg_comm = {{unshifted_var[9]}}; + commitment_type z_comm = {{unshifted_var[10]}}; + commitment_type t_comm = {{unshifted_var[11]}}; + commitment_type generic_comm = {{unshifted_var[12]}}; + commitment_type psm_comm = {{unshifted_var[13]}}; + + curve_type::template g1_type::value_type L = + algebra::random_element>(); + + public_input.push_back(L.X); + public_input.push_back(L.Y); + + var_ec_point L_var = {var(0, 56, false, var::column_type::public_input), + var(0, 57, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type R = + algebra::random_element>(); + + public_input.push_back(R.X); + public_input.push_back(R.Y); + + var_ec_point R_var = {var(0, 58, false, var::column_type::public_input), + var(0, 59, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type delta = + algebra::random_element>(); + + public_input.push_back(delta.X); + public_input.push_back(delta.Y); + + var_ec_point delta_var = {var(0, 60, false, var::column_type::public_input), + var(0, 61, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type G = + algebra::random_element>(); + + public_input.push_back(G.X); + public_input.push_back(G.Y); + + var_ec_point G_var = {var(0, 62, false, var::column_type::public_input), + var(0, 63, false, var::column_type::public_input)}; + + opening_proof_type o_var = {{L_var}, {R_var}, delta_var, G_var}; + + std::array scalars; + + std::array scalars_var; + + for (std::size_t i = 0; i < kimchi_constants::f_comm_msm_size; i++) { + scalars[i] = algebra::random_element(); + public_input.push_back(scalars[i]); + scalars_var[i] = var(0, 74 + i, false, var::column_type::public_input); + } + + curve_type::template g1_type::value_type lagrange_bases = + algebra::random_element>(); + + public_input.push_back(lagrange_bases.X); + public_input.push_back(lagrange_bases.Y); + + var_ec_point lagrange_bases_var = {var(0, 65, false, var::column_type::public_input), + var(0, 66, false, var::column_type::public_input)}; + + typename curve_type::base_field_type::value_type Pub = algebra::random_element(); + public_input.push_back(Pub); + var Pub_var = var(0, 67, false, var::column_type::public_input); + + typename curve_type::base_field_type::value_type zeta_to_srs_len = + algebra::random_element(); + public_input.push_back(zeta_to_srs_len); + var zeta_to_srs_len_var = var(0, 68, false, var::column_type::public_input); + + typename curve_type::base_field_type::value_type zeta_to_domain_size_minus_1 = + algebra::random_element(); + public_input.push_back(zeta_to_domain_size_minus_1); + var zeta_to_domain_size_minus_1_var = var(0, 69, false, var::column_type::public_input); + + curve_type::template g1_type::value_type H = + algebra::random_element>(); + + public_input.push_back(H.X); + public_input.push_back(H.Y); + + var_ec_point H_var = {var(0, 70, false, var::column_type::public_input), + var(0, 71, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type PI_G = + algebra::random_element>(); + + public_input.push_back(PI_G.X); + public_input.push_back(PI_G.Y); + + var_ec_point PI_G_var = {var(0, 72, false, var::column_type::public_input), + var(0, 73, false, var::column_type::public_input)}; + + constexpr static const std::size_t bases_size = kimchi_constants::final_msm_size(batch_size); + std::array batch_scalars; + + std::array batch_scalars_var; + + for (std::size_t i = 0; i < bases_size; i++) { + batch_scalars[i] = algebra::random_element(); + public_input.push_back(batch_scalars[i]); + batch_scalars_var[i] = var(0, 74 + i, false, var::column_type::public_input); + } + curve_type::base_field_type::value_type cip = algebra::random_element(); + + public_input.push_back(cip); + + var cip_var = var(0, 74 + bases_size, false, var::column_type::public_input); + + typename proof_type::commitments_type commitments = { + {witness_comm}, lookup_runtime_comm, table_comm, {lookup_sorted_comm}, lookup_agg_comm, z_comm, + t_comm, {oracles_poly_comm[0]} // to-do: get in the component from oracles + }; + /*zk::components::kimchi_transcript_fq transcript;*/ + proof_type proof_var = {/*transcript, */ commitments, o_var, {scalars_var}}; + verifier_index_type verifier_index = { + H_var, + {PI_G_var}, + {lagrange_bases_var}, + {{sigma_comm}, {coefficient_comm}, generic_comm, psm_comm, {selectors_comm}, {lookup_selectors_comm}, + psm_comm, // runtime_tables_selector + {psm_comm}, // table + psm_comm, // complete_add + psm_comm, // var_base_mmul + psm_comm, // endo_mul + psm_comm, // endo_mul_scalar + }}; + + typename binding::fr_data fr_data = { + batch_scalars_var, {cip_var}, {Pub_var}, zeta_to_srs_len_var, zeta_to_domain_size_minus_1_var}; + typename binding::fq_data fq_data; + + typename component_type::params_type params = {{proof_var}, verifier_index, fr_data, fq_data}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component( + params, public_input, result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/basic_verifier.cpp b/libs/blueprint/test/verifiers/kimchi/basic_verifier.cpp new file mode 100644 index 000000000..5499a95e9 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/basic_verifier.cpp @@ -0,0 +1,464 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_oracles_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_oracles_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + // ft_eval + public_input.push_back(algebra::random_element()); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_verifiers_kimchi_basic_verifier_test) { + + // PARAMS + using curve_type = algebra::curves::vesta; + using ScalarFieldType = typename curve_type::scalar_field_type; + using BaseFieldType = typename curve_type::base_field_type; + + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var_scalar = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static std::size_t batch_size = 2; + + constexpr static const std::size_t prev_chal_size = 1; + + constexpr static const std::size_t domain_size = 128; + + constexpr std::size_t WitnessColumnsScalar = 15; + constexpr std::size_t PublicInputColumnsScalar = 1; + constexpr std::size_t ConstantColumnsScalar = 1; + constexpr std::size_t SelectorColumnsScalar = 30; + + using ArithmetizationParamsScalar = + zk::snark::plonk_arithmetization_params; + using ArithmetizationTypeScalar = zk::snark::plonk_constraint_system; + using AssignmentTypeScalar = blueprint::assignment; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + using kimchi_constants = zk::components::kimchi_inner_constants; + + // COMMON DATA + constexpr static const std::size_t bases_size = kimchi_constants::final_msm_size(batch_size); + std::array batch_scalars; + std::array cips_shifted; + std::array pi; + ScalarFieldType::value_type zeta = 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + std::array zeta_to_srs_len; + ScalarFieldType::value_type zeta_to_domain_size_minus_1 = zeta.pow(domain_size) - 1; + + for (std::size_t i = 0; i < bases_size; i++) { + batch_scalars[i] = algebra::random_element(); + } + + for (std::size_t i = 0; i < batch_size; i++) { + cips_shifted[i] = algebra::random_element(); + zeta_to_srs_len[i] = zeta.pow(srs_len); + } + + // SCALAR FIELD + + using fq_output_type_scalar = + typename zk::components::binding::fq_sponge_output; + + using fr_data_type_scalar = typename zk::components::binding::fr_data; + + using fq_data_type_scalar = typename zk::components::binding::fq_data; + + zk::components::kimchi_verifier_index_scalar verifier_index_scalar; + typename ScalarFieldType::value_type omega = + 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + verifier_index_scalar.domain_size = domain_size; + verifier_index_scalar.omega = var_scalar(0, 0, false, var_scalar::column_type::public_input); + + using verify_scalar_component = + zk::components::verify_scalar; + + typename ScalarFieldType::value_type joint_combiner = 0; + typename ScalarFieldType::value_type beta = 0; + typename ScalarFieldType::value_type gamma = 0; + typename ScalarFieldType::value_type alpha = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename ScalarFieldType::value_type fq_digest = + 0x01D4E77CCD66755BDDFDBB6E4E8D8D17A6708B9CB56654D12070BD7BF4A5B33B_cppui_modular256; + + std::vector public_input_scalar = {omega}; + + std::array, batch_size> proofs; + + std::array fq_outputs; + + for (std::size_t batch_id = 0; batch_id < batch_size; batch_id++) { + zk::snark::pickles_proof kimchi_proof = test_proof(); + + zk::components::kimchi_proof_scalar proof; + + prepare_proof(kimchi_proof, proof, + public_input_scalar); + + fq_output_type_scalar fq_output; + std::array challenges; + for (std::size_t j = 0; j < eval_rounds; j++) { + public_input_scalar.emplace_back(10); + challenges[j] = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + } + fq_output.challenges = challenges; + + // joint_combiner + public_input_scalar.push_back(algebra::random_element()); + fq_output.joint_combiner = + var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // beta + public_input_scalar.push_back(algebra::random_element()); + fq_output.beta = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // gamma + public_input_scalar.push_back(algebra::random_element()); + fq_output.gamma = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // alpha + public_input_scalar.push_back(alpha); + fq_output.alpha = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // zeta + public_input_scalar.push_back(zeta); + fq_output.zeta = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // fq_digest + public_input_scalar.push_back(fq_digest); + fq_output.fq_digest = + var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + // c + public_input_scalar.emplace_back(250); + fq_output.c = var_scalar(0, public_input_scalar.size() - 1, false, var_scalar::column_type::public_input); + + fq_outputs[batch_id] = fq_output; + } + + fr_data_type_scalar fr_data_public; + fq_data_type_scalar fq_data_public; + + typename verify_scalar_component::params_type params_scalar = {fr_data_public, fq_data_public, + verifier_index_scalar, proofs, fq_outputs}; + + auto result_check_scalar = [](AssignmentTypeScalar &assignment, verify_scalar_component::result_type &real_res) {}; + + test_component( + params_scalar, public_input_scalar, result_check_scalar); + + ////////////////////////////////////////////////// + // BASE FIELD + ////////////////////////////////////////////////// + constexpr std::size_t WitnessColumnsBase = 15; + constexpr std::size_t PublicInputColumnsBase = 1; + constexpr std::size_t ConstantColumnsBase = 1; + constexpr std::size_t SelectorColumnsBase = 10; + + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + using var_ec_point = typename zk::components::var_ec_point; + + using verify_base_component = + zk::components::base_field; + + using shifted_commitment_type = + typename zk::components::kimchi_shifted_commitment_type; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + using var = zk::snark::plonk_variable; + + using binding = typename zk::components::binding; + + using verifier_index_type = zk::components::kimchi_verifier_index_base; + + using proof_type = zk::components::kimchi_proof_base; + + std::vector public_input; + std::vector shifted_var; + std::vector unshifted_var; + for (std::size_t i = 0; i < 14; i++) { + curve_type::template g1_type::value_type shifted = + algebra::random_element>(); + + public_input.push_back(shifted.X); + public_input.push_back(shifted.Y); + + shifted_var.push_back({var(0, i * 4, false, var::column_type::public_input), + var(0, i * 4 + 1, false, var::column_type::public_input)}); + + curve_type::template g1_type::value_type unshifted = + algebra::random_element>(); + + public_input.push_back(unshifted.X); + public_input.push_back(unshifted.Y); + + unshifted_var.push_back({var(0, i * 4 + 2, false, var::column_type::public_input), + var(0, i * 4 + 3, false, var::column_type::public_input)}); + } + std::array witness_comm; + for (std::size_t i = 0; i < witness_columns; i++) { + witness_comm[i] = {{shifted_var[0]}, {unshifted_var[0]}}; + } + + std::array sigma_comm; + for (std::size_t i = 0; i < perm_size; i++) { + witness_comm[i] = {{shifted_var[1]}, {unshifted_var[1]}}; + } + std::vector coefficient_comm = {{{shifted_var[2]}, {unshifted_var[2]}}}; + std::vector oracles_poly_comm = { + {{shifted_var[3]}, {unshifted_var[3]}}}; // to-do: get in the component from oracles + shifted_commitment_type lookup_runtime_comm = {{shifted_var[4]}, {unshifted_var[4]}}; + shifted_commitment_type table_comm = {{shifted_var[5]}, {unshifted_var[5]}}; + std::vector lookup_sorted_comm {{{shifted_var[6]}, {unshifted_var[6]}}}; + std::vector lookup_selectors_comm = {{{shifted_var[7]}, {unshifted_var[7]}}}; + std::vector selectors_comm = {{{shifted_var[8]}, {unshifted_var[8]}}}; + shifted_commitment_type lookup_agg_comm = {{shifted_var[9]}, {unshifted_var[9]}}; + shifted_commitment_type z_comm = {{shifted_var[10]}, {unshifted_var[10]}}; + shifted_commitment_type t_comm = {{shifted_var[11]}, {unshifted_var[11]}}; + shifted_commitment_type generic_comm = {{shifted_var[12]}, {unshifted_var[12]}}; + shifted_commitment_type psm_comm = {{shifted_var[13]}, {unshifted_var[13]}}; + + curve_type::template g1_type::value_type L = + algebra::random_element>(); + + public_input.push_back(L.X); + public_input.push_back(L.Y); + + var_ec_point L_var = {var(0, 56, false, var::column_type::public_input), + var(0, 57, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type R = + algebra::random_element>(); + + public_input.push_back(R.X); + public_input.push_back(R.Y); + + var_ec_point R_var = {var(0, 58, false, var::column_type::public_input), + var(0, 59, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type delta = + algebra::random_element>(); + + public_input.push_back(delta.X); + public_input.push_back(delta.Y); + + var_ec_point delta_var = {var(0, 60, false, var::column_type::public_input), + var(0, 61, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type G = + algebra::random_element>(); + + public_input.push_back(G.X); + public_input.push_back(G.Y); + + var_ec_point G_var = {var(0, 62, false, var::column_type::public_input), + var(0, 63, false, var::column_type::public_input)}; + + opening_proof_type o_var = {{L_var}, {R_var}, delta_var, G_var}; + + std::array scalars; + + std::array scalars_var; + + for (std::size_t i = 0; i < kimchi_constants::f_comm_msm_size; i++) { + scalars[i] = algebra::random_element(); + public_input.push_back(scalars[i]); + scalars_var[i] = var(0, 74 + i, false, var::column_type::public_input); + } + + curve_type::template g1_type::value_type lagrange_bases = + algebra::random_element>(); + + public_input.push_back(lagrange_bases.X); + public_input.push_back(lagrange_bases.Y); + + var_ec_point lagrange_bases_var = {var(0, 65, false, var::column_type::public_input), + var(0, 66, false, var::column_type::public_input)}; + + std::array Pub; + std::array Pub_var; + for (std::size_t i = 0; i < public_input_size; i++) { + Pub[i] = typename BaseFieldType::value_type(typename BaseFieldType::integral_type(pi[i].data)); + public_input.push_back(Pub[i]); + Pub_var[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array zeta_to_srs_len_var; + for (std::size_t i = 0; i < batch_size; i++) { + public_input.emplace_back(typename BaseFieldType::integral_type(zeta_to_srs_len[i].data)); + zeta_to_srs_len_var[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + public_input.emplace_back(typename BaseFieldType::integral_type(zeta_to_domain_size_minus_1.data)); + var zeta_to_domain_size_minus_1_var = var(0, public_input.size() - 1, false, var::column_type::public_input); + + curve_type::template g1_type::value_type H = + algebra::random_element>(); + + public_input.push_back(H.X); + public_input.push_back(H.Y); + + var_ec_point H_var = {var(0, public_input.size() - 1, false, var::column_type::public_input), + var(0, public_input.size() - 1, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type PI_G = + algebra::random_element>(); + + public_input.push_back(PI_G.X); + public_input.push_back(PI_G.Y); + + var_ec_point PI_G_var = {var(0, public_input.size() - 1, false, var::column_type::public_input), + var(0, public_input.size() - 1, false, var::column_type::public_input)}; + + std::array batch_scalars_var; + + for (std::size_t i = 0; i < bases_size; i++) { + public_input.emplace_back(typename BaseFieldType::integral_type(batch_scalars[i].data)); + batch_scalars_var[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + curve_type::base_field_type::value_type cip = algebra::random_element(); + + public_input.push_back(cip); + + var cip_var = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename proof_type::commitments_type commitments = { + {witness_comm}, lookup_runtime_comm, table_comm, {lookup_sorted_comm}, lookup_agg_comm, z_comm, + t_comm, {oracles_poly_comm[0]} // to-do: get in the component from oracles + }; + + proof_type proof_var = {commitments, o_var, {scalars_var}}; + verifier_index_type verifier_index = { + H_var, + {PI_G_var}, + {lagrange_bases_var}, + {{sigma_comm}, {coefficient_comm}, generic_comm, psm_comm, {selectors_comm}, {lookup_selectors_comm}}}; + + typename binding::fr_data fr_data = { + batch_scalars_var, {cip_var}, {Pub_var[0]}, zeta_to_srs_len_var[0], zeta_to_domain_size_minus_1_var}; + typename binding::fq_data fq_data; + + typename verify_base_component::params_type params = {{proof_var}, verifier_index, fr_data, fq_data}; + + auto result_check = [](AssignmentType &assignment, verify_base_component::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/basic_verifier_types.hpp b/libs/blueprint/test/verifiers/kimchi/basic_verifier_types.hpp new file mode 100644 index 000000000..25af8820a --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/basic_verifier_types.hpp @@ -0,0 +1,141 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PICKLES_VERIFIER_PROOF_TYPES_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PICKLES_VERIFIER_PROOF_TYPES_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace nil::crypto3; + +using curve_type = algebra::curves::vesta; +using FpType = typename curve_type::base_field_type; +using FrType = typename curve_type::scalar_field_type; + +template +struct proof_generator_result_type { + using ArithmetizationType = zk::snark::plonk_constraint_system; + using params = zk::snark::placeholder_params; + using types = zk::snark::detail::placeholder_policy; + + using fri_type = typename zk::commitments::fri; + + ProofType placeholder_proof; + typename fri_type::params_type fri_params; + zk::blueprint bp; + typename types::preprocessed_public_data_type public_preprocessed_data; + typename curve_type::scalar_field_type::integral_type out; +}; + +//////////// BASE ///////////////////////////// +constexpr std::size_t WitnessColumnsBase = 15; +constexpr std::size_t PublicInputColumnsBase = 1; +constexpr std::size_t ConstantColumnsBase = 0; +constexpr std::size_t SelectorColumnsBase = 1; + +using ArithmetizationParamsBase = zk::snark::plonk_arithmetization_params; +using ArithmetizationTypeBase = zk::snark::plonk_constraint_system; + +using params_base = zk::snark::placeholder_params; +using types_base = zk::snark::detail::placeholder_policy; + +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_witness_type_base; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_permutation_type_base; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_quotient_type_base; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_public_input_type_base; + +using proof_type_base = zk::snark::placeholder_proof; + +using proof_generator_result_type_base = proof_generator_result_type; + +//////////// SCALAR /////////////////////////// +constexpr std::size_t WitnessColumnsScalar = 15; +constexpr std::size_t PublicInputColumnsScalar = 1; +constexpr std::size_t ConstantColumnsScalar = 3; +constexpr std::size_t SelectorColumnsScalar = 11; + +using ArithmetizationParamsScalar = zk::snark::plonk_arithmetization_params; +using ArithmetizationTypeScalar = zk::snark::plonk_constraint_system; + +using params_scalar = zk::snark::placeholder_params; +using types_scalar = zk::snark::detail::placeholder_policy; + +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_witness_type_scalar; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_permutation_type_scalar; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_quotient_type_scalar; +typedef zk::commitments::list_polynomial_commitment + commitment_scheme_public_input_type_scalar; + +using proof_type_scalar = zk::snark::placeholder_proof; + +using proof_generator_result_type_scalar = proof_generator_result_type; + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PICKLES_VERIFIER_PROOF_TYPES_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/batch_verify_base_field.cpp b/libs/blueprint/test/verifiers/kimchi/batch_verify_base_field.cpp new file mode 100644 index 000000000..e4e23773e --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/batch_verify_base_field.cpp @@ -0,0 +1,256 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_batch_verify_base_field_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include +//#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_batch_verify_base_field_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_batch_verify_base_field_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 25; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + constexpr static const std::size_t batch_size = 1; + constexpr static const std::size_t eval_rounds = 1; + constexpr static const std::size_t comm_size = 1; + // constexpr static const std::size_t n_2 = ceil(log2(n)); + // constexpr static const std::size_t padding = (1 << n_2) - n; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + + constexpr static std::size_t witness_columns = 5; + constexpr static std::size_t perm_size = 5; + + constexpr static std::size_t srs_len = 1; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + using kimchi_constants = zk::components::kimchi_inner_constants; + + constexpr static const std::size_t bases_size = kimchi_constants::final_msm_size(batch_size); + + using component_type = zk::components::batch_verify_base_field; + + using opening_proof_type = + typename zk::components::kimchi_opening_proof_base; + using commitment_type = + typename zk::components::kimchi_commitment_type; + + // using transcript_type = kimchi_transcript_fq; + + using binding = typename zk::components::binding; + + using var_ec_point = typename zk::components::var_ec_point; + using var = zk::snark::plonk_variable; + + using batch_proof_type = typename zk::components:: + batch_evaluation_proof_base; + + // zk::snark::pickles_proof kimchi_proof = test_proof(); + + std::vector public_input; + + curve_type::template g1_type::value_type shifted = + algebra::random_element>(); + + public_input.push_back(shifted.X); + public_input.push_back(shifted.Y); + + var_ec_point shifted_var = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type unshifted = + algebra::random_element>(); + + public_input.push_back(unshifted.X); + public_input.push_back(unshifted.Y); + + var_ec_point unshifted_var = {var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input)}; + + curve_type::base_field_type::value_type f_zeta = algebra::random_element(); + + public_input.push_back(f_zeta); + + var f_zeta_var = var(0, 4, false, var::column_type::public_input); + + curve_type::base_field_type::value_type f_zeta_w = algebra::random_element(); + + public_input.push_back(f_zeta_w); + + var f_zeta_w_var = var(0, 5, false, var::column_type::public_input); + + curve_type::template g1_type::value_type L = + algebra::random_element>(); + + public_input.push_back(L.X); + public_input.push_back(L.Y); + + var_ec_point L_var = {var(0, 6, false, var::column_type::public_input), + var(0, 7, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type R = + algebra::random_element>(); + + public_input.push_back(R.X); + public_input.push_back(R.Y); + + var_ec_point R_var = {var(0, 8, false, var::column_type::public_input), + var(0, 9, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type delta = + algebra::random_element>(); + + public_input.push_back(delta.X); + public_input.push_back(delta.Y); + + var_ec_point delta_var = {var(0, 10, false, var::column_type::public_input), + var(0, 11, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type G = + algebra::random_element>(); + + public_input.push_back(G.X); + public_input.push_back(G.Y); + + var_ec_point G_var = {var(0, 12, false, var::column_type::public_input), + var(0, 13, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type H = + algebra::random_element>(); + + public_input.push_back(H.X); + public_input.push_back(H.Y); + + var_ec_point H_var = {var(0, 20, false, var::column_type::public_input), + var(0, 21, false, var::column_type::public_input)}; + + curve_type::template g1_type::value_type PI_G = + algebra::random_element>(); + + public_input.push_back(PI_G.X); + public_input.push_back(PI_G.Y); + + var_ec_point PI_G_var = {var(0, 22, false, var::column_type::public_input), + var(0, 23, false, var::column_type::public_input)}; + + std::array scalars; + + std::array scalars_var; + + for (std::size_t i = 0; i < bases_size; i++) { + scalars[i] = algebra::random_element(); + public_input.push_back(scalars[i]); + scalars_var[i] = var(0, 24 + i, false, var::column_type::public_input); + } + curve_type::base_field_type::value_type cip = algebra::random_element(); + + public_input.push_back(cip); + + var cip_var = var(0, 24 + bases_size, false, var::column_type::public_input); + + commitment_type comm_var = {{unshifted_var}}; + + opening_proof_type o_var = {{L_var}, {R_var}, delta_var, G_var}; + //transcript_type transcript; + + typename binding::fr_data fr_data = {scalars_var, {cip_var}}; + + std::array prepared_proofs = {{{{comm_var}, o_var}}}; + + typename component_type::params_type params = {prepared_proofs, {H_var, {PI_G_var}, {PI_G_var}}, fr_data}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component( + params, public_input, result_check); +}; +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/batch_verify_scalar_field.cpp b/libs/blueprint/test/verifiers/kimchi/batch_verify_scalar_field.cpp new file mode 100644 index 000000000..547e832ac --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/batch_verify_scalar_field.cpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_verifier_scalar_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_batch_verifier_scalar_field_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + // ft_eval + public_input.push_back(algebra::random_element()); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_batch_verifier_scalar_field_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 3; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr std::size_t srs_len = 5; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + zk::components::kimchi_verifier_index_scalar verifier_index; + typename BlueprintFieldType::value_type zeta = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega = + 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + // verifier_index.zkpm = {0x2C46205451F6C3BBEA4BABACBEE609ECF1039A903C42BFF639EDC5BA33356332_cppui_modular256, + // 0x1764D9CB4C64EBA9A150920807637D458919CB6948821F4D15EB1994EADF9CE3_cppui_modular256, + // 0x0140117C8BBC4CE4644A58F7007148577782213065BB9699BF5C391FBE1B3E6D_cppui_modular256, + // 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular256}; + std::size_t domain_size = 128; + verifier_index.domain_size = domain_size; + verifier_index.omega = var(0, 6, false, var::column_type::public_input); + + constexpr std::size_t batch_size = 2; + + using component_type = zk::components::batch_verify_scalar_field; + + zk::snark::pickles_proof kimchi_proof = test_proof(); + + typename BlueprintFieldType::value_type joint_combiner = 0; + typename BlueprintFieldType::value_type beta = 0; + typename BlueprintFieldType::value_type gamma = 0; + typename BlueprintFieldType::value_type alpha = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type fq_digest = + 0x01D4E77CCD66755BDDFDBB6E4E8D8D17A6708B9CB56654D12070BD7BF4A5B33B_cppui_modular256; + + zk::components::kimchi_proof_scalar proof; + std::array challenges; + + std::vector public_input = {}; + + std::array< + zk::components:: + batch_evaluation_proof_scalar, + batch_size> + batches; + + for (std::size_t i = 0; i < batch_size; i++) { + typename BlueprintFieldType::value_type cip = 12; + public_input.push_back(cip); + batches[i].cip = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename zk::components::binding::fq_sponge_output + fq_output; + + std::array challenges; + for (std::size_t j = 0; j < eval_rounds; j++) { + public_input.emplace_back(10); + challenges[j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + fq_output.challenges = challenges; + + // joint_combiner + public_input.push_back(algebra::random_element()); + fq_output.joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + // beta + public_input.push_back(algebra::random_element()); + fq_output.beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + // gamma + public_input.push_back(algebra::random_element()); + fq_output.gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + // alpha + public_input.push_back(algebra::random_element()); + fq_output.alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + // zeta + public_input.push_back(algebra::random_element()); + fq_output.zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + // fq_digest + public_input.push_back(algebra::random_element()); + fq_output.fq_digest = var(0, public_input.size() - 1, false, var::column_type::public_input); + // c + public_input.emplace_back(250); + fq_output.c = var(0, public_input.size() - 1, false, var::column_type::public_input); + + batches[i].fq_output = fq_output; + + public_input.push_back(zeta); + public_input.push_back(zeta * omega); + batches[i].eval_points = {var(0, public_input.size() - 2, false, var::column_type::public_input), + var(0, public_input.size() - 1, false, var::column_type::public_input)}; + + public_input.push_back(algebra::random_element()); + batches[i].r = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(algebra::random_element()); + public_input.push_back(algebra::random_element()); + batches[i].opening = {var(0, public_input.size() - 2, false, var::column_type::public_input), + var(0, public_input.size() - 1, false, var::column_type::public_input)}; + } + + typename component_type::params_type params = {batches}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component( + params, public_input, result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/demo_verifier.cpp b/libs/blueprint/test/verifiers/kimchi/demo_verifier.cpp new file mode 100644 index 000000000..e42b8d4e6 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/demo_verifier.cpp @@ -0,0 +1,209 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_basic_verifier_test + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "proof_data.hpp" + +using namespace nil::crypto3; + +template +void print_byteblob(std::ostream &os, TIter iter_begin, TIter iter_end) { + os << std::hex; + for (TIter it = iter_begin; it != iter_end; it++) { + os << std::setfill('0') << std::setw(2) << std::right << int(*it); + } + os << std::endl << std::dec; +} + +inline std::vector generate_random_step_list(const std::size_t r, const int max_step) { + using dist_type = std::uniform_int_distribution; + static std::random_device random_engine; + + std::vector step_list; + std::size_t steps_sum = 0; + while (steps_sum != r) { + if (r - steps_sum <= max_step) { + while (r - steps_sum != 1) { + step_list.emplace_back(r - steps_sum - 1); + steps_sum += step_list.back(); + } + step_list.emplace_back(1); + steps_sum += step_list.back(); + } else { + step_list.emplace_back(dist_type(1, max_step)(random_engine)); + steps_sum += step_list.back(); + } + } + return step_list; +} + +template +typename fri_type::params_type create_fri_params(std::size_t degree_log, const int max_step = 1) { + typename fri_type::params_type params; + math::polynomial q = {0, 0, 1}; + + constexpr std::size_t expand_factor = 0; + std::size_t r = degree_log - 1; + + std::vector>> domain_set = + math::calculate_domain_set(degree_log + expand_factor, r); + + params.r = r; + params.D = domain_set; + params.max_degree = (1 << degree_log) - 1; + params.step_list = generate_random_step_list(r, max_step); + + return params; +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_demo_verifier_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_demo_verifier_test) { + constexpr std::size_t complexity = 8000; + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + constexpr std::size_t WitnessColumns = 11; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 1; + + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + + nil::crypto3::zk::snark::pickles_proof kimchi_proof = test_proof(); + + using component_type = zk::components::curve_element_unified_addition; + using var = zk::snark::plonk_variable; + + auto P = kimchi_proof.commitments.w_comm[0].unshifted[0]; + auto Q = kimchi_proof.commitments.w_comm[1].unshifted[0]; + std::vector public_input = {P.X, P.Y, Q.X, Q.Y}; + + auto expected_result = P + Q; + std::cout << "exprected result: (" << expected_result.X.data << ", " << expected_result.Y.data << ")" << std::endl; + + zk::snark::plonk_table_description desc; + + zk::blueprint bp(desc); + zk::blueprint_private_assignment_table private_assignment(desc); + zk::blueprint_public_assignment_table public_assignment(desc); + blueprint::assignment assignment_bp(private_assignment, public_assignment); + + std::size_t start_row = zk::components::allocate(bp, complexity); + + std::vector result(complexity); + + bp.allocate_rows(public_input.size()); + component_type::params_type component_params = { + {assignment_bp.allocate_public_input(public_input[0]), assignment_bp.allocate_public_input(public_input[1])}, + {assignment_bp.allocate_public_input(public_input[2]), assignment_bp.allocate_public_input(public_input[3])}}; + + for (std::size_t i = 0; i < complexity; i++) { + + std::size_t row = start_row + i * component_type::rows_amount; + result[i] = component_type::result_type(component_params, row); + + zk::components::generate_circuit(bp, public_assignment, component_params, row); + + component_type::generate_assignments(assignment_bp, component_params, row); + } + + // std::cout << "actual result: " << std::endl; + // for (std::size_t i = 0; i < complexity; i++) { + // std::cout << "(" << assignment_bp.var_value(result[i].X).data << ", " + // << assignment_bp.var_value(result[i].Y).data << ")" << std::endl; + // } + + assignment_bp.padding(); + + zk::snark::plonk_assignment_table assignments(private_assignment, + public_assignment); + + // profiling(assignments); + using params = + zk::snark::placeholder_params; + + using fri_type = typename zk::commitments::fri; + + std::size_t table_rows_log = std::ceil(std::log2(desc.rows_amount)); + + typename fri_type::params_type fri_params = create_fri_params(table_rows_log); + + std::size_t permutation_size = desc.witness_columns + desc.public_input_columns + desc.constant_columns; + + typename zk::snark::placeholder_public_preprocessor::preprocessed_data_type + public_preprocessed_data = zk::snark::placeholder_public_preprocessor::process( + bp, public_assignment, desc, fri_params, permutation_size); + typename zk::snark::placeholder_private_preprocessor::preprocessed_data_type + private_preprocessed_data = zk::snark::placeholder_private_preprocessor::process( + bp, private_assignment, desc, fri_params); + + auto placeholder_proof = zk::snark::placeholder_prover::process( + public_preprocessed_data, private_preprocessed_data, desc, bp, assignments, fri_params); + + bool verifier_res = zk::snark::placeholder_verifier::process( + public_preprocessed_data, placeholder_proof, bp, fri_params); + std::cout << "Proof check: " << verifier_res << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/detail/b_poly.cpp b/libs/blueprint/test/verifiers/kimchi/detail/b_poly.cpp new file mode 100644 index 000000000..3b90a4f74 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/b_poly.cpp @@ -0,0 +1,110 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_b_poly_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_b_poly) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::b_poly; + + var one(0, 0, false, var::column_type::public_input); + var zeta(0, 1, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = algebra::random_element(); + + std::vector public_input = {1, zeta_value}; + + std::array challenges; + std::array challenges_values; + for (std::size_t i = 0; i < n; i++) { + challenges_values[i] = algebra::random_element(); + public_input.push_back(challenges_values[i]); + challenges[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename component_type::params_type params = {challenges, zeta, one}; + + std::array powers_twos; + powers_twos[0] = zeta_value; + for (std::size_t i = 1; i < n; i++) { + powers_twos[i] = powers_twos[i - 1] * powers_twos[i - 1]; + } + + typename BlueprintFieldType::value_type expected_result = 1; + for (std::size_t i = 0; i < n; i++) { + typename BlueprintFieldType::value_type term = 1 + challenges_values[i] * powers_twos[n - 1 - i]; + expected_result = expected_result * term; + } + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "b_poly_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/b_poly_coefficients.cpp b/libs/blueprint/test/verifiers/kimchi/detail/b_poly_coefficients.cpp new file mode 100644 index 000000000..044b05b9b --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/b_poly_coefficients.cpp @@ -0,0 +1,105 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_b_poly_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_b_poly_coefficients) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + constexpr std::size_t n = 3; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::b_poly_coefficients; + + var one(0, 0, false, var::column_type::public_input); + var zeta(0, 1, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = algebra::random_element(); + + std::vector public_input = {1, zeta_value}; + + std::array challenges; + std::array challenges_values = {1, 2, 5}; + for (std::size_t i = 0; i < n; i++) { + public_input.push_back(challenges_values[i]); + challenges[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename component_type::params_type params = {challenges, one}; + + constexpr std::size_t expected_result_size = 1 << n; + std::array expected_result = { + 1, 5, 2, 10, 1, 5, 2, 10 + }; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < expected_result.size(); i++) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, + result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "b_poly__coefficients_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/combine_proof_evals.cpp b/libs/blueprint/test/verifiers/kimchi/detail/combine_proof_evals.cpp new file mode 100644 index 000000000..23a12c505 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/combine_proof_evals.cpp @@ -0,0 +1,200 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_ft_eval_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include "verifiers/kimchi/index_terms_instances/chacha_test.hpp" + +#include "test_plonk_component.hpp" +#include "../proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_combine_proof_evals_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + for (std::size_t i = 0; i < KimchiParamsType::circuit_params::lookup_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].lookup.sorted[i]); + circuit_proof.proof_evals[point_idx].lookup.sorted[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + public_input.push_back(original_proof.evals[point_idx].lookup.aggreg); + circuit_proof.proof_evals[point_idx].lookup.aggreg = + var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(original_proof.evals[point_idx].lookup.table); + circuit_proof.proof_evals[point_idx].lookup.table = + var(0, public_input.size() - 1, false, var::column_type::public_input); + + if (KimchiParamsType::circuit_params::lookup_runtime) { + public_input.push_back(original_proof.evals[point_idx].lookup.runtime); + circuit_proof.proof_evals[point_idx].lookup.runtime = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_combine_proof_evals_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + constexpr static std::size_t srs_len = 10; + + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_chacha_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = zk::components::combine_proof_evals; + + zk::snark::pickles_proof kimchi_proof = test_proof_chacha(); + + typename BlueprintFieldType::value_type zeta_value = + 0x3CE960ABCAC273BBEEBA92D1EF87514B51187BFE5E8797B5DE97B01FF7C64484_cppui_modular256; + + std::vector public_input = {zeta_value, 1, 0}; + + var zeta(0, 0, false, var::column_type::public_input); + var one(0, 1, false, var::column_type::public_input); + var zero(0, 2, false, var::column_type::public_input); + + zk::components::kimchi_proof_scalar proof; + + prepare_proof(kimchi_proof, proof, public_input); + + typename component_type::params_type params = {proof.proof_evals[0], zeta}; + + auto result_check = [&kimchi_proof, &zeta_value](AssignmentType &assignment, + component_type::result_type &real_res) { + // w + for (std::size_t i = 0; i < kimchi_proof.evals[0].w.size(); i++) { + assert(kimchi_proof.evals[0].w[i] * zeta_value == assignment.var_value(real_res.output.w[i])); + } + // z + assert(kimchi_proof.evals[0].z * zeta_value == assignment.var_value(real_res.output.z)); + // s + for (std::size_t i = 0; i < kimchi_proof.evals[0].s.size(); i++) { + assert(kimchi_proof.evals[0].s[i] * zeta_value == assignment.var_value(real_res.output.s[i])); + } + // lookup + if (kimchi_params::use_lookup) { + for (std::size_t i = 0; i < kimchi_proof.evals[0].lookup.sorted.size(); i++) { + assert(kimchi_proof.evals[0].lookup.sorted[i] * zeta_value == assignment.var_value(real_res.output.lookup.sorted[i])); + } + assert(kimchi_proof.evals[0].lookup.aggreg * zeta_value == assignment.var_value(real_res.output.lookup.aggreg)); + assert(kimchi_proof.evals[0].lookup.table * zeta_value == assignment.var_value(real_res.output.lookup.table)); + if (kimchi_params::circuit_params::lookup_runtime) { + assert(kimchi_proof.evals[0].lookup.runtime * zeta_value == assignment.var_value(real_res.output.lookup.runtime)); + } + } + // generic_selector + assert(kimchi_proof.evals[0].generic_selector * zeta_value == assignment.var_value(real_res.output.generic_selector)); + // poseidon_selector + assert(kimchi_proof.evals[0].poseidon_selector * zeta_value == assignment.var_value(real_res.output.poseidon_selector)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/generic_scalars.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/generic_scalars.cpp new file mode 100644 index 000000000..4e00a6f21 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/generic_scalars.cpp @@ -0,0 +1,232 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_detail_constraints_generic_scalars_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_detail_constraints_generic_scalars_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_constraints_generic_scalars_generic_input_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::generic_scalars; + + // params: + // std::array, + // KimchiParamsType::eval_points_amount> evals; + // std::array alphas; + // std::size_t start_idx; + + std::array + eval0_w = { + 0x2A016E5F91F6C33552FC86A7A88C034E5CF1301E4982545A15AB709ECE150E09_cppui_modular256, + 0x1D1B8F16A3DF52F90BA4856A11D397FDD175DEBBDC84F9D9FD71C46E5CB311CC_cppui_modular256, + 0x034EB5403A85D023EFD87CE7E37CD027921DBC8F5A59C6338A12965B2E1D2D9B_cppui_modular256, + 0x3D708DC37C2985BD9E8F39D40FFB35A8C1D1D4106AC17CEA1668C944007FE789_cppui_modular256, + 0x23ACD65BE15FC86FB7FC4FD45701CF7F348C13DD6DFF400DF808BE97006E06C8_cppui_modular256, + 0x3F6D999688174F2CC9C37FBFDA241825F50505E56EF660E29FBC52F06008F2EF_cppui_modular256, + 0x054FF59489820600DB800466FDF3063D387CAFC5464741417FAD07A00E2B558A_cppui_modular256, + 0x078062AB9E022D286A47F28A6A57C368598416D076C558A8288A05AD9A1451DE_cppui_modular256, + 0x09B0CFC2B282544FF90FE0ADD6BC80937A8B7DDBA743700ED16703BB25FD4E32_cppui_modular256, + 0x0BE13CD9C7027B7787D7CED143213DBE9B92E4E6D7C187757A4401C8B1E64A86_cppui_modular256, + 0x0E11A9F0DB82A29F169FBCF4AF85FAE9BC9A4BF2083F9EDC2320FFD63DCF46DA_cppui_modular256, + 0x10421707F002C9C6A567AB181BEAB814DDA1B2FD38BDB642CBFDFDE3C9B8432E_cppui_modular256, + 0x1272841F0482F0EE342F993B884F753FFEA91A08693BCDA974DAFBF155A13F82_cppui_modular256, + 0x14A2F13619031815C2F7875EF4B4326B1FB0811399B9E5101DB7F9FEE18A3BD6_cppui_modular256, + 0x16D35E4D2D833F3D51BF75826118EF9640B7E81ECA37FC76C694F80C6D73382A_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval0_z = 0x38C5D08C61572A0F233A3732575F3A07AD484107EC7366FEB0903FCC30253C1A_cppui_modular256; + + std::array eval0_s = { + 0x01751A5CCC6A9B9BDF660296AF5F7C80229DC97F3646FFC3729D827E80DF39DF_cppui_modular256, + 0x264CBA1EFD870553869EC32E652FE2FC5DB4DF0C8B8550816F1947F66858B238_cppui_modular256, + 0x21D7D3F53426BA3024217C852D5B1944031F52E784E95CA0539A9F88FB3F3FBE_cppui_modular256, + 0x260E6148F06FA79CD3C8C4A379955A8823017E730AD3624A578304A44B5113AC_cppui_modular256, + 0x2E901B006A7D080B6566A472AC9DEA73BB53A57A190B1A21ECEB698A869374BA_cppui_modular256, + 0x2E70F1D4AE3E1DE24337D33C4F61C88A628368CA7FBE9BF67C16F0944C64B7DE_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval_generic_selector = 0x2C1E20B5D662CE38070228313FD0D968116779CC3CD2FFF662707412EEBD04C7_cppui_modular256; + + std::array + eval1_w = { + 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval1_z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + + std::array eval1_s = { + 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256, + }; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + + typename BlueprintFieldType::value_type alpha_val = + 0x220B73274823F8B0E46273FA8546B238F1E58529E061A811AB584A97C146AF87_cppui_modular256; + + std::array + expected_result ={ 0x3EA2D6B15BFDB5C984671ECF5BC14CB50F04F1DADECEA0CBD36C92B61F15312E_cppui_modular256, + 0x35E37366760C3D2A9C95BFEE8BE746548D84ED80E7C88ED535B5BEF0518EC54B_cppui_modular256, + 0x1A47E6230B00888729DEB23939B141C3A032851A8D06D8821CBA9388367D6577_cppui_modular256, + 0x3D2FFD65B4D7E78F53072A50E814894D76580CF03F8DF994097C3FD3179D53FE_cppui_modular256, + 0x2C1E20B5D662CE38070228313FD0D968116779CC3CD2FFF662707412EEBD04C7_cppui_modular256, + 0x19D709063B3A3E32EDB448F15674D93910478C69E394BC85A73128501155BCB0_cppui_modular256, + 0x37F4062FEFF15FFAE25149ADE7DFE424E2FC8330DF09FF8B4416BAF093464667_cppui_modular256, + 0x124D8A07FB6BFD8E8E61FF9BD400C12067B5A8D214D4DFCA30206A763D088B4C_cppui_modular256, + 0x0C400758CECEEAE5A929CA341595ADA15AA0C8C582CDF3A424CCBFE0C92DE235_cppui_modular256, + 0x0EF41780273377126FC396C87B03CA6AB9488BA955C5C2EC2253F6A9459C1A9E_cppui_modular256 + }; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + + public_input.push_back(eval_generic_selector); + var generic_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].generic_selector = generic_selector; + } + + std::array alpha_powers; + for (std::size_t i = 0; i < circuit_description::alpha_powers_n; i++) { + public_input.push_back(alpha_val.pow(i)); + alpha_powers[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename component_type::params_type params = { + evals, alpha_powers, 0}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < expected_result.size(); i++) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/index_terms_scalars.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/index_terms_scalars.cpp new file mode 100644 index 000000000..05f68518e --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/index_terms_scalars.cpp @@ -0,0 +1,508 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_detail_index_terms_scalar_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include "verifiers/kimchi/index_terms_instances/lookup_test.hpp" + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_detail_index_terms_scalar_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_index_terms_scalar_ec_test_suite) { + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 0; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::index_terms_scalars; + + std::array + eval0_w = { + 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256, + 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256, + 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256, + 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256, + 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256, + 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256, + 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256, + 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256, + 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256, + 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256, + 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256, + 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256, + 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256, + 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256, + 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval0_z = 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256; + + std::array eval0_s = { + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256, + 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256, + 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256, + 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256, + 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256, + 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256 + }; + + std::array + eval1_w = { + 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval1_z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + + std::array eval1_s = { + 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256, + }; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + + typename BlueprintFieldType::value_type alpha_val = + 0x322D5D64C86AFB168AC57D2D8AB3512647B4802C8DC4DE07DB2C51E094C4D9B7_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x000000000000000000000000000000005D27C70754796C79C9D9958673CF2ABA_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x00000000000000000000000000000000C2278ADB337FA07CDFB689C4651FFD6D_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256; + + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + + std::array expected_result = { + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x017EEEF7695889AFB5311D7B36B31455AFF02B103BDA9BABF5BC29107B8F3AB7_cppui_modular256, // varBaseMul + 0x259D030170979C4754D0CEBF9E6AE529563BEB3A27C7003F57CCD4F80F875E4B_cppui_modular256, // endoMul + 0x0F297E2FA4E61DD377911C6B14C03F5CABC1114813C5D5C4CDCBDFBE84C526DB_cppui_modular256, // endoMulScalar + 0x0EF5278F0AD55CDE149D4E396A01E9B72A0D73FB4CF033C570B1B7E0C24C5FCE_cppui_modular256, // completeAdd + }; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + + evals[i].poseidon_selector = zero; + evals[i].generic_selector = zero; + } + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + var joint_combiner = zero; + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = { + zeta, alpha, beta, gamma, joint_combiner, + evals, omega, domain_size}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < expected_result.size(); ++i) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_index_terms_scalar_lookup_test_suite) { + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 0; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_lookup_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::index_terms_scalars; + + std::array + eval0_w = { + 0x333AEBFBB633C5DAC78A664A97F382E44C7A3BA4129409B01465FDB51FBA6159_cppui_modular256, + 0x229705503FFBD3EA924B51FCE0710B063063B9554809B21630FA2265C608A930_cppui_modular256, + 0x13D016250A5B9CE89D5E6E22E0380EAC6C61DE4DDE38FC84EB8A08B1C8C4AD3C_cppui_modular256, + 0x32117E44C1F03F26F3481897755A9422E1280E4A9D21E5D00FCC436323657D7A_cppui_modular256, + 0x0F68E895335D3BBCE935310D52AA1406C20558F286D06CE3FDF70B9E74AF2877_cppui_modular256, + 0x19E176931CC0BB2D6CA0CFF8CC6A7C02CCD0137FA0F443CEA01C0F69E727E2B1_cppui_modular256, + 0x2E9559BD9D6C15B5632A9E5E0BB2C4FC5BFFD29C2B41366AE197545C5094B357_cppui_modular256, + 0x1F81313571BFF50D79E013B3F622BE5FC78724A8546F77C75B68CA780015E2C6_cppui_modular256, + 0x25D45FAB67F88E14B7EC510C28297DBA94DC373AA850AB17C49437DA44B53E68_cppui_modular256, + 0x2C278E215E31271BF5F88E645A303D15623149CCFC31DE682DBFA53C89549A0A_cppui_modular256, + 0x327ABC975469C0233404CBBC8C36FC702F865C5F501311B896EB129ECDF3F5AC_cppui_modular256, + 0x38CDEB0D4AA2592A72110914BE3DBBCAFCDB6EF1A3F44509001680011293514E_cppui_modular256, + 0x3F21198340DAF231B01D466CF0447B25CA308183F7D578596941ED635732ACF0_cppui_modular256, + 0x057447F937138B38EE2983C5224B3A80753EFB1A4269B28E394029D89BD20891_cppui_modular256, + 0x0BC7766F2D4C24402C35C11D5451F9DB42940DAC964AE5DEA26B973AE0716433_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval0_z = 0x134EBAD0F9C35BE923C101FBDDE3E6223FE9688D939FD620137F2BA1473CEE2E_cppui_modular256; + + std::array eval0_s = { + 0x38F09DAE5B20B0CE58B9146FA85FBD460B0560AC4A84C269A6B116B90CAD9930_cppui_modular256, + 0x14D2BCE16FBE86ADC87964FCB51A1D5DE50D20EFE036FE5CD1AC1A3CA30F0CFE_cppui_modular256, + 0x00EE6F2F707C48EB45BEB0B8DB361BBD7E965362E0CE8EF02A86C61942B6EC34_cppui_modular256, + 0x21497448D1A0FE3B4066DAF2476EE883962B5ABF7FFEBB54F2AF2AECBE591846_cppui_modular256, + 0x3BAA01F94DFF5E931CAC909968174CFF50EA6ED08377422EB5E56F5F43ADFCB2_cppui_modular256, + 0x2EF00DA590D3C70C042C6A36B7C0FD4F359979E5325A1AA04EF3239D65944055_cppui_modular256, + }; + + constexpr const std::size_t lookup_size = 4; + std::array eval0_lookup_sorted = { + 0x09BF9BB8DC4499B5E044B8F9725BD1F76D2B96D40E030CE45BFC8513C7878585_cppui_modular256, + 0x392178C04F97404121297069CDD6C7D1CAD07541AC82B6055EE72CB44E312ECE_cppui_modular256, + 0x22D44A9961BAEB007DE32FFDE0ACC05D545609F271F2C9994B309A96EE341689_cppui_modular256, + 0x2101DB892E0BFF916221891846B5208F85DC8AFADF3CEF66EC40AA026D7E2616_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval0_lookup_aggregated = + 0x2491DCAD05BC2541E67009FD6430FD3E3F761F6777745636741FBE38BF40178A_cppui_modular256;; + + typename BlueprintFieldType::value_type eval0_lookup_table = + 0x3061EA493FBB49E185D2C7B05A5F918B243FC5FF0BF157EE88C117C1C5FF250F_cppui_modular256; + + std::array + eval1_w = { + 0x1E62E030CDBC0C7188D33E5CDF44CB9F627F51A218E31B3DE5DC042D3985933A_cppui_modular256, + 0x35A92D4340842C373BAD02F6EB1EEA07DFEBD5F375AC8EBAFAE9FE39E1FE1C7D_cppui_modular256, + 0x3371121F8357D809F17C1DD8A213D22316F2F17856750CFEBA7A4FBA8C88B2BF_cppui_modular256, + 0x0E017A1556A517DFEE62C99B2676A0A6084213E8664A18BEAB37D0EF12057DB0_cppui_modular256, + 0x3D09624DEA00B11359E51D17B08F5407A1EC500F074D015BF701B0AF6B3A4A1A_cppui_modular256, + 0x14D9A308E0A2CAAE9570769666FBC6BFF2775CB2A19DEFA50F5974E5D9F476DB_cppui_modular256, + 0x2A7D07B5B324E6C311C3CD5DAB4DAED7CEC8E18B0291EC789814FC16726D0F10_cppui_modular256, + 0x28A9C3CF223D9F510AD33071132CA3554BDD14E05E541DD81E554C357E864CF6_cppui_modular256, + 0x17D8769C9C06FD70F8D32E73F6049D93D11F72C5D34E44C1B17E48853F3D42EC_cppui_modular256, + 0x0707296A15D05B90E6D32C76D8DC97D25661D0AB48486BAB44A744D4FFF438E2_cppui_modular256, + 0x3635DC378F99B9B0D4D32A79BBB49210FDEAC78CC68F8BB070FD7211C0AB2ED9_cppui_modular256, + 0x25648F05096317D0C2D3287C9E8C8C4F832D25723B89B29A04266E61816224CF_cppui_modular256, + 0x149341D2832C75F0B0D3267F8164868E086F8357B083D983974F6AB142191AC5_cppui_modular256, + 0x03C1F49FFCF5D4109ED32482643C80CC8DB1E13D257E006D2A78670102D010BB_cppui_modular256, + 0x32F0A76D76BF32308CD3228547147B0B353AD81EA3C5207256CE943DC38706B2_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval1_z = 0x3C6E48DF402AA8AD73AEE593E6A45E617A6FA5F0FD6537195830BDFAD5007FB2_cppui_modular256; + + std::array eval1_s = { + 0x2FB75D341DB2B66FD0065443D9B82C300A41DC7634D6F22B42A202C7C6EF6CFC_cppui_modular256, + 0x1BF54A529308426DC3223933D80FA71FD9D5D5BFE3FB24B003B0D136BCF81505_cppui_modular256, + 0x0757119D804CFB7B01FDF885E9FC678171B8F91B10D9D04496074912E139823D_cppui_modular256, + 0x3E0F44ACEC3F8EF9F2333B1DD953B283147266DFF7F74E0F63D79E305DA37165_cppui_modular256, + 0x30E36F8F7BFA41EFD55E63F55DA4E1BDF91A7346B271550FD209678CEA1A8AC3_cppui_modular256, + 0x381319F662909FA6C285987D1008BE056C3C31D19220AAADE93D92228C2A4B33_cppui_modular256, + }; + + std::array eval1_lookup_sorted = { + 0x0E825D8D5AC24EBC0C7AAD891B5F2AEC4A9880E98D318FB20580DCD7833A30B1_cppui_modular256, + 0x30D59443C883D3A96862BB9AAA2066387D5CC76A14CF208817617FBB21F39AFB_cppui_modular256, + 0x2B321B4F0969B1DEB9AFAD31F1E1CA18A45B1A9B4AE177613630EAD1184A35E1_cppui_modular256, + 0x08DF00E4FA6F4A9261CCF816AF7792D42CEF4E7E6942C8DC3171A0002538DEB5_cppui_modular256, + }; + + typename BlueprintFieldType::value_type eval1_lookup_aggregated = + 0x297BAEF6E597DAB1C9D2694509368B8D3A5FE32AE7B2AF577633806DB48952A3_cppui_modular256; + + typename BlueprintFieldType::value_type eval1_lookup_table = + 0x12A589E01D18A29D5D176B45C9EF899EF6ADDF3A6594EAD2461666E05876B2E9_cppui_modular256; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + std::array, 2> eval_lookup_sorted = {eval0_lookup_sorted, eval1_lookup_sorted}; + std::array eval_lookup_aggregated = {eval0_lookup_aggregated, eval1_lookup_aggregated}; + std::array eval_lookup_table = {eval0_lookup_table, eval1_lookup_table}; + + + typename BlueprintFieldType::value_type alpha_val = + 0x093707BDEAB062634AFCBC9251180B77691009161382D1638490414AD45A33BE_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000082A48F2CCDBC01E4F4ADB977A324D6F6_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x000000000000000000000000000000007F486CD9B2A0B5C2198305055395F920_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x38F09DAE5B20B0CE58B9146FA85FBD460B0560AC4A84C269A6B116B90CAD9930_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x38C743A28755C1E00F0771302FE6A07A2130C21884C1A7AF1800DD8FD9FC6547_cppui_modular256; + + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + + std::array expected_result = { + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular256, + 0x3C298FAAF54E18672778EBEBAC6157CEE2D95F16122B92D0BAAD88BB8C2E0E1C_cppui_modular256, // varBaseMul + 0x2C4C103BC45214A6E78FB9CF5E6F7B8980C36AE4A2455988028C3C907D8C7F08_cppui_modular256, // endoMul + 0x18BDD6D3E3CFAEC53B93C6AD4B9B167FB44B5DFD1A92D34C93BBD1C9F713978D_cppui_modular256, // endoMulScalar + 0x2B2CCD26ACF301C648598FDF4FE0DF29BBD189A233E5BFDEC169B4545F151046_cppui_modular256, // completeAdd + 0x3D6B79823727C40A68B323E82437399AC3DB02009F7680E211415847A6A2DF53_cppui_modular256, // lookup + }; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + + for (std::size_t j = 0; j < lookup_size; j++) { + public_input.push_back(eval_lookup_sorted[i][j]); + var lookup_sorted = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].lookup.sorted[j] = lookup_sorted; + } + + public_input.push_back(eval_lookup_aggregated[i]); + var lookup_aggregated = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].lookup.aggreg = lookup_aggregated; + + public_input.push_back(eval_lookup_table[i]); + var lookup_table = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].lookup.table = lookup_table; + + evals[i].poseidon_selector = zero; + evals[i].generic_selector = zero; + } + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = { + zeta, alpha, beta, gamma, joint_combiner, + evals, omega, domain_size}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < expected_result.size(); ++i) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/perm_scalars.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/perm_scalars.cpp new file mode 100644 index 000000000..ad3140b9c --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/perm_scalars.cpp @@ -0,0 +1,232 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_detail_constraints_perm_scalars_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_detail_constraints_perm_scalars_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_constraints_perm_scalars_ec_input_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::perm_scalars; + + // params: + // std::array, + // KimchiParamsType::eval_points_amount> evals; + // std::array alphas; + // std::size_t start_idx; + + // var beta; + // var gamma; + // var zkp_zeta; + + std::array + eval0_w = { + 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256, + 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256, + 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256, + 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256, + 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256, + 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256, + 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256, + 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256, + 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256, + 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256, + 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256, + 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256, + 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256, + 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256, + 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval0_z = 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256; + + std::array eval0_s = { + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256, + 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256, + 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256, + 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256, + 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256, + 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256 + }; + + std::array + eval1_w = { + 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval1_z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + + std::array eval1_s = { + 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256, + }; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + + typename BlueprintFieldType::value_type alpha_val = + 0x322D5D64C86AFB168AC57D2D8AB3512647B4802C8DC4DE07DB2C51E094C4D9B7_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x000000000000000000000000000000005D27C70754796C79C9D9958673CF2ABA_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x00000000000000000000000000000000C2278ADB337FA07CDFB689C4651FFD6D_cppui_modular256; + typename BlueprintFieldType::value_type zkp_zeta_val = + 0x10D6264E9E2FD66DF8E432BBA507EA36F9BA431B00A80B80757E56DEADC39D7A_cppui_modular256; + + typename BlueprintFieldType::value_type expected_result = + 0x0E7F540B2F6CE243D4F603210A7EF55620EEC89679E894777E34D1AA3A33C689_cppui_modular256; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + } + + std::array alpha_powers; + for (std::size_t i = 0; i < circuit_description::alpha_powers_n; i++) { + public_input.push_back(alpha_val.pow(i)); + alpha_powers[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zkp_zeta_val); + var zkp_zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = { + evals, alpha_powers, 21, beta, gamma, zkp_zeta}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/rpn_expression.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/rpn_expression.cpp new file mode 100644 index 000000000..8ef8a2762 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/rpn_expression.cpp @@ -0,0 +1,1121 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_detail_rpn_expression_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite) +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_lagrange) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + typename BlueprintFieldType::value_type result = 0x1E2AE13562C642ED35261EB5927960C75105852F6F962D463BF981EF969050C4_cppui_modular255; + constexpr const char *s ="UnnormalizedLagrangeBasis(-4);\0"; + + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0C0D3C26FCD47AFF64D9055FCE9858335ADE6B0706289AB4019630784E8B7527_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000059E5EE71CFF4B24FA2A3A131F77CFFC8_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000085D434481165E938FEA628354AA5B9E5_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x00_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x35ABB5BAFCAFF26391BB9A23EA996222FD0D4B0F392A78E4AFD5610ECE614E49_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x1421DEB15F5CE205068512B010382353DC0AA1B40386A1C14774C65664BB8182_cppui_modular256; + std::size_t domain_size = 1024; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val, &result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(assignment.var_value(real_res.output)== result); + + }; + + test_component(params, public_input, + result_check); +} +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_vanishes) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + typename BlueprintFieldType::value_type result = 0x2692756edf321604d16f4d2151fbad0a9780c75ebb49f9b56a89adde2f6a1f48_cppui_modular255; + constexpr const char *s ="Alpha;Pow(24);VanishesOnLast4Rows;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type group_gen = BlueprintFieldType::value_type::one(); + + typename BlueprintFieldType::value_type alpha_val = + 0x0C0D3C26FCD47AFF64D9055FCE9858335ADE6B0706289AB4019630784E8B7527_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000059E5EE71CFF4B24FA2A3A131F77CFFC8_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000085D434481165E938FEA628354AA5B9E5_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x00_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x35ABB5BAFCAFF26391BB9A23EA996222FD0D4B0F392A78E4AFD5610ECE614E49_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x1421DEB15F5CE205068512B010382353DC0AA1B40386A1C14774C65664BB8182_cppui_modular256; + std::size_t domain_size = 1024; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val, &result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(assignment.var_value(real_res.output)== result); + + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_dup) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Beta;Cell(Variable { col: Witness(3), row: Curr });Dup;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val](AssignmentType &assignment, component_type::result_type &real_res) { + assert(gamma_val == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_sub) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Beta;Cell(Variable { col: Witness(3), row: Curr });Sub;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val](AssignmentType &assignment, component_type::result_type &real_res) { + assert((beta_val - gamma_val) == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_add) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Beta;Cell(Variable { col: Witness(3), row: Curr });Add;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val](AssignmentType &assignment, component_type::result_type &real_res) { + assert((gamma_val + beta_val) == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_mul) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Beta;Cell(Variable { col: Witness(3), row: Curr });Mul;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[3] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val](AssignmentType &assignment, component_type::result_type &real_res) { + assert((gamma_val * beta_val) == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_pow) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Beta;Cell(Variable { col: Witness(10), row: Curr });Pow(2);\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + evals[0].w[10] = gamma; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&gamma_val, &beta_val](AssignmentType &assignment, component_type::result_type &real_res) { + assert((gamma_val * gamma_val) == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_load) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Alpha;Store;Beta;Alpha;Pow(20);Add;Alpha;Load(0);Sub;Add;Sub;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;\0"; // - alpha^20 - beta + alpha + 1 + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type alpha_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D5_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8D3_cppui_modular256; + typename BlueprintFieldType::value_type joint_combiner_val = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + typename BlueprintFieldType::value_type expected_result = -alpha_val.pow(20) + alpha_val - beta_val + 1; + + std::vector public_input; + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_val); + var joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations; + std::array evals; + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_complete_add) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type expected_result = 0x0EF5278F0AD55CDE149D4E396A01E9B72A0D73FB4CF033C570B1B7E0C24C5FCE_cppui_modular256; + + std::array + eval0_w = { + 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256, + 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256, + 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256, + 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256, + 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256, + 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256, + 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256, + 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256, + 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256, + 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256, + 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256, + 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256, + 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256, + 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256, + 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval0_z = 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256; + + std::array eval0_s = { + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256, + 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256, + 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256, + 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256, + 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256, + 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256 + }; + + std::array + eval1_w = { + 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval1_z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + + std::array eval1_s = { + 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256, + }; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + + typename BlueprintFieldType::value_type alpha_val = + 0x322D5D64C86AFB168AC57D2D8AB3512647B4802C8DC4DE07DB2C51E094C4D9B7_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x000000000000000000000000000000005D27C70754796C79C9D9958673CF2ABA_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x00000000000000000000000000000000C2278ADB337FA07CDFB689C4651FFD6D_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256; + + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + + evals[i].poseidon_selector = zero; + evals[i].generic_selector = zero; + } + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + var joint_combiner = zero; + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_rpn_expression_test_suite_endo_mul) { + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + constexpr const char *s = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + const std::size_t array_size = zk::components::count_delimiters(s); + const std::size_t N = zk::components::rpn_component_rows(s); + using component_type = zk::components::rpn_expression; + + typename BlueprintFieldType::value_type expected_result = 0x259D030170979C4754D0CEBF9E6AE529563BEB3A27C7003F57CCD4F80F875E4B_cppui_modular256; + + std::array + eval0_w = { + 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256, + 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256, + 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256, + 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256, + 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256, + 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256, + 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256, + 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256, + 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256, + 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256, + 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256, + 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256, + 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256, + 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256, + 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval0_z = 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256; + + std::array eval0_s = { + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256, + 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256, + 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256, + 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256, + 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256, + 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256 + }; + + std::array + eval1_w = { + 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256 + }; + + typename BlueprintFieldType::value_type eval1_z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + + std::array eval1_s = { + 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256, + }; + + std::array, 2> eval_w = {eval0_w, eval1_w}; + std::array eval_z = {eval0_z, eval1_z}; + std::array, 2> eval_s = {eval0_s, eval1_s}; + + typename BlueprintFieldType::value_type alpha_val = + 0x322D5D64C86AFB168AC57D2D8AB3512647B4802C8DC4DE07DB2C51E094C4D9B7_cppui_modular256; + typename BlueprintFieldType::value_type beta_val = + 0x000000000000000000000000000000005D27C70754796C79C9D9958673CF2ABA_cppui_modular256; + typename BlueprintFieldType::value_type gamma_val = + 0x00000000000000000000000000000000C2278ADB337FA07CDFB689C4651FFD6D_cppui_modular256; + typename BlueprintFieldType::value_type zeta_val = + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256; + + typename BlueprintFieldType::value_type omega_val = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + + std::vector public_input; + + public_input.push_back(0); + var zero = var(0, public_input.size() - 1, false, var::column_type::public_input); + + using evaluations_type = typename zk::components::kimchi_proof_evaluations< + BlueprintFieldType, kimchi_params>; + std::array evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(eval_w[i][j]); + var w = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].w[j] = w; + } + + public_input.push_back(eval_z[i]); + var z = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].z = z; + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(eval_s[i][j]); + var s = var(0, public_input.size() - 1, false, var::column_type::public_input); + evals[i].s[j] = s; + } + + evals[i].poseidon_selector = zero; + evals[i].generic_selector = zero; + } + + public_input.push_back(alpha_val); + var alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(beta_val); + var beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(gamma_val); + var gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_val); + var zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + + var joint_combiner = zero; + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = {s, zeta, alpha, beta, gamma, joint_combiner, evals, omega, domain_size}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/unnormalized_lagrange_basis.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/unnormalized_lagrange_basis.cpp new file mode 100644 index 000000000..3dfd43608 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/unnormalized_lagrange_basis.cpp @@ -0,0 +1,140 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_details_vanishes_on_last_4_rows + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_unnormalized_lagrange_basis_positive_power) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::unnormalized_lagrange_basis; + + typename BlueprintFieldType::value_type group_gen = 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + int ith = 5; + typename BlueprintFieldType::value_type x = algebra::random_element(); + typename BlueprintFieldType::value_type group_gen_pow = group_gen.pow(ith); + typename BlueprintFieldType::value_type expected_res = (x.pow(domain_size) - 1) * (x - group_gen_pow).inversed(); + + std::vector public_input = {group_gen, x, expected_res}; + + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), domain_size, var(0, 1, false, var::column_type::public_input), ith}; + + auto result_check = [&expected_res](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(expected_res == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "vanishes_on_last_4_rows: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_unnormalized_lagrange_basis_negative_power) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::unnormalized_lagrange_basis; + + typename BlueprintFieldType::value_type group_gen = 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + int ith = -5; + typename BlueprintFieldType::value_type x = algebra::random_element(); + typename BlueprintFieldType::value_type group_gen_pow = group_gen.pow(-ith).inversed(); + typename BlueprintFieldType::value_type expected_res = (x.pow(domain_size) - 1) * (x - group_gen_pow).inversed(); + + std::vector public_input = {group_gen, x, expected_res}; + + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), domain_size, var(0, 1, false, var::column_type::public_input), ith}; + + auto result_check = [&expected_res](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(expected_res == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "vanishes_on_last_4_rows: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/detail/constraints/vanishes_on_last_4_rows.cpp b/libs/blueprint/test/verifiers/kimchi/detail/constraints/vanishes_on_last_4_rows.cpp new file mode 100644 index 000000000..4b89dff72 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/constraints/vanishes_on_last_4_rows.cpp @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_details_vanishes_on_last_4_rows + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_zkpm) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::vanishes_on_last_4_rows; + + typename BlueprintFieldType::value_type group_gen = algebra::random_element(); + std::size_t domain_size = 1000; + typename BlueprintFieldType::value_type x = algebra::random_element(); + typename BlueprintFieldType::value_type group_gen_pow = group_gen.pow(domain_size - 3 - 1); + typename BlueprintFieldType::value_type expected_res = (x - group_gen_pow) * (x - group_gen_pow * group_gen) * + (x - group_gen_pow * group_gen * group_gen) * + (x - group_gen_pow * group_gen * group_gen * group_gen); + + std::vector public_input = {group_gen, x, expected_res}; + + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), domain_size, var(0, 1, false, var::column_type::public_input)}; + + auto result_check = [&expected_res](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(expected_res == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "vanishes_on_last_4_rows: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/detail/ft_eval.cpp b/libs/blueprint/test/verifiers/kimchi/detail/ft_eval.cpp new file mode 100644 index 000000000..770bec958 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/ft_eval.cpp @@ -0,0 +1,244 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_ft_eval_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "../proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_ft_eval_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_ft_eval_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 0; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + constexpr static const std::size_t srs_len = 1; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + zk::components::kimchi_verifier_index_scalar verifier_index; + + using component_type = zk::components:: + ft_eval; + + zk::snark::pickles_proof kimchi_proof = test_proof(); + + typename BlueprintFieldType::value_type omega_value = + 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size_value = 512; + + std::vector shifts = { + 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular256, + 0x00B9CDC8FD0BD4B27E2A74AF7AEBD5734D52D75BDF85EBF1CAD03413E914A2E3_cppui_modular256, + 0x0033BFCF8112720332825BD83D44D92CADC0C30466E8102C419C30FA2665695A_cppui_modular256, + 0x0087F4BB29954E16960F2DE3A1FA5AC7B62146DB348C7C9F0E8BF10B2C8E8411_cppui_modular256, + 0x00EC71373B9F6CF15ED1949647365DB60B2E26C3A8ABBA5BB06BF23E9DBE5893_cppui_modular256, + 0x00F39197CC4C55084C68D31F64F1A172406B585CB86445F00C248C721C496D10_cppui_modular256, + 0x00B8DD039799DBEE12D2E6A4299A83E067353C0143C5DFD203190C239159EEA3_cppui_modular256, + }; + typename BlueprintFieldType::value_type zeta_value = + 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256; + + typename BlueprintFieldType::value_type joint_combiner_value = 0; + typename BlueprintFieldType::value_type beta_value = 0x000000000000000000000000000000005D27C70754796C79C9D9958673CF2ABA_cppui_modular256; + typename BlueprintFieldType::value_type gamma_value = 0x00000000000000000000000000000000C2278ADB337FA07CDFB689C4651FFD6D_cppui_modular256; + typename BlueprintFieldType::value_type alpha_value = + 0x322D5D64C86AFB168AC57D2D8AB3512647B4802C8DC4DE07DB2C51E094C4D9B7_cppui_modular256; + + std::array, 2> w = {{ + {{ 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256, + 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256, + 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256, + 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256, + 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256, + 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256, + 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256, + 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256, + 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256, + 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256, + 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256, + 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256, + 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256, + 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256, + 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256,}}, + + {{0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256, + 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256, + 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256, + 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256, + 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256, + 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256, + 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256, + 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256, + 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256, + 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256, + 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256, + 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256, + 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256, + 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256, + 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256}}, + }}; + + std::array, 2> s = {{ + {{0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256, + 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256, + 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256, + 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256, + 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256, + 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256,}}, + + {{0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256, + 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256, + 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256, + 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256, + 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256, + 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256,}}, + }}; + + std::array z = { + 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256, + 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256, + }; + + typename BlueprintFieldType::value_type expected_result = 0x0C5FFA9CCCAB64B985EB4467CE3933E6F4BFF202AEA53ACD4E27C0C6BBE902B2_cppui_modular256; + + + + std::vector public_input = {}; + + constexpr const std::size_t alpha_powers_n = index_terms_list::alpha_powers_n; + std::array alpha_powers; + for (std::size_t i = 0; i < alpha_powers_n; i++) { + public_input.push_back(power(alpha_value, i)); + alpha_powers[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + public_input.push_back(beta_value); + var beta(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(gamma_value); + var gamma(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(joint_combiner_value); + var joint_combiner(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(omega_value); + var omega(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_value); + var zeta(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_value.pow(domain_size_value)); + var zeta_n(0, public_input.size() - 1, false, var::column_type::public_input); + + std::array public_eval; + + verifier_index.omega = omega; + verifier_index.domain_size = domain_size_value; + + for (std::size_t i = 0; i < shifts.size(); i++) { + public_input.push_back(shifts[i]); + verifier_index.shift[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array, 2> evals; + + for (std::size_t i = 0; i < 2; i++) { + for (std::size_t j = 0; j < witness_columns; j++) { + public_input.push_back(w[i][j]); + evals[i].w[j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + for (std::size_t j = 0; j < perm_size; j++) { + public_input.push_back(s[i][j]); + evals[i].s[j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + public_input.push_back(z[i]); + evals[i].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename component_type::params_type params = {verifier_index, alpha_powers, + evals, gamma, beta, + zeta, zeta_n, joint_combiner, + public_eval}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component( + params, public_input, result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/lagrange_denominators.cpp b/libs/blueprint/test/verifiers/kimchi/detail/lagrange_denominators.cpp new file mode 100644 index 000000000..ab1e76c61 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/lagrange_denominators.cpp @@ -0,0 +1,177 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_lagrange_denominators_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_lagrange_denominators) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::lagrange_denominators; + + var one(0, 0, false, var::column_type::public_input); + var zeta(0, 1, false, var::column_type::public_input); + var zeta_omega(0, 2, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = algebra::random_element(); + typename BlueprintFieldType::value_type omega_value = algebra::random_element(); + typename BlueprintFieldType::value_type zeta_omega_value = zeta_value * omega_value; + + std::vector public_input = {1, zeta_value, zeta_omega_value}; + + std::array omega_powers; + std::array omega_powers_values; + for (std::size_t i = 0; i < n; i++) { + omega_powers_values[i] = power(omega_value, i); + omega_powers[i] = var(0, 3 + i, false, var::column_type::public_input); + public_input.push_back(omega_powers_values[i]); + } + + typename component_type::params_type params = {zeta, zeta_omega, omega_powers, one}; + + std::vector expected_result(2 * n); + for (std::size_t i = 0; i < n; i++) { + expected_result[i] = (zeta_value - omega_powers_values[i]).inversed(); + expected_result[n + i] = (zeta_omega_value - omega_powers_values[i]).inversed(); + } + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < n; i++) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + assert(expected_result[n + i] == assignment.var_value(real_res.output[n + i])); + } + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "lagrange_denominators_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_lagrange_denominators_real_data) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::lagrange_denominators; + + var one(0, 0, false, var::column_type::public_input); + var zeta(0, 1, false, var::column_type::public_input); + var zeta_omega(0, 2, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = 0x2F51244846217BCB9DE92C5903AC022FAD29555920E45344407B680D24D550F1_cppui_modular256; + typename BlueprintFieldType::value_type omega_value = 0x0CC3380DC616F2E1DAF29AD1560833ED3BAEA3393ECEB7BC8FA36376929B78CC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_omega_value = zeta_value * omega_value; + + std::vector public_input = {1, zeta_value, zeta_omega_value}; + + std::array omega_powers; + std::array omega_powers_values; + for (std::size_t i = 0; i < n; i++) { + omega_powers_values[i] = power(omega_value, i); + omega_powers[i] = var(0, 3 + i, false, var::column_type::public_input); + public_input.push_back(omega_powers_values[i]); + } + + typename component_type::params_type params = {zeta, zeta_omega, omega_powers, one}; + + std::vector expected_result = {0x04B51DBD555265C26C890F9356AF30F1EC63AFED99D936BDC3D491BA6A06062C_cppui_modular256, + 0x29E0A0DC50CFA8107324A4C83B2358EFEDBDE840B9C08F5550369CAA41A5A85F_cppui_modular256, + 0x35AF24EA5A46E07C6C1B50C7522A3DF49CD56EC788096049357C1BD58B97C90A_cppui_modular256, + 0x2D3A6A9C66D28D2590A2685AA2957BA6E88A0B4261F8CA691283B12CB8400CD2_cppui_modular256, + 0x0E0ED7C2F3B9C67A4C214B96ADD90D88530FA109C3161B408870B6BB1D585B12_cppui_modular256, + 0x36A1234D72594664479AE1EB7D5E2DC9CC89CD1CAD0DE2F32829432AE7CD2D9E_cppui_modular256, + 0x31D2FDE1CDAC87CCC8D10CAE161DD72C5783E200B8C42D8FB3B7E4E7E8DE1CD4_cppui_modular256, + 0x1031F1E65443D7EE7BF96057030E67B95DE36784C910A58A5B771F0BC9D67FE9_cppui_modular256, + 0x15A16E2EE8DB03DE98FB82AFBDE4018960F2A0FA1CD257C28E8C4160679CA950_cppui_modular256, + 0x0F462B51E39AC7A0BAFF4743144FE5F95EF7C60F7571231ADF708CBD007BD581_cppui_modular256}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + for (std::size_t i = 0; i < n; i++) { + assert(expected_result[i] == assignment.var_value(real_res.output[i])); + assert(expected_result[n + i] == assignment.var_value(real_res.output[n + i])); + } + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "lagrange_denominators_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/oracles_cip.cpp b/libs/blueprint/test/verifiers/kimchi/detail/oracles_cip.cpp new file mode 100644 index 000000000..c217b38ad --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/oracles_cip.cpp @@ -0,0 +1,460 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_verifiers_kimchi_detail_oracles_cip_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms_cip.hpp" +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_verifiers_kimchi_detail_oracles_cip_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_verifiers_kimchi_detail_oracles_cip_test2) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 0; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 0; + + constexpr static const std::size_t eval_points_amount = 2; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_cip_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::oracles_cip; + + std::vector public_input; + + // component input + var v; + var u; + var ft_eval0; + var ft_eval1; + std::array< + std::array< + std::array, + eval_points_amount>, + kimchi_params::prev_challenges_size> polys; + std::array p_eval; + std::array, + eval_points_amount> evals; + typename BlueprintFieldType::value_type expected_result = 0x354a5816578a0f9d8d9ddb7fa580573882cb771454a716e4838c1b29e24034a2_cppui_modular255; + + public_input.push_back(0x1A27603517D952BB0060BB01DE0DA94CFC587748DD85D4987C14883E3BA51BAB_cppui_modular255);//algebra::random_element()); + v = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0CD95BF326F609A8D27F9CD8CFA5C1A0662C588EEA1E5B84CD517DC5BA09C502_cppui_modular255);//algebra::random_element()); + u = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0C5FFA9CCCAB64B985EB4467CE3933E6F4BFF202AEA53ACD4E27C0C6BBE902B2_cppui_modular255); + ft_eval0 = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x16FE1AE7F56997161DB512632BE7BFA337F47F422E0D01AF06DE298DD8C429D5_cppui_modular255); + ft_eval1 = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + // } + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//x35557EBE9125C357A755F10D90F82A78DE0522FCBA6A3C2039F7F4F95B24F1BC_cppui_modular255);//algebra::random_element()); + p_eval[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//0x175762EC87AE06A44B63D3F5626B76591A06D32BB6A2FCCA8A62A36C1D7A59E7_cppui_modular255);//algebra::random_element()); + p_eval[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular255);//lgebra::random_element()); + evals[0].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular255); + evals[0].s[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular255); + evals[0].s[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular255); + evals[0].s[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular255); + evals[0].s[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular255); + evals[0].s[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular255); + evals[0].s[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + public_input.push_back(0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular255); + evals[0].w[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular255); + evals[0].w[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular255); + evals[0].w[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular255); + evals[0].w[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular255); + evals[0].w[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular255); + evals[0].w[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular255); + evals[0].w[6] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular255); + evals[0].w[7] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular255); + evals[0].w[8] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular255); + evals[0].w[9] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular255); + evals[0].w[10] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular255); + evals[0].w[11] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular255); + evals[0].w[12] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular255); + evals[0].w[13] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular255); + evals[0].w[14] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular255);//lgebra::random_element()); + evals[1].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular255); + evals[1].s[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular255); + evals[1].s[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular255); + evals[1].s[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular255); + evals[1].s[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular255); + evals[1].s[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular255); + evals[1].s[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + public_input.push_back(0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular255); + evals[1].w[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular255); + evals[1].w[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular255); + evals[1].w[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular255); + evals[1].w[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular255); + evals[1].w[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular255); + evals[1].w[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular255); + evals[1].w[6] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular255); + evals[1].w[7] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular255); + evals[1].w[8] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular255); + evals[1].w[9] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular255); + evals[1].w[10] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular255); + evals[1].w[11] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular255); + evals[1].w[12] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular255); + evals[1].w[13] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular255); + evals[1].w[14] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[0].generic_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[0].poseidon_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[1].generic_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[1].poseidon_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + + typename component_type::params_type params = { + v, + u, + ft_eval0, + ft_eval1, + polys, + p_eval, + evals + }; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_verifiers_kimchi_detail_oracles_cip_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 0; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + constexpr static const std::size_t eval_points_amount = 2; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::oracles_cip; + + std::vector public_input; + + // component input + var v; + var u; + var ft_eval0; + var ft_eval1; + std::array< + std::array< + std::array, + eval_points_amount>, + kimchi_params::prev_challenges_size> polys; + std::array p_eval; + std::array, + eval_points_amount> evals; + typename BlueprintFieldType::value_type expected_result = 0x092931C57CBF91630B192C9BB166864F5D3F7E3D2C9217FDB382DB82564D4607_cppui_modular255; + + public_input.push_back(0x0416077232C8D4EFD0D1120ACC756A397EA8DCDCF792E5E0F9CDFF82BDF42D2D_cppui_modular255);//algebra::random_element()); + v = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x2E0BB5E9179A691E51FB7336CB161A330268EE64C745078D6FD460E02A76729D_cppui_modular255);//algebra::random_element()); + u = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x36B33A1266C7DCF380A308055D32978AE1F469723AAEB3EDBC512B18D6C095BD_cppui_modular255); + ft_eval0 = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x30B81DB776FF4C13A0BF7BAB87E9768D7ADE52CD3D29549FB1E08798D6A3EF9E_cppui_modular255); + ft_eval1 = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2C27D4E04141972BE1147405F66D1EBAF82622DC3A0B97AF902988E38E76614F_cppui_modular255);//algebra::random_element()); + polys[0][0][0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2034A41141E8DAFD88D5625DC695D10351CD8DACB545B4D260560DE31EF123EF_cppui_modular255);//algebra::random_element()); + polys[0][0][1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//x35557EBE9125C357A755F10D90F82A78DE0522FCBA6A3C2039F7F4F95B24F1BC_cppui_modular255);//algebra::random_element()); + p_eval[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//0x175762EC87AE06A44B63D3F5626B76591A06D32BB6A2FCCA8A62A36C1D7A59E7_cppui_modular255);//algebra::random_element()); + p_eval[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x3F8D8F25CB5A2D2533B3063716C83ADDBFF999C60BC5DEBC3A633EF82EBE108D_cppui_modular255);//lgebra::random_element()); + evals[0].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x35557EBE9125C357A755F10D90F82A78DE0522FCBA6A3C2039F7F4F95B24F1BC_cppui_modular255); + evals[0].s[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3BD62FADFBC967B2DDE2CD067A531BF158C20BBE1B42BB53BF7EE8EC3834555F_cppui_modular255); + evals[0].s[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1D4D2D839270B2866A00334B3CD86E5B7A759B59329F1662039D6D2124FEE4D4_cppui_modular255); + evals[0].s[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2CA95F70D9D97BD9AB7F633B85556C1ABD1938D49ED2975FE62319951E69A022_cppui_modular255); + evals[0].s[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2601AC11905952B2EFD1BB4BE50AC2E86BBC421876C07312CFCC3AED17556926_cppui_modular255); + evals[0].s[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x16303670383248B3C7D5786C8161EE001848D3D86D98F1069A3E0136E8AF322F_cppui_modular255); + evals[0].s[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + public_input.push_back(0x30AC247252D0ABAA93BDEEFBF27F4931E8F995D58AE78FC99910719A226ED51E_cppui_modular255); + evals[0].w[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0E22763F6B830A605362663C49102E31FE20AE4A653038C3007B45CC85CBB96A_cppui_modular255); + evals[0].w[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2B98C80C843569161306DD7C9FA11332358E5FBB48C5DAD801134AEBE9289DB7_cppui_modular255); + evals[0].w[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x090F19D99CE7C7CBD2AB54BCF631F8324AB57830230E83D1687E1F1E4C858203_cppui_modular255); + evals[0].w[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x26856BA6B59A2681924FCBFD4CC2DD32822329A106A425E66916243DAFE26650_cppui_modular255); + evals[0].w[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x03FBBD73CE4C853751F4433DA353C232974A4215E0ECCEDFD080F870133F4A9C_cppui_modular255); + evals[0].w[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x21720F40E6FEE3ED1198BA7DF9E4A732CEB7F386C48270F4D118FD8F769C2EE9_cppui_modular255); + evals[0].w[6] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3EE8610DFFB142A2D13D31BE50758C330625A4F7A8181309D1B102AED9F91336_cppui_modular255); + evals[0].w[7] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1C5EB2DB1863A15890E1A8FEA70671331B4CBD6C8260BC03391BD6E13D55F782_cppui_modular255); + evals[0].w[8] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x39D504A83116000E5086203EFD97563352BA6EDD65F65E1839B3DC00A0B2DBCF_cppui_modular255); + evals[0].w[9] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x174B567549C85EC4102A977F54283B3367E18752403F0711A11EB033040FC01B_cppui_modular255); + evals[0].w[10] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x34C1A842627ABD79CFCF0EBFAAB920339F4F38C323D4A926A1B6B552676CA468_cppui_modular255); + evals[0].w[11] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1237FA0F7B2D1C2F8F738600014A0533B4765137FE1D522009218984CAC988B4_cppui_modular255); + evals[0].w[12] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2FAE4BDC93DF7AE54F17FD4057DAEA33EBE402A8E1B2F43509B98EA42E266D01_cppui_modular255); + evals[0].w[13] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0D249DA9AC91D99B0EBC7480AE6BCF34010B1B1DBBFB9D2E712462D69183514D_cppui_modular255); + evals[0].w[14] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0D69C9B3BE918CB3DE88610F89D800916F23984B5BDFB8AE753C34C234BB1407_cppui_modular255);//lgebra::random_element()); + evals[1].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x175762EC87AE06A44B63D3F5626B76591A06D32BB6A2FCCA8A62A36C1D7A59E7_cppui_modular255); + evals[1].s[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1F21634F6AEDCADE3D878584CC9EDBA4DA6A496DA05D824FB7E741305AAD2C45_cppui_modular255); + evals[1].s[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1418CE39644E82C6411C9B3FD878FD781E3438B6B6B1106657AA48AE8F73F977_cppui_modular255); + evals[1].s[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x13D3942A39464ACE1D4D56A687BBC392DCE24392E4CC6F34063C8B1BBC8E3D71_cppui_modular255); + evals[1].s[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x31EFFF757DF39A98EB88BBBB86607EAE2AC1856A6F172BF969178B21975AFF4C_cppui_modular255); + evals[1].s[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x0EBAE7C12211C4631FD3B3B04F112D99393CA706E09B4B98F2502A3720785BBC_cppui_modular255); + evals[1].s[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + + public_input.push_back(0x0EC5173646D6F7F31A1774DFCC7FB5B0EA356EE2275EC698F7BEBE691BB84E06_cppui_modular255); + evals[1].w[0] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x30D45140D88575398696BB4F1FA770B2337A0DDED0586F664DCFDE63334BF64D_cppui_modular255); + evals[1].w[1] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x12E38B4B6A33F27FF31601BE72CF2BB35A7813DF70051F180AB3CD704ADF9E93_cppui_modular255); + evals[1].w[2] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x34F2C555FBE26FC65F95482DC5F6E6B4A3BCB2DC18FEC7E560C4ED6A627346DA_cppui_modular255); + evals[1].w[3] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1701FF608D90ED0CCC148E9D191EA1B5CABAB8DCB8AB77971DA8DC777A06EF20_cppui_modular255); + evals[1].w[4] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3911396B1F3F6A533893D50C6C465CB713FF57D961A5206473B9FC71919A9767_cppui_modular255); + evals[1].w[5] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1B207375B0EDE799A5131B7BBF6E17B83AFD5DDA0151D016309DEB7EA92E3FAD_cppui_modular255); + evals[1].w[6] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x3D2FAD80429C64E0119261EB1295D2B98441FCD6AA4B78E386AF0B78C0C1E7F4_cppui_modular255); + evals[1].w[7] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x1F3EE78AD44AE2267E11A85A65BD8DBAAB4002D749F828954392FA85D855903A_cppui_modular255); + evals[1].w[8] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x014E219565F95F6CEA90EEC9B8E548BBD23E08D7E9A4D8470076E992EFE93880_cppui_modular255); + evals[1].w[9] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x235D5B9FF7A7DCB3571035390C0D03BD1B82A7D4929E81145688098D077CE0C7_cppui_modular255); + evals[1].w[10] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x056C95AA895659F9C38F7BA85F34BEBE4280ADD5324B30C6136BF89A1F10890D_cppui_modular255); + evals[1].w[11] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x277BCFB51B04D740300EC217B25C79BF8BC54CD1DB44D993697D189436A43154_cppui_modular255); + evals[1].w[12] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x098B09BFACB354869C8E0887058434C0B2C352D27AF18945266107A14E37D99A_cppui_modular255); + evals[1].w[13] = var(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(0x2B9A43CA3E61D1CD090D4EF658ABEFC1FC07F1CF23EB32127C72279B65CB81E1_cppui_modular255); + evals[1].w[14] = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x28654BAD9D8CDFD846C0DB23E11CFE750DF683B9AC1F00BD1550778F27B28C70_cppui_modular255);//algebra::random_element()); + evals[0].generic_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[0].poseidon_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x33018173752351E61F3CE0C02C14FCC1C936E2A9FC8713268ED5871BA404ECAF_cppui_modular255);//algebra::random_element()); + evals[1].generic_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(0x0000000000000000000000000000000000000000000000000000000000000000_cppui_modular255);//algebra::random_element()); + evals[1].poseidon_selector = var(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = { + v, + u, + ft_eval0, + ft_eval1, + polys, + p_eval, + evals + }; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/prepare_scalars.cpp b/libs/blueprint/test/verifiers/kimchi/detail/prepare_scalars.cpp new file mode 100644 index 000000000..8b82572b2 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/prepare_scalars.cpp @@ -0,0 +1,113 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_prepare_scalars_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_prepare_scalars_vesta) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr std::size_t InputSize = 5; + + using component_type = zk::components::prepare_scalars; + + std::vector scalars; + for (int i = 0; i < InputSize; ++i) { + scalars.push_back(algebra::random_element()); + } + + typename BlueprintFieldType::value_type base = 2; + typename BlueprintFieldType::value_type shift = base.pow(255) + 1; + + std::vector expected_res; + for (int i = 0; i < InputSize; ++i) { + expected_res.push_back((scalars[i] - shift) / 2); + } + + std::vector public_input; + for (int i = 0; i < InputSize; ++i) { + public_input.push_back(scalars[i]); + } + for (int i = 0; i < InputSize; ++i) { + public_input.push_back(expected_res[i]); + } + + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input), var(0, 4, false, var::column_type::public_input)}; + + auto result_check = [&expected_res](AssignmentType &assignment, + component_type::result_type &real_res) { + for (int i = 0; i < InputSize; ++i) { + assert(expected_res[i] == assignment.var_value(real_res.output[i])); + } + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "prepare scalars: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/detail/prev_chal_evals.cpp b/libs/blueprint/test/verifiers/kimchi/detail/prev_chal_evals.cpp new file mode 100644 index 000000000..7e9eaa5c8 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/prev_chal_evals.cpp @@ -0,0 +1,165 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_prev_chal_evals_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +typename FieldType::value_type b_poly(const std::array &chals, + typename FieldType::value_type x) { + std::vector pow_twos; + pow_twos.push_back(x); + for (int i = 1; i < ChalAmount; ++i) { + pow_twos.push_back(pow_twos[i - 1] * pow_twos[i - 1]); + } + typename FieldType::value_type res = 1; + for (int i = 0; i < ChalAmount; ++i) { + res *= FieldType::value_type::one() + chals[i] * pow_twos[ChalAmount - 1 - i]; + } + return res; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_prev_chal_evals) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t alpha_powers_n = 5; + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t srs_len = 10; + + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + + using component_type = zk::components:: + prev_chal_evals; + + std::vector public_input = {1, 0}; + var one(0, 0, false, var::column_type::public_input); + var zero(0, 1, false, var::column_type::public_input); + + // typename BlueprintFieldType::value_type zeta_value = algebra::random_element(); + // typename BlueprintFieldType::value_type omega_value = algebra::random_element(); + typename BlueprintFieldType::value_type zeta_value = 0x01751A5CCC6A9B9BDF660296AF5F7C80229DC97F3646FFC3729D827E80DF39DF_cppui_modular256; + //typename BlueprintFieldType::zeta_value * omega_value + typename BlueprintFieldType::value_type zeta_omega_value = 0x11039196D240AC7CC0D1A88749F716B6B025F6BCA2CBBD0B41D2DA46FCC90558_cppui_modular256; + + public_input.push_back(zeta_value); + var zeta(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(power(zeta_value, n)); + var zeta_pow_n(0, public_input.size() - 1, false, var::column_type::public_input); + + public_input.push_back(zeta_omega_value); + var zeta_omega(0, public_input.size() - 1, false, var::column_type::public_input); + public_input.push_back(power(zeta_omega_value, n)); + var zeta_omega_pow_n(0, public_input.size() - 1, false, var::column_type::public_input); + + std::array prev_challenges; + std::array prev_challenges_values; + std::array prev_challenges_values_from_mina; + prev_challenges_values_from_mina[0] = 0x2C0AD1A81FAC9BE59890BEA77119393E3E9EC523A44DF600FE2399C01AA76F70_cppui_modular256; + prev_challenges_values_from_mina[1] = 0x39F31DAAD9FA26835EB1F6ADB2DCE08649061681361B54082C1FA1CD800EEB97_cppui_modular256; + prev_challenges_values_from_mina[2] = 0x07DB69AD9447B12124D32EB3F4A087CE3126CEE2BE9BB8F3C0EE78EDE57667BD_cppui_modular256; + prev_challenges_values_from_mina[3] = 0x15C3B5B04E953BBEEAF466BA36642F163B8E2040506916FAEEEA80FB4ADDE3E4_cppui_modular256; + prev_challenges_values_from_mina[4] = 0x23AC01B308E2C65CB1159EC07827D65E45F5719DE23675021CE68908B045600B_cppui_modular256; + for (std::size_t i = 0; i < eval_rounds; i++) { + prev_challenges_values[i] = prev_challenges_values_from_mina[i]; + } + + + for (std::size_t i = 0; i < eval_rounds; i++) { + // prev_challenges_values[i] = algebra::random_element(); // + public_input.push_back(prev_challenges_values[i]); + prev_challenges[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array evals = {zeta, zeta_omega}; + std::array evals_power = {zeta_pow_n, zeta_omega_pow_n}; + + typename component_type::params_type params = {prev_challenges, evals, evals_power, one, zero}; + + // r[0] = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from 0 to PulicInputSize + // r[1] = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from PulicInputSize to 2 * PulicInputSize + std::array expected_result; + expected_result[0] = b_poly(prev_challenges_values, zeta_value); + expected_result[1] = b_poly(prev_challenges_values, zeta_omega_value); + expected_result[2] = 0x03B060BB64B9D6627C7336873BA524D7B752598E8B3390647BDF6B70B5BB93FF_cppui_modular256; // r[0] from mina, == expected_result[0] + expected_result[3] = 0x39B7CA68618353B26F521A651FE3F9DD365401BC8B68B07FC6D656EB010A541B_cppui_modular256; // r[1] from mina, == expected_result[1] + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result[0] == assignment.var_value(real_res.output[0][0])); + assert(expected_result[1] == assignment.var_value(real_res.output[1][0])); + assert(expected_result[2] == assignment.var_value(real_res.output[0][0])); + assert(expected_result[3] == assignment.var_value(real_res.output[1][0])); + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "prev_chal_evals_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/public_evaluations.cpp b/libs/blueprint/test/verifiers/kimchi/detail/public_evaluations.cpp new file mode 100644 index 000000000..83e5b6089 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/public_evaluations.cpp @@ -0,0 +1,334 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_publuc_evaluations_test + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_publuc_evaluations) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::public_evaluations; + + var one(0, 0, false, var::column_type::public_input); + var zeta_pow_n(0, 1, false, var::column_type::public_input); + var zeta_omega_pow_n(0, 2, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = algebra::random_element(); + typename BlueprintFieldType::value_type omega_value = algebra::random_element(); + typename BlueprintFieldType::value_type zeta_omega_value = zeta_value * omega_value; + constexpr std::size_t domain = 5; + + std::vector public_input = {1, power(zeta_value, domain), + power(zeta_omega_value, domain)}; + + std::array omega_powers; + std::array omega_powers_values; + for (std::size_t i = 0; i < n; i++) { + omega_powers_values[i] = power(omega_value, i); + omega_powers[i] = var(0, 3 + i, false, var::column_type::public_input); + public_input.push_back(omega_powers_values[i]); + } + + std::array lagrange_base; + std::vector lagrange_base_values(2 * n); + for (std::size_t i = 0; i < n; i++) { + lagrange_base_values[i] = (zeta_value - omega_powers_values[i]).inversed(); + lagrange_base_values[n + i] = (zeta_omega_value - omega_powers_values[i]).inversed(); + } + + for (std::size_t i = 0; i < lagrange_base.size(); i++) { + public_input.push_back(lagrange_base_values[i]); + lagrange_base[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array pi; + std::array pi_values; + for (std::size_t i = 0; i < n; i++) { + pi_values[i] = algebra::random_element(); + public_input.push_back(pi_values[i]); + pi[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename BlueprintFieldType::value_type domain_size_value = 15; + public_input.push_back(domain_size_value); + var domain_size(0, public_input.size() - 1, false, var::column_type::public_input); + + typename BlueprintFieldType::value_type zero_value = 0; + public_input.push_back(zero_value); + var zero(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = {zeta_pow_n, zeta_omega_pow_n, pi, lagrange_base, + omega_powers, domain_size, one, zero}; + + // r[0] = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from 0 to PulicInputSize + // r[1] = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from PulicInputSize to 2 * PulicInputSize + std::array expected_result = {0, 0}; + for (std::size_t j = 0; j < 2; j++) { + for (std::size_t i = 0; i < n; i++) { + expected_result[j] = + expected_result[j] + (-lagrange_base_values[j * n + i] * pi_values[i] * omega_powers_values[i]); + } + typename BlueprintFieldType::value_type tmp = j == 0 ? power(zeta_value, n) : power(zeta_omega_value, n); + expected_result[j] = expected_result[j] * domain_size_value.inversed() * (tmp - 1); + } + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result[0] == assignment.var_value(real_res.output[0])); + assert(expected_result[1] == assignment.var_value(real_res.output[1])); + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "public_evaluations_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_real_data) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + constexpr std::size_t n = 5; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::public_evaluations; + + var one(0, 0, false, var::column_type::public_input); + var zeta_pow_n(0, 1, false, var::column_type::public_input); + var zeta_omega_pow_n(0, 2, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = 0x2F51244846217BCB9DE92C5903AC022FAD29555920E45344407B680D24D550F1_cppui_modular256; + typename BlueprintFieldType::value_type omega_value = 0x0CC3380DC616F2E1DAF29AD1560833ED3BAEA3393ECEB7BC8FA36376929B78CC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_omega_value = zeta_value * omega_value; + constexpr std::size_t domain = 32; + + std::vector public_input = {1, power(zeta_value, domain), + power(zeta_omega_value, domain)}; + std::array omega_powers; + std::array omega_powers_values; + for (std::size_t i = 0; i < n; i++) { + omega_powers_values[i] = power(omega_value, i); + omega_powers[i] = var(0, 3 + i, false, var::column_type::public_input); + public_input.push_back(omega_powers_values[i]); + } + + std::array lagrange_base; + std::vector lagrange_base_values(2 * n); + for (std::size_t i = 0; i < n; i++) { + lagrange_base_values[i] = (zeta_value - omega_powers_values[i]).inversed(); + lagrange_base_values[n + i] = (zeta_omega_value - omega_powers_values[i]).inversed(); + } + + for (std::size_t i = 0; i < lagrange_base.size(); i++) { + public_input.push_back(lagrange_base_values[i]); + lagrange_base[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array pi; + std::array pi_values; + for (std::size_t i = 0; i < n; i++) { + pi_values[i] = 0x0000000000000000000000000000000000000000000000000000000000000003_cppui_modular256; + public_input.push_back(pi_values[i]); + pi[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename BlueprintFieldType::value_type domain_size_value = 32; + public_input.push_back(domain_size_value); + var domain_size(0, public_input.size() - 1, false, var::column_type::public_input); + + typename BlueprintFieldType::value_type zero_value = 0; + public_input.push_back(zero_value); + var zero(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = {zeta_pow_n, zeta_omega_pow_n, pi, lagrange_base, + omega_powers, domain_size, one, zero}; + + // r[0] = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from 0 to PulicInputSize + // r[1] = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from PulicInputSize to 2 * PulicInputSize + std::array expected_result = {0x18D62B16440429CDC5B94B8D9DC8A550535487AA7B64822433AD5E762009BD7C_cppui_modular256, + 0x32B7DA94FAF98733D833AFE878FA7775452875BCBC8A953CEF1002C7B3D24033_cppui_modular256}; + + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result[0] == assignment.var_value(real_res.output[0])); + assert(expected_result[1] == assignment.var_value(real_res.output[1])); + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "public_evaluations_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_no_public_input) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + constexpr std::size_t n = 0; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::public_evaluations; + + var one(0, 0, false, var::column_type::public_input); + var zeta_pow_n(0, 1, false, var::column_type::public_input); + var zeta_omega_pow_n(0, 2, false, var::column_type::public_input); + typename BlueprintFieldType::value_type zeta_value = 0x2F51244846217BCB9DE92C5903AC022FAD29555920E45344407B680D24D550F1_cppui_modular256; + typename BlueprintFieldType::value_type omega_value = 0x0CC3380DC616F2E1DAF29AD1560833ED3BAEA3393ECEB7BC8FA36376929B78CC_cppui_modular256; + typename BlueprintFieldType::value_type zeta_omega_value = zeta_value * omega_value; + constexpr std::size_t domain = 32; + + std::vector public_input = {1, power(zeta_value, domain), + power(zeta_omega_value, domain)}; + + std::array omega_powers; + std::array omega_powers_values; + for (std::size_t i = 0; i < n; i++) { + omega_powers_values[i] = power(omega_value, i); + omega_powers[i] = var(0, 3 + i, false, var::column_type::public_input); + public_input.push_back(omega_powers_values[i]); + } + + std::array lagrange_base; + std::vector lagrange_base_values(2 * n); + for (std::size_t i = 0; i < n; i++) { + lagrange_base_values[i] = (zeta_value - omega_powers_values[i]).inversed(); + lagrange_base_values[n + i] = (zeta_omega_value - omega_powers_values[i]).inversed(); + } + + for (std::size_t i = 0; i < lagrange_base.size(); i++) { + public_input.push_back(lagrange_base_values[i]); + lagrange_base[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + std::array pi; + std::array pi_values; + for (std::size_t i = 0; i < n; i++) { + pi_values[i] = 0x0_cppui_modular256; + public_input.push_back(pi_values[i]); + pi[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + typename BlueprintFieldType::value_type domain_size_value = 32; + public_input.push_back(domain_size_value); + var domain_size(0, public_input.size() - 1, false, var::column_type::public_input); + + typename BlueprintFieldType::value_type zero_value = 0; + public_input.push_back(zero_value); + var zero(0, public_input.size() - 1, false, var::column_type::public_input); + + typename component_type::params_type params = {zeta_pow_n, zeta_omega_pow_n, pi, lagrange_base, + omega_powers, domain_size, one, zero}; + + // r[0] = (zeta_pow_n - 1) * domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from 0 to PulicInputSize + // r[1] = (zeta_omega.pow(n) - 1) * index.domain.size_inv * SUM(-l * p * w) + // where l from lagrange, p from public, w from omega_powers for l from PulicInputSize to 2 * PulicInputSize + std::array expected_result = {0x0_cppui_modular256, 0x0_cppui_modular256}; + + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result[0] == assignment.var_value(real_res.output[0])); + assert(expected_result[1] == assignment.var_value(real_res.output[1])); + }; + + test_component( + params, public_input, result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "public_evaluations_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/to_group.cpp b/libs/blueprint/test/verifiers/kimchi/detail/to_group.cpp new file mode 100644 index 000000000..be2751f57 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/to_group.cpp @@ -0,0 +1,94 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_to_group_test + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../../../test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_to_group) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + using var = zk::snark::plonk_variable; + + using component_type = + zk::components::to_group; + + typename BlueprintFieldType::value_type t_value = + 0x28B65D3D28EEAB51CE0B9D26B1A801AFF855B82210E18901C47EA3E46F4F3AED_cppui_modular255; + curve_type::template g1_type::value_type + expected_result; + expected_result.X = 0x0DAFF73C33C0C65C641C6780E151E272069F84CFBAB3BA922A2AE640ACB9234A_cppui_modular255; + expected_result.Y = 0x171ADF13662AD137A9D177BEA98605DD9523A570B05C3161AF32C7B2D7ECCC58_cppui_modular255; + + std::vector public_input = {t_value}; + + typename component_type::params_type params = {var(0, 0, false, var::column_type::public_input)}; + + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result.X == assignment.var_value(real_res.output.X)); + assert(expected_result.Y == assignment.var_value(real_res.output.Y)); + }; + + test_component(params, public_input, + result_check); + + auto duration = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "to_group_component: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/zk_w3.cpp b/libs/blueprint/test/verifiers/kimchi/detail/zk_w3.cpp new file mode 100644 index 000000000..ba74afe47 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/zk_w3.cpp @@ -0,0 +1,119 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_detail_zk_w3_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_detail_zk_w3_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_detail_zk_w3_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::zk_w3; + + zk::components::kimchi_verifier_index_scalar verifier_index; + + typename BlueprintFieldType::value_type omega_val = 0x0CB8102D0128EBB25343154773101EAF1A9DAEF679667EB4BD1E06B973E985E4_cppui_modular256; + std::size_t domain_size = 512; + typename BlueprintFieldType::value_type expected_result = 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + + std::vector public_input; + + public_input.push_back(omega_val); + var omega = var(0, public_input.size() - 1, false, var::column_type::public_input); + + verifier_index.omega = omega; + verifier_index.domain_size = domain_size; + + typename component_type::params_type params = { + verifier_index}; + + auto result_check = [&expected_result](AssignmentType &assignment, component_type::result_type &real_res) { + assert(expected_result == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/detail/zkpm_evaluate.cpp b/libs/blueprint/test/verifiers/kimchi/detail/zkpm_evaluate.cpp new file mode 100644 index 000000000..658f18656 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/detail/zkpm_evaluate.cpp @@ -0,0 +1,95 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_zkpm_evaluate_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_zkpm) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 4; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::zkpm_evaluate; + + typename BlueprintFieldType::value_type group_gen = algebra::random_element(); + std::size_t domain_size = 1000; + typename BlueprintFieldType::value_type x = algebra::random_element(); + typename BlueprintFieldType::value_type group_gen_pow = group_gen.pow(domain_size - 3); + typename BlueprintFieldType::value_type expected_res = (x - group_gen_pow) * (x - group_gen_pow * group_gen) * + (x - group_gen_pow * group_gen * group_gen); + + std::vector public_input = {group_gen, x, expected_res}; + + typename component_type::params_type params = { + var(0, 0, false, var::column_type::public_input), domain_size, var(0, 1, false, var::column_type::public_input)}; + + auto result_check = [&expected_res](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(expected_res == assignment.var_value(real_res.output)); + }; + + test_component(params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "zkpm_evaluate: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/index_terms_instances/chacha_test.hpp b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/chacha_test.hpp new file mode 100644 index 000000000..2d98bd2e2 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/chacha_test.hpp @@ -0,0 +1,249 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_CHACHA_TEST_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_CHACHA_TEST_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // index terms for ec test + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/chacha.rs#L40 + template + class index_terms_scalars_list_chacha_test; + + template + class index_terms_scalars_list_chacha_test< + snark::plonk_constraint_system> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + constexpr static const std::array argument_types = { + argument_type::Generic, + argument_type::Permutation, + argument_type::Lookup, + argument_type::Generic, + argument_type::Permutation, + }; + + constexpr static const std::array, 5> arguments_values = { + std::make_pair(0, 21), + std::make_pair(21, 3), + std::make_pair(24, 7), + std::make_pair(0, 21), + std::make_pair(21, 3), + }; + + public: + + static std::pair alpha_map(argument_type arg) { + for (std::size_t i = 0; i < argument_types.size(); ++i) { + if (arg == argument_types[i]) { + return arguments_values[i]; + } + } + assert(false); + return std::make_pair(0, 0); + } + + constexpr static const std::size_t lookup_columns = 5; + constexpr static const bool lookup_runtime = false; + constexpr static const bool joint_lookup = false; + + constexpr static const bool poseidon_gate = false; + constexpr static const bool ec_arithmetic_gates = true; + constexpr static const bool generic_gate = true; + constexpr static const bool chacha_gate = true; + + constexpr static const std::size_t poseidon_gates_count = 15; + constexpr static const std::size_t ec_arithmetic_gates_count = 4; + + constexpr static const std::size_t alpha_powers_n = 31; + + constexpr static const std::array coefficient_str = { + "Cell(Variable { col: Index(Poseidon), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(8);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(9);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(10);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(11);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(12);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(13);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(14);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + }; + + constexpr static const char *lookup_gate_str = "Alpha;Pow(24);VanishesOnLast4Rows;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: LookupAggreg, row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(3);Mul;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(3);Gamma;JointCombiner;Cell(Variable { col: Witness(2), row: Curr });Mul;Cell(Variable { col: Witness(1), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Gamma;JointCombiner;Cell(Variable { col: Witness(4), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Gamma;JointCombiner;Cell(Variable { col: Witness(6), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Add;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupTable, row: Curr });Add;Beta;Cell(Variable { col: LookupTable, row: Next });Mul;Add;Mul;Mul;Mul;Mul;Mul;\0"; + + constexpr static const char *var_base_mul_str = "Cell(Variable { col: Witness(5), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(7), row: Next });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Next });Cell(Variable { col: Witness(7), row: Next });Mul;Store;Cell(Variable { col: Witness(2), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(7), row: Next });Mul;Sub;Store;Load(2);Mul;Load(1);Load(1);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(0);Add;Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Load(1);Mul;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Mul;Cell(Variable { col: Witness(3), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(8), row: Next });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Next });Cell(Variable { col: Witness(8), row: Next });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(8), row: Next });Mul;Sub;Store;Load(5);Mul;Load(4);Load(4);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(3);Add;Mul;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Load(4);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(9), row: Curr });Sub;Load(5);Mul;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Mul;Cell(Variable { col: Witness(4), row: Next });Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Next });Mul;Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(9), row: Next });Cell(Variable { col: Witness(9), row: Next });Mul;Store;Cell(Variable { col: Witness(9), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(9), row: Next });Mul;Sub;Store;Load(8);Mul;Load(7);Load(7);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(6);Add;Mul;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Load(7);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(11), row: Curr });Sub;Load(8);Mul;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Next });Mul;Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(15);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(10), row: Next });Cell(Variable { col: Witness(10), row: Next });Mul;Store;Cell(Variable { col: Witness(11), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(10), row: Next });Mul;Sub;Store;Load(11);Mul;Load(10);Load(10);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(9);Add;Mul;Sub;Mul;Add;Alpha;Pow(16);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Load(10);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(13), row: Curr });Sub;Load(11);Mul;Sub;Mul;Add;Alpha;Pow(17);Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(18);Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(11), row: Next });Mul;Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(19);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(11), row: Next });Cell(Variable { col: Witness(11), row: Next });Mul;Store;Cell(Variable { col: Witness(13), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(11), row: Next });Mul;Sub;Store;Load(14);Mul;Load(13);Load(13);Mul;Cell(Variable { col: Witness(0), row: Next });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(12);Add;Mul;Sub;Mul;Add;Alpha;Pow(20);Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(14), row: Curr });Add;Load(13);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Next });Sub;Load(14);Mul;Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_str = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *complete_add_str = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_scalar_str = "Cell(Variable { col: Witness(0), row: Curr });Dup;Add;Dup;Add;Cell(Variable { col: Witness(6), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(7), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(8), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Curr });Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Store;Add;Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(3), row: Curr });Dup;Add;Load(0);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(6), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(7), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(8), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(9), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(10), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(11), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(12), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(13), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Mul;Add;\0"; + + constexpr static const char *constant_term_str = "Cell(Variable { col: Index(Poseidon), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(0), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(1), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(2), row: Curr });Pow(7);Store;Mul;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Mds { row: 1, col: 0 };Load(0);Mul;Mds { row: 1, col: 1 };Load(1);Mul;Add;Mds { row: 1, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(8), row: Curr });Mds { row: 2, col: 0 };Load(0);Mul;Mds { row: 2, col: 1 };Load(1);Mul;Add;Mds { row: 2, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(9), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(6), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(7), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(8), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(10), row: Curr });Mds { row: 1, col: 0 };Load(3);Mul;Mds { row: 1, col: 1 };Load(4);Mul;Add;Mds { row: 1, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(11), row: Curr });Mds { row: 2, col: 0 };Load(3);Mul;Mds { row: 2, col: 1 };Load(4);Mul;Add;Mds { row: 2, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(12), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(9), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(10), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(11), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(13), row: Curr });Mds { row: 1, col: 0 };Load(6);Mul;Mds { row: 1, col: 1 };Load(7);Mul;Add;Mds { row: 1, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(14), row: Curr });Mds { row: 2, col: 0 };Load(6);Mul;Mds { row: 2, col: 1 };Load(7);Mul;Add;Mds { row: 2, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(3), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(12), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(13), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(14), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(4), row: Curr });Mds { row: 1, col: 0 };Load(9);Mul;Mds { row: 1, col: 1 };Load(10);Mul;Add;Mds { row: 1, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(5), row: Curr });Mds { row: 2, col: 0 };Load(9);Mul;Mds { row: 2, col: 1 };Load(10);Mul;Add;Mds { row: 2, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(0), row: Next });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(3), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(4), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(5), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(1), row: Next });Mds { row: 1, col: 0 };Load(12);Mul;Mds { row: 1, col: 1 };Load(13);Mul;Add;Mds { row: 1, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(2), row: Next });Mds { row: 2, col: 0 };Load(12);Mul;Mds { row: 2, col: 1 };Load(13);Mul;Add;Mds { row: 2, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Mul;Alpha;Pow(24);VanishesOnLast4Rows;Cell(Variable { col: LookupAggreg, row: Next });Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(0), row: Curr });Add;Beta;Cell(Variable { col: LookupSorted(0), row: Next });Mul;Add;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(1), row: Next });Add;Beta;Cell(Variable { col: LookupSorted(1), row: Curr });Mul;Add;Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(2), row: Curr });Add;Beta;Cell(Variable { col: LookupSorted(2), row: Next });Mul;Add;Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(3), row: Next });Add;Beta;Cell(Variable { col: LookupSorted(3), row: Curr });Mul;Add;Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(4), row: Curr });Add;Beta;Cell(Variable { col: LookupSorted(4), row: Next });Mul;Add;Mul;Mul;Cell(Variable { col: LookupAggreg, row: Curr });Gamma;JointCombiner;Pow(3);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Gamma;JointCombiner;Pow(3);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Gamma;JointCombiner;Pow(3);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Gamma;JointCombiner;Pow(3);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(4);Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupTable, row: Curr });Add;Beta;Cell(Variable { col: LookupTable, row: Next });Mul;Add;Mul;Mul;Sub;Mul;Mul;Alpha;Pow(25);UnnormalizedLagrangeBasis(0);Cell(Variable { col: LookupAggreg, row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Mul;Add;Alpha;Pow(26);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupAggreg, row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Mul;Add;Alpha;Pow(27);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupSorted(0), row: Curr });Cell(Variable { col: LookupSorted(1), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(28);UnnormalizedLagrangeBasis(0);Cell(Variable { col: LookupSorted(1), row: Curr });Cell(Variable { col: LookupSorted(2), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(29);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupSorted(2), row: Curr });Cell(Variable { col: LookupSorted(3), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(30);UnnormalizedLagrangeBasis(0);Cell(Variable { col: LookupSorted(3), row: Curr });Cell(Variable { col: LookupSorted(4), row: Curr });Sub;Mul;Mul;Add;Add;\0"; + + constexpr static const char *chacha0_str = "Cell(Variable { col: Witness(2), row: Next });Dup;Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(8), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(9), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(10), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(7), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(8), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(9), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(10), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(0), row: Next });Literal 0000000000000000000000000000000000000000000000000000000100000000;Cell(Variable { col: Witness(2), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(11), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(12), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(13), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(14), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(11), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(12), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(13), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(14), row: Next });Mul;Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(3), row: Next });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(4), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(5), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(6), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(3), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(4), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(5), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Add;Cell(Variable { col: Witness(1), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *chacha1_str = "Cell(Variable { col: Witness(2), row: Next });Dup;Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(8), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(9), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(10), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(7), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(8), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(9), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(10), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(0), row: Next });Literal 0000000000000000000000000000000000000000000000000000000100000000;Cell(Variable { col: Witness(2), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(11), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(12), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(13), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(14), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(11), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(12), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(13), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(14), row: Next });Mul;Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(4), row: Next });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(5), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(6), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(3), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(4), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(5), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(3), row: Next });Mul;Add;Cell(Variable { col: Witness(1), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *chacha2_str = "Cell(Variable { col: Witness(2), row: Next });Dup;Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(8), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(9), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(10), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(7), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(8), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(9), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(10), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(0), row: Next });Literal 0000000000000000000000000000000000000000000000000000000100000000;Cell(Variable { col: Witness(2), row: Next });Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(11), row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(12), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(13), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(14), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(11), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(12), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(13), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(14), row: Next });Mul;Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(5), row: Next });Literal 0000000000000000000000000000000000000000000000000000000000000010;Cell(Variable { col: Witness(6), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Cell(Variable { col: Witness(3), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Cell(Variable { col: Witness(4), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Cell(Variable { col: Witness(5), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Cell(Variable { col: Witness(6), row: Curr });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Cell(Variable { col: Witness(3), row: Next });Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Cell(Variable { col: Witness(4), row: Next });Mul;Add;Cell(Variable { col: Witness(1), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *chacha_final_str = "Cell(Variable { col: Witness(5), row: Curr });Dup;Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(6), row: Curr });Dup;Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Dup;Mul;Cell(Variable { col: Witness(7), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(8), row: Curr });Dup;Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(5), row: Next });Dup;Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(6), row: Next });Dup;Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Next });Dup;Mul;Cell(Variable { col: Witness(7), row: Next });Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Next });Dup;Mul;Cell(Variable { col: Witness(8), row: Next });Sub;Mul;Add;Alpha;Pow(8);Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(8), row: Next });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(7), row: Next });Sub;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000010;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(5), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(8), row: Next });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000000100;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(1), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000001000;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000010000;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000000100000;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(5), row: Next });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000001000000;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(6), row: Next });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Mul;Add;Literal 0000000000000000000000000000000000000000000000000000000010000000;Literal 0000000000000000000000000000000000000000000000000000000000000008;Cell(Variable { col: Witness(7), row: Next });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC96987680000001;Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Sub;Mul;Add;\0"; + + private: + + constexpr static const std::array + coefficient_array_size = { + count_delimiters(coefficient_str[0]), count_delimiters(coefficient_str[1]), + count_delimiters(coefficient_str[2]), count_delimiters(coefficient_str[3]), + count_delimiters(coefficient_str[4]), count_delimiters(coefficient_str[5]), + count_delimiters(coefficient_str[6]), count_delimiters(coefficient_str[7]), + count_delimiters(coefficient_str[8]), count_delimiters(coefficient_str[9]), + count_delimiters(coefficient_str[10]), count_delimiters(coefficient_str[11]), + count_delimiters(coefficient_str[12]), count_delimiters(coefficient_str[13]), + count_delimiters(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_array_size = count_delimiters(var_base_mul_str); + + constexpr static const std::size_t endo_mul_array_size = count_delimiters(endo_mul_str); + + constexpr static const std::size_t complete_add_array_size = count_delimiters(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_array_size = count_delimiters(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_array_size = count_delimiters(constant_term_str); + + constexpr static const std::size_t lookup_gate_array_size = count_delimiters(lookup_gate_str); + + constexpr static const std::size_t chacha0_array_size = count_delimiters(chacha0_str); + + constexpr static const std::size_t chacha1_array_size = count_delimiters(chacha1_str); + + constexpr static const std::size_t chacha2_array_size = count_delimiters(chacha2_str); + + constexpr static const std::size_t chacha_final_array_size = count_delimiters(chacha_final_str); + + constexpr static const std::array + coefficient_rows = { + rpn_component_rows(coefficient_str[0]), + rpn_component_rows(coefficient_str[1]), + rpn_component_rows(coefficient_str[2]), + rpn_component_rows(coefficient_str[3]), + rpn_component_rows(coefficient_str[4]), + rpn_component_rows(coefficient_str[5]), + rpn_component_rows(coefficient_str[6]), + rpn_component_rows(coefficient_str[7]), + rpn_component_rows(coefficient_str[8]), + rpn_component_rows(coefficient_str[9]), + rpn_component_rows(coefficient_str[10]), + rpn_component_rows(coefficient_str[11]), + rpn_component_rows(coefficient_str[12]), + rpn_component_rows(coefficient_str[13]), + rpn_component_rows(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_rows = + rpn_component_rows(var_base_mul_str); + + constexpr static const std::size_t endo_mul_rows = + rpn_component_rows(endo_mul_str); + + constexpr static const std::size_t complete_add_rows = + rpn_component_rows(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_rows = + rpn_component_rows(endo_mul_scalar_str); + + constexpr static const std::size_t constant_term_rows = + rpn_component_rows(constant_term_str); + + constexpr static const std::size_t lookup_gate_rows = + rpn_component_rows(lookup_gate_str); + + constexpr static const std::size_t chacha0_gate_rows = + rpn_component_rows(chacha0_str); + + constexpr static const std::size_t chacha1_gate_rows = + rpn_component_rows(chacha1_str); + + constexpr static const std::size_t chacha2_gate_rows = + rpn_component_rows(chacha2_str); + + constexpr static const std::size_t chacha_final_gate_rows = + rpn_component_rows(chacha_final_str); + + public: + + constexpr static const std::size_t size = 24; + constexpr static const std::array terms = {{ + {column_type::Coefficient, 0, coefficient_str[0], coefficient_rows[0]}, + {column_type::Coefficient, 1, coefficient_str[1], coefficient_rows[1]}, + {column_type::Coefficient, 2, coefficient_str[2], coefficient_rows[2]}, + {column_type::Coefficient, 3, coefficient_str[3], coefficient_rows[3]}, + {column_type::Coefficient, 4, coefficient_str[4], coefficient_rows[4]}, + {column_type::Coefficient, 5, coefficient_str[5], coefficient_rows[5]}, + {column_type::Coefficient, 6, coefficient_str[6], coefficient_rows[6]}, + {column_type::Coefficient, 7, coefficient_str[7], coefficient_rows[7]}, + {column_type::Coefficient, 8, coefficient_str[8], coefficient_rows[8]}, + {column_type::Coefficient, 9, coefficient_str[9], coefficient_rows[9]}, + {column_type::Coefficient, 10, coefficient_str[10], coefficient_rows[10]}, + {column_type::Coefficient, 11, coefficient_str[11], coefficient_rows[11]}, + {column_type::Coefficient, 12, coefficient_str[12], coefficient_rows[12]}, + {column_type::Coefficient, 13, coefficient_str[13], coefficient_rows[13]}, + {column_type::Coefficient, 14, coefficient_str[14], coefficient_rows[14]}, + {column_type::VarBaseMul, 0, var_base_mul_str, var_base_mul_rows}, + {column_type::EndoMul, 0, endo_mul_str, endo_mul_rows}, + {column_type::EndoMulScalar, 0, endo_mul_scalar_str, endo_mul_scalar_rows}, + {column_type::CompleteAdd, 0, complete_add_str, complete_add_rows}, + {column_type::LookupKindIndex, 2, lookup_gate_str, lookup_gate_rows}, + {column_type::ChaCha0, 0, chacha0_str, chacha0_gate_rows}, + {column_type::ChaCha1, 0, chacha1_str, chacha1_gate_rows}, + {column_type::ChaCha2, 0, chacha2_str, chacha2_gate_rows}, + {column_type::ChaChaFinal, 0, chacha_final_str, chacha_final_gate_rows}, + }}; + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_LOOKUP_TEST_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms.hpp b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms.hpp new file mode 100644 index 000000000..6d74d7796 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms.hpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_HPP + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // index terms for ec test + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/ec.rs#L15 + template + class index_terms_scalars_list_ec_test; + + template + class index_terms_scalars_list_ec_test< + snark::plonk_constraint_system> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + constexpr static const std::array argument_types = { + argument_type::Permutation, + argument_type::Generic, + }; + + constexpr static const std::array, 2> arguments_values = { + std::make_pair(21, 3), + std::make_pair(0, 21) + }; + + public: + + static std::pair alpha_map(argument_type arg) { + for (std::size_t i = 0; i < argument_types.size(); ++i) { + if (arg == argument_types[i]) { + return arguments_values[i]; + } + } + assert(false); + return std::make_pair(0, 0); + } + + constexpr static const std::size_t lookup_columns = 0; + constexpr static const bool lookup_runtime = false; + constexpr static const bool joint_lookup = false; + constexpr static const bool poseidon_gate = false; + constexpr static const bool ec_arithmetic_gates = true; + constexpr static const bool generic_gate = false; + constexpr static const bool chacha_gate = false; + + constexpr static const std::size_t poseidon_gates_count = 15; + constexpr static const std::size_t ec_arithmetic_gates_count = 4; + + constexpr static const std::size_t alpha_powers_n = 24; + + constexpr static const std::array coefficient_str = { + "Cell(Variable { col: Index(Poseidon), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(8);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(9);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(10);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(11);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(12);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(13);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(14);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + }; + + constexpr static const char *var_base_mul_str = "Cell(Variable { col: Witness(5), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(7), row: Next });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Next });Cell(Variable { col: Witness(7), row: Next });Mul;Store;Cell(Variable { col: Witness(2), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(7), row: Next });Mul;Sub;Store;Load(2);Mul;Load(1);Load(1);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(0);Add;Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Load(1);Mul;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Mul;Cell(Variable { col: Witness(3), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(8), row: Next });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Next });Cell(Variable { col: Witness(8), row: Next });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(8), row: Next });Mul;Sub;Store;Load(5);Mul;Load(4);Load(4);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(3);Add;Mul;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Load(4);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(9), row: Curr });Sub;Load(5);Mul;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Mul;Cell(Variable { col: Witness(4), row: Next });Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Next });Mul;Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(9), row: Next });Cell(Variable { col: Witness(9), row: Next });Mul;Store;Cell(Variable { col: Witness(9), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(9), row: Next });Mul;Sub;Store;Load(8);Mul;Load(7);Load(7);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(6);Add;Mul;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Load(7);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(11), row: Curr });Sub;Load(8);Mul;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Next });Mul;Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(15);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(10), row: Next });Cell(Variable { col: Witness(10), row: Next });Mul;Store;Cell(Variable { col: Witness(11), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(10), row: Next });Mul;Sub;Store;Load(11);Mul;Load(10);Load(10);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(9);Add;Mul;Sub;Mul;Add;Alpha;Pow(16);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Load(10);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(13), row: Curr });Sub;Load(11);Mul;Sub;Mul;Add;Alpha;Pow(17);Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(18);Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(11), row: Next });Mul;Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(19);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(11), row: Next });Cell(Variable { col: Witness(11), row: Next });Mul;Store;Cell(Variable { col: Witness(13), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(11), row: Next });Mul;Sub;Store;Load(14);Mul;Load(13);Load(13);Mul;Cell(Variable { col: Witness(0), row: Next });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(12);Add;Mul;Sub;Mul;Add;Alpha;Pow(20);Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(14), row: Curr });Add;Load(13);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Next });Sub;Load(14);Mul;Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_str = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *complete_add_str = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_scalar_str = "Cell(Variable { col: Witness(0), row: Curr });Dup;Add;Dup;Add;Cell(Variable { col: Witness(6), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(7), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(8), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Curr });Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Store;Add;Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(3), row: Curr });Dup;Add;Load(0);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(6), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(7), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(8), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(9), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(10), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(11), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(12), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(13), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Mul;Add;\0"; + + constexpr static const char *constant_term_str = "Cell(Variable { col: Index(Poseidon), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(0), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(1), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(2), row: Curr });Pow(7);Store;Mul;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Mds { row: 1, col: 0 };Load(0);Mul;Mds { row: 1, col: 1 };Load(1);Mul;Add;Mds { row: 1, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(8), row: Curr });Mds { row: 2, col: 0 };Load(0);Mul;Mds { row: 2, col: 1 };Load(1);Mul;Add;Mds { row: 2, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(9), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(6), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(7), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(8), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(10), row: Curr });Mds { row: 1, col: 0 };Load(3);Mul;Mds { row: 1, col: 1 };Load(4);Mul;Add;Mds { row: 1, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(11), row: Curr });Mds { row: 2, col: 0 };Load(3);Mul;Mds { row: 2, col: 1 };Load(4);Mul;Add;Mds { row: 2, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(12), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(9), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(10), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(11), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(13), row: Curr });Mds { row: 1, col: 0 };Load(6);Mul;Mds { row: 1, col: 1 };Load(7);Mul;Add;Mds { row: 1, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(14), row: Curr });Mds { row: 2, col: 0 };Load(6);Mul;Mds { row: 2, col: 1 };Load(7);Mul;Add;Mds { row: 2, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(3), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(12), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(13), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(14), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(4), row: Curr });Mds { row: 1, col: 0 };Load(9);Mul;Mds { row: 1, col: 1 };Load(10);Mul;Add;Mds { row: 1, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(5), row: Curr });Mds { row: 2, col: 0 };Load(9);Mul;Mds { row: 2, col: 1 };Load(10);Mul;Add;Mds { row: 2, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(0), row: Next });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(3), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(4), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(5), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(1), row: Next });Mds { row: 1, col: 0 };Load(12);Mul;Mds { row: 1, col: 1 };Load(13);Mul;Add;Mds { row: 1, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(2), row: Next });Mds { row: 2, col: 0 };Load(12);Mul;Mds { row: 2, col: 1 };Load(13);Mul;Add;Mds { row: 2, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Mul;\0"; + + private: + + constexpr static const std::array + coefficient_array_size = { + count_delimiters(coefficient_str[0]), count_delimiters(coefficient_str[1]), + count_delimiters(coefficient_str[2]), count_delimiters(coefficient_str[3]), + count_delimiters(coefficient_str[4]), count_delimiters(coefficient_str[5]), + count_delimiters(coefficient_str[6]), count_delimiters(coefficient_str[7]), + count_delimiters(coefficient_str[8]), count_delimiters(coefficient_str[9]), + count_delimiters(coefficient_str[10]), count_delimiters(coefficient_str[11]), + count_delimiters(coefficient_str[12]), count_delimiters(coefficient_str[13]), + count_delimiters(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_array_size = count_delimiters(var_base_mul_str); + + constexpr static const std::size_t endo_mul_array_size = count_delimiters(endo_mul_str); + + constexpr static const std::size_t complete_add_array_size = count_delimiters(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_array_size = count_delimiters(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_array_size = count_delimiters(constant_term_str); + + public: + constexpr static const std::array + coefficient_rows = { + rpn_component_rows(coefficient_str[0]), + rpn_component_rows(coefficient_str[1]), + rpn_component_rows(coefficient_str[2]), + rpn_component_rows(coefficient_str[3]), + rpn_component_rows(coefficient_str[4]), + rpn_component_rows(coefficient_str[5]), + rpn_component_rows(coefficient_str[6]), + rpn_component_rows(coefficient_str[7]), + rpn_component_rows(coefficient_str[8]), + rpn_component_rows(coefficient_str[9]), + rpn_component_rows(coefficient_str[10]), + rpn_component_rows(coefficient_str[11]), + rpn_component_rows(coefficient_str[12]), + rpn_component_rows(coefficient_str[13]), + rpn_component_rows(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_rows = + rpn_component_rows(var_base_mul_str); + + constexpr static const std::size_t endo_mul_rows = + rpn_component_rows(endo_mul_str); + + constexpr static const std::size_t complete_add_rows = + rpn_component_rows(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_rows = + rpn_component_rows(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_rows = + rpn_component_rows(constant_term_str); + + constexpr static const std::size_t size = 19; + constexpr static const std::array terms = {{ + {column_type::Coefficient, 0, coefficient_str[0], coefficient_rows[0]}, + {column_type::Coefficient, 1, coefficient_str[1], coefficient_rows[1]}, + {column_type::Coefficient, 2, coefficient_str[2], coefficient_rows[2]}, + {column_type::Coefficient, 3, coefficient_str[3], coefficient_rows[3]}, + {column_type::Coefficient, 4, coefficient_str[4], coefficient_rows[4]}, + {column_type::Coefficient, 5, coefficient_str[5], coefficient_rows[5]}, + {column_type::Coefficient, 6, coefficient_str[6], coefficient_rows[6]}, + {column_type::Coefficient, 7, coefficient_str[7], coefficient_rows[7]}, + {column_type::Coefficient, 8, coefficient_str[8], coefficient_rows[8]}, + {column_type::Coefficient, 9, coefficient_str[9], coefficient_rows[9]}, + {column_type::Coefficient, 10, coefficient_str[10], coefficient_rows[10]}, + {column_type::Coefficient, 11, coefficient_str[11], coefficient_rows[11]}, + {column_type::Coefficient, 12, coefficient_str[12], coefficient_rows[12]}, + {column_type::Coefficient, 13, coefficient_str[13], coefficient_rows[13]}, + {column_type::Coefficient, 14, coefficient_str[14], coefficient_rows[14]}, + {column_type::VarBaseMul, 0, var_base_mul_str, var_base_mul_rows}, + {column_type::EndoMul, 0, endo_mul_str, endo_mul_rows}, + {column_type::EndoMulScalar, 0, endo_mul_scalar_str, endo_mul_scalar_rows}, + {column_type::CompleteAdd, 0, complete_add_str, complete_add_rows}, + }}; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms_cip.hpp b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms_cip.hpp new file mode 100644 index 000000000..b670478d5 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/ec_index_terms_cip.hpp @@ -0,0 +1,204 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Ekaterina Chukavina +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_CIP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_CIP_HPP + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // index terms for ec test + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/ec.rs#L15 + template + class index_terms_scalars_cip_list_ec_test; + + template + class index_terms_scalars_cip_list_ec_test< + snark::plonk_constraint_system> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + constexpr static const std::array argument_types = { + argument_type::Permutation, + argument_type::Generic, + }; + + constexpr static const std::array, 2> arguments_values = { + std::make_pair(21, 3), + std::make_pair(0, 21) + }; + + public: + + static std::pair alpha_map(argument_type arg) { + for (std::size_t i = 0; i < argument_types.size(); ++i) { + if (arg == argument_types[i]) { + return arguments_values[i]; + } + } + assert(false); + return std::make_pair(0, 0); + } + + constexpr static const std::size_t lookup_columns = 0; + constexpr static const bool lookup_runtime = false; + constexpr static const bool joint_lookup = false; + constexpr static const bool poseidon_gate = false; + constexpr static const bool ec_arithmetic_gates = false; + constexpr static const bool generic_gate = false; + constexpr static const bool chacha_gate = false; + + constexpr static const std::size_t poseidon_gates_count = 15; + constexpr static const std::size_t ec_arithmetic_gates_count = 4; + + constexpr static const std::size_t alpha_powers_n = 24; + + constexpr static const std::array coefficient_str = { + "Cell(Variable { col: Index(Poseidon), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(8);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(9);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(10);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(11);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(12);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(13);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(14);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + }; + + constexpr static const char *var_base_mul_str = "Cell(Variable { col: Witness(5), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(7), row: Next });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Next });Cell(Variable { col: Witness(7), row: Next });Mul;Store;Cell(Variable { col: Witness(2), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(7), row: Next });Mul;Sub;Store;Load(2);Mul;Load(1);Load(1);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(0);Add;Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Load(1);Mul;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Mul;Cell(Variable { col: Witness(3), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(8), row: Next });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Next });Cell(Variable { col: Witness(8), row: Next });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(8), row: Next });Mul;Sub;Store;Load(5);Mul;Load(4);Load(4);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(3);Add;Mul;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Load(4);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(9), row: Curr });Sub;Load(5);Mul;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Mul;Cell(Variable { col: Witness(4), row: Next });Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Next });Mul;Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(9), row: Next });Cell(Variable { col: Witness(9), row: Next });Mul;Store;Cell(Variable { col: Witness(9), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(9), row: Next });Mul;Sub;Store;Load(8);Mul;Load(7);Load(7);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(6);Add;Mul;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Load(7);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(11), row: Curr });Sub;Load(8);Mul;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Next });Mul;Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(15);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(10), row: Next });Cell(Variable { col: Witness(10), row: Next });Mul;Store;Cell(Variable { col: Witness(11), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(10), row: Next });Mul;Sub;Store;Load(11);Mul;Load(10);Load(10);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(9);Add;Mul;Sub;Mul;Add;Alpha;Pow(16);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Load(10);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(13), row: Curr });Sub;Load(11);Mul;Sub;Mul;Add;Alpha;Pow(17);Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(18);Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(11), row: Next });Mul;Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(19);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(11), row: Next });Cell(Variable { col: Witness(11), row: Next });Mul;Store;Cell(Variable { col: Witness(13), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(11), row: Next });Mul;Sub;Store;Load(14);Mul;Load(13);Load(13);Mul;Cell(Variable { col: Witness(0), row: Next });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(12);Add;Mul;Sub;Mul;Add;Alpha;Pow(20);Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(14), row: Curr });Add;Load(13);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Next });Sub;Load(14);Mul;Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_str = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *complete_add_str = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_scalar_str = "Cell(Variable { col: Witness(0), row: Curr });Dup;Add;Dup;Add;Cell(Variable { col: Witness(6), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(7), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(8), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Curr });Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Store;Add;Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(3), row: Curr });Dup;Add;Load(0);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(6), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(7), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(8), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(9), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(10), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(11), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(12), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(13), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Mul;Add;\0"; + + constexpr static const char *constant_term_str = "Cell(Variable { col: Index(Poseidon), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(0), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(1), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(2), row: Curr });Pow(7);Store;Mul;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Mds { row: 1, col: 0 };Load(0);Mul;Mds { row: 1, col: 1 };Load(1);Mul;Add;Mds { row: 1, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(8), row: Curr });Mds { row: 2, col: 0 };Load(0);Mul;Mds { row: 2, col: 1 };Load(1);Mul;Add;Mds { row: 2, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(9), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(6), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(7), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(8), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(10), row: Curr });Mds { row: 1, col: 0 };Load(3);Mul;Mds { row: 1, col: 1 };Load(4);Mul;Add;Mds { row: 1, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(11), row: Curr });Mds { row: 2, col: 0 };Load(3);Mul;Mds { row: 2, col: 1 };Load(4);Mul;Add;Mds { row: 2, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(12), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(9), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(10), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(11), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(13), row: Curr });Mds { row: 1, col: 0 };Load(6);Mul;Mds { row: 1, col: 1 };Load(7);Mul;Add;Mds { row: 1, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(14), row: Curr });Mds { row: 2, col: 0 };Load(6);Mul;Mds { row: 2, col: 1 };Load(7);Mul;Add;Mds { row: 2, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(3), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(12), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(13), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(14), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(4), row: Curr });Mds { row: 1, col: 0 };Load(9);Mul;Mds { row: 1, col: 1 };Load(10);Mul;Add;Mds { row: 1, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(5), row: Curr });Mds { row: 2, col: 0 };Load(9);Mul;Mds { row: 2, col: 1 };Load(10);Mul;Add;Mds { row: 2, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(0), row: Next });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(3), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(4), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(5), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(1), row: Next });Mds { row: 1, col: 0 };Load(12);Mul;Mds { row: 1, col: 1 };Load(13);Mul;Add;Mds { row: 1, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(2), row: Next });Mds { row: 2, col: 0 };Load(12);Mul;Mds { row: 2, col: 1 };Load(13);Mul;Add;Mds { row: 2, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Mul;\0"; + + private: + + constexpr static const std::array + coefficient_array_size = { + count_delimiters(coefficient_str[0]), count_delimiters(coefficient_str[1]), + count_delimiters(coefficient_str[2]), count_delimiters(coefficient_str[3]), + count_delimiters(coefficient_str[4]), count_delimiters(coefficient_str[5]), + count_delimiters(coefficient_str[6]), count_delimiters(coefficient_str[7]), + count_delimiters(coefficient_str[8]), count_delimiters(coefficient_str[9]), + count_delimiters(coefficient_str[10]), count_delimiters(coefficient_str[11]), + count_delimiters(coefficient_str[12]), count_delimiters(coefficient_str[13]), + count_delimiters(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_array_size = count_delimiters(var_base_mul_str); + + constexpr static const std::size_t endo_mul_array_size = count_delimiters(endo_mul_str); + + constexpr static const std::size_t complete_add_array_size = count_delimiters(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_array_size = count_delimiters(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_array_size = count_delimiters(constant_term_str); + + public: + constexpr static const std::array + coefficient_rows = { + rpn_component_rows(coefficient_str[0]), + rpn_component_rows(coefficient_str[1]), + rpn_component_rows(coefficient_str[2]), + rpn_component_rows(coefficient_str[3]), + rpn_component_rows(coefficient_str[4]), + rpn_component_rows(coefficient_str[5]), + rpn_component_rows(coefficient_str[6]), + rpn_component_rows(coefficient_str[7]), + rpn_component_rows(coefficient_str[8]), + rpn_component_rows(coefficient_str[9]), + rpn_component_rows(coefficient_str[10]), + rpn_component_rows(coefficient_str[11]), + rpn_component_rows(coefficient_str[12]), + rpn_component_rows(coefficient_str[13]), + rpn_component_rows(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_rows = + rpn_component_rows(var_base_mul_str); + + constexpr static const std::size_t endo_mul_rows = + rpn_component_rows(endo_mul_str); + + constexpr static const std::size_t complete_add_rows = + rpn_component_rows(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_rows = + rpn_component_rows(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_rows = + rpn_component_rows(constant_term_str); + + constexpr static const std::size_t size = 19; + constexpr static const std::array terms = {{ + {column_type::Coefficient, 0, coefficient_str[0], coefficient_rows[0]}, + {column_type::Coefficient, 1, coefficient_str[1], coefficient_rows[1]}, + {column_type::Coefficient, 2, coefficient_str[2], coefficient_rows[2]}, + {column_type::Coefficient, 3, coefficient_str[3], coefficient_rows[3]}, + {column_type::Coefficient, 4, coefficient_str[4], coefficient_rows[4]}, + {column_type::Coefficient, 5, coefficient_str[5], coefficient_rows[5]}, + {column_type::Coefficient, 6, coefficient_str[6], coefficient_rows[6]}, + {column_type::Coefficient, 7, coefficient_str[7], coefficient_rows[7]}, + {column_type::Coefficient, 8, coefficient_str[8], coefficient_rows[8]}, + {column_type::Coefficient, 9, coefficient_str[9], coefficient_rows[9]}, + {column_type::Coefficient, 10, coefficient_str[10], coefficient_rows[10]}, + {column_type::Coefficient, 11, coefficient_str[11], coefficient_rows[11]}, + {column_type::Coefficient, 12, coefficient_str[12], coefficient_rows[12]}, + {column_type::Coefficient, 13, coefficient_str[13], coefficient_rows[13]}, + {column_type::Coefficient, 14, coefficient_str[14], coefficient_rows[14]}, + {column_type::VarBaseMul, 0, var_base_mul_str, var_base_mul_rows}, + {column_type::EndoMul, 0, endo_mul_str, endo_mul_rows}, + {column_type::EndoMulScalar, 0, endo_mul_scalar_str, endo_mul_scalar_rows}, + {column_type::CompleteAdd, 0, complete_add_str, complete_add_rows}, + }}; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_EC_INDEX_TERMS_CIP_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/index_terms_instances/lookup_test.hpp b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/lookup_test.hpp new file mode 100644 index 000000000..3c33efcd5 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/lookup_test.hpp @@ -0,0 +1,213 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_LOOKUP_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_LOOKUP_TEST_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + // index terms for ec test + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/lookup.rs#L102 + template + class index_terms_scalars_list_lookup_test; + + template + class index_terms_scalars_list_lookup_test< + snark::plonk_constraint_system> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + constexpr static const std::array argument_types = { + argument_type::Permutation, + argument_type::Generic, + argument_type::Lookup, + }; + + constexpr static const std::array, 3> arguments_values = { + std::make_pair(21, 3), + std::make_pair(0, 21), + std::make_pair(24, 6), + }; + + public: + + static std::pair alpha_map(argument_type arg) { + for (std::size_t i = 0; i < argument_types.size(); ++i) { + if (arg == argument_types[i]) { + return arguments_values[i]; + } + } + assert(false); + return std::make_pair(0, 0); + } + + constexpr static const std::size_t lookup_columns = 4; + constexpr static const bool lookup_runtime = false; + constexpr static const bool joint_lookup = true; + + constexpr static const bool poseidon_gate = false; + constexpr static const bool ec_arithmetic_gates = true; + constexpr static const bool generic_gate = false; + constexpr static const bool chacha_gate = false; + + constexpr static const std::size_t poseidon_gates_count = 15; + constexpr static const std::size_t ec_arithmetic_gates_count = 4; + + constexpr static const std::size_t alpha_powers_n = 30; + + constexpr static const std::array coefficient_str = { + "Cell(Variable { col: Index(Poseidon), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(8);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(9);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(10);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(11);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(12);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(13);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(14);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + }; + + constexpr static const char *lookup_gate_str = "Alpha;Pow(24);VanishesOnLast4Rows;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: LookupAggreg, row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(3);Mul;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(3);Gamma;JointCombiner;Cell(Variable { col: Witness(2), row: Curr });Mul;Cell(Variable { col: Witness(1), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Gamma;JointCombiner;Cell(Variable { col: Witness(4), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Gamma;JointCombiner;Cell(Variable { col: Witness(6), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Add;JointCombiner;Pow(2);Cell(Variable { col: Witness(0), row: Curr });Mul;Add;Add;Mul;Add;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupTable, row: Curr });Add;Beta;Cell(Variable { col: LookupTable, row: Next });Mul;Add;Mul;Mul;Mul;Mul;Mul;\0"; + + constexpr static const char *var_base_mul_str = "Cell(Variable { col: Witness(5), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(7), row: Next });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Next });Cell(Variable { col: Witness(7), row: Next });Mul;Store;Cell(Variable { col: Witness(2), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(7), row: Next });Mul;Sub;Store;Load(2);Mul;Load(1);Load(1);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(0);Add;Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Load(1);Mul;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Mul;Cell(Variable { col: Witness(3), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(8), row: Next });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Next });Cell(Variable { col: Witness(8), row: Next });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(8), row: Next });Mul;Sub;Store;Load(5);Mul;Load(4);Load(4);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(3);Add;Mul;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Load(4);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(9), row: Curr });Sub;Load(5);Mul;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Mul;Cell(Variable { col: Witness(4), row: Next });Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Next });Mul;Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(9), row: Next });Cell(Variable { col: Witness(9), row: Next });Mul;Store;Cell(Variable { col: Witness(9), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(9), row: Next });Mul;Sub;Store;Load(8);Mul;Load(7);Load(7);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(6);Add;Mul;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Load(7);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(11), row: Curr });Sub;Load(8);Mul;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Next });Mul;Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(15);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(10), row: Next });Cell(Variable { col: Witness(10), row: Next });Mul;Store;Cell(Variable { col: Witness(11), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(10), row: Next });Mul;Sub;Store;Load(11);Mul;Load(10);Load(10);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(9);Add;Mul;Sub;Mul;Add;Alpha;Pow(16);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Load(10);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(13), row: Curr });Sub;Load(11);Mul;Sub;Mul;Add;Alpha;Pow(17);Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(18);Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(11), row: Next });Mul;Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(19);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(11), row: Next });Cell(Variable { col: Witness(11), row: Next });Mul;Store;Cell(Variable { col: Witness(13), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(11), row: Next });Mul;Sub;Store;Load(14);Mul;Load(13);Load(13);Mul;Cell(Variable { col: Witness(0), row: Next });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(12);Add;Mul;Sub;Mul;Add;Alpha;Pow(20);Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(14), row: Curr });Add;Load(13);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Next });Sub;Load(14);Mul;Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_str = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *complete_add_str = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_scalar_str = "Cell(Variable { col: Witness(0), row: Curr });Dup;Add;Dup;Add;Cell(Variable { col: Witness(6), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(7), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(8), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Curr });Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Store;Add;Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(3), row: Curr });Dup;Add;Load(0);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(6), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(7), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(8), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(9), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(10), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(11), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(12), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(13), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Mul;Add;\0"; + + constexpr static const char *constant_term_str = "Cell(Variable { col: Index(Poseidon), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(0), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(1), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(2), row: Curr });Pow(7);Store;Mul;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Mds { row: 1, col: 0 };Load(0);Mul;Mds { row: 1, col: 1 };Load(1);Mul;Add;Mds { row: 1, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(8), row: Curr });Mds { row: 2, col: 0 };Load(0);Mul;Mds { row: 2, col: 1 };Load(1);Mul;Add;Mds { row: 2, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(9), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(6), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(7), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(8), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(10), row: Curr });Mds { row: 1, col: 0 };Load(3);Mul;Mds { row: 1, col: 1 };Load(4);Mul;Add;Mds { row: 1, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(11), row: Curr });Mds { row: 2, col: 0 };Load(3);Mul;Mds { row: 2, col: 1 };Load(4);Mul;Add;Mds { row: 2, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(12), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(9), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(10), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(11), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(13), row: Curr });Mds { row: 1, col: 0 };Load(6);Mul;Mds { row: 1, col: 1 };Load(7);Mul;Add;Mds { row: 1, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(14), row: Curr });Mds { row: 2, col: 0 };Load(6);Mul;Mds { row: 2, col: 1 };Load(7);Mul;Add;Mds { row: 2, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(3), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(12), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(13), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(14), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(4), row: Curr });Mds { row: 1, col: 0 };Load(9);Mul;Mds { row: 1, col: 1 };Load(10);Mul;Add;Mds { row: 1, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(5), row: Curr });Mds { row: 2, col: 0 };Load(9);Mul;Mds { row: 2, col: 1 };Load(10);Mul;Add;Mds { row: 2, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(0), row: Next });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(3), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(4), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(5), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(1), row: Next });Mds { row: 1, col: 0 };Load(12);Mul;Mds { row: 1, col: 1 };Load(13);Mul;Add;Mds { row: 1, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(2), row: Next });Mds { row: 2, col: 0 };Load(12);Mul;Mds { row: 2, col: 1 };Load(13);Mul;Add;Mds { row: 2, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Mul;Alpha;Pow(24);VanishesOnLast4Rows;Cell(Variable { col: LookupAggreg, row: Next });Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(0), row: Curr });Add;Beta;Cell(Variable { col: LookupSorted(0), row: Next });Mul;Add;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(1), row: Next });Add;Beta;Cell(Variable { col: LookupSorted(1), row: Curr });Mul;Add;Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(2), row: Curr });Add;Beta;Cell(Variable { col: LookupSorted(2), row: Next });Mul;Add;Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupSorted(3), row: Next });Add;Beta;Cell(Variable { col: LookupSorted(3), row: Curr });Mul;Add;Mul;Mul;Cell(Variable { col: LookupAggreg, row: Curr });Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Gamma;JointCombiner;Pow(2);Literal 0000000000000000000000000000000000000000000000000000000000000000;Mul;Add;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Beta;Add;Pow(3);Mul;Gamma;Beta;Literal 0000000000000000000000000000000000000000000000000000000000000001;Add;Mul;Cell(Variable { col: LookupTable, row: Curr });Add;Beta;Cell(Variable { col: LookupTable, row: Next });Mul;Add;Mul;Mul;Sub;Mul;Mul;Alpha;Pow(25);UnnormalizedLagrangeBasis(0);Cell(Variable { col: LookupAggreg, row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Mul;Add;Alpha;Pow(26);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupAggreg, row: Curr });Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Mul;Add;Alpha;Pow(27);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupSorted(0), row: Curr });Cell(Variable { col: LookupSorted(1), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(28);UnnormalizedLagrangeBasis(0);Cell(Variable { col: LookupSorted(1), row: Curr });Cell(Variable { col: LookupSorted(2), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(29);UnnormalizedLagrangeBasis(-4);Cell(Variable { col: LookupSorted(2), row: Curr });Cell(Variable { col: LookupSorted(3), row: Curr });Sub;Mul;Mul;Add;Add;\0"; + + private: + + constexpr static const std::array + coefficient_array_size = { + count_delimiters(coefficient_str[0]), count_delimiters(coefficient_str[1]), + count_delimiters(coefficient_str[2]), count_delimiters(coefficient_str[3]), + count_delimiters(coefficient_str[4]), count_delimiters(coefficient_str[5]), + count_delimiters(coefficient_str[6]), count_delimiters(coefficient_str[7]), + count_delimiters(coefficient_str[8]), count_delimiters(coefficient_str[9]), + count_delimiters(coefficient_str[10]), count_delimiters(coefficient_str[11]), + count_delimiters(coefficient_str[12]), count_delimiters(coefficient_str[13]), + count_delimiters(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_array_size = count_delimiters(var_base_mul_str); + + constexpr static const std::size_t endo_mul_array_size = count_delimiters(endo_mul_str); + + constexpr static const std::size_t complete_add_array_size = count_delimiters(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_array_size = count_delimiters(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_array_size = count_delimiters(constant_term_str); + + constexpr static const std::size_t lookup_gate_array_size = count_delimiters(lookup_gate_str); + + constexpr static const std::array + coefficient_rows = { + rpn_component_rows(coefficient_str[0]), + rpn_component_rows(coefficient_str[1]), + rpn_component_rows(coefficient_str[2]), + rpn_component_rows(coefficient_str[3]), + rpn_component_rows(coefficient_str[4]), + rpn_component_rows(coefficient_str[5]), + rpn_component_rows(coefficient_str[6]), + rpn_component_rows(coefficient_str[7]), + rpn_component_rows(coefficient_str[8]), + rpn_component_rows(coefficient_str[9]), + rpn_component_rows(coefficient_str[10]), + rpn_component_rows(coefficient_str[11]), + rpn_component_rows(coefficient_str[12]), + rpn_component_rows(coefficient_str[13]), + rpn_component_rows(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_rows = + rpn_component_rows(var_base_mul_str); + + constexpr static const std::size_t endo_mul_rows = + rpn_component_rows(endo_mul_str); + + constexpr static const std::size_t complete_add_rows = + rpn_component_rows(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_rows = + rpn_component_rows(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_rows = + rpn_component_rows(constant_term_str); + + constexpr static const std::size_t lookup_gate_rows = + rpn_component_rows(lookup_gate_str); + + public: + + constexpr static const std::size_t size = 20; + constexpr static const std::array terms = {{ + {column_type::Coefficient, 0, coefficient_str[0], coefficient_rows[0]}, + {column_type::Coefficient, 1, coefficient_str[1], coefficient_rows[1]}, + {column_type::Coefficient, 2, coefficient_str[2], coefficient_rows[2]}, + {column_type::Coefficient, 3, coefficient_str[3], coefficient_rows[3]}, + {column_type::Coefficient, 4, coefficient_str[4], coefficient_rows[4]}, + {column_type::Coefficient, 5, coefficient_str[5], coefficient_rows[5]}, + {column_type::Coefficient, 6, coefficient_str[6], coefficient_rows[6]}, + {column_type::Coefficient, 7, coefficient_str[7], coefficient_rows[7]}, + {column_type::Coefficient, 8, coefficient_str[8], coefficient_rows[8]}, + {column_type::Coefficient, 9, coefficient_str[9], coefficient_rows[9]}, + {column_type::Coefficient, 10, coefficient_str[10], coefficient_rows[10]}, + {column_type::Coefficient, 11, coefficient_str[11], coefficient_rows[11]}, + {column_type::Coefficient, 12, coefficient_str[12], coefficient_rows[12]}, + {column_type::Coefficient, 13, coefficient_str[13], coefficient_rows[13]}, + {column_type::Coefficient, 14, coefficient_str[14], coefficient_rows[14]}, + {column_type::VarBaseMul, 0, var_base_mul_str, var_base_mul_rows}, + {column_type::EndoMul, 0, endo_mul_str, endo_mul_rows}, + {column_type::EndoMulScalar, 0, endo_mul_scalar_str, endo_mul_scalar_rows}, + {column_type::CompleteAdd, 0, complete_add_str, complete_add_rows}, + {column_type::LookupKindIndex, 2, lookup_gate_str, lookup_gate_rows}, + }}; + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_LOOKUP_TEST_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/index_terms_instances/recursion_test.hpp b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/recursion_test.hpp new file mode 100644 index 000000000..82515c08e --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/index_terms_instances/recursion_test.hpp @@ -0,0 +1,210 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_RECURSION_TEST_HPP +#define CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_RECURSION_TEST_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + + // index terms for ec test + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/recursion.rs#L15 + template + class index_terms_scalars_list_chacha_test; + + template + class index_terms_scalars_list_chacha_test< + snark::plonk_constraint_system> { + + typedef snark::plonk_constraint_system + ArithmetizationType; + + constexpr static const std::array argument_types = { + argument_type::Generic, + argument_type::Permutation, + argument_type::Generic, + argument_type::Permutation, + }; + + constexpr static const std::array, 4> arguments_values = { + std::make_pair(0, 21), + std::make_pair(21, 3), + std::make_pair(0, 21), + std::make_pair(21, 3), + }; + + public: + + static std::pair alpha_map(argument_type arg) { + for (std::size_t i = 0; i < argument_types.size(); ++i) { + if (arg == argument_types[i]) { + return arguments_values[i]; + } + } + assert(false); + return std::make_pair(0, 0); + } + + constexpr static const std::size_t lookup_columns = 5; + constexpr static const bool lookup_runtime = false; + constexpr static const bool joint_lookup = false; + + constexpr static const bool poseidon_gate = false; + constexpr static const bool ec_arithmetic_gates = true; + constexpr static const bool generic_gate = false; + constexpr static const bool chacha_gate = false; + + constexpr static const std::size_t poseidon_gates_count = 15; + constexpr static const std::size_t ec_arithmetic_gates_count = 4; + + constexpr static const std::size_t alpha_powers_n = 24; + + constexpr static const std::array coefficient_str = { + "Cell(Variable { col: Index(Poseidon), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(8);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(9);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(10);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(11);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(12);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(13);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + "Cell(Variable { col: Index(Poseidon), row: Curr });Alpha;Pow(14);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Mul;Mul;\0", + }; + + constexpr static const char *var_base_mul_str = "Cell(Variable { col: Witness(5), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Dup;Add;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Mul;Cell(Variable { col: Witness(2), row: Next });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(7), row: Next });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(2), row: Next });Cell(Variable { col: Witness(2), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Next });Cell(Variable { col: Witness(7), row: Next });Mul;Store;Cell(Variable { col: Witness(2), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(7), row: Next });Mul;Sub;Store;Load(2);Mul;Load(1);Load(1);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(0);Add;Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Curr });Add;Load(1);Mul;Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Mul;Cell(Variable { col: Witness(3), row: Next });Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(8), row: Next });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(3), row: Next });Cell(Variable { col: Witness(3), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Next });Cell(Variable { col: Witness(8), row: Next });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(8), row: Next });Mul;Sub;Store;Load(5);Mul;Load(4);Load(4);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(3);Add;Mul;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Add;Load(4);Mul;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(9), row: Curr });Sub;Load(5);Mul;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Mul;Cell(Variable { col: Witness(4), row: Next });Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Next });Mul;Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(4), row: Next });Cell(Variable { col: Witness(4), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(9), row: Next });Cell(Variable { col: Witness(9), row: Next });Mul;Store;Cell(Variable { col: Witness(9), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(9), row: Next });Mul;Sub;Store;Load(8);Mul;Load(7);Load(7);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(6);Add;Mul;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(10), row: Curr });Add;Load(7);Mul;Cell(Variable { col: Witness(9), row: Curr });Cell(Variable { col: Witness(11), row: Curr });Sub;Load(8);Mul;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Mul;Cell(Variable { col: Witness(5), row: Next });Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Next });Mul;Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(5), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(15);Cell(Variable { col: Witness(12), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(10), row: Next });Cell(Variable { col: Witness(10), row: Next });Mul;Store;Cell(Variable { col: Witness(11), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(10), row: Next });Mul;Sub;Store;Load(11);Mul;Load(10);Load(10);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(9);Add;Mul;Sub;Mul;Add;Alpha;Pow(16);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(12), row: Curr });Add;Load(10);Mul;Cell(Variable { col: Witness(11), row: Curr });Cell(Variable { col: Witness(13), row: Curr });Sub;Load(11);Mul;Sub;Mul;Add;Alpha;Pow(17);Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Mul;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;Alpha;Pow(18);Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Cell(Variable { col: Witness(11), row: Next });Mul;Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(6), row: Next });Cell(Variable { col: Witness(6), row: Next });Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Sub;Sub;Mul;Add;Alpha;Pow(19);Cell(Variable { col: Witness(14), row: Curr });Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(11), row: Next });Cell(Variable { col: Witness(11), row: Next });Mul;Store;Cell(Variable { col: Witness(13), row: Curr });Sub;Cell(Variable { col: Witness(0), row: Curr });Sub;Sub;Store;Cell(Variable { col: Witness(11), row: Next });Mul;Sub;Store;Load(14);Mul;Load(13);Load(13);Mul;Cell(Variable { col: Witness(0), row: Next });Cell(Variable { col: Witness(0), row: Curr });Sub;Load(12);Add;Mul;Sub;Mul;Add;Alpha;Pow(20);Cell(Variable { col: Witness(1), row: Next });Cell(Variable { col: Witness(14), row: Curr });Add;Load(13);Mul;Cell(Variable { col: Witness(13), row: Curr });Cell(Variable { col: Witness(0), row: Next });Sub;Load(14);Mul;Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_str = "Cell(Variable { col: Witness(11), row: Curr });Dup;Mul;Cell(Variable { col: Witness(11), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(12), row: Curr });Dup;Mul;Cell(Variable { col: Witness(12), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(13), row: Curr });Dup;Mul;Cell(Variable { col: Witness(13), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(14), row: Curr });Dup;Mul;Cell(Variable { col: Witness(14), row: Curr });Sub;Mul;Add;Alpha;Pow(4);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(11), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(4), row: Curr });Sub;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(12), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(4), row: Curr });Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Dup;Mul;Store;Sub;Load(0);Add;Cell(Variable { col: Witness(4), row: Curr });Cell(Variable { col: Witness(7), row: Curr });Sub;Store;Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(5), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(5), row: Curr });Dup;Add;Load(2);Mul;Sub;Mul;Add;Alpha;Pow(6);Load(3);Dup;Mul;Load(2);Dup;Mul;Load(1);Load(0);Sub;Cell(Variable { col: Witness(7), row: Curr });Add;Mul;Sub;Mul;Add;Alpha;Pow(7);Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(13), row: Curr });EndoCoefficient;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Mul;Add;Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Cell(Variable { col: Witness(7), row: Curr });Sub;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(14), row: Curr });Dup;Add;Literal 0000000000000000000000000000000000000000000000000000000000000001;Sub;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(8), row: Curr });Sub;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(7), row: Curr });Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Dup;Mul;Store;Sub;Load(4);Add;Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(4), row: Next });Sub;Store;Cell(Variable { col: Witness(10), row: Curr });Mul;Cell(Variable { col: Witness(5), row: Next });Cell(Variable { col: Witness(8), row: Curr });Add;Store;Add;Mul;Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Load(6);Mul;Sub;Mul;Add;Alpha;Pow(9);Load(7);Dup;Mul;Load(6);Dup;Mul;Load(5);Load(4);Sub;Cell(Variable { col: Witness(4), row: Next });Add;Mul;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(6), row: Curr });Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Dup;Add;Cell(Variable { col: Witness(14), row: Curr });Add;Cell(Variable { col: Witness(6), row: Next });Sub;Mul;Add;\0"; + + constexpr static const char *complete_add_str = "Cell(Variable { col: Witness(10), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Sub;Store;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Load(0);Mul;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Dup;Add;Cell(Variable { col: Witness(1), row: Curr });Mul;Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Mul;Store;Dup;Add;Sub;Load(1);Sub;Mul;Literal 0000000000000000000000000000000000000000000000000000000000000001;Cell(Variable { col: Witness(7), row: Curr });Sub;Load(0);Cell(Variable { col: Witness(8), row: Curr });Mul;Cell(Variable { col: Witness(3), row: Curr });Cell(Variable { col: Witness(1), row: Curr });Sub;Store;Sub;Mul;Add;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(2), row: Curr });Add;Cell(Variable { col: Witness(4), row: Curr });Add;Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(8), row: Curr });Mul;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(8), row: Curr });Cell(Variable { col: Witness(0), row: Curr });Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Cell(Variable { col: Witness(1), row: Curr });Sub;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(5);Load(2);Cell(Variable { col: Witness(7), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Mul;Add;Alpha;Pow(6);Load(2);Cell(Variable { col: Witness(9), row: Curr });Mul;Cell(Variable { col: Witness(6), row: Curr });Sub;Mul;Add;\0"; + + constexpr static const char *endo_mul_scalar_str = "Cell(Variable { col: Witness(0), row: Curr });Dup;Add;Dup;Add;Cell(Variable { col: Witness(6), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(7), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(8), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(9), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(10), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(11), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(12), row: Curr });Add;Dup;Add;Dup;Add;Cell(Variable { col: Witness(13), row: Curr });Add;Cell(Variable { col: Witness(1), row: Curr });Sub;Alpha;Pow(1);Cell(Variable { col: Witness(2), row: Curr });Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Store;Add;Dup;Add;Literal 1555555555555555555555555555555560C232FEADC45309330F104F00000001;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 2000000000000000000000000000000011234C7E04A67C8DCC9698767FFFFFFE;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB061197F56E229849987882780000002;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Store;Add;Cell(Variable { col: Witness(4), row: Curr });Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(3), row: Curr });Dup;Add;Load(0);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(1);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(2);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(3);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(4);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(5);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(6);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Dup;Add;Load(7);Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 0000000000000000000000000000000000000000000000000000000000000003;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ED00000000;Add;Add;Add;Cell(Variable { col: Witness(5), row: Curr });Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(6), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(6), row: Curr });Mul;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(7), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(7), row: Curr });Mul;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(8), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(8), row: Curr });Mul;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(9), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(9), row: Curr });Mul;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(10), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(10), row: Curr });Mul;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(11), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(11), row: Curr });Mul;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(12), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(12), row: Curr });Mul;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(13), row: Curr });Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 000000000000000000000000000000000000000000000000000000000000000B;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Literal 40000000000000000000000000000000224698FC094CF91B992D30ECFFFFFFFB;Add;Cell(Variable { col: Witness(13), row: Curr });Mul;Mul;Add;\0"; + + constexpr static const char *constant_term_str = "Cell(Variable { col: Index(Poseidon), row: Curr });Cell(Variable { col: Witness(6), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(0), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(1), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(2), row: Curr });Pow(7);Store;Mul;Add;Sub;Alpha;Pow(1);Cell(Variable { col: Witness(7), row: Curr });Mds { row: 1, col: 0 };Load(0);Mul;Mds { row: 1, col: 1 };Load(1);Mul;Add;Mds { row: 1, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(2);Cell(Variable { col: Witness(8), row: Curr });Mds { row: 2, col: 0 };Load(0);Mul;Mds { row: 2, col: 1 };Load(1);Mul;Add;Mds { row: 2, col: 2 };Load(2);Mul;Add;Sub;Mul;Add;Alpha;Pow(3);Cell(Variable { col: Witness(9), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(6), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(7), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(8), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(4);Cell(Variable { col: Witness(10), row: Curr });Mds { row: 1, col: 0 };Load(3);Mul;Mds { row: 1, col: 1 };Load(4);Mul;Add;Mds { row: 1, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(5);Cell(Variable { col: Witness(11), row: Curr });Mds { row: 2, col: 0 };Load(3);Mul;Mds { row: 2, col: 1 };Load(4);Mul;Add;Mds { row: 2, col: 2 };Load(5);Mul;Add;Sub;Mul;Add;Alpha;Pow(6);Cell(Variable { col: Witness(12), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(9), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(10), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(11), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(7);Cell(Variable { col: Witness(13), row: Curr });Mds { row: 1, col: 0 };Load(6);Mul;Mds { row: 1, col: 1 };Load(7);Mul;Add;Mds { row: 1, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(8);Cell(Variable { col: Witness(14), row: Curr });Mds { row: 2, col: 0 };Load(6);Mul;Mds { row: 2, col: 1 };Load(7);Mul;Add;Mds { row: 2, col: 2 };Load(8);Mul;Add;Sub;Mul;Add;Alpha;Pow(9);Cell(Variable { col: Witness(3), row: Curr });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(12), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(13), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(14), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(10);Cell(Variable { col: Witness(4), row: Curr });Mds { row: 1, col: 0 };Load(9);Mul;Mds { row: 1, col: 1 };Load(10);Mul;Add;Mds { row: 1, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(11);Cell(Variable { col: Witness(5), row: Curr });Mds { row: 2, col: 0 };Load(9);Mul;Mds { row: 2, col: 1 };Load(10);Mul;Add;Mds { row: 2, col: 2 };Load(11);Mul;Add;Sub;Mul;Add;Alpha;Pow(12);Cell(Variable { col: Witness(0), row: Next });Mds { row: 0, col: 0 };Cell(Variable { col: Witness(3), row: Curr });Pow(7);Store;Mul;Mds { row: 0, col: 1 };Cell(Variable { col: Witness(4), row: Curr });Pow(7);Store;Mul;Add;Mds { row: 0, col: 2 };Cell(Variable { col: Witness(5), row: Curr });Pow(7);Store;Mul;Add;Sub;Mul;Add;Alpha;Pow(13);Cell(Variable { col: Witness(1), row: Next });Mds { row: 1, col: 0 };Load(12);Mul;Mds { row: 1, col: 1 };Load(13);Mul;Add;Mds { row: 1, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Alpha;Pow(14);Cell(Variable { col: Witness(2), row: Next });Mds { row: 2, col: 0 };Load(12);Mul;Mds { row: 2, col: 1 };Load(13);Mul;Add;Mds { row: 2, col: 2 };Load(14);Mul;Add;Sub;Mul;Add;Mul;\0"; + + private: + + constexpr static const std::array + coefficient_array_size = { + count_delimiters(coefficient_str[0]), count_delimiters(coefficient_str[1]), + count_delimiters(coefficient_str[2]), count_delimiters(coefficient_str[3]), + count_delimiters(coefficient_str[4]), count_delimiters(coefficient_str[5]), + count_delimiters(coefficient_str[6]), count_delimiters(coefficient_str[7]), + count_delimiters(coefficient_str[8]), count_delimiters(coefficient_str[9]), + count_delimiters(coefficient_str[10]), count_delimiters(coefficient_str[11]), + count_delimiters(coefficient_str[12]), count_delimiters(coefficient_str[13]), + count_delimiters(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_array_size = count_delimiters(var_base_mul_str); + + constexpr static const std::size_t endo_mul_array_size = count_delimiters(endo_mul_str); + + constexpr static const std::size_t complete_add_array_size = count_delimiters(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_array_size = count_delimiters(endo_mul_scalar_str); + + constexpr static const std::size_t constatnt_term_array_size = count_delimiters(constant_term_str); + + constexpr static const std::array + coefficient_rows = { + rpn_component_rows(coefficient_str[0]), + rpn_component_rows(coefficient_str[1]), + rpn_component_rows(coefficient_str[2]), + rpn_component_rows(coefficient_str[3]), + rpn_component_rows(coefficient_str[4]), + rpn_component_rows(coefficient_str[5]), + rpn_component_rows(coefficient_str[6]), + rpn_component_rows(coefficient_str[7]), + rpn_component_rows(coefficient_str[8]), + rpn_component_rows(coefficient_str[9]), + rpn_component_rows(coefficient_str[10]), + rpn_component_rows(coefficient_str[11]), + rpn_component_rows(coefficient_str[12]), + rpn_component_rows(coefficient_str[13]), + rpn_component_rows(coefficient_str[14]) + }; + + constexpr static const std::size_t var_base_mul_rows = + rpn_component_rows(var_base_mul_str); + + constexpr static const std::size_t endo_mul_rows = + rpn_component_rows(endo_mul_str); + + constexpr static const std::size_t complete_add_rows = + rpn_component_rows(complete_add_str); + + constexpr static const std::size_t endo_mul_scalar_rows = + rpn_component_rows(endo_mul_scalar_str); + + constexpr static const std::size_t constant_term_rows = + rpn_component_rows(constant_term_str); + + constexpr static const std::size_t lookup_gate_rows = + rpn_component_rows(lookup_gate_str); + + public: + + constexpr static const std::size_t size = 19; + constexpr static const std::array terms = {{ + {column_type::Coefficient, 0, coefficient_str[0], coefficient_rows[0]}, + {column_type::Coefficient, 1, coefficient_str[1], coefficient_rows[1]}, + {column_type::Coefficient, 2, coefficient_str[2], coefficient_rows[2]}, + {column_type::Coefficient, 3, coefficient_str[3], coefficient_rows[3]}, + {column_type::Coefficient, 4, coefficient_str[4], coefficient_rows[4]}, + {column_type::Coefficient, 5, coefficient_str[5], coefficient_rows[5]}, + {column_type::Coefficient, 6, coefficient_str[6], coefficient_rows[6]}, + {column_type::Coefficient, 7, coefficient_str[7], coefficient_rows[7]}, + {column_type::Coefficient, 8, coefficient_str[8], coefficient_rows[8]}, + {column_type::Coefficient, 9, coefficient_str[9], coefficient_rows[9]}, + {column_type::Coefficient, 10, coefficient_str[10], coefficient_rows[10]}, + {column_type::Coefficient, 11, coefficient_str[11], coefficient_rows[11]}, + {column_type::Coefficient, 12, coefficient_str[12], coefficient_rows[12]}, + {column_type::Coefficient, 13, coefficient_str[13], coefficient_rows[13]}, + {column_type::Coefficient, 14, coefficient_str[14], coefficient_rows[14]}, + {column_type::VarBaseMul, 0, var_base_mul_str, var_base_mul_rows}, + {column_type::EndoMul, 0, endo_mul_str, endo_mul_rows}, + {column_type::EndoMulScalar, 0, endo_mul_scalar_str, endo_mul_scalar_rows}, + {column_type::CompleteAdd, 0, complete_add_str, complete_add_rows}, + }}; + }; + } // namespace components + } // namespace zk + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_KIMCHI_DETAIL_CONSTRAINTS_INDEX_TERMS_INSTANCES_LOOKUP_TEST_HPP \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/oracles_scalar.cpp b/libs/blueprint/test/verifiers/kimchi/oracles_scalar.cpp new file mode 100644 index 000000000..0640f3b3b --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/oracles_scalar.cpp @@ -0,0 +1,207 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_oracles_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_oracles_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + // ft_eval + public_input.push_back(algebra::random_element()); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_oracles_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + zk::components::kimchi_verifier_index_scalar verifier_index; + typename BlueprintFieldType::value_type omega = + 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + // verifier_index.zkpm = {0x2C46205451F6C3BBEA4BABACBEE609ECF1039A903C42BFF639EDC5BA33356332_cppui_modular256, + // 0x1764D9CB4C64EBA9A150920807637D458919CB6948821F4D15EB1994EADF9CE3_cppui_modular256, + // 0x0140117C8BBC4CE4644A58F7007148577782213065BB9699BF5C391FBE1B3E6D_cppui_modular256, + // 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular256}; + std::size_t domain_size = 128; + verifier_index.domain_size = domain_size; + verifier_index.omega = var(0, 6, false, var::column_type::public_input); + + using component_type = + zk::components::oracles_scalar; + + zk::snark::pickles_proof kimchi_proof = test_proof(); + + typename BlueprintFieldType::value_type joint_combiner = 0; + typename BlueprintFieldType::value_type beta = 0; + typename BlueprintFieldType::value_type gamma = 0; + typename BlueprintFieldType::value_type alpha = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type fq_digest = + 0x01D4E77CCD66755BDDFDBB6E4E8D8D17A6708B9CB56654D12070BD7BF4A5B33B_cppui_modular256; + typename BlueprintFieldType::value_type expected_alpha = + 0x23A8600917236F0E644D49DD5E6CA89537CE3047DA7E29D2A7B8CA6006616092_cppui_modular256; + std::cout << "Expected alpha: " << expected_alpha.data << std::endl; + typename BlueprintFieldType::value_type expected_zeta = + 0x3D0F1F3A3D07DC73FBDF3718FFE270122AA367FB5BA667AD4A4AB81167D21BE4_cppui_modular256; + std::cout << "Expected zeta: " << expected_zeta.data << std::endl; + + zk::components::kimchi_proof_scalar proof; + std::array challenges; + typename zk::components::binding::fq_sponge_output + fq_output = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), + var(0, 5, false, var::column_type::public_input), + challenges}; + + std::vector public_input = {joint_combiner, beta, gamma, alpha, + zeta, fq_digest, omega}; + + // TODO prepare real data + for (std::size_t i = 0; i < public_input_size; i++) { + typename BlueprintFieldType::value_type tmp = algebra::random_element(); + public_input.push_back(tmp); + proof.public_input[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + for (std::size_t i = 0; i < kimchi_params::prev_challenges_size; i++) { + for (std::size_t j = 0; j < eval_rounds; j++) { + typename BlueprintFieldType::value_type tmp = algebra::random_element(); + public_input.push_back(tmp); + proof.prev_challenges[i][j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + } + + prepare_proof(kimchi_proof, proof, public_input); + + typename component_type::params_type params = {verifier_index, proof, fq_output}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/prepare_batch_scalar.cpp b/libs/blueprint/test/verifiers/kimchi/prepare_batch_scalar.cpp new file mode 100644 index 000000000..a6db5aee8 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/prepare_batch_scalar.cpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_prepare_batch_scalar_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_prepare_batch_scalar_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // ft_eval + public_input.push_back(algebra::random_element()); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_prepare_batch_scalar_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + zk::components::kimchi_verifier_index_scalar verifier_index; + typename BlueprintFieldType::value_type omega = + 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + // verifier_index.zkpm = {0x2C46205451F6C3BBEA4BABACBEE609ECF1039A903C42BFF639EDC5BA33356332_cppui_modular256, + // 0x1764D9CB4C64EBA9A150920807637D458919CB6948821F4D15EB1994EADF9CE3_cppui_modular256, + // 0x0140117C8BBC4CE4644A58F7007148577782213065BB9699BF5C391FBE1B3E6D_cppui_modular256, + // 0x0000000000000000000000000000000000000000000000000000000000000001_cppui_modular256}; + std::size_t domain_size = 128; + verifier_index.domain_size = domain_size; + verifier_index.omega = var(0, 6, false, var::column_type::public_input); + + using component_type = + zk::components::prepare_batch_scalar; + + zk::snark::pickles_proof kimchi_proof = test_proof(); + + typename BlueprintFieldType::value_type joint_combiner = 0; + typename BlueprintFieldType::value_type beta = 0; + typename BlueprintFieldType::value_type gamma = 0; + typename BlueprintFieldType::value_type alpha = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type fq_digest = + 0x01D4E77CCD66755BDDFDBB6E4E8D8D17A6708B9CB56654D12070BD7BF4A5B33B_cppui_modular256; + + zk::components::kimchi_proof_scalar proof; + std::array challenges; + typename zk::components::binding::fq_sponge_output + fq_output = {var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), + var(0, 5, false, var::column_type::public_input), + challenges}; + + std::vector public_input = {joint_combiner, beta, gamma, alpha, zeta, + fq_digest, + // verifier_index (6+) + omega}; + + // TODO prepare real data + for (std::size_t i = 0; i < public_input_size; i++) { + typename BlueprintFieldType::value_type tmp = algebra::random_element(); + public_input.push_back(tmp); + proof.public_input[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + for (std::size_t i = 0; i < kimchi_params::prev_challenges_size; i++) { + for (std::size_t j = 0; j < eval_rounds; j++) { + typename BlueprintFieldType::value_type tmp = algebra::random_element(); + public_input.push_back(tmp); + proof.prev_challenges[i][j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + } + + prepare_proof(kimchi_proof, proof, public_input); + + typename component_type::params_type params = {verifier_index, proof, fq_output}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/proof_data.hpp b/libs/blueprint/test/verifiers/kimchi/proof_data.hpp new file mode 100644 index 000000000..4dac84b28 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/proof_data.hpp @@ -0,0 +1,802 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PICKLES_VERIFIER_PROOF_TEST_DATA_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_PICKLES_VERIFIER_PROOF_TEST_DATA_HPP + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +using namespace nil::crypto3; + +nil::crypto3::zk::snark::pickles_proof test_proof() { + + using curve_type = algebra::curves::vesta; + using affine_point_type = algebra::curves::vesta::g1_type; + nil::crypto3::zk::snark::pickles_proof proof; + + std::vector> w_comm_unshifted = { + {{0x29C11510848CF79EA9D58C2E7B2F9EABDE5470AB0C7D8051DB68B6A597844291_cppui_modular256, + 0x29A084D99207EE0ADE9471C2101682E8DB5E470231B1FEB7CDEC1A515447E762_cppui_modular256}}, + {{0x2347E650C19A43EA430EB8EF103F42A98B8BBAB41693BA604E762015C90F6B15_cppui_modular256, + 0x2D5E94480F83FBE47282CBD39A46022B90842A442FBA79002AE6811BAE48ADD7_cppui_modular256}}, + {{0x3A38CC8840CBC1C0E9CB2BE5AEA65569391B35932C4F632208FFB76683FD559D_cppui_modular256, + 0x0F6124FC989DFAAACD41531AB7A75E9F68150C1EC659338FF4E2340DC46591DA_cppui_modular256}}, + {{0x1E86CC275E88EE0EE3384B5D6641A9A6D4E3CF08DCE181D7FF9E2934B6406086_cppui_modular256, + 0x06CF0E315DC60DF5551A872E6C6531706506DB19A6425E1A4803BBDD1074A0C4_cppui_modular256}}, + {{0x0F695898DA469CF121E1E704B4B717AA6BC12EFCEF4F1418B42FE7A5437082B6_cppui_modular256, + 0x3BA7967BE5E834EAD6C4B2D8B7FC9533B1B830DDDCA7305B4EF1F7D1CE6F0EE8_cppui_modular256}}, + {{0x1F15E1FAA6E5A22F3B2DA293E52386FE81792BC7D956F797BEDFB11CD3A1A3D4_cppui_modular256, + 0x184919D8F8AE9E1A81CDF386C65454296917E5A26B5FB0040137AF77D4D6AE14_cppui_modular256}}, + {{0x29844A1A0AA89F411287AD425C3B4B84E1A78F04F888056E4C986242E0289EBB_cppui_modular256, + 0x08A4673C07F4E5F5EEAF76708589B110EAF49B76FB37EDAC5BDE5C29433B9188_cppui_modular256}}, + {{0x364ECE69E21835EAE10B1AA9CCBD74305E29C369ED250C0EE49CCD8F24795D03_cppui_modular256, + 0x1C5A4242982EB6E6D1BA1D5E118C46D758DC2AB6AC3E1ECBFD136B1BF5C7270E_cppui_modular256}}, + {{0x38934EBFC2667F0925DDBF006FBC361FE152FD2CDA59D8E2D9F3CBEDD9F6DC42_cppui_modular256, + 0x0583B9CA4362B661B2A4E78D811690B752564B5B60BB4C7F710D16BD098E5481_cppui_modular256}}, + {{0x3232CC46F6A60E0217926D058728DE33D8331CF44E9D589E8AAFFFEBB2FACDED_cppui_modular256, + 0x1DA9F1A0E3F493F5D873469CCD4A860A8564A3776EFC4C00E80916B5A6663CD7_cppui_modular256}}, + {{0x2F96277B56B8779865BB9CB646D9F2F45EAFFDD32F146B957354922B76944F61_cppui_modular256, + 0x2F49E80702D36688FF505B4E2CD848D461EEDCD7B6DE610A72392E05AE889010_cppui_modular256}}, + {{0x09F20586641CC98E68CF252A59480CE50A8C6C6AECE986A676221CD1E8613BC3_cppui_modular256, + 0x2DCC43E7FE9F1CBE4C2FAF0969D7A788C3636FF65466A2A8E8CAD429CB47BA28_cppui_modular256}}, + {{0x25147529907262C1F67F1C8C62B3136C9FF495727F59A5E534A3E257E9A06DE3_cppui_modular256, + 0x30B8453232FF14019691D8A749BD88D0E2FD1D39FE9A66A01165E71EDBA46411_cppui_modular256}}, + {{0x35B38418B473A631CC399723113765268A1F953D8ED9D41D5DD615026E636421_cppui_modular256, + 0x2BEEDE237F3D3ED3942323AA402318C8866BE8DFA31FE9506580846C284DB4D2_cppui_modular256}}, + {{0x3FFD762B06C4DFB4FD8FED560C84F9E9DE699620B6FB4D0ED3089042FE8127E9_cppui_modular256, + 0x2A7B651BE02C61AF87651107F8F43BE53DD2541CDDBD17BA7B316C939109598E_cppui_modular256}}}; + + std::vector w_comm_shifted = { + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + }; + + // w_comm + for (size_t i = 0; i < w_comm_unshifted.size(); ++i) { + proof.commitments.w_comm[i].unshifted.assign(w_comm_unshifted[i].begin(), w_comm_unshifted[i].end()); + proof.commitments.w_comm[0].shifted = w_comm_shifted[i]; + } + // z_comm + proof.commitments.z_comm.unshifted.emplace_back( + 0x0208422D68D15EBB4DAE8A791B23D93DFB93AC143C7463C0ABB9926E29C5EE9C_cppui_modular256, + 0x1A3706AAC63B05B15DB008736ABABA3A4F91DE35E48CA744AEA5E91C1E2288AA_cppui_modular256); + proof.commitments.z_comm.shifted = {0x0, 0x0}; + + // t_comm + proof.commitments.t_comm.unshifted.emplace_back( + 0x38BF25A7BB7EAEBFB14C570BF554072C2066143E57DAF281D69CE7240B510785_cppui_modular256, + 0x0847BFCDDC69B2EDDA8B03574FF0191B0316202347E19796C313B7E1C20C4363_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x2387EEC5D9F7CEE30CD2B0F2E3F73193B4124F6ACC3D1A19D6FED7B9AAA26563_cppui_modular256, + 0x1EE1F50EF640E86771E56D128A868DC02A5DBF285BA413E7C0FA476CBDC18AEB_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x050A5A157A04D8BE29892D3ABD96FFA1265D6CC6DBEF289F25B30CE51279F5C4_cppui_modular256, + 0x1873C5460334C7A6F8A9C6355BF9A560553A3B373A2887E06C1E4F52F3D76089_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x2DA2C4E34A74D6CA7A008F3179CAD50DED36A6DC811ACB3CAABCF68BD73FBB64_cppui_modular256, + 0x0D9F8F90FB6AF2A482FD45D9881959F4AD070FC8E41AD684FA558FA55FA9F9A1_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x0ACA51C54419CC979C802B466126FF526A17C5DA9463DD0BC76F48903DD7716F_cppui_modular256, + 0x3D0BFEA83E10FAC856AEEA3B04D9F70D25C15F91CE58F9AD6677CAA5BC572CC7_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x32EB3EB9641965E96ADFD72E5D4376DE1B62F604ADEC733ADA3EC5E1BD23A6EB_cppui_modular256, + 0x2C5664C919361E47BC5A8327950045489D0C8552B45510FC04EB7A1AA90505A2_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x3932E79F8E1725C875E4766F71BCA1EF8A0D70709EC997CEDBAF31E205966F2C_cppui_modular256, + 0x3C0A7DC230E8E2B2DD47211540CA4CB77536566548E55A7C9B49244E23CF1900_cppui_modular256); + proof.commitments.t_comm.shifted = {0x0, 0x0}; + // + // proof + // lr + proof.proof.lr.push_back({{0x0FD7D4257B544F7C5F43DEC6DEBD5CA8D075787F00271414948D53B0B62FCD16_cppui_modular256, + 0x232F23AECE45995840357A96FF6F8B0DA1B5F104DC68423D851D91E397140A11_cppui_modular256}, + {0x13D5900730BA64BF3D7F01C2CB5216AEE18ADC465739E99C4CA250A4F5C151A0_cppui_modular256, + 0x1F85E3CFEA859928DED3E452AD5B6DC6C82962AEE319789E4BD3DD8433226F96_cppui_modular256}}); + proof.proof.lr.push_back({{0x0CBF8FC044499ED163BB848EF4D5A221E480437F74641EB8C336D8E80804FC1E_cppui_modular256, + 0x1B993999EBB897518E05D15968355C903FDC7B4F7B31E75B141C8382331462AB_cppui_modular256}, + {0x1C98454CF118690E92591D466AE1E831BE802F2A15AE9E312FA66CE6A7D7C881_cppui_modular256, + 0x2BA05C71334D1ECA8CD03A9D625A0D266A4FD7FF83821F2BB2ACB7E9BE07F741_cppui_modular256}}); + proof.proof.lr.push_back({{0x3B6F4107FF516B58601B7565625C9DF90F6A18C7D7B01EDEBA39A8E0B81BDB24_cppui_modular256, + 0x2E9F1C998E26D4C3144595CD3CC2BCFA26599F2EA6D8B479BB47C85CF2646F80_cppui_modular256}, + {0x1E262782BBABE4624EAD784EDF3D9EC6339FC21037FB3A04938DC84BB5AD0353_cppui_modular256, + 0x223645C7225F920F646CC168E440EC1BE8A3D3BC1218C10A3B631873A4354E9F_cppui_modular256}}); + proof.proof.lr.push_back({{0x3C440D20F4566BAF87213FE86998484496F84833A834548AF0A6E4AC82AA6232_cppui_modular256, + 0x1E43B23D50E6E8B985BC06DD7509928727B1988228F6F1D892B73C07E0BC2B00_cppui_modular256}, + {0x1F0D55EA5526B5EF4F3C7DD207928236462EA45B70272882295E045EDB317220_cppui_modular256, + 0x1FAA58A0B3363A7C3701F1BD08FF173BC4610DAE80CEA3604DD3585D67B61152_cppui_modular256}}); + proof.proof.lr.push_back({{0x10065955B7F214914E735E5BEF85730C7D59F6ECD6B174B896BCF3947EE0FC5D_cppui_modular256, + 0x085932CEFA790133ABBA7ABBAF1CCCB578AA2FE48B6E283C26766B2887485B5E_cppui_modular256}, + {0x1A3CBDC7BC523210BF7F0DF87FF0620DF262056C5256297E5A512058BC73762F_cppui_modular256, + 0x0AE1581243C5974613218668AB43AD0E397BFBF8AEA90D0B3ED70E12EC004C9D_cppui_modular256}}); + proof.proof.lr.push_back({{0x2710EAB6C40BDCB97F436494248A75F472DB617328FC2EE84C1F3EF9DCBD3BFF_cppui_modular256, + 0x38D7AB437DEE337F2D66F00A406AF75782F95E8E74C783CAE2DEF94E19D16EB6_cppui_modular256}, + {0x1AC4B9F9CB2C53139485BFD4F57B9A64E19F63C688BB665E6037B0C98F0270B5_cppui_modular256, + 0x32315C63F461374A7B8ECA540A11A0CF30343F55545F17B7FD82C66DFA81C39C_cppui_modular256}}); + proof.proof.lr.push_back({{0x303836C38F745463E56F8047E984FCAE16AD25BFF13C2287E1C2E0D05E1C2EE7_cppui_modular256, + 0x2D3E3F3F6E8BCF6BDBFFD548800C6AB0DC89338563E7FF6E36AD2CBADB922966_cppui_modular256}, + {0x0243D538765D68B37F752A2CB56A85CAA781306602058AEA2BC0E4AF03FECDBD_cppui_modular256, + 0x190734088DC7F59473F453B08954D296DB532C4E2A1B62F0C59015D88CE69C9F_cppui_modular256}}); + proof.proof.lr.push_back({{0x1EEFAF71B70C0BC39F37B70ABEC7E7BE4328B7D9F98C1488F8C85F5073CD5E9D_cppui_modular256, + 0x182BFFE23FB4AD0C0F80BB129F87DDE0D26B822E2E4BFEDE7906D7FDE1D0E57A_cppui_modular256}, + {0x08CFA49FB087FC96627C1893C062DB06A9D8B4E5D628B26C349CCBE899B4FCA5_cppui_modular256, + 0x195D98E7527E30C5E2490196ECC11A22A14D80337D056CFA16C1E3B8AF29904D_cppui_modular256}}); + proof.proof.lr.push_back({{0x0531094297992B258C51092A991390F1BC63D6079D3AEB315614DD5C9826E47D_cppui_modular256, + 0x2C37CF446FB6539F8399357695E1D0C1B18AD72450A606773085A283182D5A5F_cppui_modular256}, + {0x1B8AB439CDCABF1D690F61782FE5E16352BC72FFA16AEE61760264725A9993FF_cppui_modular256, + 0x1F2C0382E06A42AB35ADC93AE4BE4C4612E411626E5A3888B9B885C2DA0A5D0C_cppui_modular256}}); + + proof.proof.delta = {0x33529C3F6390EB405641B8C12031B64948A6CE7B3B42E7B1168A6E7333D02352_cppui_modular256, + 0x1F7D511DE191BE5F67984802C6EBA85E5CD04CDC2C766A8B76F8F07F69A900BE_cppui_modular256}; + proof.proof.z1 = 0x3CD53D56E5BA440ABA3A534790D47BEDD633E272D3D66E30B66E4AD8ABE01409_cppui_modular256; + proof.proof.z2 = 0x272E221C5F8DAA39D027AA027DD221C25B0492E8E873F351328B3665138A2F7F_cppui_modular256; + proof.proof.sg = {0x03ECC373C543EBC3AFBABBEF616E72A2992866D77307246ABD51DCFCAC2B92CE_cppui_modular256, + 0x069DD6CE0DDF9FA50137922EC11D1E0724AABA5BAB5F6732A37555FF86FC7B50_cppui_modular256}; + + proof.evals[0].w[0] = 0x3375BD8BF491164C3EDB9F74C2B92395F55E0D629FD93F9C905E8B58BCCBE39A_cppui_modular256; + proof.evals[0].w[1] = 0x2BD1D3B3C432B9CBDD09E5A0172AA2AE24142AF767BC535CCA2A27EBE0426E0B_cppui_modular256; + proof.evals[0].w[2] = 0x15AC5253AD0BCC5CCC4BB7A7E8377DB80BF3985E9CF2402758A81A30B9CB541A_cppui_modular256; + proof.evals[0].w[3] = 0x04AE3A51AEC255989C561457BCE8463E411320DEF64C1B0C44538F07F95765A6_cppui_modular256; + proof.evals[0].w[4] = 0x0BD0DD424D799B2EBAA51FB4CDEB01918FA9500DAA956A185006FC573B202F04_cppui_modular256; + proof.evals[0].w[5] = 0x129A8A8695E1754002A044F6075C02015ADC959D2A4AE9D532289603119D17DA_cppui_modular256; + proof.evals[0].w[6] = 0x2BF8320D10669353F296A8A85874860D44D94F96489EEC6579A04FB7AD9D6D15_cppui_modular256; + proof.evals[0].w[7] = 0x124A08D137BFE974AD73E237CAA22380A7B33628360B8F4B57877B4AADA52A9B_cppui_modular256; + proof.evals[0].w[8] = 0x1CB0D03FFCECAB9D2F81E21D059EF259102B217A68AC005C9076925651E05406_cppui_modular256; + proof.evals[0].w[9] = 0x0C881E6295E1E2E62C1A82B7AFC779E78D2E43D539CDE83DDB02816E3D2ADF3C_cppui_modular256; + proof.evals[0].w[10] = 0x31F1380BACBA2EF03E272D3E2967207747A52659840465DD8276B387012760E2_cppui_modular256; + proof.evals[0].w[11] = 0x336DB80B6D3D700A1121AA7FCA04D47D676ECD3609D14B85B817BDE7B09DF27F_cppui_modular256; + proof.evals[0].w[12] = 0x286305CD450E4EB8C60B72B6AF22478C0603E7D950C5C3AA0736900BEC066D66_cppui_modular256; + proof.evals[0].w[13] = 0x122B3903D51228D827A81BB89D73E3AA136A184A35FF90818489087AA9CA18F7_cppui_modular256; + proof.evals[0].w[14] = 0x3A12FABFE97E6F4B54C66AA63020D296E6F43996C424B3C0893EE9DE9627D3C8_cppui_modular256; + proof.evals[0].z = 0x01C2A7D4001B6A1701F2AA2B14C5BF907DF393AD3D8F060DBAE7EEC522A9DBC9_cppui_modular256; + proof.evals[0].s[0] = 0x2469EE646AF7675016F4482081F0D3EE02FCE6BD31AE72BD479CA5F986FEA20A_cppui_modular256; + proof.evals[0].s[1] = 0x35B16444BC168780C67399ADA9813DDF7216AB7E927AA3BF7B71F9FF7DFC4BCF_cppui_modular256; + proof.evals[0].s[2] = 0x3BC85E0DAD2E9B5B8908D280F82E1C269FDEA22A415F7AE7BA5A93A090524BCC_cppui_modular256; + proof.evals[0].s[3] = 0x27039ABC0CDAA83905AAEFE71E26EE10FF971324AA7523AE35BF75E1979F0126_cppui_modular256; + proof.evals[0].s[4] = 0x1FA7277A70731CBA8D28C45F00B6A47C7F89BD4EB1FE5AE509F2A44CB74D76B4_cppui_modular256; + proof.evals[0].s[5] = 0x290E1A5B249C50CDB3DD98415F1AB4EE8C610A4F6B03F3EA5D87E2FD4D3B0BB9_cppui_modular256; + + proof.evals[1].w[0] = 0x14C0EF696971778663B575B2D89B0F4E8967CCE67E95C0C3376C606E18FECB2D_cppui_modular256; + proof.evals[1].w[1] = 0x0A2BA37B20948C9DE8B3F565EB6FB212296B2493B16B4E7236909E1D8D41E053_cppui_modular256; + proof.evals[1].w[2] = 0x2812ADB1DEA9B5B093AD88F080BBDCB40B210F7685C571A20F8EC625220FC931_cppui_modular256; + proof.evals[1].w[3] = 0x3FD84E9DA07AE934596DE0508D9E05CD170AA7767ECC489D4C8FB9F706DF4A4B_cppui_modular256; + proof.evals[1].w[4] = 0x2B3BECF9AAD01BE20D3E26D0A64C0C0D16CB8167E0E63DB8BE918B46CC32F605_cppui_modular256; + proof.evals[1].w[5] = 0x18A9B1F0794F2694A4B1E5664A8ECD8E06497D0CFB27BD6B9267D5A29764087B_cppui_modular256; + proof.evals[1].w[6] = 0x3104BD2759DBC3FF0BFFA3F1C20FED7D231988B29A9246F59868CE01B4F5992B_cppui_modular256; + proof.evals[1].w[7] = 0x1C3CAFA7CBBAC188094016B942092D0B3B72AB519064BFD36CED6C905B480391_cppui_modular256; + proof.evals[1].w[8] = 0x0D17F4FE8B1B2FAA783FC8357B4DF7B7A6C53FC12511C0E16838776875D49446_cppui_modular256; + proof.evals[1].w[9] = 0x2E0C3419383B9D61EF841A78D1606D876B3F88AA8E29E489C8C34F3A07AB2E98_cppui_modular256; + proof.evals[1].w[10] = 0x083AAF8C9AB47A3F89F1CF2A8EDF671F73832182E99C0BCAFB1450AF472CA880_cppui_modular256; + proof.evals[1].w[11] = 0x3FE4A9C99461154DA13BA3433A01E529B14795EC08B8A36F30EE9BD439F2937D_cppui_modular256; + proof.evals[1].w[12] = 0x22958B98F859D135929C88E6258400B5B392AF65D1D6B38C0C36D8D22CF2B7A7_cppui_modular256; + proof.evals[1].w[13] = 0x37EACF6E463248F21442AD54066298F5C3F4247E8E14E7FD6F6D7F58DA4EECC8_cppui_modular256; + proof.evals[1].w[14] = 0x091108301FB7BEB05DF4D8699F6491D1D9FE26E58C16760F980B583F497E49E2_cppui_modular256; + proof.evals[1].z = 0x0913AD25F3BFC7E0505F59EE1123ACC6200ED374E3AA388E1E9674554131283F_cppui_modular256; + proof.evals[1].s[0] = 0x3319BDF89C3E104CACB2BDB7D1355994837001BCF5363930337D9CA9909C813A_cppui_modular256; + proof.evals[1].s[1] = 0x19E60BCD31F3A0866FE4D49B30530DF3F900DB00DD6EF65A62E5A8E9F271EE20_cppui_modular256; + proof.evals[1].s[2] = 0x3BC0548D33BD4DE9C00F269A87AAE4956C061D41B21D56072EA24B9BD8B29A2D_cppui_modular256; + proof.evals[1].s[3] = 0x09044E158A924A1FE7FB4403A4F05AE4A53219123A36FD36345C50250ED25A57_cppui_modular256; + proof.evals[1].s[4] = 0x06FAE70DEF399DE03151CC0FA946AFD1927BFF046A76809C2DE9E729FA18B9FC_cppui_modular256; + proof.evals[1].s[5] = 0x2B849A7D46AED37CE27A68A88F3282A80FF24DBFDDCC3B8590BA54D569837215_cppui_modular256; + + proof.ft_eval1 = 0x14439298A43516169BA9E802FB3EA1145827D2214158298C302022C7C42A14A8_cppui_modular256; + + proof.public_input.resize(3); + proof.public_input[0] = 0x0A2BA37B20948C9DE8B3F565EB6FB212296B2493B16B4E7236909E1D8D41E053_cppui_modular256; + proof.public_input[1] = 0x3104BD2759DBC3FF0BFFA3F1C20FED7D231988B29A9246F59868CE01B4F5992B_cppui_modular256; + proof.public_input[2] = 0x1C3CAFA7CBBAC188094016B942092D0B3B72AB519064BFD36CED6C905B480391_cppui_modular256; + + proof.prev_challenges.resize(1); + proof.prev_challenges[0] = { + {{0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256, + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256, + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256, + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256, + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256}}, + + proof.commitments.z_comm + }; + + return proof; +} + +nil::crypto3::zk::snark::pickles_proof test_proof_ec_test() { + // https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/ec.rs#L15 + + using curve_type = algebra::curves::pallas; + using affine_point_type = algebra::curves::pallas::g1_type; + nil::crypto3::zk::snark::pickles_proof proof; + + std::vector> w_comm_unshifted = { + {{0x3992FC42921BCA5A98D4D4C10406B5111E259E90455A36B1BBB1CF2874BD80B7_cppui_modular256, + 0x033981CF968C0D13A3490F2C1E823AE40B572E37AD352108EC1D416D7FDA6843_cppui_modular256}}, + {{0x35DFC0800543D41BD72F2A180BD5918CF75FA4EDD71CDBBB172C717F275D272B_cppui_modular256, + 0x02AC70B44A58C1D7EE867C9AF46BD051F4AEBDD86387770F6F29A30564E4D75E_cppui_modular256}}, + {{0x02BDC45AE501633547646B6504408F790C3C8331D89CF0A2FBA6B5B6F5A33FCA_cppui_modular256, + 0x247FE1BB300C5C87A21338D1E3B273F5C2E47841A37A09AF88495F00880EA7B3_cppui_modular256}}, + {{0x26FA76D98BF5C432FB94F7AD2BC42AA36ECADCA47F4A592A92BEBC43FC5F93E5_cppui_modular256, + 0x37017857B0B5EA1B976C567307E4FC0BBDA9F3251AA6E469618BCBA2B89A71F0_cppui_modular256}}, + {{0x30F3AE5072AC07B8296583B203C8BA1C0019391786407701EDA5C1509F041F20_cppui_modular256, + 0x2592A5D822B4CA17DF205F74184A26D6880B80BA93488CB7520D5B6764565482_cppui_modular256}}, + {{0x3C7C040B106E1AD672099E4627C940BBA28F8456DE66892934169F866A2BCF2B_cppui_modular256, + 0x17056414EB6011A3A9CE9A4B5252C50DFDAB4B0BA05BB6FC45E5C6522BD8AE89_cppui_modular256}}, + {{0x2196A2907E0E67DB5B403E254594E1A86D4137202D5796DA4025C126B00F737D_cppui_modular256, + 0x154449DA9F1231ABC02E6DA74C7CD31157FC86F9D4EFEA987D17B6C269B5AF4E_cppui_modular256}}, + {{0x34E9DA740C3940491186C715AE5DB194514FB10B64EB0E4861F8174C0895833D_cppui_modular256, + 0x37D118CAA223C625D36F554ADB102A11137FE3B4508C771E17AF84FDEDD9ADAB_cppui_modular256}}, + {{0x36F77E77C07A292E2AADC4E0C0E80732AFD854C1AD3C250D30A17ACEFD9B443D_cppui_modular256, + 0x2CA08E22B289ADB468ADD71AC43663A9980BCBF4D3D216A6D7127ECE7C82C178_cppui_modular256}}, + {{0x294138D3C9B10EC9914DAC6569F1E462E31E16624441847878A1ED40748C4B69_cppui_modular256, + 0x05CBD5705CA29E8AB97A63370A3EBFBC60A0FCF22838E5553F1170B7B8D06BB4_cppui_modular256}}, + {{0x1D1345505D83CC600EF491D5FF7F2804F9D585333E22DDBECA4385B273D34AEA_cppui_modular256, + 0x2468E70D18BCE95AA038E3FEFD3A9CE7681A6B127B8333B32D638B0B82C203E2_cppui_modular256}}, + {{0x09376718CB461320CB3A60E6E6200AE5F826ECF193EB7F444F5388A58780AB77_cppui_modular256, + 0x193E86361EDEC8E92E3EC770545A5DCDAF6FF840D24574EECD66405A1233BBC2_cppui_modular256}}, + {{0x2AF2C37FF57D082642F4B9D5308021E4AB5FD59475ABA38E895C1B39740A03FB_cppui_modular256, + 0x316B90A404D54D2A1619595C893B2D8D5073BD5ED5948C1CC912DE55727CE091_cppui_modular256}}, + {{0x1E5F91CC5D42260ED79525C78391C52FE52583C17520D4F6848FFF8F711A08D5_cppui_modular256, + 0x299719F79AC5A5A58FA292F99701D19A31D884290C315592218C49D52FBDF817_cppui_modular256}}, + {{0x32B3B2D69CFBF8AF77DEC0E1A4177E3553524037776B58C06F39FDB96B9B2A88_cppui_modular256, + 0x3A381FC3874EB297BE27CEF01C21F3E2C27EAED137351E1A9660A731CA58825B_cppui_modular256}}}; + + std::vector w_comm_shifted = { + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + }; + + // w_comm + for (size_t i = 0; i < w_comm_unshifted.size(); ++i) { + proof.commitments.w_comm[i].unshifted.assign(w_comm_unshifted[i].begin(), w_comm_unshifted[i].end()); + proof.commitments.w_comm[0].shifted = w_comm_shifted[i]; + } + // z_comm + proof.commitments.z_comm.unshifted.emplace_back( + 0x10720ABB90B8D7FFECC81D72098988588E5C762EA195BFB0B747B768A352E6B4_cppui_modular256, + 0x1315086C4FD5E5B4A396E4070DFBD4569C54AF3D16908A0031D4671F40D98BA1_cppui_modular256); + proof.commitments.z_comm.shifted = {0x0, 0x0}; + + // t_comm + proof.commitments.t_comm.unshifted.emplace_back( + 0x3FEA0C91875D1BA7FF6E8CDEFB628FE78AF8903F69E9F46FB37D6EEFC3B5BAEC_cppui_modular256, + 0x3BD6777071FD029AAFBD78E2577A9AA02EE9848D5EE74B0394017A418E1382C9_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x0AE07C998702342195177EE53EFD69D554F66F9547D8512841E6F728A136E2B7_cppui_modular256, + 0x157D94A83F54D923D9C2B62217615FBFC1A2557FDE000750ED5D8DAB316DC344_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x0E76E4BB07750108A149020ACB6EDE76BEA39B670DC4A0CA42E7E7071B22AD2B_cppui_modular256, + 0x3C9EF296AB690C721A6434D20F2D93F32F59E9C0240D7FC8BD7E9AC36DB1B46C_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x201B5E1937878B6D4EB6FA896203D5CF52A8C0CEB13C5C77B369366D274A316D_cppui_modular256, + 0x3CD22866CCBBC9C38557AB044CC280FC8E7BF11F45C533B8D5D30243A1375869_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x2D4FA7AA5AEF2B52578F7786342101FFA762E25053E4B6D183F10EEEA1B0F2F0_cppui_modular256, + 0x305A5F41052B58EB5B8B5C14DE0E063784D0022DD59BABC5D726803EBB9664D3_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x1C702FBC0EA5AA3C1F193A88B9EAA5682B23B418445D0B59BC2560BB3F79BE10_cppui_modular256, + 0x3FDDD6A4C7679EADEA5749C2BB837CFD5B1D0BDA61F5728529C9F6DE4289D870_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x1E3A711D443202C9C7DB6E07373632A9B8B2CCA5CE8141AB1A6D95CC3E4F7BF3_cppui_modular256, + 0x3F4A41229D9C09DD8133022F84A095A6F1C16311C31ED58F955305C8B74654DF_cppui_modular256); + proof.commitments.t_comm.shifted = {0x0, 0x0}; + // + // proof + // lr + proof.proof.lr.push_back({{0x36D7AC3D8AF4CE326F42193455F1D9811400FFF87D5DAE91B88E4FC3AAB9FCA0_cppui_modular256, + 0x10CB889861041D2399EB637FF59EBE4EDCA54A8C4C488E972BF5118DA6B60641_cppui_modular256}, + {0x1F94FCFB79A2FD2D14057DC2BA42108F6001A293D90116F6B89903F1A82E759A_cppui_modular256, + 0x13553E6BDE5E5831030C69B05DC5AB82DAB6037D669DC08253F2D5D7646B0269_cppui_modular256}}); + proof.proof.lr.push_back({{0x00E26347431A029C3BD25EF3524F17E554CF1D7BC0ECB10A18A26883B805FF8A_cppui_modular256, + 0x0FD126CABD2824CED6172C148B8E11C60443512CD67168609E0F4A0D108870B1_cppui_modular256}, + {0x06F9C659457425E3826C3837F7276A058C1E63E66E697F168C8BF82C33185163_cppui_modular256, + 0x372D3E2233900C8350E1BAE0CF64FFC6618E70D955244C32CDBF5BFF4761F27A_cppui_modular256}}); + proof.proof.lr.push_back({{0x2E9301A7639C830F633C3B9D8CF3D11C03387A64C8CF593AB23A4FEC20154A6A_cppui_modular256, + 0x1FB092BE7A751A160A0AE9E4270B46A4FC7982EC93C6E8E3977C9E8EC8651882_cppui_modular256}, + {0x0B522423A8A0B9D6460F2919E64A2863F3FD491871011379E0305C037493B5DB_cppui_modular256, + 0x128F519068A8CBA8B1CD18DA8F91F289167B42EF1119B51AB09C3FF9EF53CF7B_cppui_modular256}}); + proof.proof.lr.push_back({{0x02066B4299B7D03CB9D81327D4D289938F2DF2791DEB0E38984908D35DF54EBA_cppui_modular256, + 0x16AF9B779DAD1E7F8E3C9D4AAC9B1CF655471DBB08013AF5DFFA789587B2E972_cppui_modular256}, + {0x063ECFA0F996C25B1E809FDAD4245541CFA744C60A25C9106444921977528A7F_cppui_modular256, + 0x29AE6773F7E0FC6BAFDD0111E45AC3E1384CCB4415B0B0932738A45FEFBED23D_cppui_modular256}}); + proof.proof.lr.push_back({{0x0623C69BDD9055AC867990D4F4272D7C13305AD2AF4074CAEA64244D886ACE01_cppui_modular256, + 0x1DB180D0E2F61D79276565CA5696D1D83C37E52A7C13C9A33CE944CC9B4EB887_cppui_modular256}, + {0x2B92C5E932EC19EAD6432CD1FCAE1DBC5437ACBDB071E3F7DD5E59F4FE31D1A4_cppui_modular256, + 0x1B4FBA7E40611A01B70A1B54A6293E992201A1F8B288F6C9E1958A935F85F674_cppui_modular256}}); + proof.proof.lr.push_back({{0x16F88740A39D6CB6D9E660A39E0B22BFB689D8B5B4BE161D560B9149ABD6521F_cppui_modular256, + 0x25EE0B835BD663441558D0E77F6C1C3B882F68B1117AD154EA098B83E0037266_cppui_modular256}, + {0x216ACE2F7625415215710E37E2C51C25969E992D63FAEF9523C5FC1CBDCFED82_cppui_modular256, + 0x2689C67EB7AEF899425D7D1437892DA7E7579576B9A60462E877C8C44AB38B7E_cppui_modular256}}); + proof.proof.lr.push_back({{0x1D01E7A6A13F09B151349DE4E22499D5DE1002ADD8886452673369857590677B_cppui_modular256, + 0x291F4D79D036E6226FA7FE2028B649E3DD5F8DE91454BEF5EFB4C34BB20FC498_cppui_modular256}, + {0x02BF93F4170A37A286B45BAB325B25D38B5EAD1158D004C9C7E92FE6ED419C14_cppui_modular256, + 0x134E8BF9C47C2ECB32BF3CA63D280F47304F86674C8B21FB63FF1125BEC75AF6_cppui_modular256}}); + proof.proof.lr.push_back({{0x28AF171CD6FC3EBB46E6EE4BA3E646BD8AE0667751F7F5F926C12E69C64C94F6_cppui_modular256, + 0x061A24803FC93052B1A977E2962022EF7DED20B417F2BDAFE9DB91E5F1ECEDFA_cppui_modular256}, + {0x186F78167B5C798DB1BFCBB5908708AA2C18DEA2BF7B4EBCFF5F14A9B6039F84_cppui_modular256, + 0x199B4E5C49F2A3BA1BEA8BB41A8B43A3F5907A5DADD01FD0C014EC7FB74E153A_cppui_modular256}}); + proof.proof.lr.push_back({{0x02919782DF31B4D837C9869B506AAB76F3D77D30D0AE1FEEA2B774687C8F40D5_cppui_modular256, + 0x2D344EAB35BB04A6C514910D0E981ED01E74BF31D7F684F5EE5D1C71A620F1C6_cppui_modular256}, + {0x3FE7D1FF6DB1B3E20A3A7DAD868EA19C6F018DB49E3AEB5460A92CFC32EE4949_cppui_modular256, + 0x2308667C08B1990F1A4E623F8FEC00B9AEC5734491EB8792DC07B4B967FCA8F2_cppui_modular256}}); + + proof.proof.delta = {0x1720017815E8FF68157836C6682D4C35F4D484B19FC73C0B4C26198DB6FB0140_cppui_modular256, + 0x38491699E96B59E51EF9F87F8AFE925B59AFBD357488CFD913928F6E238B5476_cppui_modular256}; + proof.proof.z1 = 0x1ACA51C81FE95DA0073BA8FFA4517F8696C525B493BD0D5012156F1232D4BAC6_cppui_modular256; + proof.proof.z2 = 0x3FC59290B3A6E7549D14CC78732B5AE10A90B958CF2543875057DCA3D2E21E25_cppui_modular256; + proof.proof.sg = {0x20BCBCB6258D8B9C48A97AB7A1864971981D6CF56FD525DC5E4023C03ACFB8C7_cppui_modular256, + 0x011A5FF465BEC039D22CFE481A967E225962639614D1D40C90A716CD7F24458C_cppui_modular256}; + + proof.evals[0].w[0] = 0x0C2F522FB163AE4A8D2890C57ABF95E55EF7DDD27A928EFAD0D3FA447D40BC29_cppui_modular256; + proof.evals[0].w[1] = 0x3F0169364239FF2352BFFEF6D2A206A6DC8FAA526C51EB51FC7610F6E73DFAE5_cppui_modular256; + proof.evals[0].w[2] = 0x2BCBED001BA14933A1766C68E09BF19C133AB20B87A9D0DB68321A99C4C7A157_cppui_modular256; + proof.evals[0].w[3] = 0x1430DC77EBF0048A4E26DDB817DD34D3F253AA9894C7D442B8BC06C7683D0188_cppui_modular256; + proof.evals[0].w[4] = 0x3B79EBE49FAEF6F123C168CF484296A84186EF1FB9FFFA528B0AAC0761F535AD_cppui_modular256; + proof.evals[0].w[5] = 0x16C6D43CFFB252215D05E1A05DBA2EEAADB3FAAF88B8AABDBD4E8860B9623530_cppui_modular256; + proof.evals[0].w[6] = 0x1C0801C94EA28AAD68CEA9C9524106D39DC1A3491435A23D35EEBE56DB3AB116_cppui_modular256; + proof.evals[0].w[7] = 0x21545E083F1282D939751D5E0D4EF173C7528C9E38349FE5E02BAB4686B542D4_cppui_modular256; + proof.evals[0].w[8] = 0x2E8F53F919EBB22022424A175A051F6FBDB2B57E06E1AC8A8201FBDD02CEE2FD_cppui_modular256; + proof.evals[0].w[9] = 0x1B5A53763A06BFAF8BAAF566FE885CD31355B2AC4F0F04B13F05610DE1EBAB5E_cppui_modular256; + proof.evals[0].w[10] = 0x212CC53B694BA1B3ED2D6C514B97325D62BF301F18E76B7DF94F04B7875C7E64_cppui_modular256; + proof.evals[0].w[11] = 0x22C1E6932B0336B13262867483DEE4C6B8E798C24F4245051254A64C61EAC604_cppui_modular256; + proof.evals[0].w[12] = 0x356428F289E597185A60ED494351FF93B5802480DC375E4B2C6ECAB816B69524_cppui_modular256; + proof.evals[0].w[13] = 0x08066B51E8C7F77F825F541E02C51A608FD217435FDF7E75AD5BBE36CB826443_cppui_modular256; + proof.evals[0].w[14] = 0x1AA8ADB147AA57E6AA5DBAF2C238352D8C6AA301ECD497BBC775E2A2804E3363_cppui_modular256; + proof.evals[0].z = 0x1480D3E4FD095CEC3688F88B105EE6F2365DCFAAA28CCB6B87DAB7E71E58010B_cppui_modular256; + proof.evals[0].s[0] = 0x03D8C35D2E1466E8514E20A8E658F4E2B1116AB123F7BF53F9A1C7376F788EB1_cppui_modular256; + proof.evals[0].s[1] = 0x05EDDC1E6C268DF398F068F06C51794D6F672E27FB800DFF6C5C35E5C3D84207_cppui_modular256; + proof.evals[0].s[2] = 0x1B03A1DBEA987367FDEF97CC27F7441C4845E93AD1583167DA4A1A9CCFFB1E71_cppui_modular256; + proof.evals[0].s[3] = 0x11347E33DF1631D59D66F6149D99DD22FD23B185D7D89CFE0909877C494D7916_cppui_modular256; + proof.evals[0].s[4] = 0x0E1372B72364C37883171F80BC89F2AC7043464C8C30E1D2B5D94105035A6C6E_cppui_modular256; + proof.evals[0].s[5] = 0x336A5683971A09A68D33D77B41947F8CAFFE3923190B51D443E515761A32889B_cppui_modular256; + + proof.evals[1].w[0] = 0x144FF7F30B8C75C60E63614EA792F9A41E41C2DBE40F816A602160960C071F56_cppui_modular256; + proof.evals[1].w[1] = 0x114768369E43EA7A13DE72AC855AE7D31DC52B34EB45BB96EA1BDFF54FEC4AB8_cppui_modular256; + proof.evals[1].w[2] = 0x006259A5F4A9A82296077396D476F9E59392BDDA93E63B9A582EF9BBA452A7A2_cppui_modular256; + proof.evals[1].w[3] = 0x3F9EBB3D514729A24B0C87FB434FC043F48195FA45E510BA5817F0ED05DED76B_cppui_modular256; + proof.evals[1].w[4] = 0x06F0CA9962E207949F85C22ADCBE8F27E632D14B843F2C65E264752B6100049E_cppui_modular256; + proof.evals[1].w[5] = 0x3885B6A574C4B6B89867EE499534E0F4937C7D71BA724A857F5E7F797059E879_cppui_modular256; + proof.evals[1].w[6] = 0x0554E97666ABA1659D7D107E3F709F546625481B1A5684BE24EFE9B3CBBC300F_cppui_modular256; + proof.evals[1].w[7] = 0x06C748D2C049B08C50633EBF7F7A0C68A03677CE382BF6697B7D285F30215616_cppui_modular256; + proof.evals[1].w[8] = 0x0B252004A6768951624E56F1D98B1DDB006B2284FE1C08B258D95B92BF40266F_cppui_modular256; + proof.evals[1].w[9] = 0x029236F173E5278B30CB9DAD8C87CEDE865AD1293B9BBF991F1743E8D1FD6638_cppui_modular256; + proof.evals[1].w[10] = 0x28C63DB702FFC629457818259603A154886B11D1D1FB7065037F51212E5BE2D3_cppui_modular256; + proof.evals[1].w[11] = 0x0219DC4D947F1109C90CD6C0112559A5D04528C2B264062A98DC5E7BBF85F269_cppui_modular256; + proof.evals[1].w[12] = 0x246CB73F3BB0A9AC5FA65DED8A1617E0CB8231146F0DF67467ED5E85242DF2B6_cppui_modular256; + proof.evals[1].w[13] = 0x06BF9230E2E2424EF63FE51B0306D61BA478A06A226AEDA29DD12DA188D5F302_cppui_modular256; + proof.evals[1].w[14] = 0x29126D228A13DAF18CD96C487BF794569FB5A8BBDF14DDEC6CE22DAAED7DF34F_cppui_modular256; + proof.evals[1].z = 0x1635A182C3B5623D5E7CF31D244F389FB478B0612B27937A39D48B473DB68931_cppui_modular256; + proof.evals[1].s[0] = 0x069DE7D0EBB1985B05DAB9E13348C12530D374BAD474C76C4AB9FAC8EB557332_cppui_modular256; + proof.evals[1].s[1] = 0x177B2B5F39976BE667F5D6768480F1555F52395613AF100529C99844DA28DCC9_cppui_modular256; + proof.evals[1].s[2] = 0x2941C2A82AC0067D3DD6A2C47EDD675D5B7BA071414A8324BA4CFAA1816B163F_cppui_modular256; + proof.evals[1].s[3] = 0x05EA2B93EF3D2CD3E8DDDA175F2446A8390E35219DFBA39111C8CDBFA3038FCE_cppui_modular256; + proof.evals[1].s[4] = 0x15C6FB1ACD775DF5E860906CDDF37C4E6B82CDC1A67F02F129DEAE98A11620D6_cppui_modular256; + proof.evals[1].s[5] = 0x338D629CA1F64B37674CA7B5AF91015CA50A5D335E7076E25D9F4C230C99395D_cppui_modular256; + + proof.ft_eval1 = 0x16FE1AE7F56997161DB512632BE7BFA337F47F422E0D01AF06DE298DD8C429D5_cppui_modular256; + + return proof; +} + +nil::crypto3::zk::snark::pickles_proof test_proof_chacha() { + //from test https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/chacha.rs#L40 + + using curve_type = algebra::curves::vesta; + using affine_point_type = algebra::curves::vesta::g1_type; + nil::crypto3::zk::snark::pickles_proof proof; + + std::vector> w_comm_unshifted = { + {{0x0D907C081D3359118A5E478BFCC88DDAD869FD50100838716ED04567BAA33FDC_cppui_modular256, + 0x199EFC218D385BCF2CBA3C51B4680FE89731F0C988F439AACBB208D3400D80C5_cppui_modular256}}, + {{0x08A18A9708983882B615505E3DA102163F7F76F69EF417A4B4417EEC5E00B80B_cppui_modular256, + 0x078D4CFB021A87188BB224F6EF9C823F494D2E56D1E57606EDE06473D79A8CCE_cppui_modular256}}, + {{0x2944BF7129EEC430005C0EDA0B903ED722C86E05CC04292FE084FEE760FD82BF_cppui_modular256, + 0x1DD04BD57C026FBA7123B4DCD82FAF9B2C05589675F348D5943E2FAA0A92DC87_cppui_modular256}}, + {{0x27729DAEB5BDF9CF755D600517EFA3E204705F28C0CDDB889B5435034D541902_cppui_modular256, + 0x0E5F7F8069AC0F130A14C555BAA464CA0A081E283C1C3AC53017C60BBA4ECB57_cppui_modular256}}, + {{0x064CFBD0B163449D04902AC4FFB09D3EA3A72CC8B334B1DA4375B0FC75FB25A8_cppui_modular256, + 0x35CF294F1E13BA90AA8C74762D40DE0E59DCE6914E643DC1D18E4FCC035C285D_cppui_modular256}}, + {{0x07B2F882DA399E1BED7BACE6F99C6115B08968A868213430090F481B7A638108_cppui_modular256, + 0x00BBDB1CA3C8DFCFAD7C90D9DFAA7967374C9B99135CD6B21683AEB440BDA088_cppui_modular256}}, + {{0x1207F4C62F9CEA36FF582F6AC59114AE99B294A50D8968284CE5EC72C3F69185_cppui_modular256, + 0x29B160C5C45B5688F9EF8E90A0F990AE59FEB83B77BD4CE364A320010F2AD2AA_cppui_modular256}}, + {{0x10E774D4C3398E6C41559F33C20F58A1A49380FCA39624654DDFB5DA28358080_cppui_modular256, + 0x378EC7B1439AB7EA73A86DCC3DDFBBCA1133B335B07CC3BD8CEFDE3040D1BDDE_cppui_modular256}}, + {{0x358BF013A8F51D5DEA2653574E146ABD5D6E854F5ACB9ECF67C5DBB0AC73E1E0_cppui_modular256, + 0x01EE00512CFA85115782AF671DE135052E7460CDDB25E6D38243F78A92930160_cppui_modular256}}, + {{0x19699D87F672F27129E3DBBBF87A39C2699E5CC5056E26E1B8C8F74A17F89FFA_cppui_modular256, + 0x2A5CB241A09940A2A989500F511B980216BD610BCDDCA3A4E6206ABCC9A02AB6_cppui_modular256}}, + {{0x0AB47B8B2AE32661D556855B29E6CDF5D0D166C1DAA6AD4C8E234954C87C4B26_cppui_modular256, + 0x286B1D54C945B309FB6192CDBE8D1E54656CE66885ADCB46FF46188EAEED4B80_cppui_modular256}}, + {{0x37E09A88844FF75DC337A74E15AA71FC45E6B2B8986F1038E8628B715161A205_cppui_modular256, + 0x0B4CD3CDCE9EC4E73E88C5613ED290A5417F86C618737E86BF7D945ABE8B93C3_cppui_modular256}}, + {{0x2D1F5A5E3141E4CC44619445BFC27075AD39BC47B168069E6FA15BC327D00B79_cppui_modular256, + 0x2F2B377BA8D7BCEB3F25E3D97BCDB0F01DA7C06351AAC19129BF2895C1B798C7_cppui_modular256}}, + {{0x39B6E743A1CCC0F94ABAB2079AAEBA5B402F568F394AA35337D14C4302A70821_cppui_modular256, + 0x09B0D8363FD8EF85113FCCCE4756FEF2D35F715FC5898CC6FC0BB7D8059E3D10_cppui_modular256}}, + {{0x3AB9777571999A038E1D00DE3B4B50C7C49BE9DE7F577C170BE3086293A64981_cppui_modular256, + 0x076552C4118AAE605ECE125CCDBC09EF70DFF9C718B24888E2CA6259DB5146B7_cppui_modular256}}}; + + std::vector w_comm_shifted = { + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + }; + + // w_comm + for (size_t i = 0; i < w_comm_unshifted.size(); ++i) { + proof.commitments.w_comm[i].unshifted.assign(w_comm_unshifted[i].begin(), w_comm_unshifted[i].end()); + proof.commitments.w_comm[0].shifted = w_comm_shifted[i]; + } + // z_comm + proof.commitments.z_comm.unshifted.emplace_back( + 0x168D1F61027A5C68D96128563D7D9BD4A8D5FD57D7623EF27619C9ADA28BAD02_cppui_modular256, + 0x013192C269F19B5EB0B95EE81907FF99798E8C42842C92180D4677DF3C8F73FD_cppui_modular256); + proof.commitments.z_comm.shifted = {0x0, 0x0}; + + // t_comm + proof.commitments.t_comm.unshifted.emplace_back( + 0x09F6972A79CF32EC6DF0A09D9ED75BEEC83DDA964A0E455966C46CA264C1FCBB_cppui_modular256, + 0x30447ACEA7AB34916BF855F68154AA6FA0FFAC405997CFF9EB327CF51947F235_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x1AB98CB40B97BC5A990960BFF0F0AA15417283D677DFBDDE225C431702CD7CF9_cppui_modular256, + 0x0F0155034568524BC1F06CDC280479FE0BCDF12F68B45E27D751A97997A9E3EB_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x2BD6941040227B2836A7735F1404A70F2AC2524181E2777CB89B682B767E0D30_cppui_modular256, + 0x290078CF248BD32BA4B2F53AA8FA2FBEEAE5C0DB09CE7D4B4ED61BBCBBB4718A_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x059FACB38DF9304AA37C79B9E7AE7669B519427BF6908314446B29E37A789B98_cppui_modular256, + 0x07D91AC73E15EC178717892C780F79237B903DA6289B50713FA6384F52E43862_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x190E9E91E941CC72B4A8F0A6A1F70FA79693CF6379DF9C4EE51B417A1B3B6CE9_cppui_modular256, + 0x2595DEEE386331BB5E515E6442BB5C9977A385C1FD40A17067B312A1C2FC808D_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x0ABC55CC82653EB6E8C4E511601CECB56FCEB47CD6ECD6BAADA9C92AA84907A2_cppui_modular256, + 0x3582678A760BEAEF57656A0C0390B584D97F64955609B30C26A537909047FA97_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x311C909605336FCF3DB3123B5F21A3D15EBEBAFED8EA896F1730EA78E2907FC3_cppui_modular256, + 0x27CD369BB1A98E95218E56B4B2ADC01BAD9F2C589F5A7F9BF213C2FA156E3BBC_cppui_modular256); + proof.commitments.t_comm.shifted = {0x0, 0x0}; + // + // proof + // lr + proof.proof.lr.push_back({{0x28B4284D06BC217B3FDFA8E15E0037E5809560C251FB336CB8A995F62CA6C5D4_cppui_modular256, + 0x3E0121EAAC28019F6321E9B8CC0CB611658115C87F8215544EFE891FB1DD3764_cppui_modular256}, + {0x232A3F9ADC67699D5798826159BF19162155C7E61C20A85A264A3DE0D165F0E6_cppui_modular256, + 0x313F74DA378E906EB1ECF5F2F8C8DD6124CCC63DBCFB1263B75CED59464035B8_cppui_modular256}}); + proof.proof.lr.push_back({{0x1B91D703280F7C53C374320A34EF2F13F7DFA0F4A5CA1957D7FE462530CB775C_cppui_modular256, + 0x15D019278BE5980D8D4ADF9670064627DEA83A41BA211CC1224C0EA16F5FD6DD_cppui_modular256}, + {0x1044A3A79383C9C8C969582927F258A41D39AA2FC56EF221BFCF1B7229C13BCB_cppui_modular256, + 0x10C2AA39603006CBB7913FCDBCD3A03BAA838EB6454478DA0974D24A4364FACA_cppui_modular256}}); + proof.proof.lr.push_back({{0x1846C6CBE29E7B8E2568D16168B6235ED2A62BF72E127344A3D5B2BC641EA028_cppui_modular256, + 0x09EB5BA1F288CA729A7152F50494A37779C6BDE4BDD82EA1F8DAA3D378D60C03_cppui_modular256}, + {0x0268F5650FBF4DD7A65C7AB3DF66754C89E6070A21D849E6BBA94DC4631CE0AF_cppui_modular256, + 0x31C6B6EED55E9FD96B6ED57928024F01949AD14F1ACF0385DE14A1520AA8CDF4_cppui_modular256}}); + proof.proof.lr.push_back({{0x1C5020E9A7BDB8C55737C2F54EC17E9E547ADF4D08D8654E2B062FD8CCDAF334_cppui_modular256, + 0x20A2DA59B94FA9F6EFB8E712D6123A739BB1832E769B581546865989CE0D03E7_cppui_modular256}, + {0x32E91D83B44B71BB9489F2428555D5C0CF7F52B2D526522DBF42C024DDC93105_cppui_modular256, + 0x3ADB189B2C5AC29EA41CA89F1E6D2065BB288CAC9A14114D2E0D3375D47B1649_cppui_modular256}}); + proof.proof.lr.push_back({{0x34D8D309EC9062FF72FDD3D7943E0BC9F2D18374986844EFA3400E0381A6601B_cppui_modular256, + 0x061EF75A6DD5A145BDD56BA772E7B3F13973BC071EC5FE8B155CDF74C7F0A81C_cppui_modular256}, + {0x1A14ED1E569E6A84366DD94DBEE5BA059777D03C338E39FEFE2A691593019F8E_cppui_modular256, + 0x25033131A7B7E63C98A3C9713FF645640947B63B25857AD694D918B3F6962C7D_cppui_modular256}}); + proof.proof.lr.push_back({{0x06A0A1F0724A99909AA80B28CC0893F62CF2C0A980EB9073F74F934D3B3F0CA0_cppui_modular256, + 0x07014B84B80D4CC17FA8F5D5FED4777C8843344122AFC95F8F992189A3774379_cppui_modular256}, + {0x003FD2DF69EC938BBBD5F74BF279C57DED0F9E5E820266BBEAF84BD12496261C_cppui_modular256, + 0x2E8FE7EED8DE629B618236451B26EF5017FD107D60B87AE6297A2CC8F0FCC03C_cppui_modular256}}); + proof.proof.lr.push_back({{0x2A861FA45AD74E6F787B352261EE9E6A156D44D16D368787FD1653D5FAF980D0_cppui_modular256, + 0x22E597450A6A73F7C6495F68F10EAF44FA7816AF25F6ECE9980D6C19B299FAA7_cppui_modular256}, + {0x031AA3F44305C3B44DEA4055D28B8AECE2CC8E5CF7AC18D22682A6B103E96D56_cppui_modular256, + 0x2EB01C4E72E61A5A969B4839B64687AEABD92D695ED288B7EA1EA0B018DEA0D9_cppui_modular256}}); + proof.proof.lr.push_back({{0x2CD79844871E9A2FD55E5E2A3DC5C4F1BB7B024B5B62C6B1F769E7B1421C3CBF_cppui_modular256, + 0x1FB7A12151416ABD40713AA195F578506445E69ADC640C0B07C60E830801476E_cppui_modular256}, + {0x1B7CB5CC78DFB297EC0DB343F227DDE8EC3DFBFB60558C63F5A4769D7FAEEC56_cppui_modular256, + 0x3730403A5983BA55B67BC0E57968CE454E89DE314527223333629DF233B4E4C9_cppui_modular256}}); + proof.proof.lr.push_back({{0x0E38EC8F81BE09BD4E06D7D17843AD402E24B44C8C58715C0215D8E8A256F121_cppui_modular256, + 0x32EC15229BFE5E19D3995922C0F2E6FC2850959B12CE206336D0301EA9537E16_cppui_modular256}, + {0x0CCE539E544C5A4E6BD795E66136C56B312D695E9B70493DDF94265BB3A93B5D_cppui_modular256, + 0x2888ACFF90475257C2B0FA8280F66262971E894F4F90091ACB55EC7A7D750498_cppui_modular256}}); + proof.proof.lr.push_back({{0x2A6E12EC58078B188573541FF00C0FB467FE0E107323F936B1F3058B14444DF4_cppui_modular256, + 0x04FDDA5B7E9FAF8493C194A5309589677695B84FC86681F87AD7712EEF65CA40_cppui_modular256}, + {0x23215F196EE69E20B1150DADCF393E680CD50105444DAE73A4C35CF49B072DD8_cppui_modular256, + 0x02506994B266E1F967587D3B7C36B7F5AD181EF32420FDFC41F540E2326B7D0C_cppui_modular256}}); + proof.proof.lr.push_back({{0x00D2311D1B8D96AEE714ED203594D2A09536A77E92F9E62A0B999E5402A55AF2_cppui_modular256, + 0x1DB7BE1192A7DA3D8587248DFB2764595DE2CFDC3D98D11811D84518A7966268_cppui_modular256}, + {0x0967F9851E3EA380D6F06B1ADCD317879641CC7300255E031D5F55D56DBADF71_cppui_modular256, + 0x32FFC3179C1E87E315C7D85CF1337EEF47C09940DF79AF7FD20E8CA115417999_cppui_modular256}}); + proof.proof.lr.push_back({{0x1E2C34E6F1403CF50F7920F6004F02158152E852C011AAC7E0BC38D9C5DB8F70_cppui_modular256, + 0x0AFE06BDB5C30345D3FE38733E6E823030500EC79C15D6C86EEA74CE9E98EEA6_cppui_modular256}, + {0x22D608813B98FEDBAB1650266627830099FB71B2DFE166610BDD38DB635FE22A_cppui_modular256, + 0x12FBBC5B5D146742C2DA1F7DF5E009530244A0EC52FCC483D51BB614B50879E7_cppui_modular256}}); + proof.proof.lr.push_back({{0x155318C000536B442DBDFA291340303D0C68E88B672B36103E5B880EF5927BA2_cppui_modular256, + 0x127B3BFE72F1F91E23EBA11485C9D18CEDF4B2E68EEE6075877ADD2208D31457_cppui_modular256}, + {0x0A4F35D241BE94120EC270A185338ADDEE39D1B8C6347D00F52AB0BCB5EEFB84_cppui_modular256, + 0x024DA534C948296B2FC300D614BBAE6A6DDF3C8368D82AD19A0AA48AB00AE40E_cppui_modular256}}); + + proof.proof.delta = {0x115EAC7ED8BA81AF66035A71BAE91E4E17E6345116DD6079A150D2D0949AAE21_cppui_modular256, + 0x1725882EE5AE967351A149954838CF76CEC484F78D1E8647047005154B06B6D2_cppui_modular256}; + proof.proof.z1 = 0x2D28876CCE213A643F6B67644AB185C2E3B4CB4010F74164018324F824A2171D_cppui_modular256; + proof.proof.z2 = 0x33D9651E658C71EC2A328CB17FE418AFDC4411663F30765A23F9F01206027ABF_cppui_modular256; + proof.proof.sg = {0x35B909072B9056F403B86BCE1ABFA1929480926339626AF31CB9EB007C673106_cppui_modular256, + 0x26E5173B2991DFB736D8F0031C75720759ED223BB2EF50E7DBF7011877BCF778_cppui_modular256}; + + proof.evals[0].w[0] = 0x36A58617D950326D48393CFFD8391F4326F03E00A8EEEB6CCA939ABC2E08BC36_cppui_modular256; + proof.evals[0].w[1] = 0x337B1390AA4C9206349F9315A17029713A2E7252B70858B2D580055DAA1396D6_cppui_modular256; + proof.evals[0].w[2] = 0x3359B795F51F6ABC6D0278EEF21015D8E896BE80B206F61A35C1DCE9251F2DBA_cppui_modular256; + proof.evals[0].w[3] = 0x13B791055EB946F731A698850643D2F3AC611586D85FEF2C64733058DACC1C2C_cppui_modular256; + proof.evals[0].w[4] = 0x37A9ED11B3B74C1E145B0223534D0957EE9F598DC42449FE5D53232A9562FE33_cppui_modular256; + proof.evals[0].w[5] = 0x063DCD5BA6F3ECEE0D8C39FF59D9D4BAA8FFCFF7594303B21A9A1C938709DD12_cppui_modular256; + proof.evals[0].w[6] = 0x1E2A687D3908CE6A817F8F371ED5A43776E99C076F302FE03070382647F759FB_cppui_modular256; + proof.evals[0].w[7] = 0x2D39CEA0A5F36E136C04F94995B3B21DD6C0901B6B70AAFE5F689634DEE0E362_cppui_modular256; + proof.evals[0].w[8] = 0x1F29B60CFEB5FD178B102D1F25F93D1F5D5ADDB1F43FE2EA5F46657A20523BE1_cppui_modular256; + proof.evals[0].w[9] = 0x1F2AF24D824BCA78584074489DC5E0F09C0494BE5BFD63617188621FE94EF292_cppui_modular256; + proof.evals[0].w[10] = 0x24794F0F5141E2BC3210697D6AE5F578F92851625424C840D8FE8EDEB8D2BD3C_cppui_modular256; + proof.evals[0].w[11] = 0x1DC7CE590CF5C0FC4FE9486AB592FD252DC4F2A2868570995411B4A363CF1F26_cppui_modular256; + proof.evals[0].w[12] = 0x01226914F47C85ED74C543B243EF6F08E051E817044AE091E55484433BCC003A_cppui_modular256; + proof.evals[0].w[13] = 0x30D2CEBC3CCD34EFFD5376AF1B62DE0BAFA730DA0B3E5253EE2ACB995E15A461_cppui_modular256; + proof.evals[0].w[14] = 0x02EC33D72F2525DE6DB847F1CF4F8A339DC8C8F59EF0870F5745B7D94A7AD100_cppui_modular256; + proof.evals[0].z = 0x2D49B1BC83C64DE87897535CEDCA5D01AF50C9BFEEB443D55E34A2F91E248D2D_cppui_modular256; + proof.evals[0].s[0] = 0x35ABB5BAFCAFF26391BB9A23EA996222FD0D4B0F392A78E4AFD5610ECE614E49_cppui_modular256; + proof.evals[0].s[1] = 0x320275D98E5CD6EA5E69432D59BF0E7CE619B28A5380C32D6D6772EE43B644D4_cppui_modular256; + proof.evals[0].s[2] = 0x1A16833874A16A00259130DE12C94508B2973BB4A2FE94D63CD61A5C85623805_cppui_modular256; + proof.evals[0].s[3] = 0x0E3F065C0D23E0D956D1277A2682F783DC0FE874C66C9C5E614A1D08919FF763_cppui_modular256; + proof.evals[0].s[4] = 0x2EFC04192BE242B78FF127BEDF1C95645AA6E392E838D9D178DF996E23A26A36_cppui_modular256; + proof.evals[0].s[5] = 0x216980C9F60FC4CACD6E49840C06D721285F0D7B216B215E0378E07A2DE08095_cppui_modular256; + proof.evals[0].lookup.sorted.push_back(0x0589F2A3E467EB48534C6BCB16F8DE87E8680679F9C6635C875FC146C7EC158E_cppui_modular256); + proof.evals[0].lookup.sorted.push_back(0x1FE50DD4D49E3457458A6EEAAC8F2FE0A1E45C951BC44ABEDBCBC327B651C6AD_cppui_modular256); + proof.evals[0].lookup.sorted.push_back(0x0811A77810337D203A13A6BDF3DE1C0EF8DDBBAECEEE2BFCF91F6F622285B8B4_cppui_modular256); + proof.evals[0].lookup.sorted.push_back(0x303E411B4BC8C5E92E9CDE913B2D083D721DB3C48B650656AFA04C898EB9AABC_cppui_modular256); + proof.evals[0].lookup.sorted.push_back(0x3B9A81860E30C68AB22BBCE0E05504FD20311889A99FA6DB25F9149668A5C44F_cppui_modular256); + proof.evals[0].lookup.aggreg = 0x22544EADAC2D44213B1106487BB7DF7EE011A3E0651C5E66B1C5523290A1A70E_cppui_modular256; + proof.evals[0].lookup.table = 0x0138A52EF62E016895365E2E28816A73796F26D1E546762B8DBE2F6207E135C5_cppui_modular256; + proof.evals[0].generic_selector = 0x21C56FFA8E36D3C79E57963E9DEB0465EFEEF42154596996DA1E1F7C10090419_cppui_modular256; + proof.evals[0].poseidon_selector = 0; + + proof.evals[1].w[0] = 0x3F1EE665939D1A669C02A5581478F0690BA3B6B43B561310B3244B222676F0D1_cppui_modular256; + proof.evals[1].w[1] = 0x22C1B531B5C7AE8F221F3FE9FF6AC3A99759ECACDA732A664BB607BF4F9AEBC0_cppui_modular256; + proof.evals[1].w[2] = 0x22CB8E2356A0BDA25C10FD7319BEB3E6B58D27BB4B306DB5938128088DE87608_cppui_modular256; + proof.evals[1].w[3] = 0x1BE6C3FAD4E00E57B35EE3657E728234F41116FF8A0A888022BE6986587699D2_cppui_modular256; + proof.evals[1].w[4] = 0x2A251331B450223F6C45DD64A84A3BF9E57A30ACDC154AA2DE22E2DFF60E4410_cppui_modular256; + proof.evals[1].w[5] = 0x14C2D1D23B12DB0906E8B60B01DD5B82F263A0E98ED9BDFB6DD45CF9A58762A4_cppui_modular256; + proof.evals[1].w[6] = 0x152F57BFF5F199110663AB0DAB3ACC6555175E44FE003FAD1A64F20EC1DACE99_cppui_modular256; + proof.evals[1].w[7] = 0x11F70235BFC5FA95479530E5756472A11C6A266CFF304C0C8C6BD1D9548A357B_cppui_modular256; + proof.evals[1].w[8] = 0x1A8763B700BCD9B866A0BA769A86C4AA2D6514957FC96EFE84CF0AD68F45EF89_cppui_modular256; + proof.evals[1].w[9] = 0x1FD5F84D2C212487BCE2BC38FAACE5B19E86679CB8CF0AA1CEAC222C5C139BC1_cppui_modular256; + proof.evals[1].w[10] = 0x0F974A421D2D60BB9F640AEDD3AE306B2AECACD3AD4C93BC9A74790D0C552543_cppui_modular256; + proof.evals[1].w[11] = 0x2AFB8BB329C45C8C1211D5317AFA287AAFFB7E5432B85744195EEDF6939E9C11_cppui_modular256; + proof.evals[1].w[12] = 0x253F49DBE48F4EDADA1DF2B3378D5DFE1726121EC58ADEF548B55682832E62FE_cppui_modular256; + proof.evals[1].w[13] = 0x0854DEEDDA38E8644A0499400A7DA4B2C9A8CA88700B9076A2B3EDC59F3B004A_cppui_modular256; + proof.evals[1].w[14] = 0x0A4EE15CAE012AB93FD856EC5A3C56DED6B94D2E8B1CF60047F4E4CA98E7F812_cppui_modular256; + proof.evals[1].z = 0x222E34FD7A0C9FCE6035D4F1EED9C61709AC3E2A731A4E31BE1A035B02D6C01D_cppui_modular256; + proof.evals[1].s[0] = 0x3B5911C2E740C8C14ED1AA6DE363DF8594B538A770913AD8A4D22210D8157733_cppui_modular256; + proof.evals[1].s[1] = 0x0753415109557403B0EB8EFCFED4D9F7FCBC2E64D4E1D588A6F540B40033DD78_cppui_modular256; + proof.evals[1].s[2] = 0x2223B07F6C7FD4290767A74B487DF859869E3FE36840CE67B542A811211F2A12_cppui_modular256; + proof.evals[1].s[3] = 0x1C34D811C8D8CC38F512A0C5F30AF18A9CCC9E6A835AFEB194C7A9A8B74DD754_cppui_modular256; + proof.evals[1].s[4] = 0x16653CB32D4A79F69092822DEE011255EA8498C87A27EC7E41A5F70BBBC01E42_cppui_modular256; + proof.evals[1].s[5] = 0x1E478EC67BB95466B79D4BEC7DF2FFF190022138A96A7DC2EEC16978DAC8746F_cppui_modular256; + proof.evals[1].lookup.sorted.push_back(0x1F48D940BEFE256DC863344D727C30FB0B64AD89CF048603C1074311EAFDAE44_cppui_modular256); + proof.evals[1].lookup.sorted.push_back(0x2DB3F15673C1B57B88C37D32677540886FE4FA9EF12B8EA3DF5F37275D34E3E5_cppui_modular256); + proof.evals[1].lookup.sorted.push_back(0x25C08A38EAC9F9615CA9C8F31DA54CFA69E489E65BD2854A0C0D2E4A08055FC9_cppui_modular256); + proof.evals[1].lookup.sorted.push_back(0x1DCD231B61D23D47309014B3D3D5596C63E4192DC6797BF038BB256CB2D5DBAD_cppui_modular256); + proof.evals[1].lookup.sorted.push_back(0x379C4A63CBE9C63A7615AA38D0660FDE88A1B062B977D6418E1097FE420C0C9C_cppui_modular256); + proof.evals[1].lookup.aggreg = 0x1170011D895C7106BB5BDDB6CE139785E23F76F89E417D4E5E6F6E72E7C87F75_cppui_modular256; + proof.evals[1].lookup.table = 0x24563CBB92D71A7914CB7075280E599E877145427119DBABEF650E1C56E51440_cppui_modular256; + proof.evals[1].generic_selector = 0x376A880B64CE6148620454A0C0FA134C80B9158F5D594E4B91D75501939895EC_cppui_modular256; + proof.evals[1].poseidon_selector = 0; + + proof.ft_eval1 = 0x3D4B1D1398F64294509994EBFA16CA0DC75C2AB5460D7B01DC6DBBFD30DA65EA_cppui_modular256; + + return proof; +} + +nil::crypto3::zk::snark::pickles_proof test_proof_recursion() { + //from test https://github.com/o1-labs/proof-systems/blob/1f8532ec1b8d43748a372632bd854be36b371afe/kimchi/src/tests/recursion.rs#L15 + + using curve_type = algebra::curves::vesta; + using affine_point_type = algebra::curves::vesta::g1_type; + nil::crypto3::zk::snark::pickles_proof proof; + + std::vector> w_comm_unshifted = { + {{0x000CB3CEDC0D40901A28D13E576FD8C91E7C8AF929CAA9E701E5AD030D2B6A4D_cppui_modular256, + 0x235ED1712D7D04EB9CE37923AEE0AF15374C8585235BC532E7C22EDB3FECD5E3_cppui_modular256}}, + {{0x3275991D0BC36798B8C9653827CD75975F2DC8382E40E205E562D7B8D98BD83C_cppui_modular256, + 0x2492F140A6C4342A8DD94379C50932BF964E343604463BC087AF0D3BE6286D17_cppui_modular256}}, + {{0x01C17D06707CE85F2BC84EFAC3E24956AE8A2541C7CB5F520F462538CC68CAB7_cppui_modular256, + 0x3606EB2291FE297730C003E34F3EB0E2B595C70AA1AADDE1DE7AA627E8105400_cppui_modular256}}, + {{0x06BBECCC5DC4E4544FDC73DE1B3D1C036216F7A09E520C9F9CDC26CE723081DF_cppui_modular256, + 0x321C333169A02BA1EA53AC96FA09763A3DD9C67E11FA026FFD55280E9F41DB8C_cppui_modular256}}, + {{0x20469D761E84D4EBBD272336C35AB925CDFB6CE9A96F9B6334ADBD99901632AD_cppui_modular256, + 0x33EFCD16ED2994A99C4714F311AEAF6FBC318DDE096E124B2AC5DA8F5697B0B1_cppui_modular256}}, + {{0x0EE28963DD2BFED601D6190B1A8ACAB3C0BD7A48BA3CE0DC3953C597EAD38F60_cppui_modular256, + 0x2986AD19B8E776B714C161FB27D543A516CB19F34571C27C0D971FB8BEFC5200_cppui_modular256}}, + {{0x0B1D36795958F1AF584E7D839FC5AB0932B341C873A8F14E4CC7C0DBC60E14C0_cppui_modular256, + 0x26A4699FEC12BC3F28E09D9383083BC6E8DCC6160244BB2ACC615BD526676430_cppui_modular256}}, + {{0x1E143FB20689F774780EFAD9E198FFA0F82930E90282022853E86959D4524990_cppui_modular256, + 0x2F0828603D6A58821F43D2F0DE75EB992849F2FDCB33C854BFD9CC00F038B29B_cppui_modular256}}, + {{0x2453FAA0ED474210B25D1131D075963DFF2086F3993902F50280B078C054DC22_cppui_modular256, + 0x06695D5D68BB67E5314B84233CE09DAAE998EDE03A711A8CB9D9C8FF72359304_cppui_modular256}}, + {{0x19BCD57208BCF2CB94D9AD7C04DDD0AEDAE680DF5C160CC41FCAB6322E2922B5_cppui_modular256, + 0x014DB3B97C439C25B5F265F277143C8030014DF3AFCA6153BC86197415D8E74D_cppui_modular256}}, + {{0x330CE397F026A466E89DA13F31525978A8BE2604EFC1954D64FA989DA3A0A515_cppui_modular256, + 0x3EC89B71C0FE64E1517C5E91D2F722577A6EA18A262E37DE1A5BCBBE3DA39C0D_cppui_modular256}}, + {{0x1550CEF977AFB848630EADAA5D1109961C980C026CCDAECBCDCE65D44A686ACA_cppui_modular256, + 0x343FB3192B5E16C468749EA87DA2AFAB02EA6D8B84EE4DBE3D501AD7720F1A5A_cppui_modular256}}, + {{0x118D4542A95F64F628A18897F1618F6FBA930BB69E2211FE6B87D7D57964E515_cppui_modular256, + 0x09BED30175068832797E00D54C9C41FD20B95CE7C85772D7A88F7CB0EC21205E_cppui_modular256}}, + {{0x3EA9A0A449F3D480AAB91D16966928C9333A51498673CB8DC2D2C4BE45C60529_cppui_modular256, + 0x315750C943269EE304319CF5EADE5CF89CB119E614D3558A9268151CBDDA0CF1_cppui_modular256}}, + {{0x3F10748D4451468AA1EBB78CDE1167A734F22AF23C66F2F6C123F77C19D0D040_cppui_modular256, + 0x353598903C926F9EFD8F0212EDA038ED7A92E3ABA1294963E4ABFBA269C1EE3A_cppui_modular256}}}; + + std::vector w_comm_shifted = { + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, + }; + + // w_comm + for (size_t i = 0; i < w_comm_unshifted.size(); ++i) { + proof.commitments.w_comm[i].unshifted.assign(w_comm_unshifted[i].begin(), w_comm_unshifted[i].end()); + proof.commitments.w_comm[0].shifted = w_comm_shifted[i]; + } + // z_comm + proof.commitments.z_comm.unshifted.emplace_back( + 0x085220F8668A7789843F474CA2AC22C37D8B8683257D13674EFB206A360D0AF6_cppui_modular256, + 0x3A30BC2478885903A20DEB3140488E871F65A68F2FAED91731D5290381D739F1_cppui_modular256); + proof.commitments.z_comm.shifted = {0x0, 0x0}; + + // t_comm + proof.commitments.t_comm.unshifted.emplace_back( + 0x1A41BFC9814D0A2014AEB92F1735FF599FA8B1D1421A3316F7E1ACFCF5C405F5_cppui_modular256, + 0x0CCB8A5EE6E665BD43A6C7735DE6834C32DB6DF2F0A61547665C0160FD436371_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x0FAC1CB76436BC2C72E96D01D5F1EDF077A84AC680E48AAED040046E1EC4CB51_cppui_modular256, + 0x19C4C61772C83577CB7924C2A58AF314CEF223DAF6AC2EF5B467CB076F009D40_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x1646873C1CF57D2766FCDD3FCEC68B1235CDE020D07187F878A4D109D9F1B6CA_cppui_modular256, + 0x3A8F6A4B50129F6ADA8A3CCEC7FF6690DBF1DDE4B3A762F63E145357EA2ECDCF_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x326AD38B0ABD5DF109E9B783F5940D1D9EECBDAD5B45297F4F941603478DB951_cppui_modular256, + 0x2ABAA4BB678E1202BCD62E08764EE5D27E38E2E1011561063E989A8C486C9B2A_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x3BAB30EA9964F453A7EE5EBB9C7914A7C2BEEE56EBB3988BC23165CBF3250D44_cppui_modular256, + 0x35E01ACB62CBCDC7546BB1327A47DD182BB5DC8CE3C31E876EBCCFF58DB1D189_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x11DEFAA969DD3F8DB78C4628814B59DFE4D8D7CF53871EFC7405BFA4D76A020A_cppui_modular256, + 0x17AE3F3518DBFBCE951BF94394D4F87936C529C0A1C9BA5C86329E36F54400A1_cppui_modular256); + proof.commitments.t_comm.unshifted.emplace_back( + 0x16473BA3DFF201C8B4620834AF90292137E1FDB6EF9DACC64EC6A3B7C24A2A15_cppui_modular256, + 0x1ADB205AADF1B55E594D704F11C415AEB63A65F2AD7296A6B4EB8D1A6441CC0A_cppui_modular256); + proof.commitments.t_comm.shifted = {0x0, 0x0}; + // + // proof + // lr + proof.proof.lr.push_back({{0x2827A5B66FC81CB3A28586C66731CE44953E68A7D1EE3FB5F828279BF43D3D8E_cppui_modular256, + 0x2A7CEF5A6569F691DE3DD0111949740FC64F7361069D09D54201B9AF36B98CD6_cppui_modular256}, + {0x05F2EDBE885A991ABD8D131E6B230133414E9BAEFA3CDF7132E536D5D300F754_cppui_modular256, + 0x1911AF60E5BF6F1D3A390A6CFEFE15066E04B17EB24B6305CD3C3308E47849AF_cppui_modular256}}); + proof.proof.lr.push_back({{0x3459151D7C79466FD7C8291BAC6B330FC66F65A121D9478634E43701AFECDF92_cppui_modular256, + 0x07AB9545D8305EBD5182A4DF7FFD4498737C574B8CF4DB8E0033058D0E2AA83D_cppui_modular256}, + {0x32782C9072D797C83B384D19323086FD3841AE9C1C1244D949A21EF3E036BD1B_cppui_modular256, + 0x2ED0514DB86F8DE544845979224926FDE5DBA2FBAE792E300A5F684419DB8808_cppui_modular256}}); + proof.proof.lr.push_back({{0x2E0CFBB75233CD34A690F73205B6934ED718C9688206E9EF33B2C2C1BF98C4CD_cppui_modular256, + 0x14CD8D00F0E063DA4190DC93010448DF1D4BE9F5A2B60F89C4D574611F58A588_cppui_modular256}, + {0x29C594585650CA82EEA28A74CAF8442AE9E182FE9A372240DA1DA33427872A23_cppui_modular256, + 0x1DF2DDA2763EE650CFB683D40C9CBCB767B83B7AF4AFBEB213C4C651EC16E0CF_cppui_modular256}}); + proof.proof.lr.push_back({{0x291254F14421542DF870729E68165B995858901FF5730705DC1B9298DD7F54BE_cppui_modular256, + 0x2E63F4904403C2366936A3919DCC19388E5446F1044417E73ED73055030B3E77_cppui_modular256}, + {0x1998074EA9846B67AD3E8F0417CA986F59F9C44D6C521D159F9B92EDF0A5ECC2_cppui_modular256, + 0x1BA3999B102FE06AA9CEE88739A7566DAF89AECCEF5AF735C6020E3BB4CF2414_cppui_modular256}}); + proof.proof.lr.push_back({{0x3DFF73C9052B999982DC03165148A1F6A1FFBD804599C510354A97C40F5A429E_cppui_modular256, + 0x302FFD54472D75A939158A9B06FE826F205E8B2069EF29E7E6AF2D17D2531F94_cppui_modular256}, + {0x101F315D1D542EB0E1A1C09C77BBCDCB8E09D40D46FC22F790BFFC09E669D755_cppui_modular256, + 0x05931993697FAC6F06998D844C06D4E424BF70C870745A86EF6801788A3FB17A_cppui_modular256}}); + + proof.proof.delta = {0x343861E35CF469E5E793B7C2856EF61A26A811634AC42C1864EB8A9DA2DB8D15_cppui_modular256, + 0x321143F97752F3DD1DC2228C51D79FDA0977CA2457C45AC233E942ED3E19CD13_cppui_modular256}; + proof.proof.z1 = 0x30A443EF808B43C465C3E1E4DC5638A268E2408DC90B231BA27D4AB23A7C4DBE_cppui_modular256; + proof.proof.z2 = 0x24E4059E049476634417FB5FE0B069B028F5BFEDF120C44F80FE47FF6DA6C101_cppui_modular256; + proof.proof.sg = {0x081439C973E43419FAB2E7FD259925C57FAE9E0BF66F65F0F3C16387FF95295B_cppui_modular256, + 0x1FBCFA7FB3E3C27016F25966E10F79847E626ADD43DA413B7A8F055C88A6FAEC_cppui_modular256}; + + proof.evals[0].w[0] = 0x2A016E5F91F6C33552FC86A7A88C034E5CF1301E4982545A15AB709ECE150E09_cppui_modular256; + proof.evals[0].w[1] = 0x1D1B8F16A3DF52F90BA4856A11D397FDD175DEBBDC84F9D9FD71C46E5CB311CC_cppui_modular256; + proof.evals[0].w[2] = 0x034EB5403A85D023EFD87CE7E37CD027921DBC8F5A59C6338A12965B2E1D2D9B_cppui_modular256; + proof.evals[0].w[3] = 0x3D708DC37C2985BD9E8F39D40FFB35A8C1D1D4106AC17CEA1668C944007FE789_cppui_modular256; + proof.evals[0].w[4] = 0x23ACD65BE15FC86FB7FC4FD45701CF7F348C13DD6DFF400DF808BE97006E06C8_cppui_modular256; + proof.evals[0].w[5] = 0x3F6D999688174F2CC9C37FBFDA241825F50505E56EF660E29FBC52F06008F2EF_cppui_modular256; + proof.evals[0].w[6] = 0x054FF59489820600DB800466FDF3063D387CAFC5464741417FAD07A00E2B558A_cppui_modular256; + proof.evals[0].w[7] = 0x078062AB9E022D286A47F28A6A57C368598416D076C558A8288A05AD9A1451DE_cppui_modular256; + proof.evals[0].w[8] = 0x09B0CFC2B282544FF90FE0ADD6BC80937A8B7DDBA743700ED16703BB25FD4E32_cppui_modular256; + proof.evals[0].w[9] = 0x0BE13CD9C7027B7787D7CED143213DBE9B92E4E6D7C187757A4401C8B1E64A86_cppui_modular256; + proof.evals[0].w[10] = 0x0E11A9F0DB82A29F169FBCF4AF85FAE9BC9A4BF2083F9EDC2320FFD63DCF46DA_cppui_modular256; + proof.evals[0].w[11] = 0x10421707F002C9C6A567AB181BEAB814DDA1B2FD38BDB642CBFDFDE3C9B8432E_cppui_modular256; + proof.evals[0].w[12] = 0x1272841F0482F0EE342F993B884F753FFEA91A08693BCDA974DAFBF155A13F82_cppui_modular256; + proof.evals[0].w[13] = 0x14A2F13619031815C2F7875EF4B4326B1FB0811399B9E5101DB7F9FEE18A3BD6_cppui_modular256; + proof.evals[0].w[14] = 0x16D35E4D2D833F3D51BF75826118EF9640B7E81ECA37FC76C694F80C6D73382A_cppui_modular256; + proof.evals[0].z = 0x38C5D08C61572A0F233A3732575F3A07AD484107EC7366FEB0903FCC30253C1A_cppui_modular256; + proof.evals[0].s[0] = 0x01751A5CCC6A9B9BDF660296AF5F7C80229DC97F3646FFC3729D827E80DF39DF_cppui_modular256; + proof.evals[0].s[1] = 0x264CBA1EFD870553869EC32E652FE2FC5DB4DF0C8B8550816F1947F66858B238_cppui_modular256; + proof.evals[0].s[2] = 0x21D7D3F53426BA3024217C852D5B1944031F52E784E95CA0539A9F88FB3F3FBE_cppui_modular256; + proof.evals[0].s[3] = 0x260E6148F06FA79CD3C8C4A379955A8823017E730AD3624A578304A44B5113AC_cppui_modular256; + proof.evals[0].s[4] = 0x2E901B006A7D080B6566A472AC9DEA73BB53A57A190B1A21ECEB698A869374BA_cppui_modular256; + proof.evals[0].s[5] = 0x2E70F1D4AE3E1DE24337D33C4F61C88A628368CA7FBE9BF67C16F0944C64B7DE_cppui_modular256; + proof.evals[0].generic_selector = 0x2C1E20B5D662CE38070228313FD0D968116779CC3CD2FFF662707412EEBD04C7_cppui_modular256; + proof.evals[0].poseidon_selector = 0x0; + + proof.evals[1].w[0] = 0x119301E40E2E7C7D465D44663295D7EA620FBCC1F53517ABDD2ECF5C944C5CA1_cppui_modular256; + proof.evals[1].w[1] = 0x2796DF6969578BE116556794B60EFDD9D3686F8BFE336F0309CA3AA73393C8A0_cppui_modular256; + proof.evals[1].w[2] = 0x06A9EE581EC0C7F41B9E54F192948200F0045C21B4BA021DBCB6C8EBA3BFF30C_cppui_modular256; + proof.evals[1].w[3] = 0x0EA289935EE95E8D771A681893AC0E4C33CB9BB88A2D0DB18F3C0CED5D37EBAA_cppui_modular256; + proof.evals[1].w[4] = 0x244FF116C46C3E79427E298089212A35DFB57750BDCD97A159A98DC014870F83_cppui_modular256; + proof.evals[1].w[5] = 0x3F04F2BDEBB8E5E732AB024C699E2481A82CA6205E3E34481A0D57BA0AEB997E_cppui_modular256; + proof.evals[1].w[6] = 0x014FCC504DEC8FE403ED2DB233134CD28CAD4F9E54DEE00C3384740355203132_cppui_modular256; + proof.evals[1].w[7] = 0x2AE2D234C19E20C167FAC3AB796EB0F1524B62DD459AE8FE0997B0544AC69E29_cppui_modular256; + proof.evals[1].w[8] = 0x1475D819354FB19ECC0859A4BFCA150FF5A2DD202D09F8D4467DBBB8406D0B1F_cppui_modular256; + proof.evals[1].w[9] = 0x3E08DDFDA901427C3015EF9E0625792EBB40F05F1DC601C61C90F80936137816_cppui_modular256; + proof.evals[1].w[10] = 0x279BE3E21CB2D359942385974C80DD4D5E986AA20535119C5977036D2BB9E50C_cppui_modular256; + proof.evals[1].w[11] = 0x112EE9C690646436F8311B9092DC416C01EFE4E4ECA42172965D0ED121605202_cppui_modular256; + proof.evals[1].w[12] = 0x3AC1EFAB0415F5145C3EB189D937A58AC78DF823DD602A646C704B221706BEF9_cppui_modular256; + proof.evals[1].w[13] = 0x2454F58F77C785F1C04C47831F9309A96AE57266C4CF3A3AA95656860CAD2BEF_cppui_modular256; + proof.evals[1].w[14] = 0x0DE7FB73EB7916CF2459DD7C65EE6DC80E3CECA9AC3E4A10E63C61EA025398E5_cppui_modular256; + proof.evals[1].z = 0x2DEFB3CFB41140464BF709B147777123731468F528CF8F14C032CA136A477469_cppui_modular256; + proof.evals[1].s[0] = 0x11039196D240AC7CC0D1A88749F716B6B025F6BCA2CBBD0B41D2DA46FCC90558_cppui_modular256; + proof.evals[1].s[1] = 0x022CE995D1CA16666888BED84A062994F864C180A393E76F3C2D14786D3FF82E_cppui_modular256; + proof.evals[1].s[2] = 0x3B4C505BF9C0962541FA4597D037BF217AF2B2CD893239A05FC64E8674967F83_cppui_modular256; + proof.evals[1].s[3] = 0x02F194EC301411C828DAC6A83F90299F99441F0EDEAB5A2D0700C32553C5A10B_cppui_modular256; + proof.evals[1].s[4] = 0x2AA071813813CCB09C5C6F5BB6E3F6BEDA421DC5E30A71518BCB05AFDFB4DDA9_cppui_modular256; + proof.evals[1].s[5] = 0x3F82F6EF12DD276FAF8BC01CE8477BC9AF5B81D28586B8EF56CB0E025FA97276_cppui_modular256; + proof.evals[1].generic_selector = 0x23EA1BB94CD1D2E0E13048E0888501151308AD086CCD0D8E7DED12FF54734259_cppui_modular256; + proof.evals[1].poseidon_selector = 0x0; + + proof.ft_eval1 = 0x0BEEA8845F1FD21B0057ACEA23ADFEB71A4922C0A579A57FA221BFF73BE63511_cppui_modular256; + + proof.prev_challenges.resize(1); + proof.prev_challenges[0] = { + {{0x2C0AD1A81FAC9BE59890BEA77119393E3E9EC523A44DF600FE2399C01AA76F70_cppui_modular256, + 0x39F31DAAD9FA26835EB1F6ADB2DCE08649061681361B54082C1FA1CD800EEB97_cppui_modular256, + 0x07DB69AD9447B12124D32EB3F4A087CE3126CEE2BE9BB8F3C0EE78EDE57667BD_cppui_modular256, + 0x15C3B5B04E953BBEEAF466BA36642F163B8E2040506916FAEEEA80FB4ADDE3E4_cppui_modular256, + 0x23AC01B308E2C65CB1159EC07827D65E45F5719DE23675021CE68908B045600B_cppui_modular256}}, + + proof.commitments.z_comm + }; + + return proof; +} + +#endif // CRYPTO3_ZK_BLUEPRINT_PLONK_PICKLES_VERIFIER_PROOF_TEST_DATA_HPP diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/aux_sponge.hpp b/libs/blueprint/test/verifiers/kimchi/sponge/aux_sponge.hpp new file mode 100644 index 000000000..1c27c30e1 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/aux_sponge.hpp @@ -0,0 +1,167 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class aux; + + template + class aux< + num_squeezes, + snark::plonk_constraint_system, + CurveType, + W0, W1, W2, W3, + W4, W5, W6, W7, + W8, W9, W10, W11, + W12, W13, W14> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + using sponge_type = + zk::components::kimchi_sponge; + sponge_type sponge; + + public: + constexpr static const std::size_t selector_seed = 0x0fd2; + constexpr static const std::size_t rows_amount = 200; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector input; + var zero; + }; + + struct result_type { + var squeezed = var(0, 0, false); + result_type(var &input) : squeezed(input) {} + result_type(const params_type ¶ms, const std::size_t &start_row_index) { + squeezed = var(W6, start_row_index + rows_amount - 1, false, var::column_type::witness); + } + }; + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + std::size_t row = start_row_index; + sponge_type sponge; + sponge.init_circuit(bp, assignment, params.zero, row); + row += sponge_type::init_rows; + for (std::size_t i = 0; i < params.input.size(); ++i) { + sponge.absorb_circuit(bp, assignment, params.input[i], row); + row += sponge_type::absorb_rows; + } + var sq; + for (size_t i = 0; i < num_squeezes; ++i) { + sq = sponge.squeeze_circuit(bp, assignment, row); + row += sponge_type::squeeze_rows; + } + return {sq}; + } + + static result_type generate_assignments( + blueprint_assignment_table + &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + std::size_t row = start_row_index; + + sponge_type sponge; + sponge.init_assignment(assignment, params.zero, row); + row += sponge_type::init_rows; + for (std::size_t i = 0; i < params.input.size(); ++i) { + sponge.absorb_assignment(assignment, params.input[i], row); + row += sponge_type::absorb_rows; + } + var sq; + for (size_t i = 0; i < num_squeezes; ++i) { + sq = sponge.squeeze_assignment(assignment, row); + row += sponge_type::squeeze_rows; + } + return {sq}; + } + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) {} + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) {} + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fq.hpp b/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fq.hpp new file mode 100644 index 000000000..fb1fe8d5d --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fq.hpp @@ -0,0 +1,216 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class aux_fq; + + template + class aux_fq< + num_absorb, + num_challenges, + num_challenges_fq, + digest, + snark::plonk_constraint_system, + CurveType, + W0, W1, W2, W3, + W4, W5, W6, W7, + W8, W9, W10, W11, + W12, W13, W14> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + using var = snark::plonk_variable; + using transcript_type = + zk::components::kimchi_transcript_fq; + + public: + constexpr static const std::size_t selector_seed = 0x0fd8; + constexpr static const std::size_t rows_amount = transcript_type::init_rows + num_absorb * transcript_type::absorb_group_rows + + num_challenges * transcript_type::challenge_rows + + num_challenges_fq * transcript_type::challenge_fq_rows + + static_cast(digest) * transcript_type::digest_rows; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector input_fr; + std::vector> input_g; + }; + + struct result_type { + var squeezed = var(0, 0, false); + result_type(var &input) : squeezed(input) {} + }; + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + generate_assignments_constants(bp, assignment, params, start_row_index); + generate_copy_constraints(bp, assignment, params, start_row_index); + + var zero(0, start_row_index, false, var::column_type::constant); + + std::size_t row = start_row_index; + + transcript_type transcript; + transcript.init_circuit(bp, assignment, zero, row); + row += transcript_type::init_rows; + for (std::size_t i = 0; i < params.input_fr.size(); ++i) { + transcript.absorb_fr_circuit(bp, assignment, {params.input_fr[i]}, row); + row += transcript_type::absorb_fr_rows; + } + for (std::size_t i = 0; i < params.input_g.size(); ++i) { + transcript.absorb_g_circuit(bp, assignment, {params.input_g[i][0], params.input_g[i][1]}, row); + row += transcript_type::absorb_group_rows; + } + var sq; + for (size_t i = 0; i < num_challenges; ++i) { + sq = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + } + for (size_t i = 0; i < num_challenges_fq; ++i) { + sq = transcript.challenge_fq_circuit(bp, assignment, row); + row += transcript_type::challenge_fq_rows; + } + if (digest) { + sq = transcript.digest_circuit(bp, assignment, row); + row += transcript_type::digest_rows; + } + return {sq}; + } + + static result_type generate_assignments( + blueprint_assignment_table + &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + std::size_t row = start_row_index; + + var zero = var(0, start_row_index, false, var::column_type::constant); + + transcript_type transcript; + transcript.init_assignment(assignment, zero, row); + row += transcript_type::init_rows; + for (std::size_t i = 0; i < params.input_fr.size(); ++i) { + transcript.absorb_fr_assignment(assignment, {params.input_fr[i]}, row); + row += transcript_type::absorb_fr_rows; + } + for (std::size_t i = 0; i < params.input_g.size(); ++i) { + transcript.absorb_g_assignment(assignment, {params.input_g[i][0], params.input_g[i][1]}, row); + row += transcript_type::absorb_group_rows; + } + var sq; + for (size_t i = 0; i < num_challenges; ++i) { + sq = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + } + for (size_t i = 0; i < num_challenges_fq; ++i) { + sq = transcript.challenge_fq_assignment(assignment, row); + row += transcript_type::challenge_fq_rows; + } + if (digest) { + sq = transcript.digest_assignment(assignment, row); + row += transcript_type::digest_rows; + } + return {sq}; + } + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) {} + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) {} + + static void generate_assignments_constants(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t component_start_row) { + std::size_t row = component_start_row; + assignment.constant(0)[row] = 0; + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fr.hpp b/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fr.hpp new file mode 100644 index 000000000..2251a5873 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/aux_transcript_fr.hpp @@ -0,0 +1,192 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for auxiliary components for the SHA256 component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_AUXILIARY_SPONGE_HPP + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +namespace nil { + namespace crypto3 { + namespace blueprint { + namespace components { + + template + class aux_fr; + + template + class aux_fr< + num_squeezes, + snark::plonk_constraint_system, + CurveType, + W0, W1, W2, W3, + W4, W5, W6, W7, + W8, W9, W10, W11, + W12, W13, W14> { + + typedef snark::plonk_constraint_system ArithmetizationType; + + constexpr static std::size_t public_input_size = 3; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static const std::size_t eval_rounds = 1; + constexpr static const std::size_t max_poly_size = 1; + constexpr static const std::size_t srs_len = 1; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using var = snark::plonk_variable; + using transcript_type = + zk::components::kimchi_transcript_fr; + + public: + constexpr static const std::size_t selector_seed = 0x0fd7; + constexpr static const std::size_t rows_amount = 100; + constexpr static const std::size_t gates_amount = 0; + + struct params_type { + std::vector input; + var zero; + }; + + struct result_type { + var squeezed = var(0, 0, false); + result_type(var &input) : squeezed(input) {} + // result_type(const params_type ¶ms, const std::size_t &start_row_index) { + // squeezed = var(W6, start_row_index + rows_amount - 1, false, var::column_type::witness); + // } + }; + + static result_type generate_circuit(blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + + std::size_t row = start_row_index; + + transcript_type transcript; + transcript.init_circuit(bp, assignment, params.zero, row); + row += transcript_type::init_rows; + for (std::size_t i = 0; i < params.input.size(); ++i) { + transcript.absorb_circuit(bp, assignment, params.input[i], row); + row += transcript_type::absorb_rows; + } + var sq; + for (size_t i = 0; i < num_squeezes; ++i) { + sq = transcript.challenge_circuit(bp, assignment, row); + row += transcript_type::challenge_rows; + } + return {sq}; + } + + static result_type generate_assignments( + blueprint_assignment_table + &assignment, + const params_type ¶ms, + const std::size_t start_row_index){ + std::size_t row = start_row_index; + + transcript_type transcript; + transcript.init_assignment(assignment, params.zero, row); + row += transcript_type::init_rows; + for (std::size_t i = 0; i < params.input.size(); ++i) { + transcript.absorb_assignment(assignment, params.input[i], row); + row += transcript_type::absorb_rows; + } + var sq; + for (size_t i = 0; i < num_squeezes; ++i) { + sq = transcript.challenge_assignment(assignment, row); + row += transcript_type::challenge_rows; + } + return {sq}; + } + + static void generate_gates( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t first_selector_index) {} + + static void generate_copy_constraints( + blueprint &bp, + blueprint_public_assignment_table &assignment, + const params_type ¶ms, + const std::size_t start_row_index) { + + } + }; + } // namespace components + } // namespace blueprint + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_CURVE_ELEMENT_ENDO_SCALAR_COMPONENT_15_WIRES_HPP diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/compare.cpp b/libs/blueprint/test/verifiers/kimchi/sponge/compare.cpp new file mode 100644 index 000000000..5675bbec6 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/compare.cpp @@ -0,0 +1,123 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_compare_with_constant_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_compare_0) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::compare_with_const; + + typename component_type::params_type params = {var(0, 0, false, var::column_type::public_input)}; + typename BlueprintFieldType::value_type value = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000002_cppui_modular255; + std::vector public_input = {value}; + typename BlueprintFieldType::value_type result = 0; + if (value < 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001_cppui_modular255) { + result = 1; + } + + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.output)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "compare with constant: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_compare_1) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + using component_type = zk::components::compare_with_const; + + typename component_type::params_type params = {var(0, 0, false, var::column_type::public_input)}; + std::vector public_input = {0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000_cppui_modular255}; + typename BlueprintFieldType::value_type result = 1; + + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.output)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "compare with constant: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/oracles.cpp b/libs/blueprint/test/verifiers/kimchi/sponge/oracles.cpp new file mode 100644 index 000000000..20bcc9c03 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/oracles.cpp @@ -0,0 +1,210 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_oracles_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "../../../test_plonk_component.hpp" + +template +void test_from_limbs(std::vector public_input, + typename BlueprintFieldType::value_type expected_res){ + constexpr std::size_t WitnessColumns = 3; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using component_type = nil::blueprint::components::from_limbs; + using var = nil::crypto3::zk::snark::plonk_variable; + + var x(0, 0, false, var::column_type::public_input); + var y(0, 1, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {x, y}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "from_limbs input: " << std::hex << public_input[0].data << " " << public_input[1].data << std::endl; + std::cout << "expected_res: " << std::hex << expected_res.data << std::endl; + std::cout << "real res: " << std::hex << var_value(assignment, real_res.result).data << "\n" << std::endl; + #endif + assert(expected_res == var_value(assignment, real_res.result)); + }; + + component_type component_instance({0, 1, 2}, {}, {}); + + + nil::crypto3::test_component (component_instance, desc, public_input, result_check, instance_input); + +} + +template +void test_to_limbs(const std::vector &public_input, + const std::vector &expected_res){ + constexpr std::size_t WitnessColumns = 15 * (Stretched ? 2 : 1); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = nil::blueprint::assignment>; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using component_type = nil::blueprint::components::to_limbs; + using var = nil::crypto3::zk::snark::plonk_variable; + + var x(0, 0, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {x}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "to_limbs input: " << std::hex << public_input[0].data << std::endl; + std::cout << "expexted: " << std::hex << expected_res[3].data << " " << expected_res[2].data << " " << expected_res[1].data << " " << expected_res[0].data << std::endl; + std::cout << "real : " << std::hex << var_value(assignment, real_res.result[3]).data << " " << var_value(assignment, real_res.result[2]).data << " " << var_value(assignment, real_res.result[1]).data << " " << var_value(assignment, real_res.result[0]).data << "\n" <; + + stretched_component_type stretched_instance(component_instance, WitnessColumns / 2, WitnessColumns); + + nil::crypto3::test_component + (stretched_instance, desc, public_input, result_check, instance_input); + } else { + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); + } +} + +template +void test_to_limbs_with_stretching(const std::vector &public_input, + const std::vector &expected_res) { + test_to_limbs(public_input, expected_res); + test_to_limbs(public_input, expected_res); +} + + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_from_limbs_specific_data(){ + test_from_limbs({0, 0}, 0); + test_from_limbs({5, 12}, 0xC0000000000000005_cppui_modular255); + test_from_limbs({0, 0xFFFFFFFFFFFFFFFF_cppui_modular255}, 0xFFFFFFFFFFFFFFFF0000000000000000_cppui_modular255); + test_from_limbs({0xFFFFFFFFFFFFFFFF_cppui_modular255, 0}, 0xFFFFFFFFFFFFFFFF_cppui_modular255); + test_from_limbs({0xFFFFFFFFFFFFFFFF_cppui_modular255, 0xFFFFFFFFFFFFFFFF_cppui_modular255}, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular255); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_from_limbs_vesta) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_from_limbs_specific_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_from_limbs_bls12) { + using field_type = nil::crypto3::algebra::fields::bls12_fr<381>; + test_from_limbs_specific_data(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_from_limbs_pallas) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_from_limbs_specific_data(); +} + +template +void test_to_limbs_specific_data(){ + test_to_limbs_with_stretching({0x1D42ED837696F2A777E7C1FF0436D46E96878B624ECDE039732E37AFCD409C88_cppui_modular256}, + {0x732E37AFCD409C88_cppui_modular256, 0x96878B624ECDE039_cppui_modular256, 0x77E7C1FF0436D46E_cppui_modular256, 0x1D42ED837696F2A7_cppui_modular256}); + + test_to_limbs_with_stretching({0xE826DABA538B6DF0000000000000000FB812F513D0FCC04106CB4BD3F32FAD3_cppui_modular256}, + {0x106CB4BD3F32FAD3_cppui_modular256, 0xFB812F513D0FCC04_cppui_modular256, 0x0_cppui_modular256, 0xE826DABA538B6DF_cppui_modular256}); + + test_to_limbs_with_stretching({0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui_modular256}, + {0xFFFFFFFFFFFFFFFF_cppui_modular256, 0xFFFFFFFFFFFFFFFF_cppui_modular256, 0xFFFFFFFFFFFFFFFF_cppui_modular256, 0x3FFFFFFFFFFFFFFF_cppui_modular256}); + +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_to_limbs_vesta) { + using field_type = nil::crypto3::algebra::curves::vesta::scalar_field_type; + test_to_limbs_specific_data(); + + test_to_limbs_with_stretching({0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000_cppui_modular255}, //-1 vesta + {0x992d30ed00000000_cppui_modular256, 0x224698fc094cf91b_cppui_modular256, 0x0000000000000000_cppui_modular256, 0x4000000000000000_cppui_modular256}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_to_limbs_pallas) { + using field_type = nil::crypto3::algebra::curves::pallas::scalar_field_type; + test_to_limbs_specific_data(); + + test_to_limbs_with_stretching({0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000000_cppui_modular256}, //-1 pallas + {0x8c46eb2100000000_cppui_modular256, 0x224698fc0994a8dd_cppui_modular256, 0x0000000000000000_cppui_modular256, 0x4000000000000000_cppui_modular256}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_to_limbs_bls12) { + using field_type = nil::crypto3::algebra::fields::bls12_fr<381>; + test_to_limbs_specific_data(); + + test_to_limbs_with_stretching({0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000_cppui_modular256}, //-1 bls12<381> + {0xffffffff00000000_cppui_modular256, 0x53bda402fffe5bfe_cppui_modular256, 0x3339d80809a1d805_cppui_modular256, 0x73eda753299d7d48_cppui_modular256}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/sponge.cpp b/libs/blueprint/test/verifiers/kimchi/sponge/sponge.cpp new file mode 100644 index 000000000..99e84ddd1 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/sponge.cpp @@ -0,0 +1,342 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_auxiliary_sponge_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include <../test/verifiers/kimchi/sponge/aux_sponge.hpp> + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_0) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + std::vector public_input = {0}; + typename BlueprintFieldType::value_type result = 0x2FADBE2852044D028597455BC2ABBD1BC873AF205DFABB8A304600F3E09EEBA8_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_1) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input = {{0, 1, false, var::column_type::public_input}}; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input = {0, 0x36FB00AD544E073B92B4E700D9C49DE6FC93536CAE0C612C18FBE5F6D8E8EEF2_cppui_modular256}; + + typename BlueprintFieldType::value_type result = 0x3D4F050775295C04619E72176746AD1290D391D73FF4955933F9075CF69259FB_cppui_modular256; + std::cout<<"Result: "< (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_2) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input = {{0, 1, false, var::column_type::public_input}, {0, 2, false, var::column_type::public_input}}; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input = {0, 0x3793E30AC691700012BAF26BB813D6D70BD379BEED8050A1DEEE3C188F1C3FBD_cppui_modular256, + 0x2FC4C98E50E0B1AAE6ECB468E28C0B7D80A7E0EEC7136DB0BA0677B84AF0E465_cppui_modular256}; + + typename BlueprintFieldType::value_type result = 0x336C73D08AD408CEB7D1264867096F0817A1D0558B313312A1207602F23624FE_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_3) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input = {{0, 1, false, var::column_type::public_input}, + {0, 2, false, var::column_type::public_input}, {0, 3, false, var::column_type::public_input}}; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input = {0, 0x0024FB5773CAC987CF3A17DDD6134BA12D3E1CA4F6C43D3695347747CE61EAF5_cppui_modular256, + 0x18E0ED2B46ED1EC258DF721A1D3145B0AA6ABDD02EE851A14B8B659CF47385F2_cppui_modular256, + 0x1A842A688E600F012637FE181292F70C4347B5AE0D9EA9CE7CF18592C345CF73_cppui_modular256}; + + typename BlueprintFieldType::value_type result = 0x3F4B0EABB64E025F920457AF8D090A9F6472CAE11F3D62A749AF544A44941B9B_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_4) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input = {{0, 1, false, var::column_type::public_input}, + {0, 2, false, var::column_type::public_input}, {0, 3, false, var::column_type::public_input}, {0, 4, false, var::column_type::public_input}}; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input = {0, 0x2059462D60621F70620EA697FA1382EC5553A3DADB3CF9072201E09871B8284C_cppui_modular256, + 0x2747337D1C4F9894747074C771E8EC7F570640E5D0CAF30FDDC446C00FA48707_cppui_modular256, + 0x2DD5047C3EEEF37930E8FA4AD9691B27CF86D3ED39D4DEC4FC6D4E8EE4FF0415_cppui_modular256, + 0x12C387C69BDD436F65AB607A4ED7C62714872EDBF800518B58E76F5106650B29_cppui_modular256}; + + typename BlueprintFieldType::value_type result = 0x165A8CECF6660C6E0054CB9B4DBA9D68047166D7F3CED2F8DC86ED2EBFD3EC47_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_5) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input = {{0, 1, false, var::column_type::public_input}, + {0, 2, false, var::column_type::public_input}, {0, 3, false, var::column_type::public_input}, {0, 4, false, var::column_type::public_input}, + {0, 5, false, var::column_type::public_input}}; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input = {0, 0x3CF70C3A89749A45DB5236B8DE167A37762526C45270138A9FCDF2352B1899DA_cppui_modular256, + 0x1BDF55BC84C1A0E0F7F6834949FCF90279B9D21C17DBC9928202C49039570598_cppui_modular256, + 0x09441E95A82199EFC390152C5039C0D0566A90B7F6D1AA5813B2DAB90110FF90_cppui_modular256, + 0x375B4A9785503C24531723DB1F31B50B79C3D1EC9F95DB7645A3EDA03862B588_cppui_modular256, + 0x12688FE351ED01F3BB2EB6B0FA2A70FB232654F32B08990DC3A411E527776A89_cppui_modular256}; + + typename BlueprintFieldType::value_type result = 0x0CA2C3342C2959D7CD94B5C9D4DC55900F5F60B345F714827C8B907752D5A209_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_sponge_double_squeeze) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 15; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr static const size_t num_squeezes = 2; + using component_type = zk::components::aux; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + + std::vector public_input; + + typename BlueprintFieldType::value_type result = 0x160A4D666FF9427DC907A5358B16C6966EB386213CE7994F87C8970F7DB8CDC3_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi sponge: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fq.cpp b/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fq.cpp new file mode 100644 index 000000000..8117cc352 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fq.cpp @@ -0,0 +1,280 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_auxiliary_transcript_test + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include <../test/verifiers/kimchi/sponge/aux_transcript_fq.hpp> + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_transcript_0) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 17; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_absorb = 15; + constexpr size_t num_challenges = 1; //works + constexpr size_t num_challenges_fq = 0; //works + constexpr bool digest = false; //works + using component_type = zk::components::aux_fq; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input_fr; + std::vector> input_g = {{var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input)}, + {var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input)}, + {var(0, 4, false, var::column_type::public_input), + var(0, 5, false, var::column_type::public_input)}, + {var(0, 6, false, var::column_type::public_input), + var(0, 7, false, var::column_type::public_input)}, + {var(0, 8, false, var::column_type::public_input), + var(0, 9, false, var::column_type::public_input)}, + {var(0, 10, false, var::column_type::public_input), + var(0, 11, false, var::column_type::public_input)}, + {var(0, 12, false, var::column_type::public_input), + var(0, 13, false, var::column_type::public_input)}, + {var(0, 14, false, var::column_type::public_input), + var(0, 15, false, var::column_type::public_input)}, + {var(0, 16, false, var::column_type::public_input), + var(0, 17, false, var::column_type::public_input)}, + {var(0, 18, false, var::column_type::public_input), + var(0, 19, false, var::column_type::public_input)}, + {var(0, 20, false, var::column_type::public_input), + var(0, 21, false, var::column_type::public_input)}, + {var(0, 22, false, var::column_type::public_input), + var(0, 23, false, var::column_type::public_input)}, + {var(0, 24, false, var::column_type::public_input), + var(0, 25, false, var::column_type::public_input)}, + {var(0, 26, false, var::column_type::public_input), + var(0, 27, false, var::column_type::public_input)}, + {var(0, 28, false, var::column_type::public_input), + var(0, 29, false, var::column_type::public_input)}}; + typename component_type::params_type params = {input_fr, input_g}; + std::vector public_input = {0x1CF10D1482EB88632AEFED15C16082007B38DDC528626195CF6B040E2C7D5914_cppui_modular256, + 0x15A406A92FA16DB6E24D125C8EC5365D76DD8BB188106C0063BA9EC51E0FB8E7_cppui_modular256, + 0x3B38AC47170B2DB158AE7C02E939B2877139040D240171F6A6BB01183902566E_cppui_modular256, + 0x05AAC7FD92471BBFF23D5E4F9AD0B64783467A4809940FEBB7BD6C91A9E9E1C0_cppui_modular256, + 0x281BD2B891CF0795B1439B3AB149ED2A535B8E08C4430112D7D4BF53F3789BEF_cppui_modular256, + 0x10B2FA452CAC5D11CC8040D5DD504222A2621FC378EFD7D08A01BAB3A3DE28DF_cppui_modular256, + 0x0158FEA0E6586A75F36FB621E9C9FC7A38970812F0F1753D3BB716655E3B9D79_cppui_modular256, + 0x2A9688F370DCC43130D38AB7AD2B3FF2A925791F587B55AD138B1F067E874C59_cppui_modular256, + 0x0CA7898337AB528838EAD23D7CBCD4861F1E5E2E5D3B1BD3B733A832C7931547_cppui_modular256, + 0x351C82EC1D20E977ABFC632BBA2330AF61270A00BC2D32B6F2E1DA93AA0D51F1_cppui_modular256, + 0x00DCE7DC20642A850002731F9B3820327CF5856B1D8C3B0EE6BD7BC03BC85FFD_cppui_modular256, + 0x3B1BCBA06B0D33F08123EDD6DF725CC1F8CD2213EA867FF4020C2D18619BB2DB_cppui_modular256, + 0x0F7C2FF92D8F0776629F87BBF25702CEAA45B1893617F7C9AC10AACB080B6E10_cppui_modular256, + 0x16E7207D6596C7FAFF46FB335E14DC57E08E150AB7F692607F3B8DCC9E6CDA93_cppui_modular256, + 0x2CD748E8C8806196ABE34DF032864491CADCF205AF70CB9152507BD16B912BEC_cppui_modular256, + 0x2219EC3C1873373A6717E7BFA24827AD89BF949B0F240D7B9D8981C2006E400F_cppui_modular256, + 0x027E878BD478FC5DE36CA783CB60297C5F75CB638C71615A04714C52E9B15E8E_cppui_modular256, + 0x2CCE580022C7D44E72BA8E7E608C3733A3F3EDC0304566097C07D6CCA172A1B4_cppui_modular256, + 0x0DC7C8FE3A9007F09283D29C5BE99AACEB9DA6996CD691BBAC5D075BDD6DA223_cppui_modular256, + 0x1FA4B95451090B8A36D503BFDBF086D4462745626B4BA4490AF42A7A6B5FD449_cppui_modular256, + 0x20254A64C61A3C1882EC3E9FCA0ABAE814B0EB0477C3396E562C1006054347F3_cppui_modular256, + 0x23CDCBDE9DCBD33AD86BF48181B1616FC76D24A18711A3953D184E772D936418_cppui_modular256, + 0x00DB22BCFC9A1D1A10A53716A7E7D4022DBF101B8767B68E78837CB8263BE097_cppui_modular256, + 0x3E283D2F0D90CAC87B3FCD95E7A8933FB2B2B43EF07FA577CA566527481AB6C9_cppui_modular256, + 0x0D24814B6FE1C8C42FC05834B95212E473B76C8B9588D1272BFAE8FA0E2B9384_cppui_modular256, + 0x11C75275709440AC01B74C4E64E2606F7826294F868F6B0265008E758C148369_cppui_modular256, + 0x007997CB753B919B586243FCAF6E5886676F180C2220BAC055AE9739CA4A1B4B_cppui_modular256, + 0x166859AE2ECE3520D33C2D146F6DBCFC819779C288E9D81C3F7369DF5642EF31_cppui_modular256, + 0x04E774B3DE1A78D6C9408D7B10D9E4614FC8AE4DFE4BFE6762278EE72BB9E25D_cppui_modular256, + 0x178AC19F836752BAF356D9E9C3C35470F27A52C16B7572EEF2C61A43B4D0499B_cppui_modular256}; + typename BlueprintFieldType::value_type result = 0x0000000000000000000000000000000006906F18EE1C02C944C3186D54A8D03E_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi transcript_fq: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_transcript_1) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 20; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_absorb = 0; + constexpr size_t num_challenges = 0; //works + constexpr size_t num_challenges_fq = 0; //works + constexpr bool digest = true; //works + using component_type = zk::components::aux_fq; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input_fr; + std::vector> input_g; + typename component_type::params_type params = {input_fr, input_g}; + std::vector public_input; + typename BlueprintFieldType::value_type result = 0x3A3374A061464EC0AAC7E0FF04346926C579D542F9D205A670CE4C18C004E5C1_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi transcript_fq: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_transcript_2) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 20; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_absorb = 0; + constexpr size_t num_challenges = 3; //works + constexpr size_t num_challenges_fq = 0; //works + constexpr bool digest = false; //works + using component_type = zk::components::aux_fq; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input_fr; + std::vector> input_g; + typename component_type::params_type params = {input_fr, input_g}; + std::vector public_input = + {}; + typename BlueprintFieldType::value_type result = 0x00000000000000000000000000000000AFEB6EEE7D0BD8B45C33CA8DDFC9DFE9_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi transcript_fq: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_transcript_3) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 20; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_absorb = 1; + constexpr size_t num_challenges = 1; //works + constexpr size_t num_challenges_fq = 0; //works + constexpr bool digest = false; //works + using component_type = zk::components::aux_fq; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + + std::vector public_input; + typename BlueprintFieldType::value_type result = 0x000000000000000000000000000000003972C78FB41D347300A463E54826F2AB_cppui_modular256; + std::vector input_fr; + std::vector> input_g; + + public_input.push_back(1); + input_fr.push_back(var(0, 0, false, var::column_type::public_input)); + + typename component_type::params_type params = {input_fr, input_g}; + + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi transcript_fq: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fr.cpp b/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fr.cpp new file mode 100644 index 000000000..17d64f180 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/sponge/transcript_fr.cpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Polina Chernyshova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_auxiliary_transcript_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include <../test/verifiers/kimchi/sponge/aux_transcript_fr.hpp> + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_transcript_0) { + auto start = std::chrono::high_resolution_clock::now(); + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 16; + using ArithmetizationParams = zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + + constexpr size_t num_squeezes = 1; + using component_type = zk::components::aux_fr; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + std::vector input; + var zero(0, 0, false, var::column_type::public_input); + typename component_type::params_type params = {input, zero}; + std::vector public_input = {0}; + typename BlueprintFieldType::value_type result = 0x00000000000000000000000000000000C873AF205DFABB8A304600F3E09EEBA8_cppui_modular256; + auto result_check = [&result](AssignmentType &assignment, + component_type::result_type &real_res) { + assert(result == assignment.var_value(real_res.squeezed)); + }; + test_component (params, public_input, result_check); + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + std::cout << "kimchi transcript_fr: " << duration.count() << "ms" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/kimchi/table_commitment.cpp b/libs/blueprint/test/verifiers/kimchi/table_commitment.cpp new file mode 100644 index 000000000..0e38e1921 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/table_commitment.cpp @@ -0,0 +1,169 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_batch_verify_base_field_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include +//#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/lookup_test.hpp" + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_table_commitment_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_table_commitment_test) { + + using curve_type = algebra::curves::pallas; + using BlueprintFieldType = typename curve_type::base_field_type; + using ScalarFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 25; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + constexpr static const std::size_t batch_size = 1; + constexpr static const std::size_t eval_rounds = 1; + constexpr static const std::size_t comm_size = 1; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + + constexpr static std::size_t witness_columns = 5; + constexpr static std::size_t perm_size = 5; + + constexpr static std::size_t srs_len = 1; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_lookup_test; + using circuit_description = zk::components::kimchi_circuit_description; + using KimchiParamsType = zk::components::kimchi_params_type; + + using commitment_type = typename + zk::components::kimchi_commitment_type; + using kimchi_constants = zk::components::kimchi_inner_constants; + + using component_type = zk::components::table_commitment; + using var_ec_point = typename zk::components::var_ec_point; + using var = zk::snark::plonk_variable; + + constexpr static const std::size_t lookup_columns = KimchiParamsType::circuit_params::lookup_columns; + + constexpr std::size_t use_lookup_runtime = KimchiParamsType::circuit_params::lookup_runtime ? 1 : 0; + + // zk::snark::pickles_proof kimchi_proof = test_proof(); + + std::vector public_input; + std::vector lookup_columns_var; + std::array lookup_scalars_var; + commitment_type runtime_var; + std::size_t j = 0; + std::size_t size = KimchiParamsType::commitment_params_type::shifted_commitment_split; + for (std::size_t i = j; i < lookup_columns; i++){ + commitment_type column_var; + for (std::size_t k = 0; k < size; k++) { + public_input.push_back(algebra::random_element>().X); + public_input.push_back(algebra::random_element>().Y); + column_var.parts[k] = {var(0, i*k*2 + k*2, false, var::column_type::public_input), + var(0, i*k*2 + k*2 + 1, false, var::column_type::public_input)}; + j+=2; + } + lookup_columns_var.push_back(column_var); + } + + if (KimchiParamsType::circuit_params::lookup_runtime){ + for (std::size_t k = 0; k < size; k++) { + public_input.push_back(algebra::random_element>().X); + public_input.push_back(algebra::random_element>().Y); + runtime_var.parts[k] = {var(0, j + k*2, false, var::column_type::public_input), + var(0, j + k*2 + 1, false, var::column_type::public_input)}; + j+=2; + } + } + std::size_t s = lookup_columns + j; + for (std::size_t i = j; i < s; i++){ + for (std::size_t k = 0; k < size; k++) { + public_input.push_back(algebra::random_element()); + lookup_scalars_var[i - j] = (var(0, i, false, var::column_type::public_input)); + j++; + } + } + + typename component_type::params_type params = {lookup_columns_var, lookup_scalars_var, runtime_var}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component( + params, public_input, result_check); +}; +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/kimchi/verify_scalar.cpp b/libs/blueprint/test/verifiers/kimchi/verify_scalar.cpp new file mode 100644 index 000000000..60f2207a5 --- /dev/null +++ b/libs/blueprint/test/verifiers/kimchi/verify_scalar.cpp @@ -0,0 +1,237 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021-2022 Mikhail Komarov +// Copyright (c) 2021-2022 Nikita Kaskov +// Copyright (c) 2022 Ilia Shirobokov +// Copyright (c) 2022 Alisa Cherniaeva +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_verifier_scalar_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_kimchi_verify_scalar_field_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + for (std::size_t i = 0; i < KimchiParamsType::public_input_size; i++) { + public_input.push_back(original_proof.public_input[i]); + circuit_proof.public_input[i] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + for (std::size_t i = 0; i < KimchiParamsType::prev_challenges_size; i++) { + for (std::size_t j = 0; j < EvalRounds; j++) { + public_input.push_back(original_proof.prev_challenges[i].first[j]); + circuit_proof.prev_challenges[i][j] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + } + + // ft_eval + public_input.push_back(original_proof.ft_eval1); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_kimchi_verify_scalar_field_test_suite) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static std::size_t batch_size = 2; + + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using fq_output_type = + typename zk::components::binding::fq_sponge_output; + + using fr_data_type = typename zk::components::binding::fr_data; + + using fq_data_type = + typename zk::components::binding::fq_data; + + zk::components::kimchi_verifier_index_scalar verifier_index; + typename BlueprintFieldType::value_type omega = + 0x1B1A85952300603BBF8DD3068424B64608658ACBB72CA7D2BB9694ADFA504418_cppui_modular256; + std::size_t domain_size = 128; + verifier_index.domain_size = domain_size; + verifier_index.omega = var(0, 0, false, var::column_type::public_input); + + using component_type = + zk::components::verify_scalar; + + typename BlueprintFieldType::value_type joint_combiner = 0; + typename BlueprintFieldType::value_type beta = 0; + typename BlueprintFieldType::value_type gamma = 0; + typename BlueprintFieldType::value_type alpha = + 0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256; + typename BlueprintFieldType::value_type zeta = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + typename BlueprintFieldType::value_type fq_digest = + 0x01D4E77CCD66755BDDFDBB6E4E8D8D17A6708B9CB56654D12070BD7BF4A5B33B_cppui_modular256; + + std::vector public_input = {omega}; + + std::array, batch_size> proofs; + + std::array fq_outputs; + + for (std::size_t batch_id = 0; batch_id < batch_size; batch_id++) { + zk::snark::pickles_proof kimchi_proof = test_proof(); + + zk::components::kimchi_proof_scalar proof; + + prepare_proof(kimchi_proof, proof, public_input); + + fq_output_type fq_output; + std::array challenges; + for (std::size_t j = 0; j < eval_rounds; j++) { + public_input.emplace_back(10); + challenges[j] = var(0, public_input.size() - 1, false, var::column_type::public_input); + } + fq_output.challenges = challenges; + + // joint_combiner + public_input.emplace_back(0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256); + fq_output.joint_combiner = var(0, public_input.size() - 1, false, var::column_type::public_input); + // beta + public_input.emplace_back(0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256); + fq_output.beta = var(0, public_input.size() - 1, false, var::column_type::public_input); + // gamma + public_input.emplace_back(0x0000000000000000000000000000000005321CB83A4BCD5C63F489B5BF95A8DC_cppui_modular256); + fq_output.gamma = var(0, public_input.size() - 1, false, var::column_type::public_input); + // alpha + public_input.push_back(alpha); + fq_output.alpha = var(0, public_input.size() - 1, false, var::column_type::public_input); + // zeta + public_input.push_back(zeta); + fq_output.zeta = var(0, public_input.size() - 1, false, var::column_type::public_input); + // fq_digest + public_input.push_back(fq_digest); + fq_output.fq_digest = var(0, public_input.size() - 1, false, var::column_type::public_input); + // c + public_input.emplace_back(250); + fq_output.c = var(0, public_input.size() - 1, false, var::column_type::public_input); + + fq_outputs[batch_id] = fq_output; + } + + fr_data_type fr_data_public; + fq_data_type fq_data_public; + + typename component_type::params_type params = {fr_data_public, fq_data_public, verifier_index, proofs, fq_outputs}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/pickles/scalar_details/evals_of_split_evals.cpp b/libs/blueprint/test/verifiers/pickles/scalar_details/evals_of_split_evals.cpp new file mode 100644 index 000000000..39e6b9819 --- /dev/null +++ b/libs/blueprint/test/verifiers/pickles/scalar_details/evals_of_split_evals.cpp @@ -0,0 +1,157 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_kimchi_oracles_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" +#include "verifiers/kimchi/proof_data.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_oracles_test_suite) + +template +void prepare_proof(zk::snark::pickles_proof &original_proof, + zk::components::kimchi_proof_scalar &circuit_proof, + std::vector &public_input) { + using var = zk::snark::plonk_variable; + + // eval_proofs + for (std::size_t point_idx = 0; point_idx < 2; point_idx++) { + // w + for (std::size_t i = 0; i < KimchiParamsType::witness_columns; i++) { + public_input.push_back(original_proof.evals[point_idx].w[i]); + circuit_proof.proof_evals[point_idx].w[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // z + public_input.push_back(original_proof.evals[point_idx].z); + circuit_proof.proof_evals[point_idx].z = var(0, public_input.size() - 1, false, var::column_type::public_input); + // s + for (std::size_t i = 0; i < KimchiParamsType::permut_size - 1; i++) { + public_input.push_back(original_proof.evals[point_idx].s[i]); + circuit_proof.proof_evals[point_idx].s[i] = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + // lookup + if (KimchiParamsType::use_lookup) { + // TODO + } + // generic_selector + public_input.push_back(original_proof.evals[point_idx].generic_selector); + circuit_proof.proof_evals[point_idx].generic_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + // poseidon_selector + public_input.push_back(original_proof.evals[point_idx].poseidon_selector); + circuit_proof.proof_evals[point_idx].poseidon_selector = + var(0, public_input.size() - 1, false, var::column_type::public_input); + } + + // ft_eval + public_input.push_back(algebra::random_element()); + circuit_proof.ft_eval = var(0, public_input.size() - 1, false, var::column_type::public_input); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_oracles_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = + zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + const std::size_t split_size = 2; + + using component_type = zk::components::evals_of_split_evals; + + zk::snark::pickles_proof kimchi_proof = test_proof(); + + typename BlueprintFieldType::value_type zeta_val = + 0x0000000000000000000000000000000062F9AE3696EA8F0A85043221DE133E32_cppui_modular256; + + zk::components::kimchi_proof_scalar proof; + + std::vector public_input = {zeta_val}; + + var zeta(0, 0, false, var::column_type::public_input); + + prepare_proof(kimchi_proof, proof, public_input); + + typename component_type::params_type params = {proof.proof_evals, zeta, zeta}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/pickles/verify_heterogenous_base.cpp b/libs/blueprint/test/verifiers/pickles/verify_heterogenous_base.cpp new file mode 100644 index 000000000..5a57d151d --- /dev/null +++ b/libs/blueprint/test/verifiers/pickles/verify_heterogenous_base.cpp @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pickles_verifier_scalar_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_pickles_heterogenous_verify_base_field_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_pickles_heterogenous_verify_base_field_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::base_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static std::size_t batch_size = 2; + + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = + zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::verify_generogenous_base; + + std::vector public_input = {}; + + typename component_type::params_type params = {}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/pickles/verify_heterogenous_scalar.cpp b/libs/blueprint/test/verifiers/pickles/verify_heterogenous_scalar.cpp new file mode 100644 index 000000000..5b5a6e575 --- /dev/null +++ b/libs/blueprint/test/verifiers/pickles/verify_heterogenous_scalar.cpp @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2022 Ilia Shirobokov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pickles_verifier_scalar_test + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include "verifiers/kimchi/index_terms_instances/ec_index_terms.hpp" + +#include "test_plonk_component.hpp" + +using namespace nil::crypto3; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_pickles_heterogenous_verify_scalar_field_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_pickles_heterogenous_verify_scalar_field_test) { + + using curve_type = algebra::curves::vesta; + using BlueprintFieldType = typename curve_type::scalar_field_type; + constexpr std::size_t WitnessColumns = 15; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 30; + using ArithmetizationParams = + zk::snark::plonk_arithmetization_params; + using ArithmetizationType = zk::snark::plonk_constraint_system; + using AssignmentType = zk::blueprint_assignment_table; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = zk::snark::plonk_variable; + + constexpr static std::size_t public_input_size = 3; + constexpr static std::size_t max_poly_size = 32; + constexpr static std::size_t eval_rounds = 5; + + constexpr static std::size_t witness_columns = 15; + constexpr static std::size_t perm_size = 7; + + constexpr static std::size_t srs_len = 10; + constexpr static std::size_t batch_size = 2; + + constexpr static const std::size_t prev_chal_size = 1; + + using commitment_params = zk::components::kimchi_commitment_params_type; + using index_terms_list = zk::components::index_terms_scalars_list_ec_test; + using circuit_description = + zk::components::kimchi_circuit_description; + using kimchi_params = zk::components::kimchi_params_type; + + using component_type = + zk::components::verify_generogenous_scalar; + + std::vector public_input = {}; + + typename component_type::params_type params = {}; + + auto result_check = [](AssignmentType &assignment, component_type::result_type &real_res) {}; + + test_component(params, public_input, + result_check); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/circuit.crct b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/circuit.crct new file mode 100644 index 000000000..851964834 Binary files /dev/null and b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/circuit.crct differ diff --git a/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/common.dat b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/common.dat new file mode 100644 index 000000000..76ebeac13 Binary files /dev/null and b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/common.dat differ diff --git a/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/proof.bin b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/proof.bin new file mode 100644 index 000000000..8ee1b5082 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/data/merkle_tree_poseidon/proof.bin @@ -0,0 +1 @@ +0x0301eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f2833a945c56b7aeead0dd4296c223e002c51eee432b1fc51dea21d408f7665ea00000000000000702833a945c56b7aeead0dd4296c223e002c51eee432b1fc51dea21d408f7665ea09024e5cdb1966a9614524cf1cab360076c5df80e19302468da2ff7bcd4ffd8f2d0b87d0477f014ee659b80b8f580e0251dd5d8467df0b60c42efd6b028ff3cb2139a711657b068a7fc09839ccb8460b327f08a1eb744d91096360500ccfc2f43142ef565311479f56404a58116ab450af4396d395d24e64ba43aeb38986af8a36f52054cb13ec89cc85b799143626ab485d5932b5fc3ce5a8de73ac7f93a5d701e822270359e9bde73401950ab82c9990eccd7f456401a993d8753525c71190100e5c6dc322afd94c08f852073b6dc3cee47f0759a5e793d63e5f24b8e633ec1b18089f69808be37a92fb76ee378462dc76db1412746eaedd93b07fede4e4ed29aa01fa2b06f5b0bb3b5c96d71468308dc213365d2be4828d41335094de9ba938cb44f618996ad58b0b8c8db35c02bc9b0b394fa7f73628d8eb79686fc23b422717c30b8e8e882037f6fefd6093e875899bbbb73bf62883ba17b62e86adc33000000000000000000000000000000000000000000000000000000000000000003b52ba9085e148eaf451fa8dd52aa97abc599adc5a0e35217b2409c27858127a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bfd012b0d5595f68b33dded4aa8528db1ecc2f209ea27aa0d4d5ca2721d75bf1c039c8ae957654c0a708716a82be6a088b9b59297637bf99930bcb52813c67305fcf6d61263a50eafc5fe625e90ee1fe0e1c1d13be854ca1dfe16c24e062ec3238bdc87405e638df6eef1ea90ad50dd4d9384559ccab0bcf5ee4e25ba790a2a38a46921ffb7c07e795cb5d40ed245ce9e06a4d26c2dfbea1d1f9e8336e605f0328cc09d32b556512e5515202feb27d250e988a3b2ceab9fc1d670f632efa9b13654e35de3f27fc40f27b222b6eaed77394c7bbdb61788bcf1c94763ae915b3e1ca70291e3f4405c121969025130058abba35c46eb64ef738ef1d316030d43ab1be01183f16020103d051e357237faa5ccffbfa210039252feecfb8f544f085125b27298ffaf814a892e8d84d94539e6490a2a17d5731accc46af2d7789cdd56091483d894fd33e45b360379e0ae5dd065d66ed7e456b71c99f258535505776e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000236681061431b2f592cd61cf5dd7f5b801dad7de2b17964bbaeefbca01b5dee40ff082a0ed9d643ffdb730ad4bfd145473b7c3bfc2add7182820882fb7258ea81b8bdf93ff5bfd7f54cf8950e7a6d2a37d07689a6c3713565bd4928f1e0055dd16c53c9aa283283f85d6848302715968b530c5c69c9f446d3fd7106c3dd3394f099bc7f23b2cf2ed0adcddc2675d94ebafe2025ec2941682ec24bd6d7a4c04e509b79f7dc845273cc3de9ed078ca01fbfa29e04cc053ed0381e8e8976ca1b30c21a7efd9f11c78afef4e0624e55822f4d373f85814c464d8b92624e3eb223d300f912497ba330a2ef972a2ba771f230cfcf3178757d0d59cc8f7f2fd0afaf05a36eeaa6aefbc36af6bec8f50c74945f243d26f752a5d0ca300007c6ecf1955e031038e61c9088fd0b82860b27d531c8f037fd558534fe7c50dc5e4d77d75502b117703523176fd35fed309b99e9dd618d7bd54daa37967ac2c77719661764545250dd052ee36fa74eb059ae11251471ae3a3a2ba44052772e46b7a3925a809bb18470f9cb4102b538416edde6cdcac129313b1a3ee899f1e4d43d5de38f1e73c0bfcd439f96fa68dd739a4fccdd9d2dab6f4adae2f39576491c5caf75df2360204c3d11cd18ddc00599418b3f93374f20fc18e84661cddfe4d2bd9e470851bee2c0036973483041a9eafe0ba558adae22221594465f0ac1bef41a346a225827a1f7cd14f6978b0e08b59b673ef46c8ebd888dfc45a20cd6279ead6d21c4f6c7936a88d85ea00b96de9e979f4d74bc4d8b35205a30b6f5333be138bd668cde23437395c2f27dad571e273f2691a55282b4d4f9b1549f24becf47a3e3ba8faa1e300009f1f40e315e01a0046b1580e999584c500168dc6f7eeb0577d82516f79f43aa119dc4b7eba004ea51942a122b401057446e3dfd222c8479888d890585f4337f7d13e52b93b59ee14e9bac12225f83c40ca91fc256cbb0243a70ce4f2b18a142fe15f6183df4667440a2ee2ac5330bf95c8c4358bdbafe9539710e7d137460971c9026469548ea3f556b14b1775d56473ac5cba9ae29ec7c363e06da7b3b92eabca9c16f90fcb5ed10046f53b98e4d3582e152a812ee57f0ba4d962548b4b1f0ecf066d34f4a6e83eaa8893a316ae86a8b2ef38de6232e6f5af8a7e47cb74399607e00363a2cadfbbeaed22e77d0e08e36baf9c1822e1cac52b3ea36c533439e75fd8beae8b639cbded5725099157c042db685c18c69272031c07a835066e2a627e6a1300d41a56512a47059a6f9262479242b276f597a47a0f9d478a5b1d00000000000000080051011002010308000000000000006a0101010101010101020201010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010202020101010101010101010101010102010101010101010100000000000000080b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d31ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b058361889070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c301111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b6168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000008010101010101010100000000000010902ad57a2aad9c2ba17a58951dcea1a262b1d49d1ed6fddf64211bde2936c1ed84152a85d55263d45e85a76ae2315e5d9d7071fbdd324f19b7781152c3c93e127d162b62d5640cda2763bae99509282bed125346a6170e71a1da03c40711c9a39129d49d2a9bf325d89c45166af6d7d4130ff35255f23e8779bf296ce5ee365c702ed8ee2af44042c4f2a68fe92dc8dba13959c84269fb3f0da8e5a33658f031d4112711d50bbfbd3b0d597016d237245ee8ecd0b99f51ba0df0478db6a70fce2d2a3ca6d6c5414dd8bd40cf8de4ec4a25b7ed1e57f6014ff180f49d48bcb0f92115c359293abeb22742bf30721b13b5da6a597aa4134ba92a183893a4434f06e00289632c5577751e0c2db25c23c401c6eaf86967feba45e8b7497809326b48b310a384f565f4cc271cc717abbbc9c271368433ff5d4faaf8e44bee9972afe3e73dcf5f1368947d0f34546e8778e1b6f048d24c5ea996341487c4f0f59b5e69c91a474e8663d4eb9da3a4547eabffdb95197b376d1c03175ad0a472bf248301ad0655eb0a9978a75a1648015b60f3a773dbcf1ec789a305e09f723352db422e3c3670345a471f1411059bf097068db427d1b2a500ca0d32377b53cb5362e08cb638b35cfa444df1d14251e9c95ee3c7fefb7a2a6b7aa507e72fc4ed148155202a0f106011392e9f9cc21f589f49a9670cb31d5d1df789fca94e63e825322a9fe8359030eb378223dbd91a5222497f756e8e0aa831e57940b79cceb288cdca7c213830baa9d60e8380461df2dd8e7d49c4f42e467ad28c303b166812d6c02de94a23a8b6c7337431c9697546f7dbfc021f110a97255719f53470740a3a1084e4652aabdc8fc0a16dd0299dec3174c804d57f65c221aafafe19cf81a504457a67990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a409c6831de69809cc67534d91d245ffa54dad055e6b5ce3416fc271cb112a0b19b3dde15d609baf3849d7e0a1bad93f41dd5a5e8cb7274ec04b662b65f64c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000109784424952b894d6fea16c99f7ef4733a251ebe4de394924d3bd4e4fa909232dfaf327e6746523a6928e3643058ba165b02fdbe20989f1d870de94c22275e51cfa1435e7ffd71a759721600076511bb2c2ee2c428c45e1916e1a5a8e46649627bfd69664481ee123d99b10d5f02fb16c14cdaa05c59a8be6bdeae32ccc56b40b79602a425542257635b65c2ca4818a6b2dce93939d43b45e2a74dbd5509a7f24b552ee440bf80d7ffdaf6db9bcbd5b5f8c5ee4302716ed221657174ebbaf8e280833a182c58114875679f752cbe6180999dbd7b91856eef458b8499e8291351911d892416332c93f0d87090a30fda57ed08519c58720897e451d9f2cbf62e30b7ee37ed8f6afb3922e84cc3165151803d431ea5efbdeda18f9b49bc98d06b20b5b25ae20dab578f4639f681731a66c72b9ffa1dd30ebec3020ba4471db83ee362edd2913a742db82b6c26dc2d024a3566ac46b33e9250c59d2855f2cbeb2fe20cabea2d172b3f22cb9b0bfc5a6b57212d3cd8cd5fda500b60df409b70b4986344e5f3c2eb7a7f71738defbf8ed354e862c29dbd314b60d0d81b276bc5b9d362bbc0c08b62ba1484ca037867fe5f95d025e78080993286d49c978421bec7a610d06122edcc45cf9f25e0320677edb2c50f4d9c6b3874fb4278eab994d62abba00181fb01c55a0216f4544b1a2cf779778321eaf8550124a440b2aa0eca7fe182d7c53904cdd9a09ecc824858ae1953c9a938b6c2c10ba5d6f52fef011dd998d33dac26975d3b6a0fbe203541c58316c5c905309a130898b84e485e46630b20033e935024a1051596c35e8d514a5103f1fcc4db488536a5cb05ecf38102978ee16a5f0baaab01da464cad37f420641606b0ca97ed74828ca37cc9b7fd86b016b31da50c5f5a1af7f6495f13b5e27074a6c6314226705ce984d744c70158751201ee9cbd63e18858a2a3b552dab17fc480e502761297eb0cfef605bbaf678015a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032a784d94a5139b67b53cc4e6030c9ca23fcd7261c364294a248ce878de105503fc47fe290914e5276b2402a13602f974766dd8d585389f9ce20d7abb0c64efb279624cc7942aa1a7e4d6499b855544ea43093739822a09e475fd57b2486a18708176cc90857fb15e59276acc8caf29090dd29a180c3d400dee4b6a78ca5da6e35767c85f8413397e0e91a00295262fbd8b2b606e423a189eb89a89bf3e1c82825049d7e5e5492788f65c04cab449073e5423968e7e5c086a2799abdca97898225abac7745571d2ac5e4e63260ae1d9673c18d748bec437309b5ba8b36fd199b15da5d9338eeff785770d453e56411db5c66207adff1c29033d15300bf95befe1c76a9893228695b09a79c683dd95c17a9057165ce258a8bcc1487bedcda5ce8281e600fc1f9f45d5d44f097cc3f7b57d8b9fc0d2f600ec882e0136accc5dfba32440404845f9556e4f630453d4261f0eea30d46610d138cceddc847b5a4fbc31f15ea68a381a5a1cee601f92d8416d6316f86cfced4834898e5ee274a1412d90353066d8c4ded252ae3d395ac87cb9da92cb0f4a2b412b05f5395df5b527669238d3d7b949578fa0998e6ee21367602c11292fd57fb504f8b8dbd1afaea6148263b1701162a653bfdda08e03006ef32a01dbf058e998b3cb22fd617d9e6718815665223cfad2a0e7d63de21f77985e14fe922f92c9c3ca2aa657015caea9c5133167d6a45a0cc3f4c61a43f452ac0aa2246f747d346dcced9f4007aff5e3e083467b7be70b0dd60626f816deca7185c8605a1de87be56de290cd190d508ea4010ad80e234c88745c67d38f668ca761df138daec3a4957797044737826017a7d30bad1851e95522c156362ff7182273fadb5600fced16f0e7be23044893633ed35b7e815c85d876a5f6da159da0b9a801e3e070233b59e9bf53c32207b92d81405607c89f04f457cb1c2bce08ead99c4470dfbb2722e1b12e10d63da099d409c25f71e21f425cce0efd966bd3d9da2caf53aaf4334c224dd33de42b891776db6049999e10a7d3e953ead502edc977d77942b68aa4339380b019bda9d57f16d2104e9f26de9b4b1b1346768e43e8be1b4183af8299c561c877addefe3b28b017a1817a4f134895a19e0356126a7303937d87a49ccce90de2c6167dc671fbdf1451e02e00a83771e6d6cfa8ce0d8665aba7e6c535130d926aee375234d7fa026991d43fe9b77a1ce98ed999d03b9221fc9df7c4636fc1dcbd570c897bf827b16271e7e5a36bafa2bea58be7df4f4cf27d88f9b35139782d0df8b4312e3db0eae08325719295f0f58d3d661d7123b50e719a103fd1f1de1493db2e7c8d10a24aab81b21d1bf2df66fcec4e7141ba48818f1f04ff563948512ec4a8ea420d4fbaea91adede438083be4ff50f2ad625e63aab78169d7622b401a11f286531f28bd60d3df2292c29c4f304999572463655cd01e8d71152b96a2fe7ea1bcb574250b3912cf5e652a8d374953b9c45175e285fa15527eaacf373e13c62fe4a4deffc260b1c444ed1252f4620ad4bde93940e690e4fd6bf02e166736537729c4f0d3284921ceed62dd1f828a062283a13ba01b6229feb48a1ff5e6cca234e6f4c0f30d2242b9c5be43abe4d42dc38115524175cd3e3f5202c1cb5bf5552cc9ca9f6fbae271af361475656c4ae4fc7be22a686bee22f22f5ea7afe88d2e166a2a9a5a6242e125c2a32d27e2f7080c5f5bb24b83260fd249d0bba9d39ea2447fd6ce6547b3b27f6723fe7bc36e9b91f6ae5898f42c52d5e7d52baac6996dfe66b4e9998e322259e3a21c9212bc04a61e4840a2888020cfe7c1a76455fcacc88df9193bae3b130b2a341b69a5e56f5088b52e470ba8d100b29ba7cea737a5e2a60b4dd059e1633de0fb7f88a65f7e911032fc86f51dd4de3fd1a32fade14cf004a1bbf2f1bf806ecad08faa0d67d4440ae076fa2e8a4c043a217c31c77dbaf49b4344510e97538ea37b602e10f52b0f3b8b9d0e0878de0ceaa99a807a5c1f1408f2a225aec392f6d11c6112dccaeae135bd9ba7557e5acc96dbe8c7dae15f2448a401f2e82ab163be2eae50265daa20bd1b5665690d429ab3600d2958a9f73b81c4b4f206ae03d88a4f83afdd771331b2091026634d505a637829f40ff40ac914787252468720f9fcd6a06e6537b352690e8b98681140ad8dd6f43892704d8d0e718fe6c23fc293371492f7c26a0c8b71a923b8ab910fa9f1f218c37631f13fa921e5ff1ac1928d51fc122eba954c9a350357c600c1be31d583724b922061c60fb047074aa25172ae03edd1456ab365cafca839ff3e43f2940c4e493d7157ccc35e88f8b55dc0c299ec5ae9a4ea7f030910b6de03c8b08beee1f9bb6becbc25d544f324752b633d6613a5165b1580fcf6ef4921fc3751987aadc6d963a4fd6cfdc9dcdb8ad4b3cd019dc69038947b0f2d53925612eb72bbaa69e0a91b9facbd2a58bfb649d8e032fe62396fc76b84f0d2ac6da9ed148f68bf25dfebb3f20cd5a8b61049b62733010814e0d11ae6674be2a1dbae5e993518add260fa4bd7796687807e8f713c20fef7eb1f2ee51998b41d5e2451a166cd0bbbbd5f9a83ba402c4b8e51708ec3f110a26f1228eaea0efbc42e25fe3ed0627307c30f2ea107544a817bde2c67284315f6a4a9024ed2d1eeb89f7f0830155aae28d7307e40f627536941cbb894bbb313a615b47346f5b5e462e84247308720a765515e5ed5d1f8f6dd3c33f4b33333f7be31dbef1412bf5a8ef4bc02f3f369548eb85bdd837121ad2a34e937adbcd167772365f891a03f1f72ef7936baa0f2443ab89bfe32853bb17594d835871142543e4a0db6b3f9a68a183df0311cf8d3ef6c957a059759967f0a1dfdea29461252529ea0ea45872aa42405354183538795155e62aaabc382038bed969e636870d5b8bdc4e5f5ceba879d198012a4d9dfa05d6218ed1974355dfb4ed2689dcb92afe5607f33ffb620cb8635c301972a690b5afcf14ca01256cf67e06c674d089195c8b62604235754b4e255aa5a7b461d381e1f027a19300bea469c2ad0e0cc320d0e1cf1b677d2b7e6b9eaea9f5aa9228359bf44b2805e66644a7580b9846353d4052738ce3735cbf5da29b94f15420f0828f8153afb106d101bc919bdc420c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002264094b96eeed3d552905d02d80c4c4ac1bf9a44b00a0daccb84b69b10518031a87cfb5bf140b59436a84eed131da51e55e96b13ff6068fb33a212bce3b36e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b56f21893236ce5ff6549186415d4340ca0680ff0744de4eaab4f07a3d20e92ea1a7375a5a97664b82837549cbeb253e7e9e4b6c54e424dd6f5a441b6742e61bf11008b1a161a7e0352767f232d9cc4851a0eea0f0413f80cc30c0e2337b5e28c3cf554751faaf77e34f544d0eb178e44326844fca727803b699fc97f1fe19359e87745c6d8bb324a2144d79b861401a81ef2f5078465baa56e69cb8ad76b9345e57f68760cba41c9f79a809774e679075327b1df6583edf11ac32c45152a528962d3b28bf90312834d645fb33883435881fe63c0fef7de9b3d02157547ce8214c44484f77660488f5edaed9deb7e84ac322ae204a4db25b59cbfd176f133308d0b2a8971964180aaefd998317ded1f90146abedc0537c50ff3cc9b420fed502cb3dc322d25e657947a442e1eb29a2821ede99cd4690f1ee4d8fafa4022abe1185127ae2b53869f0e021c9d68912c942d260425003f771ff3cb6b439b5057035c1247caa2064e95c662ac29a1e13b9c56ed2d05ace9c7858296328f7fb1cb11a6ed4ebdfb64b74ae1246708794113625c1189e807506cca4b63186a5e9392e2c0dd0d23480fd2df12d0015ed0d4f48d13f3f0f7c32c3f0e086dead7a7742e6290b2b410598f97736c224d8ffc5410c34220fa718937f99678b6a121a774510081bda13fd50be4de1c0ec26f41cd62704a20bfdd75dbba9d74c84e8a982b42f16d83a52e4ccfa1aa05b71d0e339a229999c844f8c77fc31c54e7024e782b57a35ae9dd601754bcfb9e05bc2a1c31886c57d4b371a0021290ecb31b8864078410524d28e349b26882d2d0945dccec46e44388eb3c3c9b30672636ee4a42b8fd9004299728ed4c5f0dfe63afe47390ac9a130aaaf0ebf94294216b4e03d0f08a63eed72f7e2ff853fb9c88c604fa741da7a71ef733066758e397953e1a10708a033534c725470b9a609b3b7768d6c59ef3bc48cf42ec9622ad62ecebdb41911e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b1f76359b5f3f45cdaf90a8e772d74a077b8c12c32e5772347d4977a58be87306215d6d478288ef93901a41b1502eb56d969dff372e8095f9348a5117cb7eb50b8a75c91ce47cd7380930f3741c4b09f9574e25acad879f605488b59fb4a03c2399fea2ef94da19fea09a5d4d5741abf25af6f96a31c45417113c78d9fb30871cd4edb7815d0194062fa4f080623e5032e5726180e74c39a11fbe8d624a9edd103344db98f59784f56a184280d0a0d31518974c3f16f80b78bb6821db598a052b57bd13d12210ac7b6bf77fb95525334b8bab094f385d5c8042d2090b405bab1b26a4267ec6d87f3fdff48908611bd66bafc8bb03eda1b0776d3a279f64d4e9008193bc88f6c9096e83e88fb2528d6fa54d6c91df5eefb927e746679e9c2f03159677168927498f35515e56c173f65d3774a7c93e3edb9ba644e1f43eb950271dae991cd81508548263196fa31723d4bd438711617e922441138266a52d186d2679bedab0c1f8f206b5682390711775d51beef8ef25d08448dc7f4d0e14ab351e23919bc6ac91971f91137ed28f25f71a59e1705857fca5d16950d90b63a903382cd39f683e158e12cbbef5eff4bc12e81df8806ebf407d452e2f349a4d1f34100b2810faeb8e1a2b802627e60ad61d0ae163e383fc8b6173df3fded60cfe70227f3c938206447451bbb489fe9fde0995b96263e478afe89d112adf1fcf09b73b0b0bd47469b9b698101fb078deac8198ce28ce2bfd9c6eff3076d30278da6838e80b76d98c5847cf3925b2ec2ae491ea04f85088d7d079f43d1dab16e00b2a2b99bd0e49c8c884249c8d271566b072f5909e6b0226a21ea382c067e140fa2c310b18919af3309bfa20c7ca36251d8cf32f7e0f600b68c727dff5cbb3f1060b0158a983896696ce28d7ef49bca9d4a63eff11b5c0dad0d77d8f24a113ca55d32fe008f0e773e6849bf043c9ccd1ab6e1cd33278b52c3f3fba976d15eacbd5b31efd856485735cf58b6d3b783e34a226b229507c5e48eecc7893ff7f949693643a32e880da5ee0045b63391066ced42f9488d544b3f092c2d2bf404576f63ae73545d3ee375eaa9e5a849e0ebc844359b90e733139194e93e2cd44395249db141abe41639032a5d6a9c1f5096fe20a8ea9a5cffc12c9e77908336e9154f2883c1c961b21f52dbaebd525271321523b19c0cf220020312732455f83a93006072326c343bcc047c6d8de3bccc11038debba5e742a31c54b30096127c5fd0b293a520bf2952e957a6fa058bc5c8b3cc6f14b1ea32dc255645d4cbdc5f2afbdd66cb117b61e46b61d24fbb12aaa4ee2ca0201f94616cd9b3e9ec271798654ebf0a1d360c41fcf95a94bdf055153783a35e6655888c2b5e9a0874c86986e6a3e6351339dc0718329f5c65e989f510540bd51a36987f908e1788d6a4bfdeb9650397ae1d6e855711836a22ba1abd8f0e90529ee359c592d91de74821cf21a3564484dc0c954ec74b80298e5ca7bec67ab9217ebcad467da45b5a7d76add31bfbed99bc1654c67d99e3fd0cc45e6a813c898b95e91a127aeaab2056c80a1b46e6753f0f37ddb169c89a1b4bf2d94de96a18ff0f85f6dd8359033738960298f1b86ac7923245922808f0035d892ed83deeacf3bef283e779fdd17919634a02531621857815dfe0e58267444a149c94ef022b2428b3f7a86707486e3260ce72502ca1e8be2b5353653780ac54cd2a1f7a39e4e979cdaa02a6ff46e6b136bfdad9fcf292c73e538cc6ff9026e36262dd7a7b98d902a10022cd7399699bc5ec7a89d44690290d991f3276fe56de31b0630ae94c1b2b23eea8c5b6db123bf0208e2e859952e4088771202d012cb78d29c284383260c0ac1667371c7eec7f7eb1e6027a58790010e1cd89e9f18b4eb09e3db409cf04efa24d77437be5a40a92bf4ee8954c4e892adc7165374348df15df3d5dfef38250ccf20a9456b95d6e8b4722c88d3315ed23ce4bdc596a838d772995eeaae452b83f9d1df6198254315dc0e598b96411a716981ff510e59c34db4f86205590b02fdeff26b8793dc9217ed922327642b40d19154754ea9ab41ab35144ea0480a4c9423818998316be937838e9502bf782c01ae87e15ba8d543e28bb60bcc14134fade515fee6daabf38753a6cc10a3d3bcb1f1b6637224d2561c999b2331146e61e1d893d3dd5df22e26340a108f25c1016052d36e166f452bb2a94e3adb25dee3d2b470f12fee5085fac6b8095803c01311188ffe541a45efb4f96939240d8dfc9a720d696f835a285969d958f8951d6092e77001abe5ba104b0696c6dbf2720367b25c26511175696028f9b5d76ae29f817acff7a4835dae88df0e1db443c5ef0215d97f6cfbf338057e6bae0ae992e2c28530085b7ca2517720f1e24bbc3a11000e90105398dc59b4146760c5166d1d53660fd63690d468ac5b46948552ddab0848d5ed6056f08661e54757668fde6db099f029c96f2b9753a4b96b7aad2254f9db93a2603ddf0b57ad8bb76970219260fe4f2f10d4260b5dc860e69a9e545720da8763df5f7459032f1879c0cf58243301b0d0ef2bd9f4a2379f196561aba8e149e22be1355b38b663ba950f30a7dbe1188ffe541a45efb4f96939240d8dfc9a720d696f835a285969d958f8951d6092e77001abe5ba104b0696c6dbf2720367b25c26511175696028f9b5d76ae29f817acff7a4835dae88df0e1db443c5ef0215d97f6cfbf338057e6bae0ae992e2c28530085b7ca2517720f1e24bbc3a11000e90105398dc59b4146760c5166d1d53660fd63690d468ac5b46948552ddab0848d5ed6056f08661e54757668fde6db099f029c96f2b9753a4b96b7aad2254f9db93a2603ddf0b57ad8bb76970219260a025301a262adc9ab1697311b7fe425e7d66c6ed6fe4296cd9e175eee63e12d301b0d0ef2bd9f4a2379f196561aba8e149e22be1355b38b663ba950f30a7dbe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012288482aa327ab2975692f478fd023443b3602aa93208ed085524ca96148491f8000000000000000000000000000000000000000000000000000000000000000010bdecfeaf6c8d2502ed0fccadda6eda129af56f600239a6515a606d04bb9351000000000000000000000000000000000000000000000000000000000000000009c2e7a66b323c6ec69315c8afe0de2a466a037aee5a948ef2481e170b62a2cf00000000000000000000000000000000000000000000000000000000000000000db04ff8ec74c89b5b0d8846aa0f826ff716327a6b65f1227676b0db66eb4d8000000000000000000000000000000000000000000000000000000000000000001eadd47612945b790939d24e5586c4799c1edd527665322d5aea69e5d75191c00000000000000000000000000000000000000000000000000000000000000000312d62ac7ea94efbb729642e05701feecc3de6582a543df906d631302c03f801000000000000000000000000000000000000000000000000000000000000000032e09d91b41761620b861323998a0c059658d58bd606572d9c9a3f9cb90d92ca0000000000000000000000000000000000000000000000000000000000000000311db68ad76e8cba6d79128c453c16398d9bb0c98c642c7e6615e0d73fc600b600000000000000000000000000000000000000000000000000000000000000001c25dbc719c7ad04bd2385492e1f42aca45e7e5bad747884e197badb96d605b7000000000000000000000000000000000000000000000000000000000000000036266ac50967809c747c142d217f47330ae6789d695c3a7a5026dc397860a95000000000000000000000000000000000000000000000000000000000000000001a9dcd2a886bef745d16c9cfab3db6b4194062bf33e136c0d3f23fbd16fbaba200000000000000000000000000000000000000000000000000000000000000002998b71c91eb25fe3b5d0488e590d663f4a32dbd9b9859ef81b4e2c81a6cc8ab000000000000000000000000000000000000000000000000000000000000000028438ae61ee7e5b3841ce6cd67ddd1123b7e444f645cda0433987a098cd81e7a00000000000000000000000000000000000000000000000000000000000000002671d3d2200acdf113f6f87e464096706166ce950ee0c3da30fc9acdb07f242e0000000000000000000000000000000000000000000000000000000000000000143449199e7650a99ff0b20367a945df69dd7ea545e86f018aee3ff02aa8724e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae9b930ebea437e3dc91072136ae0372d41dc939487c2081b9e895a24681cdb01721cd2e4dce1319dcb29bb38dbbbebabf7628ae0ec7ad96b19f223335a473e1086e1aa0d53f30ae2720650471865ce495ade023be53007e2e546e9aab6544d08baab7e39ce6c5d41f2a50bfeefd7c6b1333028ac3fa997b6429cf27e34a86d04f7478b414c34f11f24aa68e2113d4e235ae0d5b79abd429ce6948cffd07280096364af394961bab9510220759f2970fc25579861786647cdf84688d9e1b880227440dc81dacc19ff970d0996bb979b17a0929195ef52d27f61affa854a6fd42a7c4acfdef167f9ad77c85bf01b9ef28127cc9a0e73a4ea9f5905615a15bd952edbd0c5254392ac7cbe72c2f97c7a0b2d8c607b328474f9662e5b4441668379075461ded032e021e3c1ccbd452cb50cdc67bff063b50267d61a7d1ec6895db91784728feefb26a49092606cbd2dfdc6bdec1635d3f455a2ba5995343016a6d51aa661c639bd15535b12ef72f91e6ee5e271364e42fcfcebc88fb123c13c2cb30a249dc60f4274ce8e8b0988c35a8c09767426eba5e19fd931d04c60d0e201031937577e7e7d6cb67e0f89fd4e83781646db7331a8f2f28c51ee0679dae3c7b127fe60b3270e2aed509e87423c5db29ffc1b3ef4b2f962e3f4a6fa981efd544c12b03838c020378b51661aef2cca090a85e50eea7ba0c0d174f18f0e47b482bc386dee6ecbcd4eb96f1a49cea5a14b42f035cf00cb414d8d130659e9a3a40efa32cee9650cd143ddd973090cb50a7dd0b1d8224413a34364b0020bfccc1877613e0233122ebfa6b5f209c69ac38a64dc0dd39f996e774694786b37738d7de3b101fdccedd140594a0df639653c759b241472f9629ad5b28720c1f97972821c50360aff5ae9be418dba30e105d1b3f84bbc07ba0f03207c77f563518dc375727109f500a51641be7245cf1efa2e4c07b4663edeed062c7ca3a3c9df5f3c8a8d900e36fcc690b747c4a2f4651d1883d97a230c3e5aea6e89e9663bd410d14b3c3131c903396f48b83b5d0b9ae2e77c2685ff3a5aa11ede6f3232f15cdc2eb4c3d00712efe0d39466d72ec5f9917a933f628cf69eca8adbb87365fdf36716782cf438ed101f2c6b9928d13a066e856cc09d954ffa317e7140a8332f3d85e987d30d0269a99c6b28abb2059e4f158f70f36fa41d8f5a34a39f4d9cf5be0c19bdaefd01fdccedd140594a0df639653c759b241472f9629ad5b28720c1f97972821c503170c9a51dc82de3f6b8ca498717ad02b9e68d209dca211d51530cd7c1a32e0309f500a51641be7245cf1efa2e4c07b4663edeed062c7ca3a3c9df5f3c8a8d900e36fcc690b747c4a2f4651d1883d97a230c3e5aea6e89e9663bd410d14b3c3131c903396f48b83b5d0b9ae2e77c2685ff3a5aa11ede6f3232f15cdc2eb4c3d01c64d8804feacc94c5699d18bc86f1017e0dc2a66aef12ff8c7c898f55bec83c38ed101f2c6b9928d13a066e856cc09d954ffa317e7140a8332f3d85e987d30d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000002d7f679839c2ea2cabf7c1cc216ebbc86808eba09f514417beb3fe2f0e72bb7a00000000000000000000000000000000000000000000000000000000000000000144d3371008171191aaf170f1cc7f4f271802fbcf1bb3a36bd27bd6c0ff32ce000000000000000000000000000000000000000000000000000000000000000021613a15c5bc3945cdbf2db3ce35ead04cc0dd5515c1b0391ef29c3ababa14fb000000000000000000000000000000000000000000000000000000000000000015f9daa6cad0d1777cc543d192e1faea614c59d2cc8f1db1e2170eeed168c8ab000000000000000000000000000000000000000000000000000000000000000011ee6d93c99e5c231ecaec7d6305721aaca18d41e895bbaf60ca553ba5154be300000000000000000000000000000000000000000000000000000000000000000a4a21ad20bc8fb109d9cac4d46f7305a798aeaa169bccddbecf72e683777fde00000000000000000000000000000000000000000000000000000000000000003ab74947e41ffc9e0dc5b1166362f72e1ca14aaa348fc6c41bdf67d5d5c9d60b00000000000000000000000000000000000000000000000000000000000000002f4409733a205543f9376abf966734b17d75d77a2c4a3a47d2fc506541840b0f0000000000000000000000000000000000000000000000000000000000000000245ded450723d2561deeac4639fd934280074cea88f9220ee43d9b3089e5299e0000000000000000000000000000000000000000000000000000000000000000329fca182868beecf5a7110410324ad35251c9a7ded2a25c69f6893a2a6419f40000000000000000000000000000000000000000000000000000000000000000003bf901b319d5770918039c2f4e369df9cc55c4e28dae8ad44e3739e5e973710000000000000000000000000000000000000000000000000000000000000000016ad7f1a25bddeac9fa227a33cbf07724bd4e7b72eeb51fc2cdca35d2566ed200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013ab630e86342b7e6cada5d79378e2c1ec1cb287fc9d75380fd186d9d9f609b60000000000000000000000000000000000000000000000000000000000000001060dc3e985ce273b0e97fa533a46c62784ea0a4c58cf7bf467d10fd021c22b4128b27cdf9e5c8d1c47a04c021a3cab2261cd6bdac20286ab15fa94e3fe65c92f34d4d185901d3abc3822c14368f96109c58de07cbfe9227f7f18ce53df98da6f1b38d9a1e41433e1d56cfae128943d1e27f27ddf30dbb5fe626799bff4a3c44e1330f22f543ea4fd28e859f613bc5828b76d00f90b81a5ec7a47b437f3876cae1dbf0c4374bd286b1f8bb380f013d03b709a9e2a56798c3082db9bb5b4988dd5366fb868f30705cd1c59b44b9884c841a0395a306ec2055a1c41de4ecbb99e8c3d98be9c632f28e953e1669a2dd3242753a78bb8901a032fcdbe8077bcf62f9200549f5b8e9a07c1e4e5c79bd7b092a3bc3b9fca55d7ebc692f2bdd1d162417122fa99d757056af7af83d92bf05db340828e1970ed1b2db89e5cecd9ea8cb0b925308f7becd38d7bb7bf0ba709566607edb2cfeab92c1f47e6172e41226ef1bf12ddd08130c9421a8cf084c384af8b36a1480c8ea605bdeaee896b4feb8de221021bf04acb0881c151ee1c325ddc7b808d27f93f2f4614c4ea10de51f25569b618949d8d885a9883d096ab22ba7b6c19e01ccb18b89d9dc9b1b8bcfa10eae57b1cadeafd1d642d3345ade60e8494f10493dd62e6a5a9beaf7d3387f640bd63bc124fd6b89579a817627a95ef6fbf78cc37255f3b00038ef75d1963beb4623ccb3d4bffe4a1d47dfb5e9eec89b9a3429d03b068b30368456d0c22bd39d7feaadb02b4001b5e2b8204a1611376465cbd631e96304905e4b3ae8d0a73b328015526327bff77292675e8d91a9eb0a0304d108957a78eebd576b2d7f8ee6d37f956430d840088d6d98a1726e5614f5fcfb2ef98eef16d1d778268c134427fc806a9be3c6bfd53cdc04d8c3d85197320f1815247e27ad67f44662b6c55155b17deaf4c039402ac323fb273c27ae68cdf0e7eadda641e258a0892f02cd81b91e82150b52e1bf2a304c183bd33997f3fa4b7869ade52024057221a6ab8f4a71377596c7811e40d5cfb3e7c42cc6680c05b48796543f496bbb22adeb0e03889d988a693891bbf98a12455c0f63ee878f1e4433102090f1eea5a46231a0d09f22eff3674352f0bfdd78cd9dcb0cee709f53a499efe8dfc06d2408b9a37fc34da7839f618ca1c6c33c7cc353ae1618574485333946c4a985fa83f9dca4b245c7602a4c947330c9ff1877c704ef85bd1affec37568b0001b8a02ea81b63beb0a8c28d253da0b14b9511b6d56c3b508d869a4d2fedbe33db623cbe1716a5b2d9ab905ef1fb4140e1ba378b4248920ed1ffee02623b1371a803d2d6c3fd05579c21e135637f33f3a3be3a4d941c6b803ed8cbc842a0dd8f8b71a1308265071c106df492262fdda2d80189e277a8f8e352345bb13929990c7e2db50402ad497ba53349c095ffca01703d916bdffcaeea3b26071d86d8a9de20eae3691ce87008687cc9a7d5135ce320809ad454ce4d39755dfc0df2f302086e9c9d4307551e2e5a7c2c3568731741d11545f0a86decaa3a0ec862dedca641a76e2826139cf07c0e62199b6ece0271a5114608fe618c986f20ea65e002d17557d28c6011bfbe3acda36714a2bfb430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a5060a10b78d8b124241e96a6f6cc71e5d8951badb045a360b13e084a91b15259b1832c276429a80a23e0aaf3537064658dae275273524d2e33bb71fd2201300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010c93981c043746efe31bafcb5536f9e0179ed012ade2aebac3b47b2735cd5ea2a79a88037741234e6d34b2325f66bed8ef33402331acecf3ddcc304f33aa1f924ff9b9400df1ac65243a08b1ed45764eadae4619f254e0f73c0294a8f90b8e90b20ea3bfc6cb7fb00088664b5ffc2b52fce55604e5300e2dbcc4bac5babe4260f811b7e05dd276132e69fcf463a1ca36a89e2f9974ff6686f04cedb5b949a0c28562cd4cbcc9c773c07e5b5f47d27dcb9ac206a82ec3c2cde53b657ab40a71328c44026fa0de0718ad170d16e735eb96c96bb624a62a06df56df4d7d39d32ac168cde776bcf9aaa2f3950acd5b607c78d335e4fc94be60ccc678a661003128b14c3711f7100f5b28acb2bc9aa46e1d49f103cb8b2893b8d635614a9ad682e443ecbdf761661bb5c27896291a3448f81371b69caae457d700dca68f9e22929c61164826bef83c33c3dc5a40f9f52fc6d0e82cf29c7aa13a2412940a11ae4ace20bf78a3b9023b3b7d50784748a350b2b819a52d5cf34e16d92a35770ba1adf3f1274aa0a5befc4c72418e05c429c24ccaf9a64de1856857dbe2253d439220e321e0f9f897eb9e491147488355b48c4af367b2ff340a8f3be3c50ca1b115eee4c383c3ef8ea5ea6a979bfe6e9bb08ca07674dfb7c1fd69c962613f145981ab47907d26686dbc2ea10dbeb1da575cea8a5a982fadb727a5d9ffc7131c892a8865c22749128fcb2e06e9f87a37eca64e03d4313fa467245dac79cd0b32cd156ef2c3b1e839ba2195b06924fb14c4f35d8d92ed3c1d4a5976e88c4b7f97e6c9607873544dc42d3b8389e2eb4722d51d09909780ae3da4635fceb47df5ab01b80e24e00da4b481a4aeee41446eb2db6a09d73e496b5afc7e58947fe6314d14125cb171227a6f1d14d854b1c20395dcd618ea1359ec89896c4b0ffa511f1c11554eed92a5f148fa6ad60889ca431424aba92915861d5f2a98430c2865c8f00c7e2d0640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024688ec0ffcac010f8b454d6aa1fa7675751e9a814477e7101cef539a6bfb1ed277f1e8d563b8a7a7cc333d89a788009a71678d4e5b0236e3517537c7c324b6326865f9af42a181a72615827769d59c98c6a91d4fe8966ee24963bc4e71d9bab0bf23578be36364c87b6b9387cf514f6a27df001d1fd91c155bd3f0ed471463232219e7c65b59aba1e82a1aeb2f88c74ad9ee4812af6728383746fa4e90a82b006c2101e9d65cadb41a87c6fdc5d2260fc8333e4334635a500c84bf0713f838b1b171784b2e20dcb6da4fb56df5a6f483db984e519442a25f6e33291a36f1dd33d946f4b0093b236dc1481320482ee4872af53b296702084e74f2ce95cc2641e2d7b69a12a8262891f2f48158acc5cd531ae82691af1fd649af69c9da474925a39e30e23e91429eeeef6328c8f9febf4a2587378d398f2956b568a75efdb80091fd2bcb0af6877cf9cb618bdc3890cffecb84895850cc936384e5433c9dc6c2a2c793d0fa8358cee10bbd560abb224cf4ee132109824cd3194e1066f5e2136df22a8e45231831c6c94711359b96624c9acb7718f106a1d0d6b3c615662a2eeb71539b341af26c7b11f68e64906ca664c28b554aae1738cb23cb54f1a4d51ced33908630f8259149f68d36caa45d3bd9f9d413cf3e47ab2b81a6acf0eca1442c2148f2634d9b463beacae22f30f912be085337aa49de1ecf4eb018881c549c460172267cdf71868ee39d2c7a2da9c158a0cdca274d4ff4fe89bd3d083fe8d6d000f4961304cae2f40c0ef1a3c022f8c9e58fd45775a04c8fceeef909aeb0129fe018cb05c7f174f7fecf4e14172d48570f1fe1f484a035d1fa5f6d2db9ef98c53379b2c376ffd74d9adf258dab2939280685f9b1cc8c8ae2042c740aedad9120c29cfd34aae9d1db9ca2527a9ed5879b23314c0a806f7192812441211c4cc5e5f028d4b577b23ac91b00c55d9cb7085d7e3bc43cec866c661403dc3f8da74f15701f1e95b7662ef4f5238b7717a840c5335d9e8d3360b993d77bfd9877704001b2d12d7d8f399ce00b56166b646136c26ad4189da86419c2168caea43acc3a5fb27b3b46fa31306407f2a9becb1dfb4dc2f90fd66bf6f281f165fde2ca6a85496390603a39b23590a2e134f0826eeb599ea449068bc379038138d09b74013c2c724936cdf3166d0b670c88c7260d2ad640a2310fdd76e725413166f59d2d928b9113bc220de52f42d1790117a5283578307ccc387dbfef2a076d374c6f93dc0680acc71d4d86b8c4d7233262e2eed28787f0d9dfe18775ae886d79913eb1367c113bf75be8dd0e330de36617e8b10634178cfa8828be10113b0387bb2fc64ece02e74a4c6b93ae31bab0ee025ba7cdab2a27905eba635661cb520ba98bae0968814886e8a7f4a2cc2f20d56bad9cff7d05745c6523a7f36f33afa641797994cce054119308a755fb6257b748e4d3c91b3bb9aec476f943d08fbe5272242ba5f1d010a313f679dcc4b23b36b76fdec09690ccb30adbfa5bc9f4c20d993ccbd52fb28ac74f4628151cf3580989cc06ca94cf1442a2daa81cc2906b4123dd409a8501034caf18c523505c77160f96b2a9e2e89080da6e8e7106d86d36398a5fd79782ec2729a23bc5fd41732ce6815d542a8b896a366a4489af78f6be21f07c9aefe058ec89cbea1410aac4160c277868e3bcbe5592506871e4eb7346333d67efa0d16922aa60f1f8df33a678bf3ff46574fd15ba7ed6e857ef552bda60987caac52387bd302be2d1fd04c5424d9aaf473357a6331a4bcb4697e11955ab2add127dd0406f43c4da6478842e38894d042da8e73c2555ab3200939236bcb3a839cc4f434346fe646525787763166febe91dcecb8bb86f0051e0ec9a8e488eb0a0d1f1d25ef03986bcd5b36ac363b42024f973c6a33250b3eefa41e6e48e23d035cd98e02622e2cf02c8e2b897feb091e4e1f3dc98a71de5886e71a0c7160c413feb72921911bf2d1b57f5718cfe8ed890cd2e4410bc1450a1b250133959bd9b38d9ac22de1495ed6aea20a69bbb830a864a58aab128aad104a9a3c310c5e17c1bf33f2084444b63508b87360d642a8b92f5b996b395f9deeb81ac19a3b601742a3787e233b2eb966bd75ada93a5f8274810bab9f11b0d74b2c0b727c34b64979117da90fde284d35d57d176f3a22aacd50209e9b9560d478cec796609a3a326377b1fd014e7584e685516a76af5b9f901d71456c232f8b1d803a17908a4497b15e20cb074f35f9e661054a5b7f94db239e159a1ea6e2475545ae0758427e471a49222238b0ca06199efab5a4806b24dc61ea66039fb6b4b4074b1440eab2a5e5b6dddf248c0de17fe51a73c97de847b2166c0299426b64aa5c6624b94c7763836daaaa1b73f21e801ae58c368217b84de993fd89042d975ef092f6dfe0b9897c92555736bc45677f798442ef7589667a701c0cb9bee6ff41340c806c23f317912455500943ba9880867bbd108a7699858fe3f36887b1fcc818ec9b2d093dd56edbaab111ad5b057d5f954ead4baf0064308c3f17a01f0c20d05a13b7fefbc1d5b5aa8c2e52a4fa82a06ab152b450ff9bcf73c10aa679efe87c9f07e12e352b2a4a5575074f35f9e661054a5b7f94db239e159a1ea6e2475545ae0758427e471a49222238b0ca06199efab5a4806b24dc61ea66039fb6b4b4074b1440eab2a5e5b6dddf248c0de17fe51a73c97de847b2166c0299426b64aa5c6624b94c7763836daaaa1b73f21e801ae58c368217b84de993fd89042d975ef092f6dfe0b9897c92555736bc45677f798442ef7589667a701c0cb9bee6ff41340c806c23f317912455500943ba9880867bbd108a7699858fe3f36887b1fcc818ec9b2d093dd56edbaab111ad5b057d5f954ead4baf0064308c3f17a01f0c20d05a13b7fefbc1d5b5aa8c2e52a4fa82a06ab152b450ff9bcf73c10aa679efe87c9f07e12e352b2a4a55750000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b07212cb2f33bf9da8a147cfce467b7a240a786ea42e983bdcf00fc4b05d4ac000000000000000000000000000000000000000000000000000000000000000000afe11940397937ac75c4ed1a9975eade1c8ffba75044257671436781234eea000000000000000000000000000000000000000000000000000000000000000029493fb3f74114ffe4567b6e3a9ef9d1c83de31ad1d4dabcf511945642c903c000000000000000000000000000000000000000000000000000000000000000000c9e12312348909d70552f722d29e2e1856a3d1cdf079b70f21218285d0c6e98000000000000000000000000000000000000000000000000000000000000000015e67ec4d2f8ec3d7c06151513f6dd515a1434718005c5ca1080251207fc8c5600000000000000000000000000000000000000000000000000000000000000003dc7a805c82445094c66b45d610f35a674f80432c60f8e366c0dd2c60aa5306e00000000000000000000000000000000000000000000000000000000000000001ea7951c14c9a3103261e694b95941384109138698f040b95232e1666eee2d7900000000000000000000000000000000000000000000000000000000000000002399b6da6493d8fb6eda9be1a63bb77f33e40718ec650e501ff5e6a208dd0a2d00000000000000000000000000000000000000000000000000000000000000000f17422562011f55531d0b327fc98a4e3a23eb175d8b709c778b4d2bd062532b0000000000000000000000000000000000000000000000000000000000000000031f1f590c486f42cf0e2c10115c1ca9d144265474ad22f9cf2035120aebec1d000000000000000000000000000000000000000000000000000000000000000038b9ea4b7cae68597ff894883ca9d7cf048f15164f80ca7e4997d177feae4f25000000000000000000000000000000000000000000000000000000000000000005bb2ec576a13b9094e876fa50eaaf6cf5ad70fbe4441d2fbcc514ddd806ece2000000000000000000000000000000000000000000000000000000000000000024ecf772573e301dc03df5471c380d7d1de5ec6a5b17547fc1c6f584393b31be000000000000000000000000000000000000000000000000000000000000000024e675673b6540a30f037c6702e059702f3d7ed94e6118f5c776f48a12e4b4a300000000000000000000000000000000000000000000000000000000000000000b45a98587d30f23d87963521dadad7cb6530ddca2746a2f672594538b6554b90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018d6ae9dada2520c6d6f3a14d32f78132a514898e5cb836caccc6fae439f637436d60c4f7e16c0ece03190530d080b8996988c36aab864b63df92d3b7df8c0d421faa1fe99b17a27adb26e323bb57e0d29cc34e6ad49bdaab9f848f46a59efac26f0ef421adf2b0e20807b85e6add8cec62facafa04cc61338fedcc38b8c71e82d73fc0ecef8cebee42d069d4ab4796a9abdabb79b28a875af2dfa4c78ad950c1198ce5fe951f88dea003211d26611938f08ac8d5a46fb6c9afb69d8db0922573f5f699212b217460e10b607ee274d4e1505acdcb3d3274947733e37c25ed6e13d945580fffa449ae33e43bf9df478f7622aeb081286db36d9fff67513595d69295456fb5815766c1e24fc2ddab8ab02030ccca764d19b3855955fa7df9bc0753ac1d69e309a91aa4055aa0dcc7ec06663ce97b6445c8d178b3c67ef4f9c2f753778ee1a52bf8cbd24d1881f5a039183b1f066646a35d27edb3f8c54a9e397b4163d8c661c5afe6bd917ae773d12e30e318bcc1a8f628cdf3787361273d6a0ad0fb956f4526089da368e055d90947f1f0993087bb4a8836b486194bdd0308114012104051e110c1c3fa9acc9e64bf251705bcf8a938fe6e5641179fb8f2de3951a6f66187aa7746e628255b3e4b89e62fd300aa8722cd5e01a101921a0f5d0251af3130aa81433925cfe3499546008a7e8d5eba6f019764ab5fbb5f3a03faed23ba304ba00708138df702cd1fc99d2cebe0ac1304afa73b769b985aadd19c25c1304aa6dc458627abf856e1081c2dad0769545aa345bb5fd9396b2e748b3435c0f0f4c332979e9a4380901208265b2d4e33713e14cc0bb6a4f4868244023378730f0b3ccd686165bc7f6fedf7d9a4d2b3f0f851abc8c3db149e4c8c8bfdcc87a0b4c7cffcf619035182d05a28bfc7e284dccca6a7676aff7f33cd7c840b015a234b38300309e6fcae7d2fa5d740381d7d479ce9192d64923a5f05924bf4fea5f387e70ff0ce7d10978e11c2cbbee76c984fff41450516fd7c03036e943706c2a07818f00f3182ef6871ee3d3441189369d46a4e7b8fb8943d8fcfa03bc8f93d71a7834fb4087152f5c658cdfaba851ef0fe560756c634ac85c3c4eda51321cce2587cb04bf78ead0a39a73205457ae11126138869ce9ae533cf0e212aecde333140a8c5dff6cb97e3b75f240fbb68a8140ad0c55a85a2bc2347346d9055e466f35e44390936e586b43da4a8c42280f73f0d3e621df8ea4869cc0dc7bd364766128cbed1242897b7dcfaeb86da0278d3345c76b5835dadf8a9f8571b7622d6f700de132d2366bdbf56968afa387a6e1f0699e8fdc692efff0ed1ff2692a29bb5c3a79306e67bd72c64d35f450b100b16de023e22b0955f3e333da8be1ad2a23a133c41c0e7fd1b35bda7ad957d62a8d5e3ba2fac86b140e3716ac66e22980af5025f4d936d6cf10ab1fae5472e5de5b36bdbe0cedbbc7cac57825ff9da422313917dfee91b2151af3c238929123ecd43ea247d985e410c939c49287506c3507be2668a0934a70cb9c9411d0957078f7d811f31f713733b1e30e4f108e18e19b57139a11137172ef9e715214d0f95395dfd9199e8115b2baca2a830f69f3dd11213fb25084fcb8859072fb3bfd9527e81884c64a520682df7cc89e340bb43fddf9224f20f8a47b9832a916144afa65b417c24baefd640dcbfbf7a4265b266486280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016ca9c3a2d4f2256cc6aef7037b9baf558b4bd2f782d1697778ef7d275aef4d12fb73a0046d6e6861f1e347e1200cb14c3a4ade73bb422f118a90ab3c24e74c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e073a15f3d248795d89482322926bdbdff1f9e7131962e5cfc7d74101f1c93223b5b8aabcf48435ff1d360832d34b49e51bec962dfb736a32692757e52901132f42266639b636292cd26b8a763b26bc53457a957803b44687b04f46783c88e231bfbe9f43bf54e959dc119e9cfc41d2edf7f88a5cae3574c8a026d304356a3c2ab9d43118690265b92fe0d75c516bd7b1f778ab5f083423eedd9fbba2effd9036ef515f48b273e3240990cfb5d53461df7ea538a7d6062321d53e2cb42a5f360a260a887b00af93a737d73cd9cc9f776375e7e9564ba8df040db5fd1bbcf74234b9a43d293c6b394af0f243431b05fc7f7161e5ebaebf58b670299355d6de69056024c97ec78d3d5f3524750cd722855cbb51dbfeef002927eefcb33b9583162febab37a2e6fd05d9609b6ce063fd531fb56f06383bd11958ee65d9fa0131283c1cfd81826779f30885b5308a6309b93d70f220a1af4988327e654be58438493bc11808e780ff78e220578217a866dc5418b27a8f403e1dcbbb717861f250c807deaa3b49d2464940204258a35f2ae89f20bfa606e452c8cb788ad6c9b2bc6b026a784d8e7b537a97650210be9c4335bb1b3e0c91feb7077be3a466f24da02f1280a8cd05b5a0a20c25bb2e278574abbf0b08bab8e78bc3586f5a80021eedd60c59a918c1288159e5f72d23c13f5a1dcfa34d09c4e05ccfc66bceec665f97d713b365c384f42f104d121b27d273384b208d150903946869a5b4752c4c883f5913915d9e7215280c18adccac1ef2b4fc9245b31a1132581bd622fab7187f7d713b9d77c9437e10cd06f7a5fb623e754d3d0b8d42721bf9c23432fb35ed69de1134517379856363e8bfde546a2c2def01a69ac1cfd8907b81dd50f49e772e46b50acf6c198b713eb97c0a380559ae259941d2f8c7c353d1f613d77b35632fdaac03b8ce90a3574008bbf99ce25d78a51570516ced9bae28068abf415f9646337f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008e7ad72f4a7cc69e06ef751f6469120f484e0f14993e19a5978eda649761451e14c51531ffd53387b25556e98a3ed0bc75551002a693e081a897acbc5f22f212d2876f241306e42c259a46cc1753e4f7dcb6f58172a5fb8cd17d046e3fb0f4281dfcbfdc02ba75427e9d6e2be64798b35a7dac5e4a2e350e2393881f2d737f325adc2482d143e970f3819ec54d1043e92d57af6ebdd94131fafc170015fd083d703a2e45bd29038f447505aa660afc2c0ac0b475eaa62b5f20541c01ccce890fdd543c57e5c1593b40592e4f6f5e1f9261acb12e897adf370d7d00c2ecb1a123da93957dc6391e988e5d24f979d20e4f912d14af1530daf07acf0149f9164d383fafacb81b14e8292dc71692aae55dff85d0fa20abd4bd8677f785c04e1e000546dbdec9c99f2182296e52ff9b7817180421c47bf3b723a0d6f397955c5fb624928a80f1ca23fff4c0927d2bdc5281e57cb19541b197efa33d31838d1bdd9428525b28d7c97f3352166a22524127d1d6f6cfe5264ec434fbd80c90f6a660051cb4c763f45de1b5f31a9c7d897355cfc2de175d98e15b144a252ea185ec15801958723a725a2331aebbdff090ed303d9113f2dc1b775ef456d4bbb6777710ee35745042d4abd92dd99a9a70d08ce765fed70877404a2c2a6a7229c4daaab132366f65b23bd4fc6f72773b7e8133651fe2bdce352f12c842a01b2224ba92b4d43c5e941af496a037c5164f14bc2a47d5da6bb98e71048a3bc1dcb098d966b8b20d07e8d2e90b06fc9b3e33a0826d63725aafa0013a97b1446af0c5beceffc103001c211d40e2243d2445551ffb96e25008953cde0fe126797bf21f1facbed862347895ad042c37a9b69d17aa3ec8bef0c6a75ba5f3bfe0dda893da7dff147bde1d36b61c7ca12b2fc631164f10429f22b53f39a15e8ae62facb90b7ec27f958b0cfe7a0c40d12a40f077d6801e008c1a973fe1f7793661e35d406dc9575c86510d22374797e25bc388748dfb8699a0ca1cf2090055d42d36d62ff382c9f4aeb01923b5c153e7c4338f8b043f827ea91b1a4a718d61d381618bcd447d6c15237f0b590304e5cf006e814662e3ecdc23c89b983b43f6e47b2f078de1acf49c5d9219557a03874a224defaf4b504fa320e6687b5a36d95bf0250fd67cba8775e1153d70b0ba287060df73da4a7cead436d21bdab4556b24dad64bb70f0d9232f29221ac4141cd5bf2467243007ee94c06f735aa488efe434cecc70465b598f308c41df438ae5e61be44d4636913c5f9933e2fe3c7f0f8f4fe14154e022eb855b7052cee98e16d6290a9924760edea81a2e9834ec987e3751a513a6f57d9025cba97154e09fcf2ece1c184dd8060170f1a4ba5f0874c0b6d939c39149101456f7d391ba4c95f0a1ff1551525b871ff69a986113de82606d373ee3e9ac8345bd9bb75276518396844d81475ed48d54d8b090e34dea8808902c7a4380dc92890e071c127311f18230f1a5a8c56559fedeff0e6847ee3689457265caf49e1dd4a814ea509c198f7a1becbabaf53042981de45896af527bf07aa74ed75c99d32f5f5a52f280d5e268b92a9d8deadfe308fca961c46fa721d624fbc45a1928d175146b4ae2b7feb1258b71adfd23b03972ec078196f9585096052981fbb2d5778c13afa8a0a4e83a8a9d8f295331fffd6c30f4d0766c92acf99dd8bb93192623c4d8065643db7925d360c6bdc8b22c9fe127c54913f660dea841d5333c50ac5db4f156f1514fb40879853d4936506857780a33ff423ac890ed8da980630df4fa4b0f936e02c9ec533b096748ebc2c7dfde8b8f2e7ced1e6deb88c9c47a513797396376ccd031241cb5a7a6ebf956cb4bdf13e4fbe161eb5cb98142937fbdadf5d9681d1ce0433aebdcf4a100229647262efd8ea4dd881eba9d517dff179ac4796f8178fe43002385c573fecb2ce987157d534c07cc2ca0fdf754f318463dbce6741f738ee18a0f684b96b8ac1a8ab3ef9a33c72dbf53a6d519fba504a2dd98d47c5eea34e003fb5543b3179d945a61167bea4ef6249949d8dc0328348a7178ad88cfd00ce3e2d3c9bd79b22e46d033ab6fc0bc6f8ac06ddc27fe2abf6ddbe39ffe93b4dee37b1bac61842b36e5da6a734273013a3f9c408ccfb5c77e99255a068916a56323c6812fef79438dc512a343936d1c8a7d223ea6825d482c59868a6081e8c941126a1dbee6ffa8175bd786e54a21f80cf3dfba4d420eff1975c69a7c90428c87217c1035f388536f40c60ff24e76090d4e021e66fb8730a3c3b0ae2fcc1964149283efca0c77ac90bf39f00db189f6f2b4224b28c50d9eedf5e224df03e69beb836c510dc1a9a12c43de4fbb884e2d4283e62e73290f23a118e093e02c7ef466c093aef23e565ed3bc21b04477b1d2bd7e3e3b1c9785abf0a0b23f2ea3810b99511d9544c85025dd53578ea9a986e24c8aed4200caf873de961797259e7ac60182e26abb37afda22aca8715656791db37737278ef59c5bb3237b3be9318539fe9193ea57e990bd52a0b5c9504fa26b7eb47de074364573c734e320ad4865de07726c15a8166f42ad5f4a36afb05d94814da6891b8a4f5bca84afb261879a21f8a307fce6190cb596cd335a0b32bcf892773b7eebfcb1a471f7d5e0eb3a6d434a23bfeebf1d93d8ffa11b7076f6bb2d706fb180148b731a61204e2401840e292eb22c315a8e6d2b9a8459f31d004fd0d2eec29454333575396f7c9b1d7e1e598f20a594669aa68b416b9191078d69bdb3f1af525a740c0c547e0e43f176cfcfa4b1f249bbab7ac6cbf7a8bfcb44059b15081fad09e762da57b65dec87785f4e7f93bc833ece2649c4c5dedffd3211e273d35c7a10795b9b84ce1b631d04957cc9408dc7f09a948c86bb3d02756174a650d97c792dc38461d97822bbfe5346d343206cd8c2d47304ecac4d26d4248948fb11ddd5f1dc0ef44a1ec4a7820d1e4373d2915b597658e4a40fed3c4df310af76741e88fe51fd1a0edbcefcc64568f3be92a318bf4e38c2413b372efca3e59421aa3d7defbf697735e39f64d5ed6c9a56404d644b1b78e3eeb6d5f8346f1f9b9ee95f153f4578e8c4bbbf59d15aaf0bafc02567fec128e93a5162947a7894fbc979cdbe4f8d0dfdae831ce67abacf049c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d382d7453cabf572140c407ec2aba8cea7867d4333c54937669f5c6150d15351baa2dc31fd351bb2c9fddcd3bf0402cf0c2b12521a3793cda9ce70b1147c2fa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014cdfe52a6a3be0803860b1e625eddf75edcd374d633e8adfd6b189034cffc4f15554207d94f4733873b35969cb39f0496c4b6fdbf5ef47ce86a1ffaa3ad3412396909f9138ef2d27dc0a7ce631482a60470c98b32fc7c325ff883e474e6fa2b3310bfcd9b63619f2574074bcb4f0c45a0f1c85028fd9d5038e65027edf1d5600cdd980e9582959f424f8f1587f71c198105c2a92847105c19a71a78c30c14a3314a6713f33bfd09e82140d0a5e3e70771878bd3a5a26c8b87224f40d1c3ea982364afa0a4affb090a0a21c2c5aee3e7acc61f16fbde43d0afd8c41154a307a20e9bb66ad0d7d9bf5f41a6c1e08cd5536ac9f36a026e1cdacbf78fdb4ce670683e998603e31d43ba20642bf1af4ee4b564785e303298ed3b41a2a19618b4439636821ae5d7ff5d7dab9a4689ca3197f9af1d87344f83466de01fdf305d20de3b2b17d63634eb2559e002840e39e51a6cb27116af1b7608fd4e2e6aa595779f3a211169dcd1c28e52e0e91de5d4863b6d803931b9ea739dfae2080facfec1945d02b7fb3668370210c207fc48665144b224ecaabca63cdf7c53ea3e5ebce5df1019dccef008f11b0ff21695b44a598110d15666e8dd87a7b179fefd5d62eebc6b0c71c6ad0cecf2b17eda1d160479a35da8548ea93bfa2bc19b3dac81f293bc08296e4dd10559c15de3fdcc5a31c3fc018a2eb32c258354e3007dcee2080225051f1049cba47b9a7596c452b889d0651e222038a29be813eb928bc95e0ecc156f0bb19c297453c5e79a2238ae66475071b91af4022b63708d0c082519f41cd5c3206d81a3d56b3b4c91800ba8f685a96ce7bb512e5abd3bb1ba6ba64d756ef6b901ea3c28266bea927cced80b2c2fc400126959a3ba4b76abf85a0dbf221a4e552475ce86928664f93071dced3916568e2b5742a190a0a425d330282afe7cf45b06cdfc502a7abd31dc16a49a0ba2f89704829f5744ea086eaa593b837e3774a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d35c900d6562b8ac75e741880587b22384de2c789e0432b2f0f48fa30a30bd83490a01d00bbb65a8e05ad95e2b0bd61c10aa9eb3573b2d57c55bfcec44e728a3b64b9746af87be83004e3ee50952e2a1346725d2cfc72d57d22755ed4c73eee17a694ffb8b54f5aa28c9473083015ef9c38dbaaad4a7cde5a47d594057b67df2624141892e613c02bc268fee979c75e122ee7a510be82ba1e7619d419c25f25292c2bce0262ddb8cbeeecd5c91dab8bfe229e2e388bd7c686d7728abe0e2b362944780779f1807ded0e59ecfa8f5eee0d33d94ca36026961b24297847910ef91fe1772ecdc47951b5ed24c2385990db2e60e304383bfdb621c74e00471d4f801f425c47c539de7c33d61ae26dda9aeab48986dbd7cc8ac4b3ebc1e3c1b9ad8426101153118da1311a4dc1c996da4b177e784ca22ff5df46eab8b4b8bcbda4ab1eac3c692f8ad8ca73516846e78f518c91c44d87eecb5ba7f13332fb7fd51cd62b79538f89813e9430796044e7c7a8e56d2d71e40b2bfb1feb13bd728b35c75913887381e167d1604be162a749c4e50dd3b411aceb2f2720a52e2560b0e476f106be1cf9ff4326104aa95979057f83249a05efd075dbc8045a9de9b800e4479b13328b7c5c14bcc008a322a39b1ed21582746052a74a8053913c823ef24fe85a2dde56331f1afca2a3b339f93daefbc313c73abf5ab85bac4228eb467aab5d970794086f1e962eac55aba55797a6ce74b9b7fa8ec2781b5c665e8a73c033dfb80acddd023c9a4a9628384580fde5a3863242bf2a1d501ea11aa3aa1b3642bf3730077c937663772be77584b55b74f23b53f2a373bb084296a47428da10e2b6ae0c1f1b95cfe66f1c35ef3f148cbd4a835c73a58c34f4ee33c51df4487dde94ef38813b30920f19a4116e1944c05eb4165d5bbce1e6667575788c587b148d51a3172297242284ab6a30006ac05840953fda62e4dba43727ab88da9e7dc64af6f113d2252f30ce2b338cd388b29b5ee54d15bd5fc9285b97cd5a17874e140d8b7b122ca671e510d701eff4feaf3d396b4b52070dcd234dfbdfa6cb9a3c71df6a9d06cf99b2a5adafd4aee9c15563f0485d80c8f8258edff7d25372d0babf3f297b31f8b81a0b279543ad54019955aa6a3bc40462492d94d9d4fa7a52b16a578bdb0fc6a427a56800f6618c82c2337634b80e24da14ee00e524b092ad48c5b077452b68c7f2d13cb3cbfe7ec2f57cb9a861019fac4807b56d9a1e6e9d4ee6d9e13a1455f0866c52d81a50b6dd48583983879319a4f515aca5098eaaa11e7e349e8838d3e1488d3c03fe111bec4df86870d3242cf6227a12cc80a5b5b97b636910c03d840419cb937b6ac22b41b5885f1a3655e667bb7afc60834e0751556091aee51ef94a53bc558d55be7a0f117ba9302996f41a4db65a9dd3339bed49353a5d602b3fac9062e9c2c823811848b20ef1532dac7e7985ce3c1e0b87069725dbd1f83da40dfb7c6a5342438d389aa813d6fc0f73bf05ce1ff7102fb9fef499bb6f2b09b55d536eedf91496a707efa25f5fbfe8867ce85e1e73d3a637cd824a67e7822fc4e118a32359c6a5c5cd72fce517215839af3f095ff459b7f15107b84214332e734e56fef53637fc431b106d407c03ba2a6375d291434e6f022c0d91b815582913520b6f49e05662ec8e69913a00827459d186e58f996f5085a558acfb95210dfe884f3391506e7ccd9101d0ec00fa3b8b61b10946c659bbff9d788d35002f3f17e9a91f9b7226aa25a1c66f1a839ecb9c77c2f4eb9b9f4a9135dd599580dc116e0f26e74ae13a809d21f2c72cabce6782edcf6e2d58d30cdb7e3d143dad7a304fd6109721c7c19c2592028db7297d2c2dea6baae648d2dd830c72556fe29a23880a492793270edd9bc9c58fafe76da1192d39acd471c9cccbbe691e62b32c341ef267bc8eba40a2861b718d9665ec2cfaee932978c8eeb98ff706f3d3b13a2073656b16418135a6f9e27a106e3cf17089d3c79351c2e8a6759de14d60a9cb0425b3536666f6ba478d47bec9148862268e504ddba5e788e49e7c90b7f3a5c5057803ad11b3840dea1d44052e0c2a79545161615aea79ecfddcc40f8a8fb4ff1d513262218bdb7512570669d8147d1736c08a9317d1a66d639c35866948e399399d102a391e6291d564a248a0e9c2ea84e9d7b15335b3ee1458794ea36a7cc82dd774043de845bd93536b3a58c6131f74a19483b772fc4a5cc2a3c9c7d243e11322d0c1e87678279d89ef07ebbbb41fa2baf2a23c1a270b7f39a742b6f4fad02cdd2f3e178987d8627610f814444be07f8ba659cd32d21019f389aa490b05311fae13c98a5058c613b1ab279aaa849e0b60242f2335ca1de2f3136092c8e60f2051ec3675afa739ec4e54d865557b6216e674cce6172efdb63a1d8c6d3719f21e6662efb391bbde627857c605549715f45382f39d73005e3c64ff08ddec7e4921999d104c6e44219d87a839faab68ea2df316086bd9f8bd5cc831e4221381b817ffeeae81d8ab57ec59b6de1aa6f36d81145cca00a50f9ffb9e9952559e776b280011517e2754a813a64921e5590c92a1323c3208a7e97b9d8e979aaa61889600d8b33879c86bcdc004a954b2f8f9b0236d6c30a479bb0d7a1a8cbe6ca6626c0ce3a4b5df78225a836bdf148e9606958e745d45095c5ef7cea4f31c20cddc523c8b57e8f348641530fb50391434bb1c663a8e0a2017ffb91c88ce4f349b3ee013492568c44519fa232dde05bb3f5ca196465cd7b892dd7ac71d1e8e144660f73f0fe50c9e8f94ee9bfd424116646d352ec70067b1b7417803569edebde2eece31039abd7bd3336e4270e87f2b6f4bde26381b3406b01947102fe4d37cca6a2739eb0b2647b77189e8b60a719f86ca5fa2c867883d2e994cbcfacd051cc6c0dd17616356affbcfcbe9a402d2471782861ee8b32fc8f981c9b5d91c896bfe110f0985a5d00e74fe68e635501b9962dcd5ffcade9e30b4f7a7174cb6410be39d851104d4ca63c7f470e0b6ac2bc6497de620b6d40a23af651326a96a46207d2dba086b9e5a7dd32bdb0ce1601cf90d3907f1565d37ebe7e3cb10ebe4154ca55ad005b1ba7453f29a88e04f05c282b75af4ff31afa17d149b2554c822ae87761e31000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f3ad2e23efb6a32e97ce4c22bbc1397b50403fe83c853681c44b4357d3ef8120e00af77744b1680f64b8b276b9014e3530688456e244edc6511cda991786eb70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcba14fd8430e9746bc7b60f0799518af90ab4987282cae756a17dd8de8481b35825559bed589701c2cc61c4d112f6ef628703b4ca5165f07c640c7f4863b782f2246b6fcb133e679adcf413c81530ce466d71dc4817cc95521cf45fc80929913f9213b5e03d1fefea67a47c2dfd0e1baa65f49bc313be43dc79e09bf84482133b4fec712ac6e61e022d6335bb54fb55fd5e198628c6f1bdcb27c8b35cb58f01255e81b3167ea87328c4700ac4563bb226b6aed24b37684e1cfa4bd6aaa4b2f108751d70a7e431bf8b6553c27973726b9aa69acf1f816f45abbdd598c6c11f93e0eb9f847f7f49dbf250728c48e5564b0684406cbe47ecdd8fd70338fe8612f2999ac5db28f114d5cea1931f0961bddc86546416fd2c058e583d2bc1824034f0b5a4682f019d78cf808b14a703448435926bc7292a96d90e757d6a6fa0589b00f6e9cbe980a43b9ffc4055c6a536de9e4aa1db02145bfac4ce917856be588c21311fc5157688215123e62786990cbaa4ab2fe6e010fb759fac0e7382dba1f033c3d32ed4469e3f07bd747d6435e84a92f75c065f1f015905cc47f57acca9cfc0ff63f2658d4991ece4915103d32843f9b15a863f653fee2de4ba288ea3d15613a1c4faad8dfefa626ae0b4673cb724872263f82a6c7f8d79e3e28af0e6e193e14bf01ec468889779320818b51ef8b0beb0ccb87333c6baf97719e26cec07a0c202ebc7f081b8210ae9ceae9c23623da7a21fb91792325814e5f6ed5faf08bf71adfefb55c79c114e779e112c42188e9ac71053c345f2469d74785f6a42636220fa9aade74cf0d44cf67896d5a286ecc3e892b7b42f461058238881805c7df5b284d12ec6046e4c38446338060b4d22d338a3930c005bb433d7cf73c5478a1b63c8a0d328b252603cd6ceb094cbe9899c7de825a6df32da99da9b254ee8807c13987aa09140ba27052ec83afae30fc91f2521627a2db7fe73ab91520b12e491500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000244b378adb43737df444cbc9de4da2aeb451c0a7100efe1391f37ff0472b864934520aea1b06de6c308bd4681073f02e1b3e6f8a853585ceec88518856d9e8ec20d487e52a53474de0a1f30d7d0f024f9b92fa88df388e5d51940539bcffc01e01279ddb66d41953e4364755f8d96995329851e94b409fb9af82b7dfd3ff65200b83aa10720a8d34f4f0e086d379286dfdecb2b52785bf4efb465c93597cd40339efa4edc2790588241a7a0ba597a9002ee5ea836a96a8a3c1094955ab84dd091f66172dd5563a3b1ac361ef78af10cb30b409b75ba72599b1eae31f0426fbaf3ce4b877ae0fcb71783c55cea4061f142c8c43afea8c0613f1e8b3e6ad4c3be3099170e08dbd9d631847c3c9b6bc81d35e7226148203b88fe9907c71fa433dcb24116b9ee1785e2e0a44613bbe307f8ce135b10945797bcbc5b542ad2f42711c1817a98083461e6e51c9ac409740d19219f4169b850eeb40565beeed73132e741b08129c3e78ffc90908b67fce15ec16bf5bdadf626156c41d8c1f50336300d41a2306d9b59f04c164984556aa1e746dfadb26ecd5afb882a75986338bde80650bfeadf97bede42f52a6847590f1be5c0b4a184baba5e8b9c155bccc765c645b2da85ff93296d15e9464494836d1a38b15cb95836f7255d436dbb67926ef92cb1b154747e33291cc10a0e2d2ba14ae3cca0d24be762480c94cdce3dbcf89c3bd31d9e1c7944fb42a2fb8a23df5bfbbe98a353a0a36b05ea15e6bcab4eec39fdd076aad438af573a60a4e2bce76c841f9bd95e9c534e3196b4cec9a8c0f332a9a02fd4000af236fc97940b60686e98d384dbd59cb6140493d966081472831ef9c3932cfc0d090a8c46453362ad11ccec934aa784be2bda5e0ee31138f1e45832123c493b0e6264bc4328ed135f15a5170c1e80ac5019d18b2aa164625f5d312e016c759d35647431a2c76d8467361dddfd373fb1cbb7ce70650b4342e2be3be7d12b4f6af3a542ac2df3d8e773e25fe9dda1a818ca784c903e6a96a41800eb8cd169aa3d393a71a49ec1a9bd6b1ef24e5b633b1c4ebf84bfc97a4ca9caac7abef244e528ab4408deb55264834fd355f33e3e1d4b075cd7118cd4d77fddbae657138de046174fbffc396b4054be4cd09646fc9f81445ca8c9735937ef5ca191d5d3067a5cb8e7a072a21070dba78eb37d7e81231fdba29cc9127bc663d68cdbbf03670155d1cf28c30e1e40b255ea6be3fcc5dde59568d1da5b1512e5b999dcc283f6a51b484126da569b4f9c4586d0ed086fdcefb5cdb0298b12979e264e108f707a982d38e0c023ac20dc8daf687c4737b5f4f32b91caafc1f9b0ae4fa7c9dc80ca732f3df7cdabe73594ff0b51798465eb20cd76938ff89ad094bd8e36b882d37b741d7210c685ae0530832cecc165d558e5f8cae635bb3b8b3335c2561758a3daf5aa739dfa2d4428519eb29bcd67d6d7b878b5b39336ae1764c1bfde441b10bb6fe7f3b8425e426586f013409f5c8586c434a2ab22ee9de72c3eb86e34f7b178494ad8f166722cf5ecaef70601430ed767cd33d60ebe8481508777522cb3a00324bc01e92c8553918a83c9f9cef39c134fef401a48523190b97ff6348ffb73e23b7924209278345594bdefee92e56ffc21f46ed720f6257ee8e0c8a5625e102b5f1d2a2a27236c3b93ac241612cc923bb6df95689bc1173de81acc72cd8230f12b72fe7249a236226b2b243959add58d0d6264bdeb02eca7196e821f86c211fe7ffe288602d528fc6dd4855bd9e201cfcb91ca2480ee6aaa3211d19caaba2194f8ebf29c98ce8693c886a3ce5eafab9a68afda288e67274739ef16028254117e8b55842c9bd49e1c49aaf206dc21629bad3c5a1c79dc72d732279aa87d19a2e6d4e2b83faef40b9ca2d792d740a34247bb57ecd7759d72a596f79cd2cd3bd0500cb68547324c30dba5491bdf034786bd517936f1348f5da86d8814e1cfab432ffe2d1035041dfcda1e5249d00d11895189d673c3f92c2d6d887b9f23c194132034a6834fea55b2ee09db1328a42e530245ee63e80bd0c3952a9e7915dc2781a9cd94f180da75832c11274382262ebf26c90a098bacf13e14d91d70921c30c312b85fb97e4c1581a510bf6c4a2acb33f4f8129c2e2942dc11fd9657960912a0570af6a83d09a3908d0674df7ac272a7b03f25741f3ca9051001df9bddc49db39f7274c7f3b8914df13d920bf35464da67b53cf873374323bfa13f533024e1539269de2ec6b5bef2dc323878f3cbe21ed9f87326bcfbfd1e76d6f099613260906d9621d1394a410d23cdc7870c341de34a711c99d7d3949b1bfc1e369ecd9f81dc1156e9e18cbabe4cfb1a5cc2fb6a91b03400bf5dadaab206e677bee5fbe29223eea9161e734541b304e5a33d04957074358f013721e7078bec97111a041d814c56b29167bfa5b780e783cfcee914d42830e43baac53206fcda391a7deb6cb2b3a94d6e98405a487f187c303116eb2dfc38ab84ea0a5fb295f8d5b5821493627db17cd706be3c958485930f0a8d6822a48ae569c10a68695d700eb475991f61824e8328f941c36a7b7a6cf0f57297df7fdeaa56d3c529503563001b8a66e0b1551dbe6f7ea743605e03f1dbd842c0827f8acf0a65be2d0aaa84dd900e4d2ca028e8a85808a9a855a493f77a35f44110d4a1605e9a6bacf5ddac2ef87697f7b285c71689395799246c5fd2529825eb5d19e702ddf46065d8101dff48447074711eb6091feea2e52f2f0834f3b8fc4634d9e35be6df51d07abbe6de086b0d95c179dbe13e703efd123578d3d0569236f263054c78dad63d9d528874b238925fd205236e70eaaa3b3c24c76d1126e3fa00ff135cbe070520b2b01bcd9c0bf82b22e3ece1d68ea2eb2a15dfc1bcf37b9c7c258115f3177e9822faf4187e95098f13f8cdb0483a669b72b446f73062968d89d383a7b989388639ecaf1dfa185db4311c1f1ba36751f3a15eb8f13821d282e9a7731f754ee113e1ef83ca61246a90207cf1154bc3af5ed708a653a6417eb263547f4c0012e5b6dba9c8f722ecc615804c3aac7d07ec92d1face025b8766c2586fb15371837b98c8b2cfedc6ee8bcd903b70dedff1d1bb99d68cc67ba230b220333776fb3f5f12b227513065ed4f1b80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a684c577c7e07f54416299e309520331b59bd3b2726ef71cd1b30b10c4f0b73030c0c35e9e0864281cd74ce176dee94dc7f508c3e5ac8456cc7de056cf47c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035534776fba4c4bb0a46831c040e0f936f705067e849a981aa29d812709cadb0195398319e13a99836429a93a530ddc27759daa556ad5955106c29a0e95de4f70d00dfbd5a589698964ef96e18386ea94e5f87a5b3269c8668f3d2b672cc12212382e57d44d5301e81d5541bfa9c68a73eab708459f176e506947f3ffa3d135c0b5117b6a249ec1144872f1a3b2231f3cb7549219250652cb19625dcb1f0001608b3eb4bf4b863858e2525346e87e61259ea59eb6e34b6fa9cc7c69a0785f18408083125d8fb472a37620d931c3f7e13ca6385bcbffb3e784a6a81b3af0f99a3224954ac10cc3454e06f481150a2ae2bbb181ea0b9c020fef327e6c8e21a948005e7fc5dc6d5d7e8d9122ee36a68eebbbc550659b45ce3dabfe23e0af7ba33aa2148e126c6004a1ff69ba514af7563b946849a481da72059bb9cb20d0b585d241d785891c5a4d57a28b3e3d19e49cbbb2937168e42c977a423a627009ba49d312766c5efe1817621fdf9906785dae1ef7105318e151fca2caa951faf40f64bcb12401f1c74087e8af836061335bf0df4cb5a3c195737d4e73a206066e0a5f4462e3b43d37dd0028a438dbebc9d265911b0b516d8fcb8c0552ee0dbaf70fa80a424adc0a3d1bcd1f42887cc15866663b20118861ba09152fa5c2a000ae373cb9b0d89f564dd1c0dff9223ef6ace187165a6dc56a1f2bfd86d293102433cf6cb4a2a0eba2cf4682275b9dec5d162531e57c08f2d0904333fc048f36ac12156bebf184be708ddf721bdb9f281937519665121683cf12b63add43c3f6bf4fcda7ed725a086a6938dfd27beb314efe7c4d7a08e54c3d9f6e4e434966dc3c5ac86601f31546acdf9fc208a1afd8c2e2cf50127f840f832a8ea11fc79d7519db928805510710960aec02bdd23329595704d74870095bf8c3056aa6d97c6d043aec5d7810837c8ff0ee1590423cc20a376513cc32be30c9e03d0222ba410c994b6c54ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011ddb8c6a20b6e4d18b255795b847bb3e9a39f6fe166be2e874c08570e14551523cea4c6f4ce955e7efcd03ac4d92420e4ea94a9f3812bdcea56b1188776b306159a1158588ddc6d8e9668622d838bc95e642ea95abee5cacceedc7d7debb5140b8af58a0d176df886c4311cc81185ae01062c52d2e675f608e565f3360b49d82ddda1e86bcd8fb4444f0758cb247cff6f116cbafebcbe68cc7ddce4ce010acf32110a599c1f35cb993924abd3a19b76259ecbfcb8827bf1267aa79cca46b8961ce1cd39ef6574cae58e31033048ba92a1c2721595104de5562a9314a2a1033f2e9e9eb0ee3cee089253e7c0c51339928fe0be4e052c4529b7889b1bf1e1984b3192c81e5f598eeae9adc4d4d157a2469e4ac218e266312fc29d32ed5164e8321a8d21faaf865f391d8bc5baf1adcb4c483fa89831907d7619c71d9026e966d90d42ccf02a24b6e00e272b82f45ce6b074410834961f21895a26577f52367709399a16d95cc554104a8a11dabffbdbdd6d17af3144f7f8a430037a6fb8f6fe752bb1da1d74c5e57cd0e2a5d8db62da6ed4fa07b298d2afd8491358639768de071096d1264be4f9d115120792c706d74aeb478fdcb75d985857e0ec0b519bb8633e741d10ae6a85375de958c9e6a15a4431ac23dea547218a98fe832c972f30b02b0a05713516d67080e59919ccc625fb9effdd5648cd7fc3f899c990b2ad77ea023e18b362c4f246a5881b9fd4af250726812663c0947ed31bdafae546522a2a01839c1848f825fc5b8ae3b1de856734860064173c90db26d9d6356e43284a3d0145c55f95484600df62bb1337b80aea4140fd862303991b380786fb530c6b9f2bbe795cd9b9bab93092acb3f16ba9bc2ffcb0b2e80f550ffbb72f084fd8ab56342a154b8ad63519e3a04f7700e7875b9fa816a0d376ad1e593b148879a9d6d22b057189053f46c2778bd8f2f6f6c085fd034caa1c5ff7a4eb858a7de9c2c9ac036382679e66cfe926ece9d3ddbdc8168f77065b4615e9bceb3f5533715523340f22cdfb0c92ef6321304e3d67a88f7de3573a6e6f559d7bda1eb92c3a64e13b1055a2b7a44906a85cf3ba6e71a021fc939689ac44ea7a900481dd634e7658eb0d6ca1227dbd242d9c1afbe0b85e0a0e62d9044b89f48189130484b5ba1da4d53d33ac02656aa1103957b5947454f07fd99d728caddbe2cfffc32bb05300403401f6c0bdabb1450e66d5f762cf8b1c2d332bf4a308410e25fc0d4bc8d29b3df33fc058707f6ed2f52ad476ade921aaa8d1f2a31adca51dbba2a2435694759ee53e4014ff797f612f853029f3491573af32024ad1cbd35e231f2bdf94a2711219368ba3b149b5b940cf2a28cbec923c8b50f51b189f2adfb4e84c1190d6b81b8e13e5d8607a0c2552b9245441117c86c95c1001523eb294c1f7166161912535c71f582269d465cf36a032c97f7bd155adf7774ad67557d3ea3705f8ee1f010c8716c3bbe128bdd634857116f6c516446be965058ef7d4b9bcce40b7a2ed7f089621bdea58aa90988389918866ecccbc0cf6db79594c72dec487ddac79ab1a49e22f82114b2ab727bca6314665913d054e20f94af307d9e032d3110231cdbdb97e1ae6e20bb658322d2e0d0bde7340fc3f5cd6f07cf13d919bc07b1399e246390d082f1d3140d52fbf332526375c1510a2a6e224c53b32c2a1e2eb75b984ce5e4a1412ddd7a3979a1b61cb8dc6287919ea8938b0d3ce3bb39c3c64492f4182407d163e8cbf97a655bf8ab1e303c7bbea1b017050b6f11caabff3b4ee0f6113e8773a35baefb44bab5696caaf0fd14a224406aaca4a8b78c3db8befe4f1ea81ecd02b53034e1adceb8accdcbdb5d9a81a93530f09b8ca0210e9594c8abfb04995cc14b74b4f9a30b8e878d218b1fd535a239408a66ee94f34d341a8b5d5841841a604bfe75afb962ce8659545679cd0c8e95872016650f26f14996c3581817f870e3ae4465599d7f425ed9624dc0e29ed1f89f3c6db92e3fedb6bc0d0d4a288f0a7221f455b5bf89da325e9fd86865f87ef1bff75de8fd23d1f06889a24cbad95513a89f381c0383b4d2ff66ec85004490b1a4ae2de446ce3610ea04341c4a7afd4107341f874c0def04c40d1e4b9112eb67833b140976efa5bc612c2a7c75b05371e10065424103cdae7366fb371b03d17c048f1f8689457c5e21c0e40b57f0b353c1dc159004b885979cc4f0a92ca4f20a2c5327615715597728ff9a53f9ca32a0980288487ccc9fdd6d22d4f9aad07b6ce404e457888e527b161f7263e445334367fd77b78333602292dd2b06552f84954064ab690c413f3e7cb39c6c1bbaccd2f80ca96a6fff1f5321ae28e056126920741875b5aac79c676e9d3bf3755a004107f356959000e0acde51d71fa9ed96e1b0511a0aea07f5522435d2dc8aa5ffd2d83f4f142ffb9c9fa866cc61ae5c0d9bd73d9d4a977758d87098ff514ac2011127c0b0ebd00463605799339e51a3f2664d2bf275fd5838e1223a0f7eb53dff02393c8b64efea0f1e4a01fde867cc4404c6f7633336e6070d7a83d02675ca0521c6c3749b1015f0e1b5fe02179833bbfd5d722c8d5de98aac184f3ea98a35faf1059329f28a27c9de699b918d2edb89c4126194096fde310786fa15ed7ca71a11954559ca0a524b69f2932d854980586ef8195d80fe983ba733fc015644ea2a739a63a0c4634ff010b790ac13feeb740d36e75c9d1172f6e2a35f95def23ceb035e245d5bece5ab6b3a49e4e01f2dd91917cbaa3c1db8df6ddb2860f609293f60f4f8d70ca109b1c66f782b2c05d087b18d53c8a404ca888251f082bafaafb9d3843d5f381dad71ddf8e5dfd01dbd7f5dda77d214fdfbaab35b4d0720223286439a2f2f5bb2e4b881832a7db961681f76b0597306c92317d8a45031aaad12ed0301b06331d6b1648c46812357612efc69de2d8425e3596339236901e9a0e8642301943c9e89721af7ecd15e89f210f530726b81535d2944a98c96ab2a7c43ba63ece6edf0bb8aef4b9e050d5db81443bddc496d63eb2503f3a27ade37f28fbfe1c954b4838b452c420d7bc776c3e031a74a7f22903b4a09d26735d6bc13ddbca24e45d41b226936039b17cf02d6fcc117aae7974561121b2fb7dcb7ef9c4698f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f6e843320128c86e6178351cbfea2ae34ad7b239c4b11f8a4419797ae831c361701c53eaaa0af8bcb36f1dcaa537fe424cd9f0f1b59704d25452be7af0a3524000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049eb2cc9f0c134f2743d29a1b9d7430bce46a1ff668158da807104a61180c2e02c36323b9768cdad7b658b2a8f7867efdf2a086132c0f31fbae6bb3146f11710e073a15f3d248795d89482322926bdbdff1f9e7131962e5cfc7d74101f1c93223b5b8aabcf48435ff1d360832d34b49e51bec962dfb736a32692757e52901132f42266639b636292cd26b8a763b26bc53457a957803b44687b04f46783c88e231bfbe9f43bf54e959dc119e9cfc41d2edf7f88a5cae3574c8a026d304356a3c2ab9d43118690265b92fe0d75c516bd7b1f778ab5f083423eedd9fbba2effd9036ef515f48b273e3240990cfb5d53461df7ea538a7d6062321d53e2cb42a5f360a260a887b00af93a737d73cd9cc9f776375e7e9564ba8df040db5fd1bbcf74234b9a43d293c6b394af0f243431b05fc7f7161e5ebaebf58b670299355d6de69056024c97ec78d3d5f3524750cd722855cbb51dbfeef002927eefcb33b9583162febab37a2e6fd05d9609b6ce063fd531fb56f06383bd11958ee65d9fa0131283c1cfd81826779f30885b5308a6309b93d70f220a1af4988327e654be58438493bc11808e780ff78e220578217a866dc5418b27a8f403e1dcbbb717861f250c807deaa3b49d2464940204258a35f2ae89f20bfa606e452c8cb788ad6c9b2bc6b026a784d8e7b537a97650210be9c4335bb1b3e0c91feb7077be3a466f24da02f1280a8cd05b5a0a20c25bb2e278574abbf0b08bab8e78bc3586f5a80021eedd60c59a918c1288159e5f72d23c13f5a1dcfa34d09c4e05ccfc66bceec665f97d713b365c384f42f104d121b27d273384b208d150903946869a5b4752c4c883f5913915d9e7215280c18adccac1ef2b4fc9245b31a1132581bd622fab7187f7d713b9d77c9437e10cd06f7a5fb623e754d3d0b8d42721bf9c23432fb35ed69de1134517379856363e8bfde546a2c2def01a69ac1cfd8907b81dd50f49e772e46b5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003eff896afe69b87d5ff3c88816468b7fe047a3508d5401bb06439604e0f22df738aa39937556e076e8dcc720b3b998755696aa9e2b3afefe5f36c1ba2397e01d161996fad40b0c172ac498a7587490c6528a6dc8726ea967ae0cd015f47b07c723bb549938b213e315f564eaf8b17211acf1f9df302bdd677a3e2540a6c90d7023b065ce158f229c41be71aabb8eaa12123c95c8373e12d231e0865e7ba0e4be032bb57e020dc698371c95992ad30bc0a34a2af243092c23ed50d0e758eceee63044e7ed49296c3cdbec1f076e5a1032a00ac74eac498c6b72b25f7d7bc6b09f2959bd1148c17d0e451defc2ab44c1f242d772c77bfe4a015aaab032586e557d27878ea01a1bfb7d550a53fcf81e8ddbd0530bba35dc2d7bb2582a4f45711fbe136416b3e2a08e110355857737c971cc4d34e822d8cfe028ef0e8f6dcacf54d505eb59150aab03e837424130abf63a0ffdff712dd37089b7a8a4fb9d695b199a2161a4cb74145477ee9dcc45180c05ff2cc72107e0a4031b284f4e6ba3a41aea2b68acccbe34cee76d761971f203bcd75c8ce6f4496ac672a9b7f8842edf2da20bd8e52f339c27c5cae20a0dfc72267c806a6ca923559b4741e292000de9c76809d6220af353c64978b6574c19be4c7995e40078ff5285469f0ad03b4ff5880b01e22d573069b999db5e8eaebb90ee6019b331c5d4b0a2f5afb66ade7322afb12ae30c92359ee9dc91b9d022ffd108fda968f43aabba02d2bb1e36c6d510d38430afe55b38a3a139c7018a1bb95d65a1568416b8458c89f6cc42351db86a57e408ac4ccbd83bc29b81775110e3557265b41b14313227cfea1d1413396b32c1bd353875df9020007d1e38c687e094492027cf5b27f8587e1f82c903df7d8a1e5c1bec8bba4284901e86376cc1e97638cf5546bb3a3d08420744429ffeec5d841a3da4bb4bed3b4d809d38cb67659b9f429cca83e20c7302764ac9b44dbbb05b7b1334a307963c2fa972fb3f28871572deea9265aae7ca7a76f04afada3e43a3af21593b7ae07fa36f5f2210f1c8bf9d1bc5bb28506624188514a42334ceda60a8391246319bd9c40a45b34f4648269f15828d80991a1999d3dacb944b47455561376662485d0cffc7cdf23053635b6a7955d5a206805232e4e627dd33e3a9776510fbd4d6051ad3a94027367bba0958c858deb1ee7cbbbe7e02bccea630d0b7f83cba206deca55fe5df8d1486321e627fd243979b091be8b2104303def1ffadf831df529048e0ac28cbdac418f0e15f2e40bc0397e36ea3d7b6b375153f4d4a102c716cb72704499752a8dd7e6815e4a136dce040065c1a2e4caacba4b17035f420fe5f011e68f0beb73a9e79190514848c56cc5e51bdf991cd57a1c159058b690aa762bd8345cafb4885b3413b7fd84e4457f7c2682b53fb123ad80f5261df59333ad199812c444064078d3492d732db6e6ccb7158ee08e03108f4c134ccb91607f6d2fceb0d5c052e60dacfdece71f731b99a57c244bb3483388b61634ea6d02ca0e702bd51252e232e968d405773489815338a471ccde70174afe8e0982b572c63332e81c63b9a73e4ac00642ecf8bb0ea216d20f6990e43f64edd3ea68fad19f4506278016cbd8a993b579feb96bfaff7ec3e21d6e453174b647238c64d4e0ee0866a23de1c1abf6a791608b18adfb1233c839b34fdf14a7feaacc1b4cfaa198a982d9896879f2c6acc7d804ac8a861ab440494e1cf77c18630c10f45fb4f365b0a1805f7c2116fe09267cd5d6dbfefeb5054354a5e24dc4eac28399d8ce1374d67f4df024f7d8815af544a54a64400efce45d7d533df0d6a062d9421ed2b31a79767517ca79ab0e481ded8b4f4f4bdecb51cdfd0a47c8bb94203bf4c24ea3002dae3e6b17b78367c94e17b2c12d9a6199fb4c6bf553e21935a104ed53aa415a3dd4a2467ea22d85b8a247aa5df976977380119c93a65fee2e2498bccd92e00043cd46ddab359288638f828fd735d21767d21c37e83f95c03f2a2c7d9eca82e859a371f5d922ddd6721b02b1bc2d7cb20a86c97cca0f715e40ddc88492b29351abbc379f5f14e2ed384021fa4d359481a30c7da8307688c7e0f59f17a977113c8697c7d3b543bc6412bd57601de69318248bbb5d215901bc201aabba3f9283e42ce81d9ee0dda79f671c7cf448fec9d8221350f66bc0da503fb1a9c933cdd1543446129e668a42370f5a5cdb6ff17c8daf93db9a320a7809f9652643d07571a6142eabad9ac1231abb2237fe06e217b0cce090905d80c1b5ce78973f1c619259ebd15452653edce544ddc801f91dea739caf30047210f7dd049638c0e39e803e64e95a6405c5af85a7ab17f6226a722b2d4351a834605567623d543b8de7b3c19b16a59bfa3a507a5854e809dd958ff93c4c6eec9b31642b70d17bc472186137f88ec3f41cdc6d9c465777ceac143ad7e250984905e1ab04eb32a529c58672c807713c0be3239263b9a8883153ebc74c873f284bc9b00e8de7dc2ad63a79a217dac9d3c4904e240d5fb557095c652413020338d84dd69d85c4ee69d0dba021e825362c3b6fb1dbf2a04aa8f6a39ade11678c87bc81bb1c0d0e20662f245ff1a6142eabad9ac1231abb2237fe06e217b0cce090905d80c1b5ce78973f1c619259ebd15452653edce544ddc801f91dea739caf30047210f7dd049638c0e39e803e64e95a6405c5af85a7ab17f6226a722b2d4351a834605567623d543b8de7b3c19b16a59bfa3a507a5854e809dd958ff93c4c6eec9b31642b70d17bc472186137f88ec3f41cdc6d9c465777ceac143ad7e250984905e1ab04eb32a529c58672c807713c0be3239263b9a8883153ebc74c873f284bc9b00e8de7dc2ad63a79a217dac9d3c4904e240d5fb557095c652413020338d84dd69d85c4ee69d0dba021e825362c3b6fb1dbf2a04aa8f6a39ade11678c87bc81bb1c0d0e20662f245ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018ab1b7f472a7ade211cfeb7c19fad481ce275ec3014b7e26af3fef3869bcb8f00000000000000000000000000000000000000000000000000000000000000002b1ed3635c1d91a656327d471c094da91110f4c4432e0a4c46d32b6ccae99b82000000000000000000000000000000000000000000000000000000000000000006c6e585be4fb4522ad4f94cee3018bba938df9d7106150e48aeee701e18b44500000000000000000000000000000000000000000000000000000000000000001e2f167919a589143241bb3000afc4d4886c137a4760e29f1e0a5406c178405700000000000000000000000000000000000000000000000000000000000000000bc4ee5974fa2929a56d147262fcf556f75d198af1181bd38aec82f511ba418100000000000000000000000000000000000000000000000000000000000000002de55121f869133ef9c49c22b5999211edf37ab82ffb6cff7433a198205548e3000000000000000000000000000000000000000000000000000000000000000023964b36e9dee1cb8c8c973d352cf3b6caf48c193e69f6f201ca536fbc49ce8800000000000000000000000000000000000000000000000000000000000000002d6c463b379f9ccedaf156e9b529e1bdbf1f5e2e65c0d42ad65fd29224658bf300000000000000000000000000000000000000000000000000000000000000003188aad9dcc03d2b62c88c37fa4333968df4e2112c04d15ab4404036ba0a66b10000000000000000000000000000000000000000000000000000000000000000240b4d7a20f44818425b23bda8311dc6dac5ecd0aefecd0e5bdd77746e7b271d00000000000000000000000000000000000000000000000000000000000000000a2ff0fe4c5f7d54f983d6b20e0b22bcecac649b91f93262401279dc9cca7fd200000000000000000000000000000000000000000000000000000000000000001a2cb8b89c25ee500e60ab363bf7d9e5246c2370faf9eb8660d4a20ecfcd6d7b00000000000000000000000000000000000000000000000000000000000000000bd178854c4b53e7552f7e5f0aa4dee84ed22980d880c090a888927e895fb0cb00000000000000000000000000000000000000000000000000000000000000003f0529de06296fa081c32a46a20ff093208853e66e2e33e8ae80fd0a032bbf1a000000000000000000000000000000000000000000000000000000000000000024bc4a1c485e410e658171f3025abdbab445bcf190ccd892781f61724933156a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001069ae212e7ec8ee4f07d2b0905cb45a701457e421e22879d129dbc6997ac47c422ee060625f68f5554015ba9e61500880d59bc5e531a2f2fe64633b3629575f622705cef8e2bc9d36536855ef9591abda68e60c453a130c8514c06b699c18c4926b8b92470a57a7befcc17e76d688ba1c15fbaf0f57234dd4ffd913b1cfdca15088f660b22c05e1ef2fb9df1657094da2b0bbc7c36f613d8a4436edea688794e1f6b5b87c1a02c2c452e385b6071c4afebb2675a9b03dc7b19e98a7c9e0daf7124dbe4e88094342ef41c8a72c7e63433870054c6f7285e24fbfdc9eecc1c6b600a5c5ea8437bed8b8fc03a7dcad4f75e0a99ce51af0227ba9311fc6235c630933756bb810fb17ac5094a18640771e86616fc354ba606b85243c65c575e99e6e21511e0a58b80e71b1e7cbb47664c16f056015edbf0bf95eac4275c09e0fe1f783411b67de98928e422c06bf4fb0a04dc51b0bf4df3137d824022302553a9a0251e748958a53e91f0f78c4d5e92aaa98e57de29d11793328ba94ed51bec4bf3aa346ed13cfbc5117e95501bc30e3da2fdf90049815692b13487e5c1a929e692370e00448cf01736c70b09a50925f02a70c9fa0fdb91f7067ff38d13ff094fe6970b095b1223fd63cf5a6d1c842f64251b3fe6eb13ed5995ecb97e4ff81c8296bd0b0ad81a43712ca3c6316be66254ccd929f4856fef0ab419874501d4d5fee63b3bbfbe7ba7e5cce05717b4ddacf4075fd84a18bdf3a9f91a9e8ed61ede5cd77804404184581a331fa8e84b22530bf8a049fc803e15a30000fa9e5ace21a328892abeb86a477d0061b376885460c424deb05817c59d1df916b4156ae657d0355415414795b882ff9e4c8977ab9f3bdb2171ee81366c2f0004e517c606a82fcaad15b99a13657101e88150a9a5e3d4b8590ae4abe7f5aef21eb8e383b8b7110aa12a4665ec9a8efe177eaf565a1c2b47a71761ed14139e06fce049ad3448eef5602ca00260fb35098a8693503d732799bd1430c28bc31dc17e034461ae93553524135ffd9f04caf675796cafc28cd866430e15d670462f379d95e8cf3e6caacadd2434a8474a3ea1435d4b863e71bfcd6a2745c09ee9e733ec9af4f39b30cfdf3d2b6ad2dd4eb6ed4c5991de74f934d374606131af8a298267fd8bb3afa5b219022acc9a3edfc0f7d1ca4f6a71d97608342dbc3232393c511befdbd88b7842cd9f3124038af098be51b13e8afcdd5770a8ce5b22957263eaf24a26dbf06800507b16047fcddc06fe939dcf7b35ac5704157673c7b7ed2e85bf2c6c0acbf068142a09d7c2e2cf09d94b0c2984e8858c0c024d1bf5aa9cc89680cbc622d88c9e3b6f0c21d9d52cd6a22b936a711f9121ed9437bda751aab6a8298434b8b7393d3e7a3dc143f389d5692f5d94e8e94ce919c26f1bb23aa7cd174ba50bdc39eb35174906f1de0d0c3555c643bf8b4cc2e32debf0efeb02b8d3f96b2bbd337c1571cbf314bfe1a849ba817202230d712bc05f355ec95306091c1651f76885d27b01eb9930c01502575b2c2f194a7a90f1c619933ac4c289e1d10a4206f5307c1a6fbc8238ae2252c6d07df2efce817716d3b0f61eafb4ed2d5090f616042142dc77b6a10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037b48ad461cfbfce1ad738ce238a8363711ba9132689469012bb6f1f6dc69b3e3f01006ed5d40d5e6e854c8ad73e7efb91f1f97a8be2abb2486fa2e625b025d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5117b6a249ec1144872f1a3b2231f3cb7549219250652cb19625dcb1f0001608b3eb4bf4b863858e2525346e87e61259ea59eb6e34b6fa9cc7c69a0785f18408083125d8fb472a37620d931c3f7e13ca6385bcbffb3e784a6a81b3af0f99a3224954ac10cc3454e06f481150a2ae2bbb181ea0b9c020fef327e6c8e21a948005e7fc5dc6d5d7e8d9122ee36a68eebbbc550659b45ce3dabfe23e0af7ba33aa2148e126c6004a1ff69ba514af7563b946849a481da72059bb9cb20d0b585d241d785891c5a4d57a28b3e3d19e49cbbb2937168e42c977a423a627009ba49d312766c5efe1817621fdf9906785dae1ef7105318e151fca2caa951faf40f64bcb12401f1c74087e8af836061335bf0df4cb5a3c195737d4e73a206066e0a5f4462e3b43d37dd0028a438dbebc9d265911b0b516d8fcb8c0552ee0dbaf70fa80a424adc0a3d1bcd1f42887cc15866663b20118861ba09152fa5c2a000ae373cb9b0d89f564dd1c0dff9223ef6ace187165a6dc56a1f2bfd86d293102433cf6cb4a2a0eba2cf4682275b9dec5d162531e57c08f2d0904333fc048f36ac12156bebf184be708ddf721bdb9f281937519665121683cf12b63add43c3f6bf4fcda7ed725a086a6938dfd27beb314efe7c4d7a08e54c3d9f6e4e434966dc3c5ac86601f31546acdf9fc208a1afd8c2e2cf50127f840f832a8ea11fc79d7519db928805510710960aec02bdd23329595704d74870095bf8c3056aa6d97c6d043aec5d7810837c8ff0ee1590423cc20a376513cc32be30c9e03d0222ba410c994b6c54ec025849747f4bc5c96bd48c411aadd225ec821a50b5ee74ab514c6429f266a937e0889e59686ccb68b7e0c2d0be4cad2d01a4eb74334d50c1b0a1a49d32c7eec1d38743784809d29079e25fb3b67333be8651564e3cd3c591defa243082d49873c3460c46060650b8dfa1f991643aea98fd39726e1226767e7d49f814a092148dc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ec7024e13770133fdf141fce0fe6a199360f0f10f97feb9f395788ed8dff1e4398ce0b431f25d6e7730550750b03664ef914af2c6244cea9537e8ab3e28a99d285b89b12e8f972a934e69a36a305e5e67c368e7e34c37063b40ddae9d63401d3f245ead82e664530bec1dd2f7b2f9daaec254bab1a46c944f0cd1c7374d8547128c25e27da0ae22d9f58c4c10737ceb0d534035b84f1b9d6e402d055f1f809d3cde3fb372e4738bdbc7594c50ce6bfc39fef982daee3dbdb8bf529e27ca7e703ee79d13eee4df4e8501e81b6632bcab783479a6dadbc50b8a3255b2ad06be0c0934fd7396c8b33fba76f7ee1eeddfe0f72e80e4d278fbb00db62223f7967fcf0e7474ac7baa35583ed822d5e1cc1baeb50a09b05a26e1acc3d334ae4b1459ec20eed803cf2553632d030d6b3f87746f5a17b44fdd6ea9bb55cd9d795404d9f5000d7975689f310fdc061786b581d7b562d4559d495294e59f2c9cb7e635a8cb3155949cc9520727d6f45a8becf28816f8b48f2b5fcd8b2d3838978346b454623b6867a295b12dcfeeae6507f06790a81ef6fafc139649c1a147a8012fc136923f2269195efb98d65095d885d24583d919a581a87dba0b7d9052b9891bcd2281243eb92dda44d9f87c41d7a929462ab77e63de4d03edeffa5859d95246343fd236e1bb296f1bd1446eafb8373987b0957eecba7468efe420282c9d73f13a81f636a4f08693198e5cbef60e0752ca6de87324439a70aad3d972f026fad4f69e0016afa1689af5fcb9eef94367b5a6574670ae03f5f3c75f19e876b2bb81aad5bc1996371c35853ccb21cc1a52142109d2fc5c8133ee75bafc81c6006dff1c38b324c11c202f01c63d8bd6f62c34cb94e0eec81842bfe304e1380a954a82f4ff9d047956c14113887856b206a3eef37819668a6ac63e76581971989aec33b477ac207566b832b891ededc46791eda00c1b03790382a3d4358ce79bb93fe370d84f2817aaf9ed6767700e7246b6092183c27ee356c4da1ac334e1d0b78bb19f33cd25b6716cfa2df93fdf4b642fb461e39e0e3d7d8adba1fb32b4b5fe8d195f1ad025b36e29f83b11e0c022cce6e9e71fdee8a714f9e14bb915a8fc78ab0958a0173d585f8d9a621ab69cbfde5a8b318d4f4008c54aa1b62868ff2dbcafff0116103392dd901608bec1f94146f06ac32369bfcc5b18580ead8ef5848643a6f929c4292b1c1410a815296c62fe3127fae9fc6118abeb4b4537e7c7f9c2ec55c3f22925e3ec8957ca4cac408b8d5d8516becb4a6d747320fdc80aaf29f48a2862e17e2a70f221428b26f45b2aa268d7c051ff44e2475176acc4141dce84785ace78e01ba2cd966632bebe6cc3ef59f0bd39fe6a45160e88b58249fe7a747f6d27067d1d414d7ab07d0e1685de63301211fb57fba842130d60916b59c560def0508c37244bb7a0740889d7876ae17d6052d4610c58ca5035f8d12363cea7286508ec94118c901ca24ad0c5f1eab2bdc026567b7ea46767f51878f7d31370524c9a646b16290e6ed2d0d72cf6cc802c3d4ee60d8717ab26a165cc5c64c4fdd124cadcd52b2bfa278a11070d191e2168d925f502dba07239c787fb257ddc3f49f686f27d1301a9e2c6dcdbde56b95279a7844cd2f0d608a6b3b32d679d8eae120f1fad9700a96a59a9b4588d874230c9acc53fd74af857382b0af358a1068f1c5bd1645f0e812ef269d841113909cb7b858a25131db365358dcd892d1845f91bb5fd1ae62bcb3fd5c6ca59b58cada87ed93d5444a4044484cf2bc65f211f8d800996e6403115fb165ab0bd7d636acbfe7c421d626cfc072fe57265ce8a6d17f22b58e0c42bc5ebc5a496b48d091c04cf1b6f9abc954e1e55a7a8f4d65d786fc46e9766503bc325a8cfeebfb47a6984e8a093e73a9f828eb14bfa67c1dad6b6ae67e4d5641abb7ff3509206a821e7daf51a63ee06bce497797f9a780c943bb7219e074bf9001ecc9d94af3f1aa2a7365e4b87c3dac754166afaa93a4165ce5a0cb2c6d1ec105e72244fbfe26113910ff053cac47c17bba9a09b48e2d538ebeff37b6b34ba07005518db970abb5ab1762617f5df3ff644eeaddfe75e266e822db7ade382750d6f51170d16cf423522a85287d98d822559d74054d714c462fdae2f4245f8a43037879e62034837468a959767e290acf9a7bf0b051151d755427ca583cd787f20e9f38269f29cd4139c3a4355c9cdd9fc96b5da2b7881dcb071300b70fe1c8438216dee023768dd1a0e70e0b0890fb72974a980fb1c228bfbd9a7ed8aa476d607de9211fdc89722e5f18f1f4f76f048f8d1ef7b0e30d68f9d5388ff755b892b18a725a60b150c518248346372ad4e93462ceb94c258c84d868b83efb536522a2758da59f4eaf3ae7db7cb9c8d52b16cdc19ad6746f430ce12a1acfd4ac9add73b43bc3e37693d978b6905f13d6288e03c9a00ebc26ef068078c62c18a0f9ad104bc43c1c896c2687496fa0ec29d771fe5ac981046de08b391a0ce2b75f065302852ad37150e33f5b90d1db632ecac60a5e7a0aaa6f6cd99c1092a13b24e061117ad52c8eaf1cc0a46f2e249cd13539f7c5ef85162562b81d82406d94db1f9f038216dee023768dd1a0e70e0b0890fb72974a980fb1c228bfbd9a7ed8aa476d607de9211fdc89722e5f18f1f4f76f048f8d1ef7b0e30d68f9d5388ff755b892b18a725a60b150c518248346372ad4e93462ceb94c258c84d868b83efb536522a2758da59f4eaf3ae7db7cb9c8d52b16cdc19ad6746f430ce12a1acfd4ac9add73b43bc3e37693d978b6905f13d6288e03c9a00ebc26ef068078c62c18a0f9ad104bc43c1c896c2687496fa0ec29d771fe5ac981046de08b391a0ce2b75f065302852ad37150e33f5b90d1db632ecac60a5e7a0aaa6f6cd99c1092a13b24e061117ad52c8eaf1cc0a46f2e249cd13539f7c5ef85162562b81d82406d94db1f9f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000008100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036e19766fc6d41454d16cc4d9220a5ec3dbf95b3efbfe7a933ec95822df721f30b9c789f69cdc186faf11be8b9afc3a1169284990b28bf7a8860328f5b09ee322a3609f2f4dcc209d21ed7b24d75fe332382cc6be33b18178053ae0fc64fa7f20ebef8e64303d1c57c278706e5a8492c5cbea9071733f3d7057dbd90e85a85320b278621514b2f47e499f5bc070229e159a4a86a0455365229dbfc49afdddb432667a76e6fec502a5c61d211aee4d11b040062fd83e9208f161d97a10abb9a2934098661fd9522136083df7aa18462ef6d9186a3506b6b3c4e4be6c7d48a0d72242b77cc3f9822b2b654472ed9215720c2961c8a8385c4f48a699f6a71b7a4f01d92f64044c757fd7f33465a87ebdb3160b6c045c61fd980645ef3e90e7a99c20f983d1e2e87ad67f360bba94f0388ebca244a5c35e22107978b190f6aba6be604639c390f1556059e0527e21b19e611a8bb6a2c0234fb49d7b1ab003fd09ddf11608e19e7d591cc1818295d889daf743288d120ff5f5ffc06d469a14b0920430a3b235c9f54c0d3ace512f9e03cde7e8decc3f608671bce3222c2036721412a15573f2216c5c6c83e94404916b1bb0acdf03ba26549c707022cd451dd0ca6473a02b60350655894308b35c9ffcd6a83f146d9ce99abcac1a5f3e545eb477d1523528bffb40b982ec22c5d9b5c5a24353db3874483189ad63f34dfeffc9d87ad2b19655b921fd456e31ad72ac5285eee6e4b4dce7feeeebcd76dcc836f1b93031ba40eb11541c4ab98673f3bfc361c585196b3d311229a433f7e74624950808b1e71100883187b3af9caf7cd10fdbc667527a773dd3f76ea81d921b2d8e3203c0d3c59f786d8a537d56b2ab37da7e71dc1356f36a57896f3c93f59b70037b4810e96a6212cc347f8d9c7f99b220c103fb9c834c2f73821a3d8b95623ae2a4dbb1c632e7c95cbe312ec447ef4a14bdaeae303eec487d1c5032a52388026a3d75f1f8d3aee3fba47b641d3635f74a42d6c4c7e9cf5961727adf6cda857a1d01fa437dd241d4583cfad18b600a30acc8dc5385fef42cdcdc9417c1e0abd3398b65f2396466eec2c8c6ba979b651acbf1de1e685b88190f7c4089fe64906b9c6fd570eb4f9d131b375ea137168456daf2dc45df00ccb28b63baab8000b9519b5c08d0b4c04f0e7ca4f54e25c6e295e16609c06b40c14f0cdf4b4fac88c54cca34a3f24df009ef4c99fac437ed81657e3e9a2cc37313f22786058b7e86e7951de160b3b8c32267b4d7c18468aef7b1654fea6bd6d276add4f0d4ff4935935462122832ecfc1d036e32b212a00d2565214e96c2beb8c60e757bf062e0c23494d327de0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001131a4d9deb8d9c09c5a2bc6f1c294af29b3f972ea389c7236bbbf96fc0ef91bd0bf0e52a6417038d35ec7c8f6081691525eae93689a36247fdeddf9286b7649f0f2905b9f167aad294a1ac83443c7043f3102ab1a213fcb8792b5b390075de0e1ff4a162ee85f93d00f01e7da10e687f4e1e4c5986c8d45c1ea2422192b5e818271a6c271934d8ed7e967060cab77b705d75a670719e66467080552bad137a371492d8e4de5b18c66ec10739899a0e35a94d0097cfe6fd41845c93c99de741c208f743023aa5bd199c8e2dea50eed88de5e5934c35da05fbe44332bc6322ecfb2e216a9693e1950d4260abeea709002ea59b7a01923f9fe2c2a9bd43f880e6110accedbb50229271d6b044eba4d00fb90fa0ac00c69c290cdb327d06c008fd903e85bbe9a3af93059b723eab5a54217e5c678334898c58e243bbe150ee7e5692318b7934d6d102f463e68888929a340ad54f72d7301c75600ebd8abc9b5ac0e927e33b6e1bee762cab1c8d268890c48c26bf9237010b104b2d904d3d6e8974ad280ca8d8c8456a6ab1536b7c81a97589e963c2ce6bce0b190da91ab67616e6b43d2dca4bc3b1158a0b02a1cefa025ce29e75cb7a54c8238087a71e9d9c0f6e5c32313af5a17317d174dead7ae6cbb9c96b9f3438164a3718859f2c04d6acdb7e1571ae00b0e795e66d3ae634f55388f6b7a53f2982b2c612d2b53b163b813c6e3d061a297442fe7504c6232cced3c67331687f7abf7f3ba71166003b21b75e9536f2ff1e11e7f2407928e9377ace7de797920b87e38577715674c8d1ad1e8556090d00e1ee180dbf86d716c8853182188ab48d7425c781aa42b8681b52e17aab12befb965987bb425dcc8e15660875856cbfd5b74c6770c84b93286461989aaa2d410469a67844bda23371ea99f78a7ab586c344bce588534d9a08889e6765571dbae9efbfa6a84bd4fec66afe2a4b9afd78939874b83acde0b29908e7fb055122451610405957b42b01399501d5b46524ce05639494be4db87a97e41804fab014a691aebe41497b28f9e016f6d37a06aecdb00234ff33ce31229b5287e71a932b596e5141beb684d7061fe9092c85f97378e8f9d44dc54d680a959a7818e56e0ee1158b508e3c3a8b7899ea462b1daa65a23c828a48a94652061e412d6862920e4f2dece4a362a009f194ff5b30b5db8c1a11548feb8808dd403a3a7a12c09b08ad0c549a52174b549e9133f11f384d9aba4d2ab44dcd83876190bbdf38e6822dc80cd8cc933fc1e097cda6b5967b7966fcce87d311bdc5c324f1a32798068b1ef791b8636746d0c2524bd99198efe5e8111d78d7f90050111ecc55468ff028253ec20a911b5c6f522b42c6e330a177a027ee4009bace3c14ee61584a54494f25d533282367974bdb71d73615134e60ab1abdcac702fc50d0da43dcf1c099db04f1d1ccdd0ac15f31ae6bb02493b4240d62bae066d14815086e58bb4d4a9bfd3448f9325e52b659b382fb4bf721faefbab2333463291c383d759ec21fdd627401a0e94d23072b3dd671c1bd0142b1e26d2a76c5a8f8ccf203815bad6b7d374114b0df075b8467cd56e9ebb5059a4be85b28fc7b33f05a17bffeafb0859b79d9374daf7b03477a6f8d04c4026ad817e7baf4fa229ac83fb28831d2ecf7c022090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019e60f67afdc5c6ae6cb0d71dcab5dfac4eeab4aadfa2ca1e2f99e2e57fc0f681bc64a1269209accfa0d9433f2a5eacdc8633eaa345b72ccb7a781cd0648b5a80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002648ba6e65f713ae036b31a4e233de3380acf2c569fa540e7628ba4477ae65ca3fba9a7a9542d3bb69ded0718cb19f3d5d612de511f8bd282ed4306f71cd41d92558594be2a32ce181f2584ed653d41c31998e4c97b3ddfc2c211393a138049c0b5a1b4b4ec2edc850cfa6071c8d501c0b791e028060359dd1ef6cb3d05017063a25064faa01b2b6001f2586cd2230ed4e1c654f39af2cce8b1589515bc8d4cd0839debc0e13bf3908ed5e232aa1be9b05a98df1d52305f99e744e38e15eab551b875699c5dbd909a375b1a6929f53fe0e18b9e1f0062957df19d2f9c353dd112a1c0d96750e8fc42b4108c132a841f176c704f3ffe44f7e47bc21b4c11b5dab23fce22a660c9595119ad5541db324a275e40dcbead23f414ff917ed79af499d2ecaffc576b1b43920198aba1b15917b6ff8b03e00a1dff9a96833cf8e2531b13984a24760474e2927b4623675590aade92836aca8062954bc56c50d15b4b6ae00b348aa3aff98223c1ab136fcc17e2c8fe7efbbf4d4b305dc7dce0611207c4d04f20123a9961c38fbedd19d391fa5688650500ebafeaea5191b3cde9ca48a712d0c6062a3d0ccc37d3edd6ed7e0db53f4872f2848ae0d62e755fb9339b511cd39b9c73f8b7e9e945c2fce3bfa995094534e6adfd0b81215b3d492b4422e02ef0c9447933e852e96d1b75af3e345ca2527ecdc6c3b213495d7669d1b3055c74a18cf51658e5006bc6e5a187ef5ebda356a79984170f078159305cff894b81009381a57253fd058bf383540caa237ed3e722a672cbc2edadf50e5b8a4ef97da0c14a0278b1bfaa8535003647a966a953924a59debb629863dd5026b0c98c94e73056eb1f7f6009dc30b64c79686a76591327e5fa1fdcffe185c9904fb44ffde9931ad16a69708865f9f65e43706a939189aa7b7ec40dcaa7ba72aa89b9961b6af1e86506f8c6037a9f92d6fb5b66aebdd57af77a319d14be1ca7fd611540d583200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000179063f7713445c981d4214cbcb8a3099c4ce62bf092e2205bc1b7485115287e315e97a59bdfdfcfafa669483341e96730d54a0cd94218aaeae29442661845473c95ffaa0f2e75acf76f8434f07a2e0a124d0efc8bc61f6649f04c1f03d0d2793425ff3267286256a6d82c7432e06f695ee90759c1ecf93afc9e8a6bd5761eda17b4141bb678b210b65019cf28ad04b615142be3f8679f34dd711909f024966d14f0f18bcbccad9a919e4af3687bc767d8900c9af1657a3bae9e379a786397f9249b71c7a4ac70cc7e3d0238d1ed5b39f63d5473b4b020e8e6b7b1dce92f926e1b44992ecc57c21d7b846c2836a1693b61a47acae99bf711cbc0b1919527aa35368a9c694559081dae2e3d4104ec1e3dbbbfb5091bd8ff34d1302e96fccf7f1c36b29caf6004798ba99740ea34e59daf21ec61cc8ec90fbda5685af6281d003907784ec52cc1024d22cfeb3c9b48581c4059db381063daa803bf2fbb55b127381b0373ba822abfe6408a8250971582f637c60b59d469e471be3ded2b8b58019418631c5b57fdff31347b3e5d7f2665590ed25a1f10263b65497e05f4a65c415901634ba6fa41386648fcaac71a149eb118bddcb3c1dafe3351f435ef8d16446738dffa3863d4ce37c08305eb0e1798bb4cd0b550e9f375da688abfd26351fd7826dc86b027998f17b5e40a08acebca77abaabb060df27b51330213547a4e40a014742aeeff26d0756a1f7269249d484b44acd6b9150a0d837372cb1588a07646371339d7408d4820c20ce0162b49ff24d04f68d97b900c89b5f21632b98fc56a17cc33b0eb24d6190337ba86827a499755173c05c430a9dfd9527df13c21b4dd1826a5120f30b1188a2a69ecd8bd3899f7608830d01effd58154fc53037e79712bad1c7c86c62c9704b18c176052bfdfb07f2e71cb34bfe02de7a15ad58d9f123e4b95786e0b69ca27bd343ee26acbda5a27756e159b1d230ba9eeed269f99590c832dbe3610856a233cd7c6e9f0ff936dd04915ee10ee6aea5c7e309b5535ba17a4ed1a0a4e63a3e7a4cccce3aef2c82ec2f8b678ec8f4533882ed421658b08360ad3a4f69b5757f77d4dcadb348035e78d5be68c4398be0f045f5631d917cf0ada4abb6ed669d8a560ac3be819220afbea5c46f19f6d3647d7ed30a917cd861d39cabb710964c30b9c2d259881063fcf8b72d7a4afffc988a61fc0262e87d4290f1b3feecf0cf6c623fe6643f59ad9396e6ec27e48de909d279aa6711c593a321d914000988170c33d043d10aa3c5a5e51ba0318dec2fdf567bd6690fa0f6519615811ee07967be50e941a2dd5b74ca5fd54501cc8aa234ddc804dacf8703c01c409906cf2c7695cf25642af36d50f6c4b2d320256b4dd1baed9dcec3f7e5135075db008845dbb2fe9086d61bc75316462d3b50b7fce9a0ca1b7cd3c9f60d823b63e34494394cb34b99b68daa29f3bc04a2b6749581687495315fe2fc0a90606019b6377f4a1c7fdfa3abdce2c82a7b2b1e4ca220bc0d77fb1f722fc19ff1a04d804beea66c55bd2996ffa3946526329c8d6769b94d3ee98077b59b658bf8a1a69561787256714631801a41e013da4d96ddb975795f416ac10d5e1db5b44a5393f195688bd0a383dbed3c0013cdc11bad9e4891da0df3e4aa49366ce95d3243620030ee2959648e7ce2ea4d26a0e71c5ebd85a4fbbf367f2e01a40965d8f911000d8d104c87642610792eedc8a5070988d8764acdefa077a4ce8de2535b8543e605f821dc846929f192e27ffdd557475927a898d6416b601fee649a97dfecf17455e9b11491cc046666943d5b945f8a0543b7c8854ddbdb2ae306e31148fd63603e01dee42df9a0b08369b8d8992138f478d54c732a44a81cb25eea12503182f69094910bef2944b44ab1624e1b3677bf573760f0b715738f8a00b900d93d1036b26714be7b9441f8e750f19515a4640c693b65bfd59280e1a81069639f38105bdba3635ab9d042410773ba02a76383b91dfa8b70a0f42fef36ef78e83d95830619a570fa5d8a50a11fc27ccaece1ad5eb29875fc49a843b615322ca1d4af73203e66684f8dd46e8ad769ac6ea2e90c4ce721a7e5d81370541b14e4004340134bbed2d7d022957b4a8f4b8aeb03999a406ef51eadf4383d5ff3071391465461a161b032775734f249e844d142d6823ea566c02c03cb06498729deba9fc693215c78dd22e306dfcdcfd0dad36f79980428350dcb132b3aef1993ec6f4ae990203dea0a49aaf421bae4af1a936cfd9c5c9c88819f20291989a022dfcb51881cf3c215f5b6550bde451b50e56c930263a587e10e2174a6782ff2b02f04ae77e3213592337056c4a8a6776b84e120f40dcf0eaa881ba0cd7fb020ae5ef897a890b2ca6dcc8fa93b575988947b1edf0bf23315bf07a4f40212097224afd768576f620bdb0131b1d74b4055199865a4c4450924eb18c98f33ecb71094cc0af64ad361f424fece4e28b4bfaae6679a5b3bbaf8ff7e76f7059ba502823e42c509b52cb23b4705f879347841a97ff9fc37d559296fc45c6ea2647c202d41de96cf7620c1c4b8fa0786cb87be56800603c82aa6d8b4a53351f26b1599659130393089df511f99b689e3ef28971f878d742ebdf3347e854426fa6d5e9dc58899c275bc8e91abf14c10549df99d35488670d5d8afe78c246a6939da172e50e6de6d2e4de9217810d6b122e799fd6cfe31713c0dc294f36a516d45e234f9f5b0264ff17481c2bda51d513b19cd610de8b264edf9700f229059275bae83d44c8ba62574fa7341e3da5ee88bad92ff1ff629b52025b54ccb3395806db07588fcf2a34bf71cc353940c296469d1d7863e214ac21cc2d7ed279b672e24fda4f82a2da46d410c96d3f94ed0471f9691f71a2d01b458ed1a1b35dad8c71e212218053ccc3db35e3b733585dd92c7b355d7d9f2901d5e5f12f12517f1bc456470f220848a733147a3d3621b46db81a5b02edea15a7c47fa1096a427e565c016a95df284a01c7c932e91105754c21cd7b0ed316d2b25dfb34785443714386d14c7bbd4401eeaa076a1635bed0c7d4d3f64932a4610ea79903a65208d0cc01950345bdce5ccb54de7b6404b567c2dbd740b2155d401ba4a2a616edff6583741657468824831e06969ec200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083511a9555420ff63442a9f5462352afb8fe892569ed9cf993206d5b2dcc5cf3a5a27bf3c431c5c6d4d579df0f2a0c37fd88f1867fa807925d5a59a300f9fbf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ed3cc5a7708e7d7721a1ab5eaf62e3b76170084a7053a43139bbc96bab162f02d056016e1c673fe00bfc4c639f88aee5777af474956a8e3590051011fc2c50f0947005954caf6e31eb567c89cd84947164088488dc347e27a43a74cca8c7a8b244c54578e5bf09bcee715d7ecc3b485e12fa3ce4ccbe387cfab9db82f90b63828725015bf9af1443bf7052179ae40ef09596e8cdf8622404a96f8fa67a5d70f32a45f89915acdc4ba237cea3b4b0125d2a32c2c5c48af8b03d4b700ed51d3213c17ec0df24fd4446f1bdfd0ef91162762ef946b03034e84d5880a106fce14f82f1a7017e3919c258e2f8d0f08e4f15a9b534b5de9c037301158230b1b5414040c8429ca9e327d86338951e004c7c13da8d86bc916e83ab43459b5029f827192189bfd15cb1d3d36722eff8cd6752c51be9437a725eabaf6d41331cad7e53c752a7d458c08cf6b00a3d208565e5889f81515eb11e2aeeeb3bca9e2fa1ddcc67c3cf6cd94e560b4a3355f2df245ac37952e98c5743eb788cb8b581c6719eacd1e29a14e4c850ace0a0a44be254906fcb09311b0979a44ffc0b05ff14419801e8d174fb047b2d9436887725edb7b65ab3a3c5b5c55c1e5876da7005a9966c7d05f0f42a470c9ddd3be5667e6a7421ac746ea89af11d60462593c1a316761eece6d1f5c82d9ce08e394a307f55671bf9c1d1925c5e796159c1a51ffb89c626bc85c182dd6fe8037478ab3b3311d480a458f6ae21fa8c1a03bc6222df7138bf973ac01cafbfc063a47add714e05e59e5a0659850c8fdbe2cedf08edfaa5fd1c22604091d352b713ef4cc09b46cb10560b57d359d0825b89c41753d9af4171742d0b9220aa8876e70c17fb8ce1296e4f764374ae35b600a2c4ee6227e6b07e45bd7343ce78fb1192b1b08cc09cd7fced771c81b4766ed9da8d40758b72dc0106e004b2d82b2eaffe58f9418d20723cca430b0c376611a2dbe7f31777e685ffdf48361000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415f03b1c7d453a1f32d4758a039dcd25c71bd41a30fad10d84620060e488d80422ef778f8e62d819e057432705007e65bf84dc2f3727c2e846a181c4348fa209b5fbafb1e1abb1b79e8fd6feb190a87cb5680a8402f041392bc4cd2818c26a1d10d8783124231798460752183a7093cf213cf65eabfe97c315e8117ddf935526e679602750a732442c18760cd8a398e070da81603fb260bd17c3cb3f58803a0e718a4ce802bda34c11f73882e77fa4cdbfa55fffb82d30ad67ce91a4015ed101c3a26154223a46807840eacd577a2ed9d1c40e03358790937421adb30229350f8eb920b50b010d9c0da8dc2ffa576edd3bb25fa6b3ed8fbdbeb356cc88ef6504d75b7b2e3e0f0bbe7f15e645d850813c85ef5f799735bde0d3bd9dc8fb5bdf1a114bb061844a5fe32236a7c7704e3c98f22d5b86820e53f0e61f8f8c5117783cbfcdf682970cf45ef64beb3d85d5451f3f13dfe43260e1aadcde2d29eacf7e2149bf6a3ebac10a75b0d2f802bd655df805cf437b95543819ff20a840ea64803164316b5e42b92e9705d33347cd8a685be4666f46d7263f56f131335ae461581577ed15e0c50bad20be2aa0edefdc68915ceea871262f433bd088750b0b6da7018d705b8ee235a1b359649e6f169edbe722ceb7c5188e5bb6e0f8a3d8797a753421b6bddddfed47ecc128d594d6a4462373e28ec7743dc167be0a11cf7708690bd61a9a3e536d63fce44f981c06aea6dc86ec4af4787874629179b7ea01c1d425d8e251ea3af078e2b242da19a35db7d85299b0826c0617c6bf5a50dad004be3036d56971a09cdce95faedf0b241c524efeb5ddf4bece686ac67fc7d405d92d27328ab1d385791b74cddd18d6144dbfe5b501b4de4ba1cc99b04f58c96da6ca163c3a5fb38c86d2e71a9a06014c5169e3f606ab2bf0aeb37b7ea9f61e9171953e4f71bcf09a4f881704a8f4707d010a5845dd8373e201068503b2ea4c9c9b971626ac60a5ced988c3796b557f971dab8be2cc300aa38dbf79233c8928f77aa101d7583fbc2d6ba4d4d26bdec6aede2ae3998fa3809170981257815d9c789fb92ba4e5c8e5e93a1ae43e55488b5ce720993f7dfcfb5cfa349e3ceb227dccf65409b56dcd1d694f40f9cf24c33467128903fa238aaba38e9e16a5429ff86cf6c72e75d881de23c1ba8eb65d6d2023373b8eb5299bb26ef348d4587642a24a7f1d31be5ea97cc373deda9cfba30dd753ce02fcefca937faf82368a04abcfa498ce02b88b2d058cc9f6c14508eb7f793972859bf0c0476777c7f7f2362d6b0e78521d281335cdce222145c74388d7ef879f405d703f70eaf888e5a2310e61c076451387e941d9f206726d371f99b45225f8dc891a93776f258f19fc7c6c5dfafac026add843d38f11a03aa250f58720b5d9892ec845539665781a7b182544e380423447e789a768c60028586dd54b3331b80707e6b6da9acd5f08a1970a7153a8d41fd2bf905d31dffa8b5698700564cc473d6e59d905efd7f1da9a8d88e15ae6f804a33d90353c120f068b117a87acc5577309f17cb1d5cd303ac07bff6d9c97d11df6c186e5ff2bc69e02d7b0f764fbecb61e1aeb9be38afa28cf812e3563e2cf36644b590d39498a8ac91ce53aa9a37168edc0fee7778d23beb8ba7c93a3caf91809762785a9a2b25f8b9805b4ffb584c0ff490320e54148a414ecb84151e4aa2f82fd1e60009210ab8eb4ed070b1024cab3b6db9d41fa25d914318a11a3d085197e8b650dc32a5f007d34b4e3fbdc8af44c0639ace04a322e23443f9ceec68d0602b8edba34f3124974f870de163196f1d0aed92b45e333803a9d5601f6210d03022156de020f1a9fc23563d455bb1d45d281964464697b79b0a3f9fe1fd4290723222576b1a033572042fa4094873c21be3d3016904179f264bc2459d834e0314a371ce0c0d9febe07d90c9405d749499f212105445d5e3bb6f733069e1bba193d9d6bc17428bdea1f31abd9eaad4b5ca3925a1ba066b7319c080446df025816c547112f71e1515e7c0aace7b4072172f2442f570fd571c5788752305a61412910a7d9922c812d1557817f584eec4098006f1069e39220e1adfe7ca9d18fa1288db6435738a7561b19ffdbc911ee5e508b041df277afa2ded8fab81839a8063b5e0b65760eecd775a32be2a09a39d7d0f09808c863608aab86c706a1bf7141329051bae5ced831dc189225c58c349b071bd08e612701d54b2dca971769459a188918a02bf244b5d4b552975e626d4f8585420a396484fc431e3d7ed6c1f93e2776e75fd40dbb4a2b4aad68a19d92b09cc156f1cfe8741f560ef36e293e06c33aad7b20dbbb578d278a9cf4d7ec228d7953b13715a99fd1b66a028d31c9de35055284df2444a872d875630b2813dd72a8f2e7c4f3a35949e2c32e5fce3621cc256367a44aa8b5c1c5b510c8379cacc2d5881223471c3aaa2b5d490df8f157051a9c985bb5574a3e3a4aef37c863533d4cbe86d8c230be716dcfe7df070ea8fc3af10635754b8cc8dc8953e9160f5fcde71b28b850f3331ba6780b6bdcb6b317050ef9ca8ab473372376ac16e9f0a0323b2b7043b859c5fff2b5258123494cea13ad37ec1948174ab844cfa327346a86cfd081825b0c1d66dcb863878ac08bc5317cf6fb2d29eed8922c9a58c4a6ab01cc2d5079dd04fa1a5532cff0ca6174382315f1107dfe4115b7d966e31e3d2c14965545b72df064b9976c1438451168fb01aa9f93e881173d9252ef5e3e84c3c2d5b8d5e76b5beea99159d221b68836a21685d7f7d3af6b0f6199ecdaaab5a580e9d8f8d6843c35bc4a1cbfdd627e4e4a0bc690d4a7d4d07e686fe2bf673e694628532874e524b110071aa09ab3c3111a09b08e48ae45a9a5279cea74c901b04ece20f42f1e6643f83fe7b7fa87c56f2a1c1606280433ce06dd5435749a05cc3e45734f20e50dd4b228cb378f856353c92188a66c7359a0824add0e699abc7301afc812750a7f5081e10617a6fba3f0253833e0034e64a7d3fd78000ebac2bdce0563b2bc14e024eb525dc2c025f7bd513de8227e4bda5142a1d3cf908a9c84dea37eb9668e0f6f25262ee108ca860c151a0a7707dceead3505a561107a660f8461fb96f8ceefdc9ad91bb85e840ad73a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f536cf6207d424ebb540dbcd7c932d9629fd4f7e3bf25a9836b9245f90a588e241b1e0d418c63453cb8b5c09ea29df5851ea7c540ceff28ded365ec035669c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001cfa1435e7ffd71a759721600076511bb2c2ee2c428c45e1916e1a5a8e46649627bfd69664481ee123d99b10d5f02fb16c14cdaa05c59a8be6bdeae32ccc56b40b79602a425542257635b65c2ca4818a6b2dce93939d43b45e2a74dbd5509a7f24b552ee440bf80d7ffdaf6db9bcbd5b5f8c5ee4302716ed221657174ebbaf8e280833a182c58114875679f752cbe6180999dbd7b91856eef458b8499e8291351911d892416332c93f0d87090a30fda57ed08519c58720897e451d9f2cbf62e30b7ee37ed8f6afb3922e84cc3165151803d431ea5efbdeda18f9b49bc98d06b20b5b25ae20dab578f4639f681731a66c72b9ffa1dd30ebec3020ba4471db83ee362edd2913a742db82b6c26dc2d024a3566ac46b33e9250c59d2855f2cbeb2fe20cabea2d172b3f22cb9b0bfc5a6b57212d3cd8cd5fda500b60df409b70b4986344e5f3c2eb7a7f71738defbf8ed354e862c29dbd314b60d0d81b276bc5b9d362bbc0c08b62ba1484ca037867fe5f95d025e78080993286d49c978421bec7a610d06122edcc45cf9f25e0320677edb2c50f4d9c6b3874fb4278eab994d62abba00181fb01c55a0216f4544b1a2cf779778321eaf8550124a440b2aa0eca7fe182d7c53904cdd9a09ecc824858ae1953c9a938b6c2c10ba5d6f52fef011dd998d33dac26975d3b6a0fbe203541c58316c5c905309a130898b84e485e46630b20033e935024a1051596c35e8d514a5103f1fcc4db488536a5cb05ecf38102978ee16a5f0baaab01da464cad37f420641606b0ca97ed74828ca37cc9b7fd86b016b31da50c5f5a1af7f6495f13b5e27074a6c6314226705ce984d744c70158751201ee9cbd63e18858a2a3b552dab17fc480e502761297eb0cfef605bbaf678015a11695a6ca9d0df8226ecd7566ddf6131fb00d4a109b842e16c81860172f8ce562187e8e83ed1c85998879354163847792671e9de7ffceb84677c738263cfeeb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8bf6ce0c8c44162085faecf6a8047ad83c1575524d696dcd4be1a4eda0ee8708662980795450628168c10a6068eb9090bc5f1ed555f27b2c4f800e69d788732e9f6db03ca510e866dd6f407a519ac1289aa1751d384f3c7faee7988f5b1c171c027773b4b86b0965c7577e22380f1d66a88eeee5d65c21f5ac274ada24c245006428308417d2503dae2f6bea09acdfab541f0a152b54d8351f9a9deae082f912db24e18748ffdd4e6259f0c1d400e7229be9ff9c9ae946f2333c43c8b30f2b2d55053026fe890145119ed281516b27e2bea003756ab1c587984a1e547636cf29f4cec11a0cd1d5ea605dc852d0265cea206787493d102ffc01db7e0adc92581d2d29deb797df380753788610b79810bef534831dd397fa083ba58ee969d0b93c7e778eb7d5528ad41451f7f272e154a42b02741c27c579e01a67c69ee426122150fb01733a1a7000fa706b0b69f6e65e0615f10535ac71a90ef694b65974ed3a186d04e8fc1a36149ddae94237bca7a0078e42e65b71f9a9d7853ee73bcbc40fba4a4f09be27f25bf6e3e01742af56198499926e7edd809ea2f367e1c7178e09d3f15645e1c0bad661e15343009d8485b8ed5202fc4ebdf8b3810128846724106b6d7d29ba2ac9a5e4360d107e68a6c6853d23f412fc2a82bea5fd05bbc56a26e509ab8126ab4b5fcdf700da1768766d8ed7ef34136edd52ed1cc3b354a5c706b76998fb963b8d9bfd205b98340fda5a2269ae8c95914309baea39f8967f2e0c24502c2a595dc90ce81fa1679bbec3e05d3b102d398746298b8494e02abef11a35c0cf07eff23e410601ae672c706e65f8ff1c492e844df5935fed82cf2c8a2e40a0da866687276066eaeb8e24ab46a9101068fa830fce196942d00a4d0c0b3810927b184fb2e06008d538ce71686a52bb94882da38d76968acfffc93a4f932449feda86c945de21c2a1b7a64479e8043c07a1b123c6d3f43e01e96ece277520147de913a5c4fcc10fc892b6646a5e39c871cf795067056eb041659a3e82d13c1fcb24f29a876f15bac0f9adc2d8fe36184cdf38581d41b78731e1ab6fcc1e3ca81473ea28f9e72d9d225fa8ee36876a42c020c52f8186ca9458bb46e7d30b2896f31ae9a16c04e4203e6788e24341b2bb7139369d55758bb69daaa95dd0b70423dc04ec3e8e4ff364bb792777130e9980449f5a72eed70d79b51a2b30f7a81c518a2f27c2745dc118b8c2a3e071e3e9881f637531ed481e23a0e00ffc7b2216e313115b92fc80a1885c741d8399c10ea16485691760685dbb14d3539d115e3c88d6ac846350f0df836c8a90b0614398a03a2af77b4e9bebf03c30bf77219b2b90012a49924a3881c222a6b820984ecf56d179facb7c815f52a090b222da7114303bfd8e7f26c4bba74b07c87aea2996c9f0bb5f42e322faad6667db5ef7ff20bbdfeefa76c4501424a191ed9f7068ede5ebbe41955e6f0ca54a27aee41b751804c6cf2083d0b7e6645fca8a18723c07284d8f5422f9707fab0b577959c2f30698640f0e46bdaf6c74f4b9082a616e8030b318883b8187e102e48101ef01a312dd159f25dba5319530c0a65603d60b7dc11fd1d241ec744e7654d6b18541d839f60ca5a6dc8d4cb6cf89f8a7204718580960f9d8eb51524e8ed6008516e9f71b791ff7fa3a32e960ca372e0b7fc5a113044afba280debd53d6dd649d36929a1c69f275bcaaca7f31ff7a2c8b66d2cec16e0552cda91fcfbc14f854061aa6ec1c76d6a5e40952d8c5e58854713d9ab7bd078220b74a91851a8da83951f0f09d0700a57747db38542329abe076ca16291b52df00fd887ff7f93d46aa03eab2a92616f416e8cc25e6eecdfb226d644f1235737e8f2c4ad58f6fd5cdff5e57b1d43b78bc18603e9edb0355f93348a0ec7cbdf928ef17890611af046d51db42d46611a5c9d461ffba22b47445740b2d0bdf44102ca69ae7d14b60a3128a419d2cbf3b503b22d493c8bce10b57322500cf80a348fc74cf056581e120a4596b6b1ae93b3ed0c9a7be8cf374357578c2ece4f61033e320f529bfbb48d585d049edc3e016aec8f4068e8b660d793121172673ac25e232149ab9308ebc4b426dc331c34b215531f7303aa7764d3b5c8ce7218d36688d26480f7b6f5d4c532986b763a48619f0e746efa741aba347bfef361789cb62213c791a41ac77695d01fe6b62130f10314f6ed9cf9581d0421b286457038973efb734a30b1433d108dd0a03fbc1541384b8cbd0024189ccb53b9ffee4d42d58e1eb1ec6d5814b542b49391ca8e3812c7b47342ffdbe76334ac460011b2bd2c964addd427777d04501e7b3e3571c8021979bfb100b47b0ff8a2a1ffa7824e29a22fe9dd8de8d5d0bab3d308f4c71841e686404eff4b84f0075d5e00587db1d88239a5e306e6bbe8d81f3bc70b38e7d27f60be750386674fdb2d29fe458b86cbe21c71d29bed09a07fdd018cc7e37921809f418afc7998b024d2d601ba747936424d1dedf8e2881912f60d43381c86f07ce3b84911a0048f47e1d1f75bb9a1f4fd5189db4d327af5c6d7db4fe7715d73831c47b6ee5ffb70b81e2e08a4465e0d271805e5479d16c3cbfb3380188ea2a01b79da7726d8dc62960c73087d0a18bcbab7f8556deb7889fdc5df04d64c8683344f6c30cd0c0076f92b1c78ed88679c8569e6a7cc42f08f1e06ad7e8ac37b92daeeb9a2a07822dc288e389e8911cd24959f4f5beea04c663373aa3aec94f341fef59cd0b9a6be7605b2e3a1ec8c07169fd6e4f548e45fdfc5c75428d7e6d1602ab058eb32ddf3f74090c95d24bd7f7a69ec82753a60e9bc495d716f9b5f6ed23c93344eb9d7e7fccc3ec9928f2a7803ed68f77546d65e4e5f9dd42508954df176448b7b8800e4d6f220df873526289b7ea0c729ce4e0efcce03760d90ae7923c57eb1e8468e74c0119dd1c4d55afa431de0f7c491c6d2b948dca3d9c23ada626ac4ed4b622ffb486d5f5c3cabda50550989c5e8214c3b74d1d6ff9d33a8fc506ea1568451c7d61259805503bbc6bd01a7f1b2396f628097f2a3efec3df03553bf30d3e1be51b053dfec15593c1e44293ee0f48a6a59d4784257a3e55272c5f271d322fb259b5f8c6c65ba985eb00e7902572b36bebd5e46aefce3e0300fd0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8d56720aa3567c731bda3d94ba6fc0d173d6c5b5c3732c5c0bd760c810e4f800c798ce9b887ad16644cd5a092f85a60ac2f0db5722acc796dfde45b4baf6b9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000317fe70057e1c194a4666a02d45baadc87606e6f96cf48b13c2d845de5fba5ec3c658f745d9069632996429de4c17ec6b11d0074621177c848ab98ab28c3c9cc09d2f6e4bfed12b64ae591da5cfdb84744d9355136839c50392f9be0734eb7a7310d6b5350cea6698a257f025d0775861706056c29e88ae7ed08fc75874546613665ac8f23badf8fe72c92668ce81340d04fb2c881e91e3bb1bb057432c5196b23a826cbe73397dff28208ac4213dc4efc4f999c80e00a980254de241d9a65e71755754b262106fc1d03b5f53353d9cf5db73bcf3cd4aeb291f4367f7d5b9fee22e42543c3859cac942894c2d0d979d63df536165ebb193d349e51b9135889a0359bfdd5d47b3fc99a070415afecc0ef657a2d8ad15d309609a8e45c41b8498f21febe82cf53ae606db6f47c675b18e031c4b9bac13a8828a690e4eb584faf202f30f0bd5d91412de751bad25f276201c2edba0217092fd6e2869929e5a390cc12480db7226c747e6d68c3ec00311ad4946180c9208fd5e999b64a4443facc8a2e837c988207f963fe9497ec4bac9139f74086ef15dc61ea6332da4e5bd261dc17a343e45177d1598541454547179fc4c0c9a49c82c9df8df725495e19be979a19c34c882ef8bc941bde43aba55564bd54c676af91b4ac07dcfedf9f5696eb001dafbab52472b490ad4d6965003a2856f42907d459ecea28bd293cb83b584d8806ef5674a895b3c25d099e05038cc6d0c4db8bcdb1106220febf281435f06c7d3a3089d7f1c2331bf0025b64f6b32e601e6ec39a58dd8d180c7c65efcece695707aa6c80203036535095926b1265c920b7e9696a8fff906a06ab749b0befb69e3cbac8c94db3ae529b9077c592f5a95dc524f8164d57a00651c35ff929408fbc11ab63ea6b07e9345b781819467fb5bf6a9f17ecbe146b784d2908204f0fde683535fb7c27afce84604af8f2397163076bcd99c04c54a9bffe9d7c1eff1fa6da0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016724904e3ae14fd13a2975d4570464bc55b5f9e34705af492cf8e8f51a73ede293e4b1edc56c62647dccc1b8c1c0bee8adb2da2cb1e0764ad61fd11b6567a50059cdf5df0de29f85879d4d73c049c878aadf236201967ddf595c75ae11bbeb515f458c51d8f5c0a9b56019e67d1b8141973049e70b554c2f07361ed461362e422c8c430c450695394f2f9084654b5c00d12253eab6ce5fa4c256eecc037e5ae226ac75cc6dbda9df4df36d02ad316428c90463f173e9368d3b199d8e98f9dc321f1a00a0ebdda69e69ace61b23a5426cab02e2dcf399014fa4edf92f908027e11b174f88b4702b7b33c91ff2c42de866c0ba9c311a6d5a21fd6ea265158d96833b8ea7b1519c2c477d9b42fcc1d8c42a867a85b7b32f3514b0995cf844f9f2b32d796ba5fdaeba046737e22c620122ec0645ca2fcbd3016177efb1e3cf6d0582c81429a5b0f9ac7268665e623f6193f3da47cbe2480d45cfc38e79de620a5dc357d45fa6806668b490009931359a1225348fe2ce9089490f7640d821fcc7b0e2d257ebdd0849af842db7f4adfef83c26ce96803dd69b1b3c2c42b09b5e87e383eb758c1f3f778228be7edb19ca7464c01d8e9e1c712036316215fe1f53bd9703f3895121d25d8a2665924149d0f18c5dcaff98680dba9bd9a72a6295c5cf89a0c6fb03671cbc8f4529ec05ee12dfa8f4ddd672b564fbdfb128a5d37829dec3c14b84740437513518485a61190344b5da47b84a329a156234923b1d4e3cae787371ab5eb1c2734cd3f6f3bf949b4edc2315e3c425512b8e1f03af1fb71849ef300301f5f9f13bf35eec5d4024a1499a15a20b48bdeeaae0a8370c3a9dcc46a4c1c1c63b23f4aa8ea4e34ad288904f272e08d6570e0b2a3f2722536a2d4547bdf10ba975e8424a0b056e81fcd1e4863a757ddfe46cba06cd88d302ae6ba52734a218b797df87a82f5709e8376c808f0991b457e847dde3b14298f290e7381436c0913f3463076bebd77f28a8cdb0498330f1bf63353b6dca522abd12721b5733420a25d08a0dc949a9116db0a48e3f7ab8fe29deee7ae5c4d891c3b58d428cc6312123c0c69ada591ab517876e64c9399aaa234cc28a37f7501078e6f6918b36d35eba09500e0d437076db6b09b4d43ce1654aea5803c369ed0254abcd8c827980401e99668dd172b8ac982eaffb2d41f9d84e1a5015d0a157cd905c695ca766d162a802bb792faea5647d7802f3db42e6d8ac0cf9ecf533dafe7c408ad7ce7820833747e03b63a49e4a9e00a70a072420761f03d50ff5576ef7c3d0c842a24c11ddd22950737b9fd6e57063e193de110b14a4b53d728c870f8d3a67533a21f202683b16c0474ab576af772767efd27a392fe697d37beea286257b9515e4cac20353d3b9a4d086d0167360d743fce2cb12dc44fe61286cd15940064c1fea3850408c51d6fc27cee42c60e446b1d54982ca758fba6c2b74392c0d4975a755aa9a631422b3e8bea33293c36468442dab015644da93f6f525440b3c2f6ee2e71eb6a2057408394fb30ce4cdd8c881e4ce8233a6069bc95bf28051e503b644d56f0fa11499793b8ba30ad3f73b2ad7857f4a46bac7d88a56831934b592e494383afd109bcb787ae2981e328bbbdcedeee52154014e88b0cb4fb578da5a6ba507ed96a216e9fa219a26f15b3e4c8d704f669b8a26b9ec841fe977a9fc41ae28b50b16139c6e373ebf780a9390ad5205eeb2141efb5314eac09e15d89921cb063d1dedb2074e369d1f04f2f18be881084242f50b739bb74cace3b210d1d313caf80f82111e130e715410d2f81ab6fddf3df2bcaa8fe969c49e00a5d2aa7a76838e72c532d0ae0319cb826fe73f64b86e990496e4c582856aed9d9ad9b0a364ce0894744145e3cd7a9f68376bd516216756b674c7d0e5fe3b3eae4edc9471b78008cc7a31db228100f5e076f53816b35ffb56b8ab0cf3e77a1c15e4faf81a59d2415e47d24a9b598af8e4fbeadad3e7ef6ffbb3e5f572cfbddd463d3fb68d3f2234c47910ddc2819316ec2ff754ebc70c5012eb1755694d0a12c24ce5b8a01478e946ad6039dfb801db72fba3373aa8ca893c422c8094f6aa2d0a7d645d763df57010cb32d577cbb078336dec7a8013d4f001fff3714ed9a5955cfdfa6b749d90d81db3839a9be50258b0fb0cae3082f14964c8b49608f4832e75bf65412dee6192d8b053f2d80c13c74ccba55f9680d9a9bbfdcb5ef652d327945407cb6d2ed71f08b5a331ac47308370f0795fcabd885ea31c9c07adf38b35d10468467eb24f9f9fd270ce53b8cf7c8f0f86a0354277a15ce3661cbb9c355efe8d514c545c8060602da3f85d63f29134b25edef5b3a9d92f8f05b92912764ea660dca8004f1e1e1f1c0007a29c0d6ecb4da1210a4c5626d070fc6b407d4a462930dcead2bfb1e1e0e413d9d2f3bcd6077bda5acc82513dedcb140c271d4d36019d68fcb55056969b8bc0262d0c4329f88425a5337daec21234ee184272735ecdf450961dbe7969647453411ec2b02e256b43c5fe8b9635a4f75bab1d537fbac9cc26a43e5670f109ba80bee13d4fd1da94bc3a017469ca5b08a6794c3c40da05c592ee94b85f0ef645908200bc0a37f39b9f8a6b3bf63ea78eca2628910662373cdb137c0f3c09445573a38fe4e031c8d0f53afe63a11cac1401e9cdcd9d9aff09f35435972cd77306801cd59b3cedac1db10d80a5784e290155df9b2fd900d5ddd5f58d6334cb497293f2a14068e7470489be20d09df50d8bb4b18c415f0e837801765cfd970322c9b03683831ea7020b85faa6e9498f569890883b3cf2140baf8543bbb6927a918e11a164cb093a99f46488284b3c38d8307158e6f6706e35bc38e49d942abc1ddf71cbcd86c943fdbf4799c17f7caa03bf23d09ac4d1cbfc92e6653da831115b8d10d6101385f72ab6e7518f6bfcb4971b6b22449033b941432e8d4937e71be145828a18922b710cf5e588624001fc29e37e586e673a9966b76a5ff2df63c319efc37e78205c5c00fe75a7c900fbccc44253e2db75dd5700de409c9bd41759ea4320c0c3c84507c8886cfec62db1fd1e52dd3800c509c894e81643be9f81805cd9b2161f352ae2ec64454160ca30ff9d39bbe7d062e2e8b7822e982df610ae309e20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016c85f4738db16872331110af04f3a6e48cdeea815990b3bf1fda64f122f7b5a09b2c9f5fd6044d262e38024032223de07bf549150978b371d754f75a972a19400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023ea90477bf78c1e300f77e0dce1dbc73e2f0918c76fdac795803c1e6370e51713416b8da931b1384d73129e1758b391b07a6689506cf7e2354070d8c2642c7c1e074c093a62c2544e5247141b8c9e18c29d7607cf3f196f4e64e0a305bdbb6215049b474785b97e57a66a884349103c11f8c371eb5ed1f320fde99f02f1c5cd3a631ac84f6181b098127f89cb0910173b97ccea6425864c2693f077ccfb611d051acf0f858f9f503cb9cfba119f1beae82de5232889bec203e01c07b8d8f3193e720af1f81218c51ce3bf83b712afdf6c4b6dd2acf5c2bb7ba119464af472991a731f87a9198bb4e26105d6c65c7af07840b5c80cc536e1a7af8e753e503f4f223997c173558b30351ab9e47a98cc79e0170a15239802d78918b931c65fc27c0cdacc664fa000878bfef4e08d0cdce6a59a760835892be1fe8fa681c9b0584e3690394f309072b861e6ed68c1c67ce313ac495f3cba3743adec7e803431c9b91b06776c65a0d65b08f680a85ea52c06a76ffd12d3a831e21efc15a010ed1b3321924c33f5dfff6fca915a1f9a8a2715e5588e3f803ed668a83cd5b0833ea7e82b4a0ad9857586377f0a0d95556a15353f2a95babf92d775f7f918636530ae67204b4d128fef6b01a94d260f9d4e5fd403a1bc519d5885aafc8ae53b990d296a1ef4d56a5f4c93623f38e9fbb4a144454738e0f12fea0e788dc5886307623928301fbcdabf57d73202f46b22ba021e3c744b1e9e6a115916c388b4b9ab28fa1a303a1257305c627036f32ebb3ba02f3e0bebaa3f30e6d49ff850022c13c1d4881f54e643acbaf342149c9def686c149133513811c5de46623a543e8e75078003055dbf0c4c122b5d9bd06b4f783ecf938cd003fb9e05a5e7f67d6eb377880365293ddce06687661193369894d4a682beddaee2816f081f417a189aa2246d748d0689f9d48ec8da2fcad395aea0266ff33c944238b8ae9e17381e4438f15264ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a27e15a49ec8e700bf11d39c814140de773e2109ae7f43d06807105ae8f8fc1fe56663e0b712dc700460c3c0be165ce7c7eda64af5922e94e2a5d9bb87f6c02d8eaf131574cc0ad6ac43533325b5d312982f18c29cdacab5e0538d4a782fff35aa72bd637a058b6e389af26ead09e1e8291f28010fd1a0771a193f2d591e9c20175847c5e00ba5accb44fb9650b8006d0965f05b494d7b142a90a916ca6b6a103a3bf059e8411ce5c2c803bae5802f8c95d62d845d2b3fa595e4a49864506a25527812ca6708682e8f7eb7d6fd98f7afabc8e668fbc99ede908d85e1a5a87931892a184cdf9063bfbdd86a5ceea7a018f6b080f196f38fb4ba91379be99c0a037b2f5d77675541b90326cfc0e53c6ce6da6058fff06a5192e92f30e248d9b508e16e3d51926b7e35e846c1883126e096f7f342b777f8f9a6028fd2a0c2d6d23e2e95595e3ef9c2f8fc855aad84a95a9dbceaad62ac15779e8c52aa081b759e0a1164f1e5bc648a7aa649ba7d77020e82a9e85a9eab3eace4bd8bc5b3d3a0352098344db0055616ff0270adf1a3546c4d4f399dbe6e3e6e2060f51f46ee8797118755500c01615b30d98ad5605d9f8eb059ac65b0a21f830710e1280fb125f93173b1fc58fa551734fc6b4c5c56592fbf3c1d55227a3b5929088454d35851a51a242bfc30fdfde119606f58538a38075d1c12374641243b74ba0e8e39cc05243a68bc2f6a959fee5fc9ff7dae4e8e52f2cf839350135ad061e0fee20360c1103b1e195345c82b43bf9fb199398c6af385351e4385398b1c8684dd8768e4774e2229cc6aa6d4002a60e21d0e8663945b0e4ce6b0714bba99980a1fa9e38f9a2f378c1dd39bdab621a68163702ad8a2ec6891b5afbf6e09eaa43f2fa31db3874c3195a7ad38f068fe9c94dd5ee071a28e9bffc6f50651b9b6c4be68afd02b40d004c1ee48db33f764834e036578fef707db8a88f64581e694ca7772d3f0476a833beba392e5d18c6262f331291f4c9a41c36a6ac649e0e9233ff086978aabdab12fe53230f35a7b70006ea96575a3b010ea3f01fbfd983100e3c72626b007773e007576f1b9127c8d0fb4b5b6c1eb597895dd0c450ea7841a8716fdd7ed135cf900d8a49c16c8cc820d8c79252b480da0b8975f2bc4d97780846186ff6f49c06d1cfef1c8aca3924b64756022a13d328cb1ea502baeffb79c489ce7923c691c4729c85ec1c1f4a1a99d3c5e809f5905b78028e6c90723ba5e93ff8f70e615c89c2568640e7bc7291959f0c482c9f5dc683081727c9263763f1812a08713c454180b0f9dabe893ccaac48c5921bd41bc16bf5c4ea3179a289bdcf5de1f549c458d2514d47d1f2d322e60ee9a6d672a8fc5b148dbf87324918512d83c1bb30dede42187b23680c2ab752bfe54b96cb558b9923d57475b497bafa4a99053eab8930910ba3c4b78bbe7a9efd51f2264b21d34e557edafdd486ecdb8540a98b08bb3de3820755beda500a1a0af937a64a62eb7f4bd04f3b8a42324c44fb4df826b6381321e57049b71551c706fcdc6276c3a8828cdbc6379f4a65d321e448a80ad15842f59bd6cd2324facfd558ab7b4977d435bccefe127ed846c4add9e0c1e9f28d40b9e55fdc4f8c936c10251bdf6845a22fe585651b862c00185675dae6e22c6c31ae2fc8e6d5e08b60bee39ce762a87d2f93bf8bc5aa2820e97af0e9f3b5265592704917c6b1a9ef40a1c89157df1866ead83fcbe50f811bb686d0bf2c9ee2c5b04c65261e8c69e3473faa74bf0cf1c022fa809db181943b1551da75ad394ef9111b6bf6cfd478a738269a70044b7c7a3b4d23355bbbac260d50a1fd39edf6f9c1ba0d5c67133462bc7166d0d43eb51be9066641cd95109fb684c4f2648c8d51f3340d92f21d59cb3ed6c69b449356acd27cb20734f7b007d8db05d66e76d29483657287bfccf31b9f8ed5520420b5485b1e88650298f8db3801809285ef4b7ef0171033761406ccbd7210fbd4d4ac0bf9a29d455f8cde3704d5de201e8a297c007733f9afebc68d533b71ac0713f7df9484bf60c65f3a6298f8a0d8a17d286c03f7c6073e8a9e93c9b7d6f074cc1b272cffa022127ea85b4af1725ca3fc443c63f87dfb1cd70ede01748693e85ed4e1d9f8491045bb68481202e01175a14496e2033bde9ccad2a4209125c1a3e2b6f5de119e9d0ab6c2d73a49c5972575068f806434f9f9b3a0e6abcc975d3d649ba357588763a433ca3ef6994124cb85ccb0c2a5b1ca2701752dc6457c74799a0d3267a6b35d8e4fb3a9acef964577efdcdde15a4e35d8fe8ad239ba838b8665f2cd9a7db63232451be80ca33cc958102322313c78f2c30749e4df5b6e46600241fbffd4442485d0139b33f5762ee7af505532c3870d3cf8b61b20a491b99ffdbe040250256b3ac4bbf6859d5cdfe850afaae22e5cbdcf2471785cc9275fe00b49ebfd00eb26dc7b92764a387bdbb66c91a9e1d1a34230db8e87a336d8a01ff4b61405237e68e4193d1b6f5a573319936e5632e7cfb50bb63759cfedc4df6038719becbbc4a2cd403d2bfff4c52cf01ed8514118304af449c8a630123b209fc78e641568a4ecf3549265b99e0de1dfe127aed010453bc7c108ef39900a1ae3439c31d6523469b83ebe0dc0d9e41e4cd0acbc418ddb678dcf64d3f0b00f8dcdf89d62d443caf2c6be2f2ab2fde1127beea74982e5ca314548fd6065946c263cb248d7364b940f58109a7ffa44aa918a326576a243130918f6ece92390aa4d1af3bdd1ef8c75c8043172956ec32d1e29374bca219dfc8c07e653664c17c6868da04657e714ee586b9929ac516f71549cbf72fe3084fee496ad761ff71c3506da56e4bec2d2080ea88306c3483ea6892547f3972324a14c7dfa4e7fe947603e1025a219e0411a0b9193c18777e31b8409f89b71d03df6840c9c59688295d5ae52e5a9b707a07cc1f67b88e7bd00b85a2ed0d2de52d9cce489194a74b4ac5fa0ef39ec30c00d25387ec49c0c848f92e6b7c62bcb70b6329f9f0970c969c9555b2b988b7f9551de4fc78307780b752fa93315f70422f4c619b65cbaf81ba62cc954bf087dc8022094f8982a36f3adbffb73cd0888300997bf25ef626f654f17074b0bba7d3d90fb8b13202d0151fd21fc918a85568000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c90a8c9bcf24498e40b39e61a6cd6a710d2d56a5a2d4437990d1ab93a13dec303f86bfae94b8d6007562ca0b685449fd0e065f4b5714e2e519364ae30c57b3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020edf9d6e784fc0615c5302d6c0e442802820e92919bb752bab86ed427114a870554dc3376188f7faf3f36298559a504be1c15a8c3a772c5b998bc1357815d0c0f7664e010a951357ff4d93b8f0ad7f8c44c0aedbbf71063f80314d92f640c0b0f85be0e255c5f1338bee84d63a930d7a814d3b7a1a271ee77c8b7650ff31b62167fa0ec75ab6ea96a6a265284ec0f9b492720b9882d3997d59462a028f79ed913ca7081cb35281a947a4f1c2571f7dde1a6b0183d0fb2daba88350bef607e322e04e81e6f9499697391e3a109947fd63ec88af3280021677360fa50bf9656fd377f20604807b1e7bb24d16745778d3d150e91db98c7c9de13f788ad7c070fdb29f3d0036b7f2b2fd46d520060898cbf980cc40a2f673cbfc63713fe627f11833ea44204dee5923475240da942467163ab89c35ed5bd3de9b6d4fec5382f4f68394c175050c30636e77caffbfeb1a66c23f8c0a02574b667e3587170e09c70fb0c19f0ea56f7cabf9ec3e9517ccfc6f285fe629f6a6af44942a23f7f47ce5a1837ea4ef420e2e27ca9ba3d7950a197aa60fc3357710456c8cc6f5b8be2db51b10b11ab9319b356b2f6d1e118b240ad5a69a2d6b3ffbac61db208224ed016e11b00b6429775f0ac40f0e60362231c2fa48e7e5bded758d998460f89a9abd69cfc0a6306a77a8b4673cb5760fb2bf01ed125652f9bcc28272d399629664b29a91a27140553a69e7dbbe7dbefc9aac4caf125f92a36ef590edfba333a27842f1b303211ddac6b227af4aae4674b99d9717e48fd60b8d63411a44c20bc167da0240c13b1cf77ab74e9786eb2b7cb00503d28653f876866becc409d3dc91cd5e7a34724035b1f1fa90977a5b33ce2988c9687a6651f850aedc8681967cf6619173eeb27e5191171254742f79d6b4d5ce539f342611d3b2c86659b9da2fe92a80d6d4c2b7af8baa9520575af6398306fc6d247dfbd051731efdb2b6188216ab30fd7480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013c1fe2a7cdc4e165d1393099fde63769cc76d07ed15df1d6e145776e84ad94c39a50075dcbb283a06d8880e79295b25240d5a790ddb5bee8ff77c5b7cc75a4f2289af49ee88760515368ccabcdfad21eece03c39e86b235dad26f0ccc0eed7624caeca8a7afa95492dc864b72ef82f73741ac5b5ae5a4e602095174ebd659d6212027fdc68458abd73efe52705b981ba1fcace19ff33ecddad20d2b85a5200a117de958f97b6d6fee6a8c80d4cdd67f79493103f40ef6a674cd7ef3bf0f1d533d39f7a75158f50c122ef0d5b0894357ff89ce03ad666c2044802acc870f302a027fc294751b226b23ba7933ca3397b2931472cc64c00da6098cb6c9e036cccc139de4110eaf835115ae1c41942ef58b0b11be4f2a261cc9cf7ac32590d01497341f57132d6672890a029b2cf4935e539675f9f20e4d69ec7e095291b217835b276e315f8ec684427c77f8724189849033ade7f8a01d7851c347fafa761218690acfeb44b34f242d69306e4d72b5d5b5dbc77f5dbb05bf5775ea7bc9edef02602c357a4cb0711f8c0174a98a56d7e29ab6c0e6a8387cab2d5d5c8169e318109c12726de0b7be8674d68a783702ce879e52d5100bd3fe4dc4c30e5093d2682b32067330403c6e2abeead521830e21a5e8e711a8b733131b366f4b2575a2892855037ed73cfe2c8004c4af0d6d000b3bbce471fe356a56806ee81f1d655cfbd1e106be692be91dba9db7acd85a3ae30e2bc0fd988165666f8cde312673bfe9438d333a3955703a7ae97e0bfa67fdae1d1d83bbd0a8c91316018d371d65ecff387008f933773ef23767125d9c233b67004478f6c3f1904427a3cac6217045cc80ce17d5e1469819c2dc162b7e5122ccc805a36e2447aba078faf4893fb6eeadf914264e77a5002839684b34c41d03f5f3ec856ab842da4a33671562ddb4e88abf1427181da0ef60c74a90296056392d35121fc42e1a2220e7eab3ff234be0162903046cdab03ea837f43dcb2133aa873428cfdae28ac5d5cec11fb0fdd00f49f30b16e45ea7d7d4a7b4cf2d363e4c766eeb88159b500e99a2dc38b417ed1ecdf9af2bdd398310bbdb6ee94196c9151773c5cec1191f83741dec36dc1ff6f258d1df3bee88bf65bcdd49591bf44cefcf56d5ca8c63b74dca10494f3083de0fe7735a298c9fcdc9beb3d57dc7c04ca006e059d5bea8b9eb343a48786d5e69df3b055a0d52978f7a6b3e68e83cc3e6980a0d658905666a9af61ddd74d88f4785204691235ffcc9bd00e3ad457a59d908925fd9f4aba62157c013753fda68e93a145a8213fe83a4ed8c9f17c2a2e4447e2b7c51d1e0917e46daa36d51fcb33a7d66a6d20fc99aa78ffc37500e0525bd22de7d675575a907816489d797f5cbd9a4ec53831206d54b246d59b690485e9be0eb8b552657c4ccd84705bff4a2ee012ce65a3f2737b2a4864d9706e8b9e583f7f13ff70fc8a83ad3d42252c3841d77909213d0057262c962882535e3b382b9bb77a3bc64ea7ff09c623674c365f358b641878d2a83e5f6e1df9ffbda4364bd83cf863f24b95e7c22fac36cd709fe01a3db5d16249bdea6a7cf68ea043f480537cf876f26f246f35442679da3627aea22b9b9bd34eaf8e30c4e60ecaf8fd8a858d7f191d9997dfe05b754881ffdbb8a8a0a4ea52e06672f002237f5b3511e8179543df126d0ff541ed909fd85a922a607827de82aec101d7f85a01ae782ebd436dd1080df42f2317a3c9de834d96ce8e723a0c41702dba33f0dcc1474a136a16fec2661f167b31892e9433f2c046a85dbe6310c13f20c39c402c65acfaa11cc0d7d33763a7add745f8f4fc86aae209fb2e7bf6e3318d4d087726b95caed3833854ba3216834afb2f85aeb706f90809675c60f761dff4627a01470ffaddd503683ff0a817861cf02d0260c09549680ded5499d40112f809927586390277cfe2fe27de408d49312ab2a7947eaac25e42a0528e87b1c4d9b37bc1d13cc9ac2345acb146860e22d2e8a024ccdbd001d7c422de3d9321e49dd3261906050fd8d92bc3ddb05af6699012c57e1f94aefcd4d116b5d9bd72e88154bfd0dd0aff29cd145131569853ca23e2831a4df61853a486c258a2bbd085ae83cf5afeb6dd6309c75add7804f3e17c751edfb5ca8d73869ca0a96f9ee13e3facfaba10a46017bbe0b9b1dfec099992e801525498d21e99b025bed71223ac9a859d0ac16145b166c1811d5910a7b401f6feaf8377c790fb94d1e82c0dc0000000000000140341b5ea18ee43d36fa55fc548bf1e90824af99935e2f87a0a0f57a435a075ef23b1b690cb3c0a405859ba3e4b6efb97055faf40fcbac624030d3cce10f2c79b0385ef4dbbed565a904e5b3a9ce7779711a943bc8fe030eca280b8b5d5ec35b6f29c3d9870fee0cb7cd08d0143ad5ffc03c6d81e4a692682c6ac312c789a362953fcb54a2d565967894ee0d693e4ecd263d3219a982e39dbb0f4d4b3113e6041c26ad8cb58e7236a998a7ecb8bba8ab0ee91908ae711822525e87fb7ea8f2987f007fe2152183ece4035a49bccfc08f99616db01750eac2f60d2fa1de325c80db30336093e585fcc1842f8e64fe6909a27f51475b6f57649e086a2461b97073ec398dd7a81b308aa1c244f53273598c13a45dd339d4bce673ca48cadb9f29cc4f19301fc20c0d45cd9bcf052700e67509df491dea44932e20199ff2c08b1dc9a22d65a5ef8a28311e4e67236691c0b484b36fbcd11f7f3236a71a6c2cb8664b560def379aac982283416a3fbf6b2c6a02dc2469ce206517acb5dce77b13722a431f2228c7cbaba6b08548926f64b0dec7d8b45957fa6baecf4c0a1a375eceb203001d74fc09d7c8faee1cbd94f8890fd8d37baeb4336586297a84b6019cc534400e62c08cc3fb644210f293d0d6b26c0a77c67028af0403e77c442111ea91e1ae1c5967d8bda721d4f22f49d6c4168cc00c5f46a135876015e757aa3ad259f6b4278360ac62b268672c14bffbcc3dfdbbddfebb675f04434ebbb80767968e9b7b0178473b375de1a8636635c447ff4eb979f861c5f1588f33cf0b8a54e2f5929b293e74b4152b1a9d9186cb7375c0d99a3c310a4b11bcb0d1fbf45aaddfda8d60379d9d879f58a130b5592249e0f960d4481f79976e255723ec7db800d368afb02ab2fca27ce5867c49931ea76e70846e805ac19665c680b10dd8bf5c5dd81cbe13800a4e028f9656204a87053a784b84eb532a71b583fb012de7555c87d8a1630648eba52f51d2767ba6153a918f881cc3507033af33b91a88e3f0c848ae59140bc52b2afb765105240ac40fd49d1d04c70b7d99a2f5e15c876e79f282210eb02fd4ff9abcacbb23eb6319bbd75372e44c815f38ad2f5cca2e71b9331f95d1222070db57b5a3a209d45b1137ed137e2fd608a7d8e15e255fe9be70e6db11414f398f90eb83ac7bc724e747d73d07c3960e8e5509b42e5a1c3abf0b03bbff3fc9300032cb5d722fa07247fcc412cf7c0d3b51a313186d9742f004ab192708885e08ed940ccad4897a7c591729959fa93157fc9aca6f801c1ed55498b242f147cd31cf330f11e1168559b506cdd152f498c5116368d04eff60d40b42df585dbfb704cc494079ddb3a20aa78b65c4fd8d09b9ea8c32da916f4041e5b43ad7cca88225efdf2507c4d274f87a5241d5cb6bc0ca3b2a9709f9f4bd21b61711e51f2fe017d1ffde137ebd2023b924a11a623e95c93c0d33903f31675fc4883dd295f49427407a3fa9b6e615203b76f2a1a0bf3b5796e4f6f31ccf0a93dfc5cfb083c96c3ff4e896b247d9b4f8f522407dc2874abcd2d6f9426b3be15016e391d1cb5a46184f135de5d7b0c4d9a47e7817a6dedfb12175650308c4f00445af3994954d091ab8d3f1d2899376fce000c4ccc9a96d5b6e6fc639d96ed785c787491a98794e10db1cfda164ef2062b116b97090f20f01e860fbf078069159c3e93aa935c17112673764ab54c502e6dc442ac59666fdfc5dcf69a8ffb20a8f84d4397c8fb67d282a97157745679526d0b503bb50a2c47ccac6015386f2b6ed0bd7b9e63dadcf075e5cb4e4e793982a6dde54ddb167fe8b5ec6cb76a9b719852f936aced0b27128c30dca4f14bbae2b830a69061cf456f9d792602a7f9909c8ca92686b6ca79e3a0f9c667940e111a4842d70b4292eec86541013bd4f3c1aec4d38a17a8d4be52c5cc94e6c35441ea4de989e93004ae946692e55e6a02120e66bbadaea5f10c10ad386b4c07eec53d63e782aaa6ca45f02742c35b224b9e9aaa60900921f61ae1c6df094a179cfc41fbc73a5f16eb62317695cbde788605105419573e63b40b70bb6fa68406aa17e4240045a08c5f2f5e09581b1af09d663ff31518c4214316b1f052dfd4137e498c0e1d94d920305d4a390351835818d99646a79c07ad7a6f7127ded6944de50cff0046a58ba9a9790bda49b9200ac4c389cd4c220d48ea3f705a28e110d888cf34d84bc5a90180ea992c21d40c2e52a250d3c218c564ab43f00df5472f647518e37be41c5c9857715a6febe9194a59d591b21f21e54d3db912efa658b61656ed2653d2118184872cf43bd8b7f1e56c3dee410cc9cf77ed14d179aef6a73e25aa8178209322364e2a3c5d3cbc5c33252900d4ab01b10b917ec0fd15dc2c3b9063a234ccbb78a6c1949cbfe83cda73b42fff0187622a46441eb0ad16792276c0c8661e8556cf11bbbd9d7f68a41834f26bd0efa2b22b6b5b4481b528801d327799ab870f12e102ed5834e8d7e472524bb3fdc67c081fc60b47f3558ca90abbd59418ac2877d2ed448209a408dba053555b3f2eb9d23721e08712a2c1a62b1aa1ee88246526710e57bdb7eebef1a2cbcf3e12b15bd06ba34a69e3a0f9c667940e111a4842d70b4292eec86541013bd4f3c1aec4d38a17a8d4be52c5cc94e6c35441ea4de989e93004ae946692e55e6a02120e66bbadaea5f10c10ad386b4c07eec53d63e782aaa6ca45f02742c35b224b9e9aaa60900921f61ae1c6df094a179cfc41fbc73a5f16eb62317695cbde788605105419573e63b40b70bb6fa68406aa17e4240045a08c5f2f5e09581b1af09d663ff31518c4214316b1f052dfd4137e498c0e1d94d920305d4a390351835818d99646a79c07ad7a6f712cadddad1d41b9fcd7011c70a898264a5510fb3c63cfee5b4591f6faafe0f3515b270102c77de69b1134b14c1574e6a9010c50ba57a30676c0a7036b7041545253886ddb7f75609d8066ca6ba95bb8cad08d8e508396665a50d3ba609bc13d32cc6d9b80b9de1e2cbeaa910009a734ca65b956038aa46414660321991c847c22f3517acc034db6cf13231f157ba52fac253006af8dd2705205f63cdc2c6dab2182761c63ae6099fe82d2650999aa776bf7c0f80e47cd57635cc6fbb2fd554930be1c7efbd0ce6a9ee326ed2b6c38584d0142d743b3f03973ece89e888e563a016339d9867b55ffe5a18c08d29110df9bf5fdc734c4f4b31f46f3940a54d9e393d1193277748d6b605550d7a27d963a63389acf2c9dc57ff955a5a5670674c202525702ef00ceec32abcda72f57cc5da729dc21ecaf6e31a0406ab2e455d61f63b672ab0a5433b5728b3404fd0d7db71661dd13fc0f82f7c3ae9890ed2fd406518a8f0db227add9b8262b3e4bda631fccd165d510b52461a000e994c1181d739098b4c084f74236e55e1c2e921440a349227b89c06b423d7cc1e62c606865550123a37699435e30ec44a568cde1dcdbed0318cd17af7bd512ad11cd5c9d6d0620e62c08cc3fb644210f293d0d6b26c0a77c67028af0403e77c442111ea91e1ae1c5967d8bda721d4f22f49d6c4168cc00c5f46a135876015e757aa3ad259f6b402efd0d6f9e817264d1fe390649f7b4f681b9e7d86f048c5af12e4dc5ff7f2d90f5875770bf6ce4e92b758d9ca2550ac43514ac5931874a2c338da4f6c75af3107ac7aee44fd37b932434a1f220a7e8528c1681f9feeea9ed07dc4d83074fb221d3c23661362377d9a63f4cee0fcfba642d5f06321493ee43ce4fe2a0bcbdd360187bad12fde52d41ae57b9736ac6e343bb1384c4d839144e95bfe85e937284f0076def5b88ac21f81428dee1918a3eb2d5591a73f4b378727049356d3a61069132ad6b36b230e6d14865a245d7359b3d89b0965695fda191738894a8bf200a410b9f9af0ad148bad6e298a66e258d776d888b9019add367e3a965242874383e0b14c3520c7881a6fc2554d91fa8466fb2a26b79f7d408d42399332ab16666e417737feda9b0310d319985982cf142b036ab2a89606b967cc029e965ce877af32b68a2641f89d8c68fc962932bf9f423b6e30ad8556c3a557166d1bfcfc219a713b4d7bcf8f6b60cbe61e882943fda74325401fdd71c88addc0936c84357e0062e60ad87c30c218aea5f1c67b0c0f6b8316e738b48f6c50049a850c5826bf6aa2562fc6494289885ad3761420fff7559e583e9f6d62630025d1b9187d12a72e70bb6fa68406aa17e4240045a08c5f2f5e09581b1af09d663ff31518c4214316b1f052dfd4137e498c0e1d94d920305d4a390351835818d99646a79c07ad7a6f70692a7d46d6faf399b876bd3baabe4a57470859aa8ad93e4baf7f75da6d6bf7420204bdf0d655d6c41ccf2f9853aac513f486e82d6352006f85b2480996770500467544f734479e929372a5737f2e8d6b483d7b96c6c20256ef178197b4f365f126bd8a8d4ebcbffe640af7ac0c809f920f7a5abba160c65de1477721bbceb0706c33f9df5220336c5c4686f1d8a7d3a2a493b202e2906aba4eec73df9608f2d1921fddce6849d3061dde3179b229a46a912768a99cae641d8bb4a70c00a9dfb1cee877ac159d55b44fe87c8abc29221c71e57f97ceba0dc44cf445dd10bf7372e004c735bb06385a454f8d9c8dd06ce517c2d900ebc25902ec864bd13000c2b3ac00f45d7ffadfd780f297813509cf5ffc8d9deeac8037ab5a0e177db2518ca1166b9d23c4f77b6f64a943ca97536dd8f7d68777d1a055467d136044bd916043fd7a90b7b4e620efd1f84742e46a1e492462324c85fa47e0d6655b101127faa25967314fe2397b4727e11abea860ff54fca33cd9efae37e7290c0c9aa8bc3e52f91866653ea5101091f788a08f66ffac11e86a02b7aed398a8c37bb080ba6572b79f522baae87799d4f3f3b25a1d625dce399d2695523102e98408e583c68873809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf2e1912c77d47e57ad5aecdb85161559cd1f942c4d75bf17f5411b09111877fc62b8964ab148d43538d327d0dae8eeda9b123dde7921cdb10c36739f5b301f61024d8218a02a7775cf5248b4466db9bf101454f87f5340c81ae17c78c8e934fa525bbc4ba16f1729b8a53bacf45a2ed66ea34971a70d7eb45e428ff81bad59f3410b5349ee93a6e00a93c4236515cc50ef190b879821fb092f83fb8d65818d35c0c0375458577f34bffcb1491ac2f3dcaae619d2d1f9593cbd2a3b82767f6c6370c018547376854e857f20302b09643eb9cf13ba0b3db042f8d321aaaf3c2476911a1472d453f3aeac3da6274f8faa367346e4e6a3b8db521f3bc5dc63b8e2ac11015afa3d49acb3492ae2292115a0801619bcdaa98248ef6ec963f6e4968efa73a0f5fb20d91fa16ec6dd6553acfd61083cc5ae864add82f39d143344d8aaaf70163c5b01c95f1651f568c5979f9df657b4ab6c310a42a37e8f086f11c3d574b3ed03a236510c0d5897c0fa8a90023ca3320c8299581e969d52422f2313245252f91866653ea5101091f788a08f66ffac11e86a02b7aed398a8c37bb080ba6572b79f522baae87799d4f3f3b25a1d625dce399d2695523102e98408e583c68873809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf089b1d281b335576e6756e1c234354953b4508bab1214da19f540a203293ccc3097544984022c49e174a22e0c2f50e784af69e69d331fb9a90e9c51d7f07bd013b24e4e4fef452a3c9b5d5e9704af251b2c4ba2a640d411c028e366e51c605693781775f9f269331c70ccbeed9cefa57cd2559ad62b9df2ac14e87ac1f90f20c1b01a23129771cba59556e3b6e473216c65a0da22d1a9da1b29d51793193fed70819ec8ef5ac494a487e85166b3e6f68c7463826b016bad52fe93eb5db8e7ca006f3c324d9b21e9f09b998c2507c931943a6cb23efa9bd331fd89fd3dafc28123b0b2f4eeb4672492e6e3f18678d901a6a6f496d1fb79fe4ae61fc6beee9a9f51d2012e4010b31c89f843b9e9c6f8e3c9b8f59a3fb97442193923165127bddb51caf82de065b4e7a93960beb7c8ad7fad307cca46e424697c9ca6bda2ce1bcd53fd7a90b7b4e620efd1f84742e46a1e492462324c85fa47e0d6655b101127faa25967314fe2397b4727e11abea860ff54fca33cd9efae37e7290c0c9aa8bc3e52f91866653ea5101091f788a08f66ffac11e86a02b7aed398a8c37bb080ba6572b79f522baae87799d4f3f3b25a1d625dce399d2695523102e98408e583c68873809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf3796e62d9a3adf51f51f762b4125ab735e46c87d62eddb25cff549dbd034bddb09717471fc0b8dc92f86de5acfc20fc942411336ebeb8c856a5ca292ecb1f30e0e5e6ecfc178b564267ee5c7d300b670e5ff5459ef8bdbd2b1812268a7d81e5825fb40ba2a6e7f5f0b405d144d7ddcf711cfb21692ea973b6d1ebfda09f1dfd7084266d9736fa64c300a8cb9c074c236e6524cb4ba427367a191c83517eaf57d323ba91295da469c6bcd65c733774eb18df91943f23686a521fd49924a6eb6300b2bf70b4c49bfb347766e6afa2c5cc14220cebb76f439e8a2254c99a66ff8a93d9c684bf2cee7bbcb92d0680cfb6f484957277acbcf5e901ef7093b439431fa0447d98a26d9e00c3aa51badc91450436cda2f30a26d8e65bbb41a719cc4eda3206549b25b4beccb63b51516c2920bdb5e3ee385d284797bc0456bbb2d2c6d0112f9b67ed849d828ec66d3ed389aa072f5ee58a906fe2ed8a39310e9020b4dbd01dee938be00526a6ba8c5cd70384bd6ce6281ed079f6a11adbf7ca1929c6d0608ed940ccad4897a7c591729959fa93157fc9aca6f801c1ed55498b242f147cd31cf330f11e1168559b506cdd152f498c5116368d04eff60d40b42df585dbfb704cc494079ddb3a20aa78b65c4fd8d09b9ea8c32da916f4041e5b43ad7cca88225efdf2507c4d274f87a5241d5cb6bc0ca3b2a9709f9f4bd21b61711e51f2fe0004a4a1ef31dfb6c76796f93f382aeb09af1cb4421b149542f76425064421b14158e410c21debe0a1433544b3203902078abe0ebd678ad7b6a9774e6f927f4f50141654c852ffd5a6846fd91b51ab489a9825ac5e0645d5cfaeb6ee61c845e86051b6d74019e40602f88d806baf2a3f640aba6e0198116729e41e46b4257e9a928372ec2b7a1a301899e6e74303a67f6be39d0c7d3050042fa8a8ca13f89942b1f545aae7ba046ff895d6f4952658862bfa1cbbfc247548a688988c986155dce3acd36e88f527007f91a036b29a8c51e501afbddebbde41b5ab5a6676ffc2c391263c9f2e445a8ca9cbaba8360dc8f0707c6b1235bd33596df713e57f07edb4925a2fcb1fa6de5a01ef30715c439cd90a5d9838a93d4b3021d2543dc7f648f952037893d4b560619e74437eddf27e2e404b096b8893277ea3d09c389bb8ceabb2bd63fda74c2a0ad5b2da56b4ac4d1c5e9130128bf6b214e833e6682ad6aedfe1ebd61ebc9b396bc696e8b25e9aaa51c142accf2b914a908051a465f88bfdb752cf4d55e9f67edcd963382e8399a6b69a133c0906a5dfe2fb31f6fadc1ab0d793304d04e0b2cafe050ef2eccf469150a13e09e6ec2e8f9df8394c9b7a9fdefa03809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf182c7f114349e729e60169583e487c072b528480609cb37a26da13ebb43c7cef2c70ba20cedc3b07b6c54d6ecf2794905eee053bb5d8e89d48c3cd445eb36b5a301ef6654955725b942054ebe6e35398dedadf83f48c5d6a8d704585073ed14c22955e79aae906e217489cecc4630b72dd9628cfd3b834e87b1b2a3907aa1b2428f50c8ad6ae6dad1b01e766f490304bcf8402bd3d3025e4b140635ed3d8764f3211e04787e590d09dec2c1ef531f4c07dafcc8d0aab1ea07132f14a4f7fe24006025c139027a8411d3f6fab44c2b487dfb267dc7b82a55853413a78b8a06d3a3d2d10defe841d36e5980f0baa63606bf8b0b2f5cd0d3497204e982345b1e7b53a7a04280038909ee2af37c1d1463dfef95099285609eff640f3723332b0514a1e9a2cbf338795f4f5e7c77ff20dc89ac6d2b28f6cf61862ac908a48a8e6db2e22160591f08ccdf8fdf8dbc6c185e3683362badddbdc23346b6e078068b2117c32a8cd9bc64097bb1c4a4afc69e7768fbdfd6e6abafe54f6dfc4045ab36de6902e60ad87c30c218aea5f1c67b0c0f6b8316e738b48f6c50049a850c5826bf6aa2562fc6494289885ad3761420fff7559e583e9f6d62630025d1b9187d12a72e70bb6fa68406aa17e4240045a08c5f2f5e09581b1af09d663ff31518c4214316b1f052dfd4137e498c0e1d94d920305d4a390351835818d99646a79c07ad7a6f7154072a4714b1c50296fc3ddd3f71e4dc4d9ccfeb756c7a9045dd6a2d9bce3c711dae2132148d347198b9e7135b5372dbb052b03b6bf5470f43757b7555f44e6293e74b4152b1a9d9186cb7375c0d99a3c310a4b11bcb0d1fbf45aaddfda8d60379d9d879f58a130b5592249e0f960d4481f79976e255723ec7db800d368afb02ab2fca27ce5867c49931ea76e70846e805ac19665c680b10dd8bf5c5dd81cbe13800a4e028f9656204a87053a784b84eb532a71b583fb012de7555c87d8a1630648eba52f51d2767ba6153a918f881cc3507033af33b91a88e3f0c848ae59140bc52b2afb765105240ac40fd49d1d04c70b7d99a2f5e15c876e79f282210eb02fd4ff9abcacbb23eb6319bbd75372e44c815f38ad2f5cca2e71b9331f95d1222070db57b5a3a209d45b1137ed137e2fd608a7d8e15e255fe9be70e6db11414f398f90eb83ac7bc724e747d73d07c3960e8e5509b42e5a1c3abf0b03bbff3fc9300032cb5d722fa07247fcc412cf7c0d3b51a313186d9742f004ab192708885e08ed940ccad4897a7c591729959fa93157fc9aca6f801c1ed55498b242f147cd31cf330f11e1168559b506cdd152f498c5116368d04eff60d40b42df585dbfb704cc494079ddb3a20aa78b65c4fd8d09b9ea8c32da916f4041e5b43ad7cca88225efdf2507c4d274f87a5241d5cb6bc0ca3b2a9709f9f4bd21b61711e51f2fe02a68851d2f99d25052ad821b7e49ac5cbb80b9e8ff7ae41c9a146ce44c2b7acc0e8442d82aea09603b3a7912a4067abce426e3c2ac395030b391759f02207ed539746b3058b2168ee352c335759573780ed0b2648bad3ce592a68c9aa52961c836f922bc36eacaf1bb4a9c4918728af6c1f8c1e37278982421422b1790ebd08a18cecd1b154ea6d0553a9bc5541565d434a5bb29ebc82cbb602e5b947e918bf8126bafcb5aa05d9f87379c78679413753e31ed4605c224a9a226e9642a4e182127bb95657ad080f1aa644d44d5d2c717c02e1d1ac117eb4db2c8cc19ab08de581af5865dfb39f9ba49df9f744e3dea3abb953497b8bec4bfdbcae23e114e88b70f1aad449654adc991783c12d8c4872c9df2e1fb2346ddf3c690ee3d8d2b48c1181abea0d648794de57789ec50ed67223453c5243a471e2109c0080c6164e2752721d9ea655720a1c57a2a3875e58e044c5738e656dbc6079cc1c1239e53b2c01206c3fc3b9d1a3d5e31494377edba6d50ea4a1679fe3372223ac92e7c53438a0ad386b4c07eec53d63e782aaa6ca45f02742c35b224b9e9aaa60900921f61ae1c6df094a179cfc41fbc73a5f16eb62317695cbde788605105419573e63b40b70bb6fa68406aa17e4240045a08c5f2f5e09581b1af09d663ff31518c4214316b1f052dfd4137e498c0e1d94d920305d4a390351835818d99646a79c07ad7a6f7024fa107f475cae0df2883260acdd1d9a67e4b81aa9033c98567df9fd20770112e3cd003713973ee95a74816016dd779dbdeb6d4269af2f331e414e14a81e6ee36487d1de918f24945be5fa0d8c173f77666c9569ef959160024e72dc67acb151885be7d13cf6eab78be86fb549e3e78dbad14bd31482fcdb409821c43852a191820c6adfea630a9d4a84395037faf8864ca269d62df185ee36aafcfc15840aa2574aa3ac365964cbf516f4342d481674f181f311e46aff73d3f09c18e1d44ad14fa62a0acbfb725ad86ea8808b53fa21817b39dc838f8ac247f29d9c177e4121b120d956a9e3e4c8ebccf5ba35a1010c2849702a39eef366a8137ffbec5550b1d2012e4010b31c89f843b9e9c6f8e3c9b8f59a3fb97442193923165127bddb51caf82de065b4e7a93960beb7c8ad7fad307cca46e424697c9ca6bda2ce1bcd53fd7a90b7b4e620efd1f84742e46a1e492462324c85fa47e0d6655b101127faa25967314fe2397b4727e11abea860ff54fca33cd9efae37e7290c0c9aa8bc3e52f91866653ea5101091f788a08f66ffac11e86a02b7aed398a8c37bb080ba6572b79f522baae87799d4f3f3b25a1d625dce399d2695523102e98408e583c68873809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf130594f3f8df0ce2e59c15010ecbd3be0a28f966ef816069fcec6a48aa67f87336d5a0860350df06e730b95ab21b41d67aabae7e99fab489cf0a93fc331884351b60d7d58324de043d71847442db5db360cfb817fbe459b32bb2db14480e59a301cf53a9dc0d88c0bfd610d352f63a3d6266eb5b77f0d023ddaa6179d93a5b962a6f12331eaaa08de7d4a8056fd47a1d68afc923238d84094cb01cbac067b6cd1a909c405138bc7d78e81b47000d71e64264622b8cd9aa48fdc5cc388eb454f224b978a6d2002eb444fb435c6c638d1cfd2fba8b1961261c9493b028583c5a0934857edfb243381334b32400286b61df45679d4de1c3a22ac04b4e9e76ce82813ac00f45d7ffadfd780f297813509cf5ffc8d9deeac8037ab5a0e177db2518ca1166b9d23c4f77b6f64a943ca97536dd8f7d68777d1a055467d136044bd916043fd7a90b7b4e620efd1f84742e46a1e492462324c85fa47e0d6655b101127faa25967314fe2397b4727e11abea860ff54fca33cd9efae37e7290c0c9aa8bc3e52f91866653ea5101091f788a08f66ffac11e86a02b7aed398a8c37bb080ba6572b79f522baae87799d4f3f3b25a1d625dce399d2695523102e98408e583c68873809af91ec04faf30f2e8d11c0f54d2d2ec4bbd742851a0c951a84926fcf7fa432b278d3959d8b23f3f35095d9d3ab9d77a793eeab53430c67ae77a74d1c58bf041700fefe1d4f78cc4acb5c0eba37d7383aa2bd47ca43b805200a6c0bc8116f23eccc84c63862c979f00dbd3e0147c5afd0b8144d10acbc40b902e800f602a507d085c7b56a37487bafef205075e0405038f0792de5515e2b61119b366943ef3274c7d6bc604683a333e26c44281f48958c6c5800b57cc02eeb467ec4ab5ea30ceef6f474b927cb8269534e46d35c2fa8c3d31c259583e45c8c9953f0670da737847b5f31dac157f250d96c54088c7320489351ea3025db0572ee06051fc7c63c3f6053a4eea3a96fa8e4b07ca1bec6fa7d91243610e27183feb9fbe62e8dff200882218c9e3566866d5f8c76dfea31eaaffc1d058fbff79986ca88807c175211b819fef8ac02ea1abf1e10a907d7212ae6d34d3d549479de0dfaa447bbae1917a6dd59d72584ed5a3cbe2fd187f9a72f3386e598d205e5d2ef52fd69c112a13fb221d56e2cf24ad65e85f55a18da76d9038cd38d4dd110eb45311429d8d96d05708fcac91f5752c80080886cb748ff129d8362024a4b9ee5da2f405d0be1ad098b4c084f74236e55e1c2e921440a349227b89c06b423d7cc1e62c606865550123a37699435e30ec44a568cde1dcdbed0318cd17af7bd512ad11cd5c9d6d0620e62c08cc3fb644210f293d0d6b26c0a77c67028af0403e77c442111ea91e1ae1c5967d8bda721d4f22f49d6c4168cc00c5f46a135876015e757aa3ad259f6b40d1230b108e768283cff3a8cb8df2f351104cbc95e7dd7adac7fb2a240c36e7d31763cb9974488c2e4d4e7760557479647b7e83c55748f140eb759988d2e50a003d75adf023f8a771d7d90e31e73f3db70cdd51f3bc793664d8e5515d9fdf1c42278c20f4e761fea0618f55e06004d975dd8abf737f7954f9c4ccd30f96436ba0c5728cd9f95109eb664113dd95bf1ae548db7cd2eb9a619f08ca5ba2d22de9c094b7571372dab41f0ace867f11b54721ab12662440357f8c1f16a98470cfda804953f06a10c6cc29a73e5383471cac8f5bad042028f2b818d4d039bd54b05a538eb1ec35d68a7175f6dd38ce3ffae87f330e2a8e2cd7537f493440d9bb97b770b1f8431813ec2394351f2a03250393eaa4fcb4414f93a6e02cf2c764fd18bba0c532a983e85dbb2dad5bf191fa4996c8de3137575e48230ccb06dff0faee43f1efb4f2141a2130dc6f319c87e6dfbef266edaa7e0c0e67bf7338c50f65f4a753fee5712de4b8b57354333c34ffa9f7c6fbdb777bba4cf3eca152f898131b1e31f2228c7cbaba6b08548926f64b0dec7d8b45957fa6baecf4c0a1a375eceb203001d74fc09d7c8faee1cbd94f8890fd8d37baeb4336586297a84b6019cc534400e62c08cc3fb644210f293d0d6b26c0a77c67028af0403e77c442111ea91e1ae1c5967d8bda721d4f22f49d6c4168cc00c5f46a135876015e757aa3ad259f6b434a2ea618254ddde1f22433e258b721ee2c7d5e164c7a3a4414721bb0d51dce01897c07d2190016116aed8015cc09033ad23dcf34f38405f4173a361693505b01cd5cb0edcc63df32384060b74b1a56deb6405170473c2d2ec8185145c8aa412178750285321896b3ce19c9dca2f0b0495a128221f73b5fbe28f32c72b4f594e2705fe3b2b194e1fe0b2f5377588e2e0287821de414a15d76d97b95b6f23175f280cc15ac492f7856ae21716b2ac458780173a4ff543aed4d8ed6bab591ea6961ec2b287430c0a1b90cd1e70d351f5b46c2280879a2a4ce8b3180d0dc12ca91e08dac058f13592f3248825f21a747a24145e920a78210e01b38ddfe84e4a8e1d057db2a59b51d3ad477d6868e50d5ccfd2520ea167630ec94fb4ba545fb511ee0ec470d7240b106f0f4714572f91101ced6acf3cfccc3b5b9c850d4fe79c2bb51e1f0450c38de96cd8d6d26b3d122a4fa480f0812627fab6d45adca0121c4e5013371d7c16e2e26ac84b7fe3619237cf470ede184aa95bd221aa67dc3f226b280159079ffe42751c86c40f415379b8bf2b3539d57825f065a0d5d701079ebdc73eef5279de35610c30bf3c41a22f700ae88cae6d00d503580c75ce2f290346ab04cc494079ddb3a20aa78b65c4fd8d09b9ea8c32da916f4041e5b43ad7cca88225efdf2507c4d274f87a5241d5cb6bc0ca3b2a9709f9f4bd21b61711e51f2fe01cc98eca3726092e2961e599a5a2ec90d6d8dda866da871d25ae5f0d8c626faa3b3a9a5dd837281643f528c4d84aa27a957a69dd865b11b87edf42fa57d82a4407c063083e4a658bcc08c4c47f1aeb78aca2a197a1f35e6bad9306dfb03e4d0639400087a59d51a5a32240e6df45606fba77fcade424251ff4a88f7fd9fec2e33ab77390ae59e6f697e1d6f29fa01740cdfec883b7c43dc881901c1ca73c54a62fc0ff8f8ef266ec3566c1bf8d86dd61fe939a2ae17cef489009c63b4d59cf520fbfd00bfb83b4ea58143cafb11599e5e34f1ea27f2cc10534af9e5bc95317310ed4f598e6394107fa1e5317bf696cab9e198f9d2a81a33f400baa5070b880122690576fbbf7aa88a7c1a71627430b7e1042313601dada08eb85e44c9ba2f1df0aa17dedbcb39b0dfc14319fd78b7fd3b5eb6d446c5ab4efb040c6c5d477b6cd0842312aab78621f64811016a2a0331a51badcda69e6282af7cafe9f0d68204b3b709a84586650a4e09a6aa8e6b529b0d6d3e06278ee8fcb2743da81d876e7fb0159079ffe42751c86c40f415379b8bf2b3539d57825f065a0d5d701079ebdc73eef5279de35610c30bf3c41a22f700ae88cae6d00d503580c75ce2f290346ab04cc494079ddb3a20aa78b65c4fd8d09b9ea8c32da916f4041e5b43ad7cca88225efdf2507c4d274f87a5241d5cb6bc0ca3b2a9709f9f4bd21b61711e51f2fe0000000000000005000000000000002cd210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000003b3b11ac9f9cea695ada26d9ad517512b10425a00a2bd8856af830aca2112bf30000000000000001000000000000000119c9f7a4fd16077b1a245e34448726b0e628e8c93a11d5553849d3a024088a29000000000000000100000000000000002191c4bebc8a78c7fe8390d217c774f014d31f6bb55c95ec4fccaf58225966870000000000000001000000000000000030150357d165f1106da0883a444d850015b91098e105b249aa7489fde44e31b4000000000000000100000000000000012a301b4bc055b3517f9e86f94df8ef70969e438f0da189cf326bcd928b27a193000000000000000100000000000000012ee1a906a5bc65972529cb399955b33e8ca03f3ad9f012ae6ae99369a5314482000000000000000100000000000000002bceb0a054ec951915aa95953c452344323ac5cb73af2ba7fccbd04d750845d80000000000000001000000000000000028f9117896989b215149efab690e6c8495648fe7c889eb69e9906985fb2bc79600000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a800000000000002cd01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a00000000000000010000000000000000129280caad19ad66af3e65d4f4417f09c993cca57b2bea0f27a689729861806a000000000000000100000000000000013fd3235f23465ad5f269bce28fc4f8d5b5a04f99b996785517527fab54202a6100000000000000010000000000000000349ae226e952cdc14ef67078f3a76da8c359959a37cf595bcd15c5f2b1b8b2cd0000000000000001000000000000000006e487e8d9cfca29f2da7a4308d5d45558c20a550caf530bf13d947f46b8d6610000000000000001000000000000000125c75a378e5dd193eced008710856a877a1fba6d8da7ebe05c85a189b0646d5e0000000000000001000000000000000130996ed33bf5e37f3995bc695d05e2b3282ef29f19de87942a286146554193e90000000000000001000000000000000011d4c9186f3cf952cc1fa424a60fac90dd97cbb2c2c07d8c9597e53de775dafb0000000000000001000000000000000016f9e9e6c8f04e163c761e8cff0289085b22cb365f6be2f052d0e43bd703943100000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000002cd3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000002836914796b3af9202eb3f93a321d885de240c1f036104f0a0b653c85d3c4a8a000000000000000100000000000000011dbf3860769b655a8c0afa561b007d86a4f508d73ce21da6a8c45ffa40d287180000000000000001000000000000000004913a6153cfdd7062ac20becc4acf04bd9a5e2f982e8e7257eede53b697d1700000000000000001000000000000000006a9a55fccafced1ff3caa5a86ce9b72a5f916e85221c6c3fbabcabc81235f560000000000000001000000000000000116b1e3e177e35be3d3f43d153470d416c8c4ee623aefec8c7a91df995244fb00000000000000000100000000000000013f7413af0f26a73387c8cf26ed57208866be2185c1bac101f0826b8c3d554cd1000000000000000100000000000000000b39c29094fe14786ef2b0fc3ed8748f015b5fd97ebeeefee4aa36565096d771000000000000000100000000000000002afcb03999b265e91b6f30b76b81a2aba1a9e681a55db57cfd1589db2df8fad8000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd2900000000000002cd05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000001000f2490f6084fb518193a1d8558f62ae9c25a615c896458fe0b3373cbc3570000000000000001000000000000000127bb67837e0eaf4fcb69354c41e31dacc83fb864dd5e6f2de591035154241aa1000000000000000100000000000000002a7591d2d6dcf5e77c50410758a1e893cd3e054d7b59cdbc548fb8e0ff40f896000000000000000100000000000000000e8f306c71b5e1d0f1593c487d45f8059a49932ad04a646434b6005647e73ad4000000000000000100000000000000013b13c6b61bf6fecc421a8f216f8ab76ed7cc9da26bc0ef688083035079fdf070000000000000000100000000000000013c06d07240636e9bcfd0a303a322ff85a8979e8ad02c3401c3ce3914a243a88d0000000000000001000000000000000030a394bc4dfa947c036e1b3d920370d9d436dd82d08fc84f03259f117d5b923a000000000000000100000000000000001c2c0200e075e9d662289dfde7170695ae3004d5aac88df919ff4dc1f1c0bbbd0000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc0000000000000133210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a00000000000000010000000000000000078e7e7b88bcec70439ca6275b2fdbc3234d49b8954a4b7832f840aee2f7cc3b0000000000000001000000000000000017398c859be04316ffc9f47f08d4e86d0d95c2580bd34c3288434fd9bbc8440f000000000000000100000000000000013eaebe31a63fc9475b2c967e676ad52f012c13399c1460ade9559fcce9e1e642000000000000000100000000000000012231e37614812fc921c7e29efce3af1291337333d91bd31205936a3332654a7f00000000000000010000000000000000093bdcc384975577b3bc406719326af284b51bba10ed5f43278209cadd5fca7b000000000000000100000000000000002cefb9593058d8a2f946e2cd6f1f30001bdd725139e5c754d8fda060dbf27cd000000000000000010000000000000001040024211e5d8d3fbfe3a3099a147208e271e3a2db68ae84431c8fc41b4bbcb0000000000000000100000000000000012046db0b757be0be0a522ccd3bfc2c95e7431bc5eb0b7b0c145b51caaaa331fd000000000000000100000000000000001efe6caf72e6e56c773c2c5eb2ee7a86ce73ec942869d671d7208bda584ef694000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000013301eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000003e4da23eccf98a113facf043cea58f2f2e4e45affcedba4d5cb780980190f9a0000000000000000100000000000000002f94d72d9cf3816f6c10e820ab6e298dcbf29a44805d68042d565d3a7e570a8b0000000000000001000000000000000109491de1750d38199b28b923f959c622700d9e227e6570e15731c34003140cee000000000000000100000000000000010a836b8a76bf2c6d7960a4c5f9786eaa9cc87ae7fa31e491cc5c868994d3c707000000000000000100000000000000002a53a04c45a2df691efae71cc0abbee628aacebc157a8a4d2a258e20fe4a228c0000000000000001000000000000000036d3bfe0930372a4b516f541c7e13e04fa481b724b351bea2cefea3b0a62ce3a000000000000000100000000000000010848e4ac7b9b2a7dc4955ce2d219b11b3a6f033fb56f576a2d0613af5d4890570000000000000001000000000000000112ce1375dfdae40b30297c2004ffbd1452229ae0ae2363b1f9e2490755222a360000000000000001000000000000000010e7a3c6840b622841c7e0fefa4a2c89390e345be291bf77b21095b57d19410d00000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000001333698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000003a4698954880cddcc8acb41e78532b430b036f1e5b95375f15658fd93f5f96750000000000000001000000000000000039ecc2e354ec7a1dc2369957170aadc6942b54316e05cae6b87703bdfb86fcaa000000000000000100000000000000012b8fc1f0978d8b60a92cf23aeddc041eb0d1ab851f682c019d2e220da4ca26d40000000000000001000000000000000118e9b801e4b5580553ea4690a2a6f0630fcbc7ff70945f2f8c679dd8de572ed7000000000000000100000000000000000f369397c1474717753495dbc1559ed1be96101ded459f199139a4d5f3bfc80900000000000000010000000000000000377a9e623828da10a9116d2b6631f4d193d1643c7b5a96c69fd18ffbf10319da0000000000000001000000000000000129f950b74137e02f74b7df2060960aeeb45ba82563a2913889032d6a653a5b16000000000000000100000000000000010e3ccb796a89eac38499699299942aac912819c80241cbdae5999156ad3b1cdf000000000000000100000000000000002c9101602e1da112b3720e3637e5ef550cbbb69f6c39469900b8de44de731c8e0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000013305859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a00000000000000010000000000000000350ca49a7c5ee4d22f279a19906d04869ea8fa67f75f51a0dc0316be1e5d6a3c000000000000000100000000000000000d9b52de3092db7795672f779e54e7514661976d3a7927733ed8859e9bea7d93000000000000000100000000000000010a6bbce9418e91078fb3089f270bad29cc7ca32ba0aa6ce43801181d7869c344000000000000000100000000000000010ccaecf7dc3f7910a15ee1cca844f92bd01f1eb5a30268ca3045de4f3d3179620000000000000001000000000000000037678131146568b70a46d51ad79ee7d48e6c26289be48a41acd00014edf4a929000000000000000100000000000000002cf2714e6565f4025e2a6942b25147716ff133afd601c286cd7eddacd80b7f06000000000000000100000000000000012183a9eaaa871645b78a032bef1fdd921f4b9deaee4dc20f1d1335d8653b58ac000000000000000100000000000000010e5e333fbeadc6ee60d0bd27568ab662e00071920504b5cb7c93ee63d8fbae5f000000000000000100000000000000001cb94d79452c7657f8b9e74bc961f22670b2a447e6dceec87d5959e9ae5e4d5b000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa04000000000000024c210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000012e88e60cf21ffe366de317836b3285ee4f2897e8c7ad0722d84ba31e3a79616100000000000000010000000000000001129a8b31a1ad2adbe9d3543b7b6991a253b964d35976574f32886935f00d700f000000000000000100000000000000002309e31c7b29bbf70655f118212d7f9911e25065fdf571c492d8e419c56d46ac000000000000000100000000000000002de4e7f05c6660e64c47d565e072bd1a233635454aeedae937dbe88d85c1736c00000000000000010000000000000001239904bb705546f66e4ba2a0392ec40caab4e8950f03dbc9504133cfc81881ff000000000000000100000000000000013685ffb2385777d0c4ce7985bd681ddb54e3e3bedd007c2e7a147092996caa9f0000000000000001000000000000000025324ce96e87e0872d7a61a0bd1c6617ce861044fb41248f03349411a98422f30000000000000001000000000000000119a770049d307e9d8ab82d137b31576e0971544a8a29adf60fe92c5c9071573b00000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a8000000000000024c01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000010e96aacabfd4d549571cc02166bbaf6ec6ef7f521eedd062e5df07437f7b2e690000000000000001000000000000000109637da592631fa98b715a6f67163b6b48bfcbda3bf8ef517b27a230b1f8f65500000000000000010000000000000000129b597c1dce57a6e7b7c29ee76fee5c094b6b37e82ce07f32bcbf4456b5447a00000000000000010000000000000000304483ae7a6dade5a535f147f9b7a890ca34b62f017ed868a51a26d9cb6b764600000000000000010000000000000001191358d8f0e4a8bdedb23c3f9986188101a27314291391474b505178d943586f000000000000000100000000000000013fd41f015024b785fd2b07515e59ae27f9f39c4799ca22b7a7db545c69c51d9a00000000000000010000000000000000293d4abd597417e3ab417dc4767f9594483e2b2fa8a7144ff161ad06cf951dff0000000000000001000000000000000133f2a9e78f95c7c82d10c197f01b6f263e5cd4d62ffad3c09d77f384520184aa00000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca4000000000000024c3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000001198744e67b94d26990f929a2bc020e7de04addf5260ddd08fad3a47c4fc028b30000000000000001000000000000000109d55a73a726be238ea9363a2f0573b26ae9ed0249933de54f8ce7b17d0f0b7300000000000000010000000000000000029c1f7ae01dce53f19326615dca8c19ff178aa1ce3abed62927b86da8506c7e000000000000000100000000000000001ec5ab482188ecc07719a27ef2f1feaf886d4318c0be7af6cfb4386a7a1c6d54000000000000000100000000000000013e165019d2bce545cf54bb66455e3d2870d6fa05dc4dee2f2877ea04f74a1f35000000000000000100000000000000013f68a538d5eb785342f4325f7d07f3341cd921c94634d0d5e8b32b5363d7dfd9000000000000000100000000000000003463379f443052f957cf9ec52f562a474c275dcd9d4028905968cf99487201ea000000000000000100000000000000010d01aa773cf30ee8341b55e803413443d95b75ad65624e1a6236099b3f045a24000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd29000000000000024c05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000011a5749d4362b5dd311e54f946a5baa680a1076c6bd4bc437f0c11ab329cdf3b0000000000000000100000000000000010de5d808c2ec675df1e2ec29937f4519ad729ac28f216f3d72b98f63a8595fc2000000000000000100000000000000002208d6574cd2f46206830e9ffc44892b532dde8a9b213254bad45e27ded7e017000000000000000100000000000000002d77e7c2e34709582fcd90b5f63fc6c87d44ad0bec159c1f2e624a6c454560f60000000000000001000000000000000109840298866fa04a85966679faee5c7d8ba39b306b6337c5ca331424263ca1e50000000000000001000000000000000138aa8c109410f503296efc9fa7b06c102b6cb033a2318183ff21799f499141c0000000000000000100000000000000001037e6930ceb14ef8c42e0dc57c0614d54b991ff7802e17cecb39e57f83f76360000000000000001000000000000000130df3a062946a640fdd1a58f447887a345bee15ff0aaaa4d5fe03bc9cdb4d2d40000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc000000000000037c210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000122a0a47eb0a05c9d1071a47b8149032bed27276765e7cb6f2ed5d147b235126f000000000000000100000000000000010da7d249a237a7fd6627a508b70b5b3fde341bd360605949acfa9cc87471194f000000000000000100000000000000003f325ed03f46644ff1b94b3693d270b5a614a5a2bca3569a4930a9f603d5edc6000000000000000100000000000000002b567b09b2e27ec81a2656bded994130d0d4f73d2ab9c1c8af9386b9c2f3c3b6000000000000000100000000000000001e4a90d7453b7f8dedf1c84ae45768398d4a41d0ca9c8f43acc32bf8ac1c0b46000000000000000100000000000000002f7f758255303d99d9b25d2c467fa9d2ec43e37e2bbab2dc676d1694e313e6f60000000000000001000000000000000014bca6f9fe7b224c0d5db5000b55accba7e240aa53afee83da07fcb4db035c5e000000000000000100000000000000012d218bb7b8ab1bfce296193010df0afe8c5faa45bdd66e8dd97caf5bccc152be0000000000000001000000000000000010d830e51d4c0f773c0a6254794b36dcd7fe091bcf6f9a6a8a768cb738757a710000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a8000000000000037c01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a0000000000000001000000000000000127a737c09a588d2c01b953efbd42be758ea2a1544662ee21b6a717cd67f90fa3000000000000000100000000000000011ec70ea0802f573a11f67132d4fb4b16233cd29340c1e8a0f24af2bef7fdd30d000000000000000100000000000000003c808a3ef8399fed98053f625f112eaa0110d6eba94d73cc735a2362a6ea346a00000000000000010000000000000000135a5043a12adfb6fe06371542626dcd4c6ba025646c80b76877d297a8c35905000000000000000100000000000000001d313a2229e84cda60e96550693c379c4d9a32fc542a6c20b6b1be5bb711545a00000000000000010000000000000000209f198d944d33120fff4fda14c5ffd3c6dc636de14fc00b21c820b6381616610000000000000001000000000000000029db72e17bd4489558b5a665a6c7f766051f5c53897d23bf63b3934d6bd02f3f000000000000000100000000000000010821de8af803ddc057187349ee885632b80fee67d960fa25fbf65bca94236b77000000000000000100000000000000003a3c1807f6dd6c18206fcb3031feadc7381d0a6815ef84d8459646f7b5be57d8000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca4000000000000037c3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000105c247bbd5acbb1cc072db559358598ef44c3464f164e8c07bd44375d87444230000000000000001000000000000000139d3377a8796e62af2d036fc37380b664c1804d556190286dca925563c9c82020000000000000001000000000000000003237004072ac1f6bb142b97ba9d49e995dd692f43e56adfdb60b40435411b0a0000000000000001000000000000000029fb939f69bd44d8ce9ec05fdbc0e7b5c4a321fdf8768e3a4cd8e29b1aa5fdbd0000000000000001000000000000000003f93bada0e2d02ebb58a1615f869de0aa419917434b51379028d1dbc9ed7d3a00000000000000010000000000000000117757c1fb86bf93d13cc1ae4406f12cec7e2b9d553e5308e6bcb92240fafc7a00000000000000010000000000000000233a3386f91346b72a348bc3cb89a60943246b70594db16789f33cf2c0d1fd75000000000000000100000000000000013efb5835cc9609e5619e2e6693ecae07481752e45187ca2ab24102312fae38ed00000000000000010000000000000000218d1ef26e073bd5d517e0ff029df2e6cd95bf8e7e83f29302d0d500fbb80367000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd29000000000000037c05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000013cbd42510d57fe865e567c51ff22860558a77035406caa9bfe858bae087658e4000000000000000100000000000000013bd7df4426ec5dd4c3da1c7be30dbad6a7c157516d88f43c64f502016700f8540000000000000001000000000000000006bf8867786f696ab3b500f99c381accde585795c19b1af218a2383fad9045fd000000000000000100000000000000000b831d8b9c4f230891dd5fbf3bf76d4d38e7ea1c8747a1fbc314c66f1d8b299300000000000000010000000000000000265c3ccadbb106d0c9d2a1b199efeb199c2052ae3422d9098589146e7be02d300000000000000001000000000000000031d1bd2825b6daf30a349a0c92d0e49b7de1b45fd2e49b7b320c1ac0d8458441000000000000000100000000000000002ed6cad074a7117d0da15d11fc95581de0b25eba7e5220673653e8059833a161000000000000000100000000000000012abc977badad82cb2440429ce45f4aea51d5515b9465ed1d2e10229efda40877000000000000000100000000000000001c074659ca7ca9a1d4ada27ad089f55507ed767de9c4baa59b0634fdaa4e9a73000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc00000000000001f1210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a00000000000000010000000000000000091fc5a5a528139cd8422eb3802b1885bb34390fc2725bce0e3ff96ecb625df60000000000000001000000000000000116bf01076422050004684342e180cff69317951f848caeff562c14c151f34d2000000000000000010000000000000001175470f699923248b6ca119333730ed54a21728d2ef8a4f5d8b5301a5825ae0a0000000000000001000000000000000119647d17cd0107e8832b443a69fc18ac459975f09cfd05e55dcf612346f14302000000000000000100000000000000001f7322c7599d3f94166d74d0a3306723c5fde3990e3eb947226d3d7aa9dabc9800000000000000010000000000000000310cc1ad3b1dc8c2cf6c3b31b6e589e79c2baaa508557d157e70e7e96a5770450000000000000001000000000000000007f00c784cfa955d375ea050bef18bbfc356dc20acf8a07f13009ebdde9dedee000000000000000100000000000000002eb00d80e3d4d05473dfe12bce15a3a58b1b9f0906894c2442b5ab412df910a7000000000000000100000000000000001efe6caf72e6e56c773c2c5eb2ee7a86ce73ec942869d671d7208bda584ef694000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c00000000000001f101eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000001457ec047b02e96e4041408829abe4b2991444c5436e328c86d74a78744b23c900000000000000010000000000000001161f0279f697057235f1ddc0aff63cd75764fed20f9a7f2ae74531c74256be3f0000000000000001000000000000000107625fc8209cdfdc46eaaf8897bf3a77dc79152b865a1984cf0728ab7de08be8000000000000000100000000000000011faccd508c3515812927f2e47a0d13730152561f7fad6574cfd99564aa57c05d0000000000000001000000000000000017654de9fdc595866397a0731296ecb10eedf4abab8abca6ef66f0e4cb1dd0450000000000000001000000000000000007f86ce78ecb7cf6eb055e02082f627802ec5707d49941491df26bf05d47fb6e0000000000000001000000000000000030a9b496891a52f38c4545a4e37d2405f955e57bc0d423abc4114c8ef578ced000000000000000010000000000000000243409665f9584c68a364ac4eeccd1529d9132906fde706879dd954f16f2960a0000000000000001000000000000000010e7a3c6840b622841c7e0fefa4a2c89390e345be291bf77b21095b57d19410d00000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000001f13698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000000203938121b5da462e3a05a35e4826a688c7efaacb3cf84210ae62763b168e6ce000000000000000100000000000000013c1eb49a18b228d45a5fa2bbf050ec5dfe6d83babdb98e8369956236cd205e000000000000000001000000000000000107a965b733ed8b1c98289d43e1082b31de09aa937f7e2fd9d49774f38936b46a00000000000000010000000000000001143f2b05fe803644c0cb399f7e30ee2fa0b8db455df659ce53df0913bf67f60600000000000000010000000000000000203380091d8e82cad8e96219e061abfe05511e291f5d64f477d0eb49ef3f00b2000000000000000100000000000000002ec527a2602eb4d32d7cf1fb3eabe78e25c1f4b369d7e5fdc01bc4239395a34800000000000000010000000000000000153ad5f74984d92235a1f9c1c9ea1007a99306db56b90d85d52f0e1b8a4ac6200000000000000001000000000000000035c4b38e12f89c46b89ede64e4cb5ed89689ee549dd97e8e3fb2a7de27e28494000000000000000100000000000000002c9101602e1da112b3720e3637e5ef550cbbb69f6c39469900b8de44de731c8e0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac159300000000000001f105859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000003c24535c33c76a2a8081b93566f849e99a1f8cb3ef1b2475a689ed08012e8fdf00000000000000010000000000000001138bd9b9deb91ec48b21442491431fcd73788c50f356f3f5508d11775cd8e1220000000000000001000000000000000119d8e1409a54402f84d35127ed3d975b4e24c30a7de20dd95d851b34ee4f192400000000000000010000000000000001103b1a2c73631b02792fc4ec797d3253a2e68f04f470b41595ad9d45b877a45d000000000000000100000000000000002ca65f4942f717c9e52ed9299604ce321b0c51fffef617a2177db5c506df976d00000000000000010000000000000000122388be730c12d6255b0827f3352b8c5196a3ca13e957640d65f5eabb83aea4000000000000000100000000000000003e760d6a7f7225e164bb04d5773b12d657b9b232b1029ae3b6ed6369161cd0c3000000000000000100000000000000001ada48b6acdbad286c90690dd27b6716b562a65729a3087cdedde01601952f36000000000000000100000000000000001cb94d79452c7657f8b9e74bc961f22670b2a447e6dceec87d5959e9ae5e4d5b000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa040000000000000208210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000124687f81d7a1160e1146e482d8db7b8cfb2e91c20e4d4b3bc0e00040915a99a60000000000000001000000000000000100ab9f793d5dbb7f3064b3a4b2d13c4b5c2db5c5c231b514870a5481e76aae56000000000000000100000000000000012fcd1d32312a2f62b964f9311cdb1de1339d7f4c6821b4faf4097fdefe02c3910000000000000001000000000000000036f6a21ae9c1e1b739a51ffceff0d9487a4a4209ae5e1343f0c8ab146cecdca200000000000000010000000000000001320738fb22d315e99b3240df560a5ad852246234ac4448e9fe37f536cd854cbc000000000000000100000000000000013e73cf6bf240f5efb59cab664585ea1017a6a87657ae98ca9f2bebb271e925940000000000000001000000000000000128a1753687f3b31ac3a45cedf4e3e2b3392960f84839487e5bea6e23dcf7153e0000000000000001000000000000000119a770049d307e9d8ab82d137b31576e0971544a8a29adf60fe92c5c9071573b00000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a8000000000000020801eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000011318a03a995e9536d9c2ec94925621cb3d08d0af3ad7360a58a35c53eef243480000000000000001000000000000000121aba6c9cd8d6e183eb859775ddd53bc885c81ff447e4a209c4eed2e7c069183000000000000000100000000000000012cd8bd8dea9d08d2ce3e2ac3d1acc5924b38f732a3575c9d223ceb8de0634bbf000000000000000100000000000000001f070fd67bcca309f0272f4c3c3e86ef8cd377fb4d5fd3a1599931f34cef6a1e00000000000000010000000000000001106159f1f50fbc3dec9d90a280ff4887358a0862009178ffdc52d481a54df24c000000000000000100000000000000012d1dd8e01137400f53c6cddbee6c3a281327f168fd30fc0802e1c544f245db03000000000000000100000000000000013303e666d9bb52202b8fe03e09d37ffe00a0b572677669777c2e681f5ebacb790000000000000001000000000000000133f2a9e78f95c7c82d10c197f01b6f263e5cd4d62ffad3c09d77f384520184aa00000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000002083698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000001052dafc1135bc38efa6ad4f981ee19df3eb86db41e2a341303e7961b22ef932f0000000000000001000000000000000125bb679561756d2e53711a68ebabce756308bd1fc3e9efcc3f84745f49883d2b00000000000000010000000000000001375350ea2164fda1937c51504b605f66c1e4fc7f39f134c80b3a61a2e1bfbc2d0000000000000001000000000000000014de02715864e5a26a1bc819a0912bee5030b5d439af936a4fe4e5cffad12d140000000000000001000000000000000112a54a1796874002542049cbc2bc75bcb5663119f4323843b0cc8967a3d62547000000000000000100000000000000012cd1b6bcd16773dff79038a09d89d8ad6eeaaefa842cfd76491d8f80ceef59f2000000000000000100000000000000012ab711aba1f567bbbfec429a7fc9f86012c2db5bcb52644c172a0eaf34e8dd2d000000000000000100000000000000010d01aa773cf30ee8341b55e803413443d95b75ad65624e1a6236099b3f045a24000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd29000000000000020805859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000109a9d399bfeb00356fe37c68dbebe40d1f46c627f2924f48c2dba6d3ba59d6c9000000000000000100000000000000013a4b7ed40d658cf45b2ab013f9925e11116e980439bb0b62a6a209e57bb5b9af000000000000000100000000000000010a733afdaabc638dca9b6ddeaf9b0e57f5360e43661df2ca266a5420de03ba6d000000000000000100000000000000002e7c2013c27959833c6831062611abcb64f01eb75f052b55a4d99aaf7049fe170000000000000001000000000000000110296d02eb69807aced7201570a499f9a7f75dfb161c34f96e6e2c7b5fa1ece80000000000000001000000000000000115efea86e86f42c6791a290548782e0eadeb5f831d6520e31795714df78935ea000000000000000100000000000000011d102739b279e795b8f45a1db7f9237c8b0d0ddf307de08f644e6062650cc1100000000000000001000000000000000130df3a062946a640fdd1a58f447887a345bee15ff0aaaa4d5fe03bc9cdb4d2d40000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc00000000000000ce210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000013c01c680a7096350879a9a4c01532ff34bf4b77684f82ec5c2ac27a33d5a4884000000000000000100000000000000000c3441dcd7e4fce9bbd83f06e988a11069b9dcb556ad27153ae2e0b35372bf69000000000000000100000000000000003c4c22e5f6304ff0e614687105a7b2e0199e0336bc07b31624ad150d48c8dee300000000000000010000000000000000388df9d3f60153ac186ab48ee9f075cc9906fc63c996ba6f4a6fac08969429c8000000000000000100000000000000011a6e200185f60fd20b32b8c4c7fdecc3fcc5de3ab9d594a04e2fc13f8211b5960000000000000001000000000000000129706dfd2c21c448d21112f9dbd9b343cf685171c83e277fd27d6bf11b11fdfb000000000000000100000000000000001a62dc7905a7174dccb8813321eb5c0e025dfb76e27b42c714fe4a47c269d264000000000000000100000000000000001f80d53c628193c3432663f1bfb2ebb744754c7d205a935fedfc9686dc28a30300000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c00000000000000ce01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a0000000000000001000000000000000126b0e515b6adf9361501a406add22706015783cebe1c06db33ff334f9dc3b922000000000000000100000000000000003517312abe8838a6bb9a26c863a43bb666eca6d635ad9cdd189eba722a3ca30d000000000000000100000000000000000f08df64438007ec9b0ab60b9e8bcf104c2167c52445043ee7cddea5c32fc75b0000000000000001000000000000000007540de0cfc988b38444665787649fef35c4f140db0f17668d328066daada48d000000000000000100000000000000012db474eabd805d3794d3af60444f470c6aec5c8d6e84b860a54a7676b296738e00000000000000010000000000000001347fe8602e309a2a014a78f7cedde13192c404c2875221e4f2d3d1b93a82eaec000000000000000100000000000000002d5a06629a52a08388ea90cf2fbcf580dfa87ed1f479cb6e32b3d2e646373d7a000000000000000100000000000000002126fbadbe57c19910b2e514be53d04adac4ddb21920e7c4a4a940b03b5c1431000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000000ce3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000011f8f3b0069592e2b965b4a28409b538f5919dab139689b7b0608183a8cb9a90a0000000000000001000000000000000034a134999125f496856d91dc9909d5f62f3891ce1f0c8687237b265574735943000000000000000100000000000000000a8c1f717aa91dfa1138d1c49e3f248b25e52e8109b945bae8dcaeb91706f2a60000000000000001000000000000000014aa2e868175f7f0f54ef6698bbe1a91147dd06ccd54010d4d638481db4845d20000000000000001000000000000000118a884c813b3b24977667658732759860c72c8b51e8190eb765dbe9d7a3fc8c2000000000000000100000000000000013b75a7a51912cd3dfdaba07aba6c0b106e3b8242453b57aeccb801ba1e0019a80000000000000001000000000000000024c44711bcc797fbf55e340e135a9bbce64e561a5606a16ef089e434bbf071f10000000000000001000000000000000022414d74a4aeb2ea7fab9ea430e1ee96c810c8624cccfdaf73a936ff17a90ad7000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac159300000000000000ce05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000013ab0f0aa6b8be672633b486d6bf46182da028ae1c4b40b08b86f9f709843c303000000000000000100000000000000000215d463ba40b8a7e671c2aa90204a6dc3ef67e4bbdf347b96a745c54c816b2a0000000000000001000000000000000029b9b5851c2c1f0f32dec9d6bab0d86ab0c7c4beff4d77de4688eefeb6e089b10000000000000001000000000000000016639ea6c6f6a60de5d88cff1f62325b779394fab336b14544014a460fde9db9000000000000000100000000000000011ae56cf3c92e4dd1b1efa1155cafa5a4afca21f01a324468faa7e63845f6859c0000000000000001000000000000000111fe1ce4e351c63de3b384c8355e4916c4f0ae947ed7967061ca03dbdf120a7800000000000000010000000000000000066a3cda9d2eefc2e9577e15f0048c7585ca806fcb86f408520553c7466f2f88000000000000000100000000000000002287c5ee26f370c2e7ff01c9bbb1f8289700768c3a9c196093408b2e228b8d670000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa040000000000000006210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000107a1c995da52ebf4e01bb34007f83e9d88acd03b53fbdf66c0c853e715141097000000000000000100000000000000003f6fb64f3703e8c265e2351cac88cc41589eac7c6dd269113a03d0ae1bd7694b000000000000000100000000000000003d9b68bd86e655cfb4b9615cc846604c8a6ae8608a03c1eb9d81d1eee078161e0000000000000001000000000000000131fb43b042d3b21dc7ef77b80a21c75dd09ad21e143bc9974ba8f2f4d44beb630000000000000001000000000000000117729a14808a868182eafd20ff5b6f88116d06cf30e67b8f3aa799cf0f03fbb000000000000000010000000000000001073b9bc7a8de0c6caa0d923ecf0f3da596119ec04888743a4fd2ba6098714d190000000000000001000000000000000115164458773a3e65d9b832e6c8cb99aa01c8754b0423c34a8e9d3426b3207803000000000000000100000000000000011388497ab1d8e11f798cdb87477112fc86743ec1c17d958b9c8b1fa31204017f00000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000000601eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000013e348145b960cdead66d665d9301e78f21321b0ab1cdbd9be600cf8f368b706c0000000000000001000000000000000003725e4636686e13c6c22b12188d5d41c07a717587a445a2b41997afd8e673e10000000000000001000000000000000002f8d71fd09589a95b7daca7793fdce317b22ab80019346949d5760870a785930000000000000001000000000000000139ff0f482b4fc3f2aac96ceb26e6266331ab2941c3721fa8236e5ef99ee9eb550000000000000001000000000000000122ef55143ea33a613471cc59a8960d5b6e2bdf308c33e9b4f9d751db351c2e18000000000000000100000000000000011d678cf05ba9c75bd345becc3801fdd53d7dd8012101cfe667f694be7db3d6390000000000000001000000000000000123f64447faa25a98fe9ff9f3b110dca95f2a9084d06262b87ff720451e12d519000000000000000100000000000000012e5224e7ced286be529fa52e40b623a4c9da29058d8f8fc52f21a1517f6970ef000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000000063698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000125112a6b754c1b36b6c616eb3352c7faf4cb6df14aac9900f1463332069eb64b00000000000000010000000000000000193c253e0acdf25295dda76be026f8a579b1f8cb9d490a3ba5357d9f31c822ce000000000000000100000000000000002cad3cb61a6e04c1ca56d416564d094028311917068c0393b7aec7cb8f21bfad000000000000000100000000000000011e9e6fcc4d046b6ece5a39ff4857a24e361da7443d88acf5b888d4f0f94c982a000000000000000100000000000000013349098ad6186b454b03753f424bb748ba896d29d7cba2bb4b6c185d469d28a8000000000000000100000000000000012f540e1e5f17acdf10b5d03b436a567215bfde02c1e888f8372df1c70f1b0e56000000000000000100000000000000012cd57cebd8e34e8d39b0b3c082ed6cda610ca949fc57cb430dabcc623d29e7ee000000000000000100000000000000013fb6f88763c5add9d2695eb520d52a2807e2f206fae591fe864863251eb3407d000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000000605859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a00000000000000010000000000000001305188f48cf37e07fa1c31e8304f8007d52212dfe51f3c8aefa8c878a271943b000000000000000100000000000000000b8eda727ec1881d285550f98b156de62b836da534c5d7d405147001b0f78c6f000000000000000100000000000000000ae2d249801eef744c4e5083df1e1ac4fd7ff465a453a0a61ef17ab0964d93540000000000000001000000000000000120b7cc620e7f34444fb093de69fa1e54a48cf74b5420b2fda7886fdad6042ea0000000000000000100000000000000010a7c9a91615b2243db4ee9a5889466154b6bf41ee1b8d9d59d67f5189c66fb1d00000000000000010000000000000001269393d3c5f10726b37370a8252afb642bc0949d3db0b26d82382d5f0777517200000000000000010000000000000001136d6a136523c95a82c3df4f34d3e470e8c29a09e6b97070fee56000a6013cc3000000000000000100000000000000011828a2930b8b248d2e655be73fb9f08f49fa382e85eeb55c3ecbc9f0a19d954b0000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa04000000000000003e210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000013e8c98a4318a29845416f508e7313299ec1f533f9bc654c5e2718f313bc00f3d000000000000000100000000000000002da29ac8268b4a502e1d84577d5c751d0529f9df7ccc78e37e29380ac617bc37000000000000000100000000000000003d70a26c4078687ac1e5bc08dabba43bcf60865a22fe05102a27c68cc393e37000000000000000010000000000000000385298471c8f5f02edcb4d67b256b0b501ade2d45092ec7be88aa2ce64d279100000000000000001000000000000000003c8ac342ed96b7e89ec96f97f76650ba940558dbc967ecbdeb6e20d3a5315d90000000000000001000000000000000038821df9b86e79ed601c263a421720e2adfe16240514d44fb0d8b87479e6910f0000000000000001000000000000000115164458773a3e65d9b832e6c8cb99aa01c8754b0423c34a8e9d3426b3207803000000000000000100000000000000011388497ab1d8e11f798cdb87477112fc86743ec1c17d958b9c8b1fa31204017f00000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000003e01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000011da90932af0db07633216d8654ba0e5b31b0e484e834c02fdf467fe1c821d5f2000000000000000100000000000000002eea761ce3027eb066a08e371f1c1796b4c443c13df4823f835833b4474ed6f20000000000000001000000000000000010425b64bf99eb5aafa02d17438b78c047285b74e3ec81f91b242b3cefc647b90000000000000001000000000000000036f7d187b82356c1f27f878e526f9b10458935be7a73917d160ab4a0190d7a05000000000000000100000000000000002490039f77d96f8f0417737d9620253dd6a2d7923c2a0887998ef7f8ff63d725000000000000000100000000000000002bca0e4a39775163593f2e9b6a267a91fef45e76e70d8093124bbf9dea7e444e0000000000000001000000000000000123f64447faa25a98fe9ff9f3b110dca95f2a9084d06262b87ff720451e12d519000000000000000100000000000000012e5224e7ced286be529fa52e40b623a4c9da29058d8f8fc52f21a1517f6970ef000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd000000000000003e3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000117c15d5f4b2348e37a63de8ee4f4c58ba46e270948cef309995bff8c595522b70000000000000001000000000000000010510ccb1c49c6df88fa11eb69e420b379cd127b60a5ce9989cd88e89c087a11000000000000000100000000000000000333de200eb7f5647b3c9185981961c818e116aea03f76394b18ab2f66688420000000000000000100000000000000002b1c4722e2dcf59bcd735ca7d202176b0d9e5439d5d91de4e7aa3ac4f4ff24c40000000000000001000000000000000036c9a258014f26c76671a1b427ad2a130d1c16dfddf8ffec1a87531e7daa6ae2000000000000000100000000000000000acab266f1135533e80058efc0c0ec4b6e4f9b0af8fd23b3faefe0a180ca9e99000000000000000100000000000000012cd57cebd8e34e8d39b0b3c082ed6cda610ca949fc57cb430dabcc623d29e7ee000000000000000100000000000000013fb6f88763c5add9d2695eb520d52a2807e2f206fae591fe864863251eb3407d000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000003e05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000116063a6e8de14437a308c175fcfbb751e7b1fde624983f2cbe3cd5134d07f2de000000000000000100000000000000003cca5d153e63a8604297020b1e4d99e2b4574b4d3e1f5cb11146765b59a55939000000000000000100000000000000000e13e60311a83f2ded551d17deefa8d05b35bc3cba1aba4942e45961f8d9e371000000000000000100000000000000001e28576b751ea809d54080e431e34bde6c3b9468b1a49ac1a1c80a0b908a66220000000000000001000000000000000014b0623b58268a94a31f171cdeec617b021ff09ea55ad1e2a2ee677b79e65d5000000000000000010000000000000000041749b13a37f0a21f9d529238fe4938b99fee4fbc9a2f593c80ab1db189602400000000000000010000000000000001136d6a136523c95a82c3df4f34d3e470e8c29a09e6b97070fee56000a6013cc3000000000000000100000000000000011828a2930b8b248d2e655be73fb9f08f49fa382e85eeb55c3ecbc9f0a19d954b0000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa04000000000000003b210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a00000000000000010000000000000000259e630ffac87209d7d3b0eab7dfebb6d3a7c53b1e8117e90bd5fb21ab2a836c000000000000000100000000000000003c3ef14bccd33f77d76996a62f7e5e84e1b07f0a7893b215563f7eaa1076334c0000000000000001000000000000000134d5aef0c256d0d047ba60216d2981a0c459c989b0dab307c59f525f4f86cd5400000000000000010000000000000000385298471c8f5f02edcb4d67b256b0b501ade2d45092ec7be88aa2ce64d279100000000000000001000000000000000003c8ac342ed96b7e89ec96f97f76650ba940558dbc967ecbdeb6e20d3a5315d90000000000000001000000000000000038821df9b86e79ed601c263a421720e2adfe16240514d44fb0d8b87479e6910f0000000000000001000000000000000115164458773a3e65d9b832e6c8cb99aa01c8754b0423c34a8e9d3426b3207803000000000000000100000000000000011388497ab1d8e11f798cdb87477112fc86743ec1c17d958b9c8b1fa31204017f00000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000003b01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a0000000000000001000000000000000030989bdfaf51ffd84e534bd5633de9aa791763d548b0d75ceb76a1444e9056c2000000000000000100000000000000002223d622d967ac85abb0460fa960d6afb0cd55a287b605ae6b2f901bffefcfd60000000000000001000000000000000115be6008274581f40802b29ed0284459a661e6a5441e9e40441f6fd8cbb75e550000000000000001000000000000000036f7d187b82356c1f27f878e526f9b10458935be7a73917d160ab4a0190d7a05000000000000000100000000000000002490039f77d96f8f0417737d9620253dd6a2d7923c2a0887998ef7f8ff63d725000000000000000100000000000000002bca0e4a39775163593f2e9b6a267a91fef45e76e70d8093124bbf9dea7e444e0000000000000001000000000000000123f64447faa25a98fe9ff9f3b110dca95f2a9084d06262b87ff720451e12d519000000000000000100000000000000012e5224e7ced286be529fa52e40b623a4c9da29058d8f8fc52f21a1517f6970ef000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd000000000000003b3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000030d5d69e75f7c38898e2e32e3865a45d35d110abf7f2399f43c1e35e291cd44700000000000000010000000000000000286f18a13db5e06662615470a95ad007ef6a7dd703e1cd3a360cf3909f6e7a5b0000000000000001000000000000000103048166df0b7276179d7759c2881c253cefcc78e1d7b4dcc1b32ee0d37fae1c000000000000000100000000000000002b1c4722e2dcf59bcd735ca7d202176b0d9e5439d5d91de4e7aa3ac4f4ff24c40000000000000001000000000000000036c9a258014f26c76671a1b427ad2a130d1c16dfddf8ffec1a87531e7daa6ae2000000000000000100000000000000000acab266f1135533e80058efc0c0ec4b6e4f9b0af8fd23b3faefe0a180ca9e99000000000000000100000000000000012cd57cebd8e34e8d39b0b3c082ed6cda610ca949fc57cb430dabcc623d29e7ee000000000000000100000000000000013fb6f88763c5add9d2695eb520d52a2807e2f206fae591fe864863251eb3407d000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000003b05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000003f9d08fb83663974f85f8da896ec81610cbf21b565b7d53e439d7f2a19671ef6000000000000000100000000000000000e9f82d782c291b56fe49e01000e8ac70c16559d6ff32a74a8650a963d8f22d50000000000000001000000000000000113e474399e9db14d77a88ddb7a1a31a6508dc7a3aa8e79fcb0ef614816e7653b000000000000000100000000000000001e28576b751ea809d54080e431e34bde6c3b9468b1a49ac1a1c80a0b908a66220000000000000001000000000000000014b0623b58268a94a31f171cdeec617b021ff09ea55ad1e2a2ee677b79e65d5000000000000000010000000000000000041749b13a37f0a21f9d529238fe4938b99fee4fbc9a2f593c80ab1db189602400000000000000010000000000000001136d6a136523c95a82c3df4f34d3e470e8c29a09e6b97070fee56000a6013cc3000000000000000100000000000000011828a2930b8b248d2e655be73fb9f08f49fa382e85eeb55c3ecbc9f0a19d954b0000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa0400000000000000d2210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000011061ad183f233217e825ef678f5dc5dba554c665b6c8b4f98e56ed3439fe276700000000000000010000000000000000319fafbceb3a881077a6d5da783006264253c2d22eeeef02f9ccc2627a9264c50000000000000001000000000000000120e7f08201b4f0612980c01c2cf19e12c3b4b66dd2b864867607251243f990c5000000000000000100000000000000013d97be742703a02cf8b6b83ce16cea88447ca0987638f5084e5178bd25648c00000000000000000100000000000000001a264e6c1677381b172a4f77b0747b174f72804f86d3201e46f75dc7ca9daef40000000000000001000000000000000129706dfd2c21c448d21112f9dbd9b343cf685171c83e277fd27d6bf11b11fdfb000000000000000100000000000000001a62dc7905a7174dccb8813321eb5c0e025dfb76e27b42c714fe4a47c269d264000000000000000100000000000000001f80d53c628193c3432663f1bfb2ebb744754c7d205a935fedfc9686dc28a30300000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c00000000000000d201eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a00000000000000010000000000000001334e34d3021c2082694372d381ca4c176d88eb37f8c562228d66df85f4cad95f00000000000000010000000000000000330ac33254f67093e02859eb110444c29b3ee8d43e090f082b88698e2ac28a16000000000000000100000000000000013dd7f2a42ee040ceebeb8caa8173de4da1c66606341e3837de05b54a2ddd6d100000000000000001000000000000000135e0a79f8537eae07bedead762f2bf0acf090e3dca1db411dc292be93e5716f30000000000000001000000000000000014c16bfa48a5352a207669c159a27d73de62e5b8f476ee9a72071f145a265e8c00000000000000010000000000000001347fe8602e309a2a014a78f7cedde13192c404c2875221e4f2d3d1b93a82eaec000000000000000100000000000000002d5a06629a52a08388ea90cf2fbcf580dfa87ed1f479cb6e32b3d2e646373d7a000000000000000100000000000000002126fbadbe57c19910b2e514be53d04adac4ddb21920e7c4a4a940b03b5c1431000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000000d23698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000011dedb2f7d040c34ad977d89e89747863516d54d96fce9f0548513548f19d5772000000000000000100000000000000001e5243a82aca004a8ec2ed6e0811f63240030baf6c127e2543c0dc2e5e909f1d000000000000000100000000000000011a1db1e99e3a50d2aa6023c60c230dfb9b407b1b53cd36eba4eb169330fbb52a000000000000000100000000000000010730dde287470fd3266ea32a0031cd3568bb134900fc080cf9d3d1419137084f00000000000000010000000000000000277a92933415883dcc6e879b0b9a43b3d0f1b58fabacd869f0f913d03ad91998000000000000000100000000000000013b75a7a51912cd3dfdaba07aba6c0b106e3b8242453b57aeccb801ba1e0019a80000000000000001000000000000000024c44711bcc797fbf55e340e135a9bbce64e561a5606a16ef089e434bbf071f10000000000000001000000000000000022414d74a4aeb2ea7fab9ea430e1ee96c810c8624cccfdaf73a936ff17a90ad7000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac159300000000000000d205859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000102e4ff7db22363e519b57a48ef8d6666493cc91274fc9142fc7720ec54cf02dd0000000000000001000000000000000023a780d6bc42a264cf76c6d6dbff0f46aa754b7a805b21021cf8ee0ac626797200000000000000010000000000000001031ea70196372691bfa7265e03645cb723e3e940b25d6a501f3e6f47dbf626f200000000000000010000000000000001167d3d3ea96158348992d2682344b0193a23873dab3211220b45fe14d8e512ca000000000000000100000000000000002206ff03d9e41c651a1859efd34a3a7ba6594308893be6bdbd65be5e52a7ef1e0000000000000001000000000000000111fe1ce4e351c63de3b384c8355e4916c4f0ae947ed7967061ca03dbdf120a7800000000000000010000000000000000066a3cda9d2eefc2e9577e15f0048c7585ca806fcb86f408520553c7466f2f88000000000000000100000000000000002287c5ee26f370c2e7ff01c9bbb1f8289700768c3a9c196093408b2e228b8d670000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa0400000000000003d0210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000011ebe319d2adffc420b4ae2c1280b7bc4b335c246bd2c72db9a5564b406674fd20000000000000001000000000000000130631abababbdae99bf57258a2a2597912aa4bab2542f021d349e588141f3c5000000000000000010000000000000001284fc9a4f0bdd56d4359175c58b2eb50f0c1f648790918bbb79a76aae737e9d30000000000000001000000000000000133ca046b3189edb293fd4626982f6be79af715a09a3b1ca14c662baeeb66574f0000000000000001000000000000000036463f78099572f67356e60503bb9fa46cbc06b23911c79a7b2e3b7efd81ddb8000000000000000100000000000000012a696d5e838e49553aa8b6f367fbf00c77dd0e3e61dbb640025e72e4a8294f970000000000000001000000000000000036351b04b6caecfedf3c14bfdb51755ab79ce77cc4177e6b5fa227975dec9d8e000000000000000100000000000000002b7e82a5f40981ab48939e0ed9ace58538539a5029146249a4b1d227ce10efdf0000000000000001000000000000000010d830e51d4c0f773c0a6254794b36dcd7fe091bcf6f9a6a8a768cb738757a710000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a800000000000003d001eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000012ea3ffd80830f8f2c9ef52711e956b2e0d898ef53462e6fdac20c9799aaf727f000000000000000100000000000000013f2161852b4b6be1664f2546d47c8215c0ef8474d951cec80983f88c3acd2ead0000000000000001000000000000000137c501a747f49cd0dfb53ce2a7d17987acb3e8693fefcd8c5808116cd40205390000000000000001000000000000000128a3dcc1c1a9328ed5440cb53a462e4e8fef708c71b3ffc60f6d5d5e8daf182300000000000000010000000000000000284cff448241097a1173e34716dc9cb4fb956ee30a4394f6dbf49cae6d589ecc000000000000000100000000000000010fa6123614a3e759cd1355dc7d49f5a33d3af9101f9352a5a0cb8d44d6b44d6f000000000000000100000000000000000b319537240b590452ea0ace8620438caea667a043f01ff96f834756959e6d620000000000000001000000000000000038a065186562002e8346a156b8c8d7c4bf0faf4d01c9080f895b642e40f72ad0000000000000000100000000000000003a3c1807f6dd6c18206fcb3031feadc7381d0a6815ef84d8459646f7b5be57d8000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000003d03698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000012d3205fe3a4c3714d69a831f88d285118f78c7efc62543104f5c9a3f8da6aec2000000000000000100000000000000010ff436ebbe8b883234024ebe5206e5ca147ca50824b21a92da1a9a8ba9c636c2000000000000000100000000000000011e6713d0a01163add8dfd9ba67fc180cc78e25f281ecc39df34341c9c5a7924d000000000000000100000000000000013671d12c22f662db98332857f65afc378bba71c8dbf6e9b78b9d70d8fbc4bee2000000000000000100000000000000001b2531527c75b0be907480acb3d3dae7934bafd16298a98054048c3f4d0f08a800000000000000010000000000000001301e916a5f37efee74bccfd391b2bf1d22c3eb8757408256d064a1cd5bb16f4b0000000000000001000000000000000035fbd4d4c647ea71536043b504e7484d537a7ecc2d4f7e930e9121b42dfdf325000000000000000100000000000000002ea06a800c8920272247fae1ae73f4e5e91bd34bb333ae460ebae0b790d19eb100000000000000010000000000000000218d1ef26e073bd5d517e0ff029df2e6cd95bf8e7e83f29302d0d500fbb80367000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd2900000000000003d005859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000122bc6428cbf23876f319911672b2c1f71efb3c3ce19b31b40b7e60ad7522589d000000000000000100000000000000011a6f01898860a536a6a925e56ffec2e88d6b71e5bd31cd24a02b63777e6728b600000000000000010000000000000001157aa213113fdefcd5a762d2dd2ae7c520b21db78053ab62788f0189959fe2c4000000000000000100000000000000010c97bb0aa5eb37f2be168479ddef48460684e97b424480463549224155e8879700000000000000010000000000000000241dccc1346c247420eeb4211e4f4068d91b8f246b056cd681291d6b14b213df000000000000000100000000000000012eae2ec73dc0282fa547de1a4ac2218c664310a1cf847ba8bbddbadfe05ae1c1000000000000000100000000000000003b41d25528509731df83a1f9940b0d5926a4c1844c8e2ccfb8a4108084745683000000000000000100000000000000003305d1ac9ac50a6230157c8e099558757203c678d4e1e4d1969ac1304ee2a8b4000000000000000100000000000000001c074659ca7ca9a1d4ada27ad089f55507ed767de9c4baa59b0634fdaa4e9a73000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc0000000000000033210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000002f24cec21dd1cd191f52493fc3dd3a0689c7ec6614cc1940dd394647466b564b000000000000000100000000000000001a6217c625c85ce696b521a625f854e9eeabc73af8865c9077f5b7047e9ca46600000000000000010000000000000001348a46807fa1594620cf3aadc5464c2c232e6c093c7a7300d68e9a7c49e46b3b000000000000000100000000000000013b9e469ed9ce033d85630a613b4483baccc99f02abcc2388a8ed6a15186fcb810000000000000001000000000000000003c8ac342ed96b7e89ec96f97f76650ba940558dbc967ecbdeb6e20d3a5315d90000000000000001000000000000000038821df9b86e79ed601c263a421720e2adfe16240514d44fb0d8b87479e6910f0000000000000001000000000000000115164458773a3e65d9b832e6c8cb99aa01c8754b0423c34a8e9d3426b3207803000000000000000100000000000000011388497ab1d8e11f798cdb87477112fc86743ec1c17d958b9c8b1fa31204017f00000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000003301eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000000dbc39253e04859df54b367280609dbd91baa75db9ec7fbb305d452b7425313400000000000000010000000000000000259cbb7465ed27962ca86c13aa8e3355ea7a05b744d383c270d6211da93b2e6d000000000000000100000000000000012f4fe677de4a2eba9d21f9ce59d3a1ebf1448226636029c078bbd0a1dbc1c12c000000000000000100000000000000011848c4b76b18dcb4bd5946bb72bf20df9d1604c5ffda7b551a59197eeab965a7000000000000000100000000000000002490039f77d96f8f0417737d9620253dd6a2d7923c2a0887998ef7f8ff63d725000000000000000100000000000000002bca0e4a39775163593f2e9b6a267a91fef45e76e70d8093124bbf9dea7e444e0000000000000001000000000000000123f64447faa25a98fe9ff9f3b110dca95f2a9084d06262b87ff720451e12d519000000000000000100000000000000012e5224e7ced286be529fa52e40b623a4c9da29058d8f8fc52f21a1517f6970ef000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000000333698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000000ed8c3f9594c2f93a33a72156e3fafebf8d05aebc542cae31eb04bf3bc85e683000000000000000100000000000000001b52721b723e0e70ed53d30c9c6eb9056d84195c3dae4e09774b1a4cfe0d34e2000000000000000100000000000000012470c83d242bd5aa2a413be6d50305912baf77302210ec14e98f780fc52157d3000000000000000100000000000000011c1ff3bc277f32b7ca02b22c5eb2d27cd60c9f9a7bb548dc82ea6f03c7057be40000000000000001000000000000000036c9a258014f26c76671a1b427ad2a130d1c16dfddf8ffec1a87531e7daa6ae2000000000000000100000000000000000acab266f1135533e80058efc0c0ec4b6e4f9b0af8fd23b3faefe0a180ca9e99000000000000000100000000000000012cd57cebd8e34e8d39b0b3c082ed6cda610ca949fc57cb430dabcc623d29e7ee000000000000000100000000000000013fb6f88763c5add9d2695eb520d52a2807e2f206fae591fe864863251eb3407d000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000003305859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000001cead699b8ed012d1abdae22ad1cc5db19decd143fe89f5bfeaa16814319bfa0000000000000000100000000000000000ab5f0e8e9ee8174f7815ee10a53727e7d59d77f82f58a0a8b9f949512c202bb000000000000000100000000000000011968e1456af15b51ffcdd90441299c6908201d5b648f518751e8949ef361bacb0000000000000001000000000000000108cfb70aba08c4b517de722c043006439040c5729361a5fc0788c725506e951d0000000000000001000000000000000014b0623b58268a94a31f171cdeec617b021ff09ea55ad1e2a2ee677b79e65d5000000000000000010000000000000000041749b13a37f0a21f9d529238fe4938b99fee4fbc9a2f593c80ab1db189602400000000000000010000000000000001136d6a136523c95a82c3df4f34d3e470e8c29a09e6b97070fee56000a6013cc3000000000000000100000000000000011828a2930b8b248d2e655be73fb9f08f49fa382e85eeb55c3ecbc9f0a19d954b0000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa0400000000000002d4210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000124d6284dec32e79b5fbce8700df15cedf5a938b70f06c0e7f55ad62cb465fe5600000000000000010000000000000001315f115108d16f4f72e5829b706434c129c34f633331472f2543929cc123a6060000000000000001000000000000000036599a1683e131361aa910ba5515c7dd87a2117c3cc1201e8be8b092b3cad3fd000000000000000100000000000000010173785ffd64b1a67cac730e6a3a686b76b5070a26c7023d4de9cc20cb507618000000000000000100000000000000001617ea029d7f9e7e1ea7480899959e07b42575f9e365e91026281f2a790f2459000000000000000100000000000000012ee1a906a5bc65972529cb399955b33e8ca03f3ad9f012ae6ae99369a5314482000000000000000100000000000000002bceb0a054ec951915aa95953c452344323ac5cb73af2ba7fccbd04d750845d80000000000000001000000000000000028f9117896989b215149efab690e6c8495648fe7c889eb69e9906985fb2bc79600000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a800000000000002d401eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000010257e730d267369317aa8ba513848de49dfee2cecadb179fe35cc2398524178f00000000000000010000000000000001160c170befd428a4f72c11eaecf24790094937bee2568615a23d8677336be1d10000000000000001000000000000000037b56c084100f643fa2ca67ea480f78d8bae6d2b3b03cde060e5220ecce6aa82000000000000000100000000000000010ef39d30cf2159bdbc5c1692bf0777ffb92ddf4b821902f04e6e38751f17f6ab0000000000000001000000000000000004bd974fbfc2e29d196bf9469094a1459195ff7af82150ae95e780e553daca6b0000000000000001000000000000000130996ed33bf5e37f3995bc695d05e2b3282ef29f19de87942a286146554193e90000000000000001000000000000000011d4c9186f3cf952cc1fa424a60fac90dd97cbb2c2c07d8c9597e53de775dafb0000000000000001000000000000000016f9e9e6c8f04e163c761e8cff0289085b22cb365f6be2f052d0e43bd703943100000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000002d43698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000001344d6b4072ebde310d4147ca3c7863dbdcb8a546c6aef666bf97f6c81f9755180000000000000001000000000000000115a0b7cfd6399bc578316ce7b9bce016708d44a4f4c34b2b6e7448642910792c000000000000000100000000000000000cee4ad8ad736c540f8c2427005e58b24701eb8c0b0837c342ba455cd8033e58000000000000000100000000000000013fdb29be32f19f8c1ed3881e26dc5294696968dcf22252332322ac4792c76e50000000000000000100000000000000003377439f3fa8de90aca651eb9598b366ecd687496b9293c7a7798e0ba709d1f0000000000000000100000000000000013f7413af0f26a73387c8cf26ed57208866be2185c1bac101f0826b8c3d554cd1000000000000000100000000000000000b39c29094fe14786ef2b0fc3ed8748f015b5fd97ebeeefee4aa36565096d771000000000000000100000000000000002afcb03999b265e91b6f30b76b81a2aba1a9e681a55db57cfd1589db2df8fad8000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd2900000000000002d405859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000126d93993977f203e2822ae42de2137be626fa9abea86095fc5f67588d0ed37f30000000000000001000000000000000121b6a8dadb34429d784e7a8b5af61b8da350714312b89426a50f9a3f759a1579000000000000000100000000000000002f134005299e832e5e070a6c9a62f5155c4f6b0bacbc8cef65bb86b216ee67310000000000000001000000000000000130adc9aa3ee206daf4ea2997340d5171d4656c469456a968a6e929bdae76952f00000000000000010000000000000000180b5ceb2af44ef19973c0e1b63b7d5c5432f1b968ca1f07ddcd16cdb39482fa000000000000000100000000000000013c06d07240636e9bcfd0a303a322ff85a8979e8ad02c3401c3ce3914a243a88d0000000000000001000000000000000030a394bc4dfa947c036e1b3d920370d9d436dd82d08fc84f03259f117d5b923a000000000000000100000000000000001c2c0200e075e9d662289dfde7170695ae3004d5aac88df919ff4dc1f1c0bbbd0000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc000000000000029e210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000013c31aa43270db1f61cc53ff8f83015c17f5a781e7c17b7e7d7cfaa31adfb461c000000000000000100000000000000003a359792316acac44950c9f9819b1f485d9e9077bd6c99a48e2e4b42dcac6966000000000000000100000000000000003bb0ba906f54e6554b307ae69ac5aa08e5f874818fdee55ce660c4370d65a944000000000000000100000000000000002f8fe040d9f00257bf8507a51bf3c408ed2440c85be6daec9954dfa18774db070000000000000001000000000000000007f16bdf071f201660cc356f0714825cb63a870ca6bb1314888e9ecca69510900000000000000001000000000000000128538b768f88896d714030b88d9eaae26c8f90311bbb7da706f2d8f917cd0e460000000000000001000000000000000132e10618d059ab6cb58be74eab6c457046e6465aa648a08f63d58003659a13020000000000000001000000000000000028f9117896989b215149efab690e6c8495648fe7c889eb69e9906985fb2bc79600000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a8000000000000029e01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000012e8f2f5e5a251776bcb326d35ff9d47ec78b196971d75e1c32c8298a86fa2710000000000000000100000000000000001fc04392f7f8162441d8af033c1bc3701be552044c0c162742b0285d8a9be628000000000000000100000000000000000d7be5526cc6ce22c492e33f1f248b2fd3625fc1137c19a57b7b527264c53535000000000000000100000000000000002fa14370a1ce13d915acfe4b6de5ed84375916be76f29535e338cb87d9e4828500000000000000010000000000000000395a2e43d5c86dfd6bc7294ce21103da40fee0db82d4cfce8c60075e80cab6d00000000000000001000000000000000106e4f02325401c8eb24d74f5d49ac52b6ad7ff0db2bdb5c41472dc6e43ed09e5000000000000000100000000000000010224702e5290aa93785fff5640ea9bedbc89ee6bb37471e7e23f4e1fbd4606940000000000000001000000000000000016f9e9e6c8f04e163c761e8cff0289085b22cb365f6be2f052d0e43bd703943100000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca4000000000000029e3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000001378c4e921c654a0d382cba341ec7541b6b3be79f1f34c33036fb911b459d97ca0000000000000001000000000000000001d3b57c2fde5578e758e1ea9972c6bc0fb3d5a80cc2472e1885ad7d200583c5000000000000000100000000000000000a8d71362d424cdf9d287a7f97bc9d0bbe2702dfbf2c2e05f0ea8e26a5deab6b000000000000000100000000000000000833709c3c0cf6cff4fd18e0a84d7099a24ff0e76cfc7e8524709a1134a0ab6d00000000000000010000000000000000203036fe757e3a73d4e79011445c215314b93d921d0fe85d1a3996bbc8aa2ec2000000000000000100000000000000010156b5a376a4b1ff81beb14161811b63523ad839b7fe69cc02462ca43507adc8000000000000000100000000000000011eaef76bea62dd642890dac84e04067afe6320fa56fb9071df140eee32a6296f000000000000000100000000000000002afcb03999b265e91b6f30b76b81a2aba1a9e681a55db57cfd1589db2df8fad8000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd29000000000000029e05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000117636e3ab1a737021943e99e9dc46345d495657e8b6d9825a35df079478860b3000000000000000100000000000000001c9d9fc6a56e8ed8f0a188259e3eaa563032ee3da5cf893a04b351d11dbc22e400000000000000010000000000000000286ff9a80acc1591d178ce7771cadd4f00a601a1afe4f81fbd339d2ebf8ac065000000000000000100000000000000000c6d7b47a1a83e9fa202761193cfa20ca5a7336082b2c0dd408fb662b3805782000000000000000100000000000000002090cd0c1a4c918eebb663e2a2fab67e8e8da7c6ea78ed8b7d710581ad487cb00000000000000001000000000000000131e22c1afcc9687789938ee225a1e57e036c17123e082d3f52a1ac46cefbc70600000000000000010000000000000001265901ce2c9af89548d1a7f57a64631f255a5ce0162a93ee9cca3ebb9ecf3792000000000000000100000000000000001c2c0200e075e9d662289dfde7170695ae3004d5aac88df919ff4dc1f1c0bbbd0000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc000000000000006e210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000011307653a6493592a8a20ec8487840dae3b0845a079e29972cd156c7b604fe23c0000000000000001000000000000000037f1c0728dad2a1efc9b42a3686d9df8640c8341b9512a746ed25e5366ef453a00000000000000010000000000000000242678d0232ca2b5649cb07f723a0b43bb2f9114a8af0906d49a38f9cca82d5800000000000000010000000000000000241979c0a1ea2e2efc3474b47728a2a4832cbe23ec09c2854bf03be83924816d000000000000000100000000000000012164a59d4c85ebce2a95f14d0918f0344beadd2cf292bc6552c1c64577ecd8d800000000000000010000000000000000098d91260084cf4be33019603966781287c523bc08c2413cc1cf0fa0d33f7064000000000000000100000000000000000ea565ae2188a0c794698d03f3782915f1566e5b8160980abb269bd52d198882000000000000000100000000000000011388497ab1d8e11f798cdb87477112fc86743ec1c17d958b9c8b1fa31204017f00000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000006e01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000013e8c3fc21821dc45331fe74f9f794a6ac137ce8c58adc96457bf4f54c521cc3400000000000000010000000000000000148880f62aa1d79e3bf7d889902e056f68d8b6779b2e024cf4b2717958252372000000000000000100000000000000001e7df6d7068ca6d037896d44418edf88944ba293e8c07a79387e09e6246c2eb2000000000000000100000000000000001e0382ddb5583464c7c485937224c43008d798e20e4b325193edb5d584cfc2a50000000000000001000000000000000122053f87e1202b3d21064c113216311cc26067d6978a8caf54f7d0e2d7ebe7c5000000000000000100000000000000003f31fc130416383da5d0a2f37c9c518eef3fc2d0d369905396af6a13c41c97e2000000000000000100000000000000003d84a3d2df9f5050896e8cf5b4f84c44f5f0df4096bcdf5745213f4b4ba9d0f6000000000000000100000000000000012e5224e7ced286be529fa52e40b623a4c9da29058d8f8fc52f21a1517f6970ef000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd000000000000006e3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000134c66653adc1f847854d2503cfafd4943774d1de5dee621c5a4fa380b498cfdf000000000000000100000000000000000d7db20947884131935c4cd31ea3038fff87cfd704602ad33f498ced9a09c73100000000000000010000000000000000289d93222cdbc33079b9297b198bd91bbddc5cb605844df81f0fce34a8cce121000000000000000100000000000000000abc290519770f02bed376cca2483a4ca104a1a884bf716328f2d427554878d600000000000000010000000000000001158f410092a375d502fbd55ba699e8102479348930d09b0c4f5b47c53202e48d000000000000000100000000000000001f9bf0f4d2a36f89c461fbf6c3ff2f6c123075ef3723cdc25a29086151cce33500000000000000010000000000000000169b22d32137c14fa077556a39bef4c659c3cfd6b080fc576e370d87922e095b000000000000000100000000000000013fb6f88763c5add9d2695eb520d52a2807e2f206fae591fe864863251eb3407d000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000006e05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000011edf391e74a7c295aefa5a3d6cb42f738996c30cdd182ab819c04cb9d36cbfce000000000000000100000000000000000fcd5754ef9ed99c6e416d7a601eef3cfe2ce796de171426ee7b92438cb5bbfc0000000000000001000000000000000032db4ca30bf2fe80851f36a0db99ef76bcdc5e21f5171f11440d304377052575000000000000000100000000000000000e72efff312476c1330d78e80773f6ffdcb63531890fac0ba16b5c88deac5fcb0000000000000001000000000000000105ccae43eac31950456e5b074b8e2baf3d2c5925984ee68721d700493f44680300000000000000010000000000000000367e699b00bb05ad572bcf8b3c0d3db2d683ea2113917344e164fa417a9a074c0000000000000001000000000000000003d0c733ec300f1f6ec75d0d53a0501a7a66f3f6d9fef13faf07556e180b7ed9000000000000000100000000000000011828a2930b8b248d2e655be73fb9f08f49fa382e85eeb55c3ecbc9f0a19d954b0000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa0400000000000002c9210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000005a7fc32f2bd7add9f70020f9c55f65ec4f289909f6cef4eb9d16cbda317c1080000000000000001000000000000000104e68cb15566fc4cebbe4b4025dc51411c9cdbe17286b60f6db2c4a4e9c46ea50000000000000001000000000000000138839aea2ab9c3265073ed1eeb0d5440bddaace7d88f0ecb8a7c231c12de04fb0000000000000001000000000000000030150357d165f1106da0883a444d850015b91098e105b249aa7489fde44e31b4000000000000000100000000000000012a301b4bc055b3517f9e86f94df8ef70969e438f0da189cf326bcd928b27a193000000000000000100000000000000012ee1a906a5bc65972529cb399955b33e8ca03f3ad9f012ae6ae99369a5314482000000000000000100000000000000002bceb0a054ec951915aa95953c452344323ac5cb73af2ba7fccbd04d750845d80000000000000001000000000000000028f9117896989b215149efab690e6c8495648fe7c889eb69e9906985fb2bc79600000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a800000000000002c901eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000001df43a6f3cfaec0977d7a5053afa4306ff967e407043864cc46210f9fd0e1425000000000000000100000000000000013ba20a4720d260f4f90b1d1335b36a61a2f0712037c6fd202e81d3b47c0734150000000000000001000000000000000137cec829e46690700d076f87e3cf1d52d22df3ce5ab427c3fd87404c7cb2fb410000000000000001000000000000000006e487e8d9cfca29f2da7a4308d5d45558c20a550caf530bf13d947f46b8d6610000000000000001000000000000000125c75a378e5dd193eced008710856a877a1fba6d8da7ebe05c85a189b0646d5e0000000000000001000000000000000130996ed33bf5e37f3995bc695d05e2b3282ef29f19de87942a286146554193e90000000000000001000000000000000011d4c9186f3cf952cc1fa424a60fac90dd97cbb2c2c07d8c9597e53de775dafb0000000000000001000000000000000016f9e9e6c8f04e163c761e8cff0289085b22cb365f6be2f052d0e43bd703943100000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000002c93698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a000000000000000100000000000000000499cc921dc6b31abc234294cdbf9dc3e28fba30a45b38750a838a41903217a800000000000000010000000000000001373b9729007bb46497d7129da411c9ef2692205755405c79b2ae0871cd362729000000000000000100000000000000013c5de8cf1879782e9d524ef6841d1b9ee520d99b867e79ae5b126d163f04fdec0000000000000001000000000000000006a9a55fccafced1ff3caa5a86ce9b72a5f916e85221c6c3fbabcabc81235f560000000000000001000000000000000116b1e3e177e35be3d3f43d153470d416c8c4ee623aefec8c7a91df995244fb00000000000000000100000000000000013f7413af0f26a73387c8cf26ed57208866be2185c1bac101f0826b8c3d554cd1000000000000000100000000000000000b39c29094fe14786ef2b0fc3ed8748f015b5fd97ebeeefee4aa36565096d771000000000000000100000000000000002afcb03999b265e91b6f30b76b81a2aba1a9e681a55db57cfd1589db2df8fad8000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd2900000000000002c905859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a00000000000000010000000000000000359f741033664d077b8137cbd5ac6f3d3245641d04ac9824ba053f9bad62db5f00000000000000010000000000000001200b1959d45e122d26090155a35d04cdf16402c88a88c0c97bf34106b1b6926e0000000000000001000000000000000136db2a14bd4ce5262d0146ff1627d26a613b5320b536394734ae7d2234010671000000000000000100000000000000000e8f306c71b5e1d0f1593c487d45f8059a49932ad04a646434b6005647e73ad4000000000000000100000000000000013b13c6b61bf6fecc421a8f216f8ab76ed7cc9da26bc0ef688083035079fdf070000000000000000100000000000000013c06d07240636e9bcfd0a303a322ff85a8979e8ad02c3401c3ce3914a243a88d0000000000000001000000000000000030a394bc4dfa947c036e1b3d920370d9d436dd82d08fc84f03259f117d5b923a000000000000000100000000000000001c2c0200e075e9d662289dfde7170695ae3004d5aac88df919ff4dc1f1c0bbbd0000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc0000000000000095210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000001804ecef8e4ce163e895e470e548a278ca19db8c66f3c9fa4bf619403f0afd6f0000000000000001000000000000000131c0c72daa73ce92ada1f24fe3fd90bb47b6f7b2647d6b13406ffab9d71ed5f70000000000000001000000000000000026aa95d4c05f4f590a3cbd2e66aaf9382dd5b26be6d8aa6b781cc1cb91f0612300000000000000010000000000000001139701e581f21a8b73f283d1a3007dee8a8ed5a5bc36d487ec85c727667a5d30000000000000000100000000000000000a38a95d5e91fd2ed426b88c927b00ce86b0da999efc60d714cd9a88364550ee0000000000000001000000000000000126f9f56fbe451c017698564bcadf8dced1b013c10ce93f8ef06cdf02c96a3a9e0000000000000001000000000000000119725ec163ccd5886be583bfc36c214fbd8cb9ada892637f0a4aa6bb60291cd5000000000000000100000000000000001f80d53c628193c3432663f1bfb2ebb744754c7d205a935fedfc9686dc28a30300000000000000010000000000000001303b2a999721de9bb67c442a2bad44db2ccb2ec835f4dbd9ed12b6796d65d446000000000000000100000000000000013efffcc37d4e1b62685284bc206c355b1214efd1af1f5a10959678d4316dae9c000000000000009501eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a0000000000000001000000000000000020c399e2036ac1c7a45cee2c556a2ff5b15762adb5c07cc37a89c31b02973b1000000000000000010000000000000001371d5a525fa57c8b4996caa5da95830f3accbac06845b691ba5e933768037ce800000000000000010000000000000000141830bac3c37c1d6bcb4e3765915b08a5937b03c39fbc4ccf383d2591a2fba4000000000000000100000000000000010d6793f169c47532b11a7ddd213258f9275a69b0a3d4f0fecc78c4c241d24d510000000000000001000000000000000003736728468ba93f8e19f05d62d555c22ab489cddc7006f1d3558030517296ce000000000000000100000000000000012e04a02004fb846db5cbcb68591b015fc0a9a5c8155d88224dfd4f0868d83d120000000000000001000000000000000108793c22094edcc59e83e0cd32763fa638d0d62a84c77b57a1bac84c7945a446000000000000000100000000000000002126fbadbe57c19910b2e514be53d04adac4ddb21920e7c4a4a940b03b5c1431000000000000000100000000000000011b11c0110b63c5a518256bddfdea32804efd62fa7de5e0942345580fe4cd9e0700000000000000010000000000000001014ddba70d98582988badea2accd2e2a633266d65d70edd0e5720e258cfda8fd00000000000000953698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a00000000000000010000000000000000155faaa3146faf1d7c112706e855a8939add17cc239b02b9e68dba49799c94dc0000000000000001000000000000000139a84045ce12244af6396bc43be9e5182166cb58102b62643590551b0a198f7f000000000000000100000000000000003bea1500dd12428f7d21e6a8e82c8ef837522cc08a46e62b7a677f44f2f3e8fb000000000000000100000000000000012aafbb48bd2f4114310f803bbbdc2c973de572b805af43cc7531503a9ec8f5720000000000000001000000000000000003d8d05ccf562e729f584c9722f665937004054ed3fcee6894a4a6e9ba5dbf3f000000000000000100000000000000012597bf453ebc23767e0478a49aa67ec5c1297112cc0f33126238e8bc4405c0e00000000000000001000000000000000120b475f1f6e0eee7d4631c774e7f553c04c0d104c14481d6813c0cab93be67e20000000000000001000000000000000022414d74a4aeb2ea7fab9ea430e1ee96c810c8624cccfdaf73a936ff17a90ad7000000000000000100000000000000012fc6b04b1af34e69e255adf13588dacb41867ed2b1807940c21a833f70efaffe0000000000000001000000000000000106a25c7b0ebe75f2924e3ea9acdb0f38e9b4b0a3bac0c5508a0a322fdcac1593000000000000009505859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a00000000000000010000000000000000224a21a712372a5f2c4ac53a207cdc580b749a25d467f8fc3adb607a39f311ce000000000000000100000000000000013bc1d2a9619764893515b38ce7ec20398e33d92673adc9de5d94b6bb254f9ec60000000000000001000000000000000032d371e909bd6936a0d2676d63d75a697a4159ed29e1d1777ae41ae6627be563000000000000000100000000000000012cf39576fb763f7897f39a01255db600c5fe6c779afe58f89c1beb266b9b7081000000000000000100000000000000001061470c513ec5409d510d07f983ed1a04cc4ec5c9e5a4ca25ebf09cd14f1b500000000000000001000000000000000125800d588a5dde5a24f0ee36ba1b3d186866080e78cee8ce43e61ebacc4f612100000000000000010000000000000001261de00e1b83302175be76bd520949be1b5e9b571c0f47867590741d9f2c703b000000000000000100000000000000002287c5ee26f370c2e7ff01c9bbb1f8289700768c3a9c196093408b2e228b8d670000000000000001000000000000000108daeb3d67ce5a5cdd50ef618b9cb67d394291c0b8ffb145cf18613531b756ac000000000000000100000000000000012d9c05cc7644e0ecefbd37cf65baed9d578c19b01dee294da6f61c25fd64fa040000000000000267210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a0000000000000001000000000000000015fbc033587f59c7495216efbed873a242c89fdd434afe44786145a059de92fe00000000000000010000000000000000194e932dd2c29ef7dd17fc20d854eb059ca9f8d5f187294902de707545796289000000000000000100000000000000002b90c799cc51efd32be25c5adca4bf3a452722990234d8e460cae42f4b22b7130000000000000001000000000000000105df6f818200dcd260c76684a24f98e63ae30a398555bc2d479757d447dc8fc9000000000000000100000000000000012078a066bd6fcbe8a743df72e40639109025862775ad753c86ed853f7b13ca3700000000000000010000000000000000189486d43ad7ccda1872cc765ffae7e3c3ebb794180f45af10ec6dd498f126dc0000000000000001000000000000000025324ce96e87e0872d7a61a0bd1c6617ce861044fb41248f03349411a98422f30000000000000001000000000000000119a770049d307e9d8ab82d137b31576e0971544a8a29adf60fe92c5c9071573b00000000000000010000000000000001099a07d40b265406f1c130e23aa780bbbd2c7791e9d21ba287b89e5d5995663b0000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a8000000000000026701eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a00000000000000010000000000000000298488c6240859ae902453ff72234692ddd1fb2cce01735d729443f223d7edc40000000000000001000000000000000016a76e6c459ad10ddeca2e9f0cb1047a5a543bc9b97233535487a4e608f435b8000000000000000100000000000000002df6f2260ff08237c55a8278dd41ba3b13cc31bb7c4b0e5c6b950442a9434f220000000000000001000000000000000100abed93e8f39c7c424de479061113853811d07c71ac04b390474e31ce45ea4e000000000000000100000000000000013dd0032928ede57f0f3ed57618245eeedfbca622ba1910aee8af465f858d48b70000000000000001000000000000000014965ebf2b578e3c2c9153485938516e0b0bcf1bf55060ddc14d43b86e28f0bf00000000000000010000000000000000293d4abd597417e3ab417dc4767f9594483e2b2fa8a7144ff161ad06cf951dff0000000000000001000000000000000133f2a9e78f95c7c82d10c197f01b6f263e5cd4d62ffad3c09d77f384520184aa00000000000000010000000000000001259afec45a3e5bad7ce80251a9f2082c0b0be0ff909e203d96f4713caa022b54000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000002673698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000000bddc239b8e176f2786fb0a0b90622b3c2508b3af9a783bcb2e8adfe3d901e0000000000000000100000000000000000cd5b6b852b5ef7e9ec5c8312039b750c549d6715e80944240191c9033905e6e0000000000000001000000000000000024e7068d16c47adfe4b499476ab3b14a9489daa0781a80a9db9b2d032a063b270000000000000001000000000000000104390c79b7af38d655528e2c784c4ddddd58059ffe6151ee3e7b6eebd0c7f4e200000000000000010000000000000001211b4b202b07a45da9dc4b204d19bd8f32b82921272905646a13d24a53df666800000000000000010000000000000000385fee89e9c35097a018cd2a9ac73b191bc940e6f7ced28aba4dc98140dd8b9d000000000000000100000000000000003463379f443052f957cf9ec52f562a474c275dcd9d4028905968cf99487201ea000000000000000100000000000000010d01aa773cf30ee8341b55e803413443d95b75ad65624e1a6236099b3f045a24000000000000000100000000000000013e854eda8525330407061fea4d27ea91dcd05a973fe817bdbc3db61f80a9249a000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd29000000000000026705859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a000000000000000100000000000000002827533b43769c8467a8e0fb71c9aa6fff06886fd59d650e767777e30d41491500000000000000010000000000000000221dbdc14415996322283546cfbf270df336abc8bc3550e09d19e79e4d97f258000000000000000100000000000000000aee8d3181d1457048298f1a8bae6dcbe3f0415ca98961d152e318f66b339ed00000000000000001000000000000000133a3ed0aa83cbc24f25a8997527afa76b61ad727bd82f9cc2d2417edd06ee1bf0000000000000001000000000000000117607e0cc359f438d10f3410f0070ae8b47f26106c6cd45d9ea3f694163e13040000000000000001000000000000000028ba73b3d7246e3ce208d5a4722f9182a2c1c38f5cd63f1670153f1d1c1585d4000000000000000100000000000000001037e6930ceb14ef8c42e0dc57c0614d54b991ff7802e17cecb39e57f83f76360000000000000001000000000000000130df3a062946a640fdd1a58f447887a345bee15ff0aaaa4d5fe03bc9cdb4d2d40000000000000001000000000000000117e1609d74df2fb10a3a9c38f46ef4f17bfed0159ea390764c382cc493966716000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc00000000000003cf210264774e6544974397c9acd58c9b2f0080fb243c0a2d24080d9de82f9412e9000000000000000a000000000000000100000000000000001989b2561ff8fbc1d14bd64c00fb8c55c3ad8f441853be49b2444a14b10be677000000000000000100000000000000003c086217d9e887cb9d7af352bdf527f48c7fc7ed947f6bce9c35e0a262c1d76a000000000000000100000000000000000901e3b9cc7d14f0fa41f165edb87cbbcece31addb8e25106b3e996a219047cc000000000000000100000000000000000488c15d47b63b8648a2584e86f5d0a05890124040fead74f602e328f0352aea000000000000000100000000000000013aa38e362b2744899a75797c21aa8d663af06499d2260f2d0a529055cdb973a0000000000000000100000000000000012a696d5e838e49553aa8b6f367fbf00c77dd0e3e61dbb640025e72e4a8294f970000000000000001000000000000000036351b04b6caecfedf3c14bfdb51755ab79ce77cc4177e6b5fa227975dec9d8e000000000000000100000000000000002b7e82a5f40981ab48939e0ed9ace58538539a5029146249a4b1d227ce10efdf0000000000000001000000000000000010d830e51d4c0f773c0a6254794b36dcd7fe091bcf6f9a6a8a768cb738757a710000000000000001000000000000000014a27f2d44ac87c2049b99ee71ca187826a6a085ed5f45715ea246c6758439a800000000000003cf01eb182a52640432b50a978599b79e4f0d27756f01013c50d97aafc9100f103d000000000000000a000000000000000100000000000000002f3404ca312a7985c16af4f81452d1f9194659657ac32e1c78b1f079ce57aaf500000000000000010000000000000000200c73a7470ad3fc4f7102345ca8303030aeb03cf9bbdc851ceea8b7cfca0c6f000000000000000100000000000000003011d3623893a455f53a9a5413f2497af9c7075f82df699b6d5465653fe3f4cc00000000000000010000000000000000387cbec0ac83eb2ff663cb2530d17820f87d9d55a2ded46f5473b7951db9fef40000000000000001000000000000000107a41fd71db4a1673fd29129ebd459fc18b8fc5f809be0c39adc11112085bda1000000000000000100000000000000010fa6123614a3e759cd1355dc7d49f5a33d3af9101f9352a5a0cb8d44d6b44d6f000000000000000100000000000000000b319537240b590452ea0ace8620438caea667a043f01ff96f834756959e6d620000000000000001000000000000000038a065186562002e8346a156b8c8d7c4bf0faf4d01c9080f895b642e40f72ad0000000000000000100000000000000003a3c1807f6dd6c18206fcb3031feadc7381d0a6815ef84d8459646f7b5be57d8000000000000000100000000000000003b587503deacc7928379462f427e15e745ad35810037880c3a301f5e38129ca400000000000003cf3698f828eb95fe6f469bd0068712fd0038eed01d32186edd8c2cdcd6b4474fae000000000000000a0000000000000001000000000000000038bcca67990307076eeb7a50c4935aa194f24d6aed088d84da6c7050051f2602000000000000000100000000000000002a6df271b92ae3d9ca1cce630e02cf7aeb80e140d8641f0bace829d135c945d0000000000000000100000000000000001a53a143a6b113ae98477bff24a43239f4406b03b4c7e4439be1520cf437d8ad0000000000000001000000000000000034321b6544a588672c0dc9d82373d054f4b05f8f6667ca637a1abd5cb73ba0570000000000000001000000000000000126a2b911401ebb3706a148109f0e33d8e58d672fe6db27f958f718d8e654606700000000000000010000000000000001301e916a5f37efee74bccfd391b2bf1d22c3eb8757408256d064a1cd5bb16f4b0000000000000001000000000000000035fbd4d4c647ea71536043b504e7484d537a7ecc2d4f7e930e9121b42dfdf325000000000000000100000000000000002ea06a800c8920272247fae1ae73f4e5e91bd34bb333ae460ebae0b790d19eb100000000000000010000000000000000218d1ef26e073bd5d517e0ff029df2e6cd95bf8e7e83f29302d0d500fbb80367000000000000000100000000000000002f44a75a0e63beccb3360d588b7047581ff9ec7bb723ccaf74d3495032bcfd2900000000000003cf05859ea6307516da9768b97df5abd1fa89a188c96049bbba269042bbe8d1fe3f000000000000000a0000000000000001000000000000000004968b0840be045181b86f73bbab5b70a6edf986482badd095833f3ddf57590f000000000000000100000000000000000b79ddf4d17acc59713f0f2e111897ae6fcf3aae012f61329f3a1d3b4209058e00000000000000010000000000000000317d250fc3116912102618d8340dd2ed29d965a9c14a03c695909a220548f4f4000000000000000100000000000000003a55d97c01aa902febc25f39c2f90129af1009c82d75a9001423c64ae5b015d1000000000000000100000000000000011104cf515ee38d10b335f1ff0451793b9fdf7905a3a5e12e1da51137f5e8214c000000000000000100000000000000012eae2ec73dc0282fa547de1a4ac2218c664310a1cf847ba8bbddbadfe05ae1c1000000000000000100000000000000003b41d25528509731df83a1f9940b0d5926a4c1844c8e2ccfb8a4108084745683000000000000000100000000000000003305d1ac9ac50a6230157c8e099558757203c678d4e1e4d1969ac1304ee2a8b4000000000000000100000000000000001c074659ca7ca9a1d4ada27ad089f55507ed767de9c4baa59b0634fdaa4e9a73000000000000000100000000000000002b2525a9a1cf09714c880293a5b472768fd05a359f03f77e2f24a357ef0b9ccc00000000000000a000000000000002cd0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000031bdf7329086b48a20327ed719f91c9f742b69898be1b755f1d40ecaeaf8c9560000000000000001000000000000000130cffee8584e33e93fda2cdadbe14df528d7a124b79c3278625595ebda6daef0000000000000000100000000000000000473e1f204ccb4806456400adf3e97f3a3017c8d7b770ab62d94f9eb7b88b74300000000000000010000000000000000066114e4619540bef3c050f85ce1113a4d9addd2b24e7d94686903bf7e8cf7f80000000000000001000000000000000110ed63c4b41ffcb717167fe027aae6093cde8b42deb51b9d74ea9dddaa5046210000000000000001000000000000000136c308593f3e476423148961b61f49ad69f39eeb38e6809cfccebf8603e3c98b000000000000000100000000000000001a2e05aa8185136862995e5ec9f2927063d2dff30a733396ca19f76689aa133b000000000000000100000000000000002997304c67ce3176a517bd359d9b0c8e1ce7045e1cb1080a27ea3aa30e8a16d4000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000000cd1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000000c2981d07f5ccb12370a348b0bd7520d9625ed4d49284876ba08df9febf2e34500000000000000010000000000000001290dc46b9f558269bc578ff43c4326792f1336bef498a7a2f51b41fd18f46f33000000000000000100000000000000003a9b344f95e65e2e473dd344963a2d2c67fb51bd22d98e96bb7e13fde74430070000000000000001000000000000000002d2efe1190ce34d0cf02ad05dc3bbf057dcc831aadafd12114be3248017bfde000000000000000100000000000000011ae3af7d96ca266e42967f7fa40a897ac78fe64f4aec22511448beff928729660000000000000001000000000000000117857ef5a99912e454d96e63a01e534bdc9bc2f6072bcb0f0333c2aff9df6c4c000000000000000100000000000000002ade54bf391d828df094d24ef9ea8439b6fce7ca1091f942806f1435b196efd2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d5900000000000000cd070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c300000000000000080000000000000001000000000000000010747d104f8837787e8b2c593d4481c6534e72ebf683490125e20bef5ec078f5000000000000000100000000000000010d36ce13008d3d7f1070b244a0b846827306d8ef8caab101476456f2c0f1e2b80000000000000001000000000000000038d1ee8a09f6f3fc371f74ca23a32cadbd8077bb20685c6c8827bc398e78a306000000000000000100000000000000000de6a0cf81f1735b3f1e1e5c481e2616cf881973f11b1ec54eda1ff56a5723f6000000000000000100000000000000012e2d22e5264bd4671a96e3c21ece563157e226a725d9d0b25cc672d1069b26e2000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000004d01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000003ab38f9cbfc5d3c6113e8c639bc810e8fa8e0050bb40c43244e191c4554a6be1000000000000000100000000000000012cc3995611b8bcbcc645ce64e9a6dc3cb1460a0196ddcf062507f177da40fdff0000000000000001000000000000000020339c53f4ba8f58c233b2ddd67d0d3f084716a713edf531cf76508ef5aa2e74000000000000000100000000000000003c7ee22981a176f07369929732497d393084af2e1f03328ad9faafb0626d2eb6000000000000000100000000000000011f0d82c86423e2dc3b65791258066b26c550c16204e40f554262d6476a741d6a000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000000d30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b6000000000000000600000000000000010000000000000000133c15d0cae295381d3035e30c1357c93d11b1fb02a32a3b3459d12cdc1fed1f000000000000000100000000000000010a8d0b924ba524ef9a9fd424b1f7998f6735cd9d4f68aa1232831e1b7cecb6a0000000000000000100000000000000003691fbfa0c2b33f8e31ce405962c381b6f891b121ca646e2f66a0c1a4dc1cb5d0000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d79000000000000000d168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000008628043b595be831ae9519a3c992eb250b4185ebcd946470bb3349c874666780000000000000001000000000000000138e0db3733e497eea9f89cd422cc8202f74c421fa6269c7332a3e0b6f5540a21000000000000000100000000000000000f5b449679d27b00de38caa5c0dea420d83f888fee146e3af60fec3b88637b43000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a000000000000000d328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa100000000000000040000000000000001000000000000000008c62030e9a3592d22c0ea2c3ea2930c6f414abccbac5b7ca9b1d544fa1aabab00000000000000010000000000000001376563708577b6963efcfb5fc5f26111e0dcec9da7560c0c651dc8cf834f482f000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000050af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd00000000000000030000000000000001000000000000000005d5bbf01900c7ffd7a4f1c857b2fc1358af18ec3eac6c4d49e5bb5e2ea9f4cf00000000000000010000000000000001023d495543e407e6376a45335b72abe709e25b55e8c72d3dcf10b9c6a92ea62300000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000001330b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000003da2369ab265249deb8f1a5c2b5a0048b44a52631b906dd414b1d7d949bbb279000000000000000100000000000000003b4ea48081ba71c14af2d27ed1d0af0d1b673a55d1cbce0c2ce618089f1f4571000000000000000100000000000000010a22545361b3223cbd4167cad43d3c9277a61cb31a634b00558eea3c9f36152c00000000000000010000000000000001393007cfe4025d90c45dbc49f1090535f1d12605465d5c7ff26d8048b98f436b000000000000000100000000000000001d3a33261c1aefc065e2938935052104d5e142dc31b0d1a3a7bd0f96ff8c3bfd00000000000000010000000000000000334642938d99ad2ecb080a776255ef43bc6c6453ecbf8dc28bffcef9f0bc3cd8000000000000000100000000000000010506dde5dd96ddf3158af18787ddb3360a64d703ebc165f5274346a72ea243f4000000000000000100000000000000011d0ef67444b50171db9c88c9e52df97176b7c2ab07a608d219dd15d5118b3c9e0000000000000001000000000000000036bb01a4b536947b566ca640bb47f9cdc8d60e8f94896645b59d38af2d1c98350000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000001331ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b058361889000000000000000900000000000000010000000000000000302db697f1f312f1c777dbbbbd8b69afef47847c49ec17b93525c5924f758663000000000000000100000000000000003d67a35d2e2b4d1de080d534898ccf4253a9faa4f7c1ccf55f56f54a5db26fed000000000000000100000000000000013204b7eb760427f2c74a024e72fe40eb1fbe4096e36b7a2541c3b8ddd7b24c30000000000000000100000000000000012282654d620c6ffea151acbd256377a0f60cdb2d99bd30504bfd9e40ceb9054c00000000000000010000000000000000216be63c7eb1fd0e4e7c6afcbc45436670616b6be3f4445ce1f426813808686a000000000000000100000000000000001541a7dd2928c891aa0d555aedf41dc11c83bef66351a342e1b00158f8bcc34d000000000000000100000000000000012e75ac72e22ab64cc4b0c9c5da0562e5fbd08d142d5bfb8b1c2d25fc0464c016000000000000000100000000000000012cb4fabd4a2ceef84ed50f70407519ef4fd53889d086f5657489d779a56cba0c0000000000000001000000000000000034e8f7e0bebc4e6ceac176e58697aa811375831824b908ae505b1c454c9495bb0000000000000033070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000002c5fc525f61ad81ec62b5c44c0e796e484bbd777347a51579bde3bfd0c760fdc00000000000000010000000000000000144f93c33a3dc8774009ac9b6ee886d370b6a1d88c893847e816ec0474777eaf0000000000000001000000000000000133324b1fc3841667cd45743c3e83e7950157db733647a3f255f3df641e67bc98000000000000000100000000000000011ac29b44a33a62d2107180580ae3a5764b6f4b6a573c229d81bea778b13e0c2800000000000000010000000000000000319f8576925bb57d78ca608078d678d249a8971e008b9318a1aedb1af0ce20c9000000000000000100000000000000000b0f9797b7fc01e0f99e19fac202cf8e9f7d22a66f5f34b0dd54dbe91d8ae7ee000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000003301111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000002e751dae7c829ea6b036b8662f835dd5e76b8a614c14ad28d4803b0e07c89ce400000000000000010000000000000000252658d8397b9fdbf8d6da2a132b32e17ad5b114fcb85fa0585fb9efb50b15df00000000000000010000000000000001128fd4ea15befa84e2e845d0a414dc1e2193b610ad5f18d3c542439b2e0b27f50000000000000001000000000000000104b7782a10d61ab567baf6fa63cc1c4ee192375a3376796475f7a35454ee9edc0000000000000001000000000000000014e3e42c208ed7f12b863776f1f3362ac5893adf769836f84a1880bcd5fbb82f00000000000000010000000000000000125e7372803fb22d1929104ba7f6bb623f18bfbb32de6f06821b37ab7a94181c000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000003330193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000001f059d0d822fb7fdde8f716ce33839224b031de0c1b9ee6dd485b41fb35647920000000000000001000000000000000031b2af18b8c17bb5d304f2b41bcb85ba65dd3a276d63f2058fee36b0720fe22d0000000000000001000000000000000129998cfb669b9e27c180c8e03ef8cf25a24440a1eabf13ed6e472018863e4f01000000000000000100000000000000013cc0589924693fb71be9fcad991f9c01c709d4589e84faf8ebd9348e0f7db1a7000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc593230000000000000013168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000006c1a1396a3d6e3e0baaa11bf66bb5f1c0c9af0830dcc8da1f99ee0a05f13a8300000000000000010000000000000000274d96f3962c79fa75d870dae4034052ab15a3cf0f6bc631a4d8224d9d2d945e0000000000000001000000000000000139507992e986a491f2534d3dca8acf66c66a8c343c9ef7d22286880f378d7efb0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000003328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000003eae36b2c30156ab30a8473944f348acc401331e509125116a661186f59cdcd500000000000000010000000000000000203c09d8af4e0d778510b5513d6799e058d34e51473c25f2bb9b197f94b669bd0000000000000001000000000000000103bd687b49ebfd40ab80ddabb59fe40cb319484c5385cd704f72a3790b53af73000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000030af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd00000000000000030000000000000001000000000000000032559649b9dd4c00d1b0a9fda4fe0425cc64652e7e36b838bd18422c55be46e400000000000000010000000000000000114898ab1790b60a81364775bb889beaf6adb7e668c5f50b87ebdf435ff7e717000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf5000000000000024c0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000011b216ca36e9bf1ba207ccef605387ca5f94af604a13b590b75da166b21d0f1bc000000000000000100000000000000012ca67917d83600df969381940c41c4ee77e32b2e52e85cbfec5cd498279d9e09000000000000000100000000000000002af2202b4736ad4f01ed796424f53de236ad5c9abb3ced2a48d2354e7cacaf8b00000000000000010000000000000000385077ec4d3bf994ebfaef24b026a577688645e0fcce40bd0b0821cb13a9f80100000000000000010000000000000001131346fc15b7791a8efce11f84c88b5f6d12d0cb9880f354f850256007ac927000000000000000010000000000000001215858bf0752edee88f87c68fff01583610e612caceee3bf41c63ddbddb9ddeb000000000000000100000000000000000d63cba247168e95cf9aeb8f1df5bd77e39fa0565f18b2099ee86888056a70bc0000000000000001000000000000000117c617d6cd76595b8396a03633d137791828190842015a26c868e22e7373118e000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a000000000000004c1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000010161525735a552d62f5a6e7fdd81b0405a076c17d18fe3a9a2538fcd3ae13664000000000000000100000000000000012ea3ecc0e748fbca95a07d6d4ecec60d09d611f9e97dabbdd2129205688e18aa000000000000000100000000000000003261e2db2886b347a61735862f558c235bb7b3ac58620f512667ec4f7636b9b8000000000000000100000000000000002f27cb40d33adb8e0639fbeedbaa7215e47c8d40f28854a787d0e3f4ce6431d9000000000000000100000000000000012e48799ac9f19e32c787636debd36ecea8736bc029fc34562d6756570016ca5e000000000000000100000000000000010a0d9998c2b92dbb8931d91814e0b008dfa5cdf5f6897340dbc6259bbddeecf1000000000000000100000000000000002597be56c8553c2f0aec7f6eead4988440f231aa5dc89fa25f1fa07e517f4a00000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d59000000000000004c070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000012c0423266b357cbd1404033d891956e2ffd522010a9964b98305e5a9ec7f39b4000000000000000100000000000000012d12ffeda55ed8a13182ac3d973f3024feac5545a9176bed189f80580fe2086f0000000000000001000000000000000012556d80632f65973e142f5c45b79fe11552461e9224a7704d0ebdb1c2cb5bce0000000000000001000000000000000010bad76738c6bb903a75f5cd1e590d85c7a7492340825b79c59ca2735969e84a00000000000000010000000000000001353f05c35984aaa843809c567827742ea2aebe103c571f4b5f22c9bbf3450c760000000000000001000000000000000126b2362b2b4260025afd1c5881450ddc880253a16e524401ed5597441184f2f5000000000000000100000000000000002167607703c840ea4670ffd89100e17258197af01fd9154f758c8a4925ccb0c0000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000004c01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000013a1e8969c2f17dff6de49d49ff2b5cc18c5c9fb651b039dec29ec43b00a4e817000000000000000100000000000000012cc3995611b8bcbcc645ce64e9a6dc3cb1460a0196ddcf062507f177da40fdff0000000000000001000000000000000020339c53f4ba8f58c233b2ddd67d0d3f084716a713edf531cf76508ef5aa2e74000000000000000100000000000000003c7ee22981a176f07369929732497d393084af2e1f03328ad9faafb0626d2eb6000000000000000100000000000000011f0d82c86423e2dc3b65791258066b26c550c16204e40f554262d6476a741d6a000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000000c30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b600000000000000060000000000000001000000000000000114614ba1f08ea79cd6cb66fd189a4aa3dae54932de3664838245821c27536de3000000000000000100000000000000010a8d0b924ba524ef9a9fd424b1f7998f6735cd9d4f68aa1232831e1b7cecb6a0000000000000000100000000000000003691fbfa0c2b33f8e31ce405962c381b6f891b121ca646e2f66a0c1a4dc1cb5d0000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d79000000000000000c168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b000000000000000500000000000000010000000000000001209bb03a1a108f5d80cd76130475f542770094e973be2d794a061f44c1b45f840000000000000001000000000000000138e0db3733e497eea9f89cd422cc8202f74c421fa6269c7332a3e0b6f5540a21000000000000000100000000000000000f5b449679d27b00de38caa5c0dea420d83f888fee146e3af60fec3b88637b43000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a000000000000000c328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000010da17a76f1a3c069661874da0c08817195df46f0287894ad9f0443acdb2442d300000000000000010000000000000001376563708577b6963efcfb5fc5f26111e0dcec9da7560c0c651dc8cf834f482f000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000040af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000011aea55ec4fa23801eb579b79f0172980e491af816ea6cb6b06a493fff1af6de900000000000000010000000000000001023d495543e407e6376a45335b72abe709e25b55e8c72d3dcf10b9c6a92ea62300000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d000000000000037c0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000013285554bbd3d21df3a07afef8a9ea887508f437527d8a9b646e78fee057bf83200000000000000010000000000000001090c08a1dd139ec1fb45a91f3ac401508e3b4447f4f48d7a696b99da62c744900000000000000001000000000000000032d078e26f7fd763541209c12683dfd27f16c5f8609138a1b57969739bc00a6a0000000000000001000000000000000030ad4019b5320e694440e397b3d8dbfaa951ebcbe8bdbb4ec3864db4c6c797350000000000000001000000000000000024f06db3d122810ebb82d6db5dbc17ed5968b840fc1b8b333283a3ae98565a1d000000000000000100000000000000001b6f1fb4bcfa638906445b87a897946adc94ac4980c912c2777682dc4d8eaca2000000000000000100000000000000001d535402fb80e37af3802c447259f82c5e6a1593357730c414db10bcb451ace60000000000000001000000000000000126cc9600efff7464181d9b340931609fb6f12bf254b67a114b47abf67eb20edc000000000000000100000000000000001d87165ddde6d54f54eab67360861795670b48edf536440c746b5071d1e7536700000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a000000000000017c1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b05836188900000000000000090000000000000001000000000000000121ce141eb84538da8f67457c638b32e0e9d167c68714dc1d2c9e2f3243f78ea7000000000000000100000000000000011bff4e87226130ba1dee00b9913a2038b25f4dd2a7f6f0dbc665632c3769bfa50000000000000001000000000000000009f8a8111c3e3a1eb42f513587367367fb51727754db6bcbe4fe49fcdc68b1a600000000000000010000000000000000010580195e32de8a4f5fa0d763ec11059a59f554b1855bcf74cb67867f4e1461000000000000000100000000000000001de3aa7d5cb1fe25c4c051434c4dacc31de80ceb7c0e5323e156fcfa879948de00000000000000010000000000000000178fa4e36e6803c9b0fcbc258d3ed12b8636502674b86cdd7d3229589cecb9bb0000000000000001000000000000000021344e2bae5282f055a0b427cf3f6c6669c3f464f53a987036634dbc083764ea000000000000000100000000000000012cb4fabd4a2ceef84ed50f70407519ef4fd53889d086f5657489d779a56cba0c0000000000000001000000000000000034e8f7e0bebc4e6ceac176e58697aa811375831824b908ae505b1c454c9495bb000000000000007c070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c3000000000000000800000000000000010000000000000001169376cf9910dc70006e401d583eb448f676f7978c9fbc48f0fd9945e46bf0880000000000000001000000000000000104f3d987170da74ce53496364e567fde300685ff79c4e21bb13e7757ce0969fa0000000000000001000000000000000016272f17ce28ead10907c6a7e11148c815fb07a8f2c7466a14262dba2d40ccce00000000000000010000000000000000367224d91e71e557b706117a79190c66c507ee739167ee3d044f62d6d2bbd14f000000000000000100000000000000001e6037e47ecfb40b9c547b5ab8fa5579becc5b48be201cbcc71452a760171c88000000000000000100000000000000003e418307403fe6fe1bce279648b95a52998f64e4d2d1ec5d9ce71760f437c761000000000000000100000000000000002167607703c840ea4670ffd89100e17258197af01fd9154f758c8a4925ccb0c0000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000007c01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000010fe2ed69d080176a5e4b32893ee849d1215870c9d1728db8351267d933dfb2e200000000000000010000000000000001122db229431769d510d6c307e7b64116e26370ef409a5247e5923b9c5e1e16c0000000000000000100000000000000001434c37b1f36f8c91a0c3118132afdaa9f8f73daf94137b34b9d036a4add6e1b000000000000000100000000000000003af3839c09fe1805fdb7d6f233eccb3e513014f5b92ecf508b500bdefbe37a16000000000000000100000000000000003ca55dd4e43f5a3d40a1f1874445424009c4522ee5d8045b8e629b3d8e8923620000000000000001000000000000000005e603da4b71c6223d2ea56e8de57dd773cd4f377192b66a42c7d4d3b841ed85000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000003c30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b600000000000000060000000000000001000000000000000123a9e5133b194d59a456a493c04333f9a4cdf0640d22ae69eb0b775a50b0b3390000000000000001000000000000000110aad70905b23b30beac840232bd70dbf0ea06170c9d2af32d66073429ab4e8b00000000000000010000000000000000238b3a2f271a014633f4170ac01dc2033d706f27b31c89434c1de1c81050d2a100000000000000010000000000000000080adef57096b64f93f6fcaf10762b774b49a78ea6fd74fcb894144a9d2d1a62000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc59323000000000000001c168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b000000000000000500000000000000010000000000000001328f05cdc9b8078b13b76ea27114e1cf807c5793a76f17fca26ff44180c74988000000000000000100000000000000010bd471fe4be12abb3533f41af96568f580b03c4b9f9d3a8054de58228a5328030000000000000001000000000000000002ff6ecd20a354eb7e7f3113f5494a50512422acab4db02936777a1124b56655000000000000000100000000000000002d069ae31f4d343cc289900447ba6a652ca3e68f0d805b7631400c38eb019adb0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d2000000000000000c328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000010da17a76f1a3c069661874da0c08817195df46f0287894ad9f0443acdb2442d300000000000000010000000000000001376563708577b6963efcfb5fc5f26111e0dcec9da7560c0c651dc8cf834f482f000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000040af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000011aea55ec4fa23801eb579b79f0172980e491af816ea6cb6b06a493fff1af6de900000000000000010000000000000001023d495543e407e6376a45335b72abe709e25b55e8c72d3dcf10b9c6a92ea62300000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000001f10b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a00000000000000010000000000000000148c4c24a64067bffd66ea13c66d5dd8e941e60ef62ad251c779ebf8f7276592000000000000000100000000000000012345534d3b125eab63033730fbd099df7aac99039a2ab15b57a758e0d793623f00000000000000010000000000000001171ddbf41b690a7a341fa1edef5c906769493e8b514ef3ea71b02cd69781ba410000000000000001000000000000000103a7785b50fd85626681a1ec66ee36381e1c02ce213b7e39c95aee000c0fa5bf000000000000000100000000000000003643ad7b061e965a51a6011a9d069b838f317d213dad6c8ef851786dd1f3e2c80000000000000001000000000000000028e60ca56323a1fcf1a632e1100ddd4b4865a0ad69a07b6e725a2c74a08c84440000000000000001000000000000000021d58a47a0871489fb20abe61b73a07bfec02a8883caa75a1732947e1bcb26a800000000000000010000000000000000228fea4d19b77e682e45e503219a1c3b445fd980aef0c834a065b57e35ba33610000000000000001000000000000000036bb01a4b536947b566ca640bb47f9cdc8d60e8f94896645b59d38af2d1c98350000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000001f11ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000001ca36b83266c973015fbed48b869fba1b4b8ae82c674e37a86df40abe3a9919f00000000000000010000000000000001165312f71a9f29a1a03599d9535a8f6a2ce6ab4ccb550d6ad3cfba102bb9f4750000000000000001000000000000000124b1a1596d126d106dc84e26e23b8be580b1f1a1afd9de1a89677bb65de8c7520000000000000001000000000000000106f11f29259e4b3667d2e9b0f39b8dd734f093c8132fa3521e1ca0f54e359a8b000000000000000100000000000000001d447ac6f6282e54297eb6fa4cf46b2a9e60317cf6f97ead30fb097c6222177000000000000000010000000000000000078dc00c2b3fc647ecdac337d8ade7300e0359bd2d8ff7ded51047d4de6e198d000000000000000100000000000000000b6f57216ee6dacdb1293b892715e859545bedd9fcf29ec9005c73f95a3ed0830000000000000001000000000000000035163a28439443291e9afb63d08793c2dcaf1fce85499e2bac14751001ba95da0000000000000001000000000000000034e8f7e0bebc4e6ceac176e58697aa811375831824b908ae505b1c454c9495bb00000000000000f1070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000001b05c31d46ef736eabf49db41be28cc84b1bf261b64f3aba73a236b38ba8851500000000000000010000000000000001078a02834e7ef2f7da4d187e8082f20637a5a4dab457a9f3f389c4b56e20a57a0000000000000001000000000000000125571e76482c42a0a2e16fc60c5a0f17362b275878800e7b5c77ac9a456c16da00000000000000010000000000000001012b8da0e4135fa9880a5eccdc8b99c57dc339a7f728188e09cac6d80c13b85d000000000000000100000000000000002228115a0c1a059f15e980720cf36c13b0e7698b9c7cc1406e61c5b062a35813000000000000000100000000000000002efbfbe4495ad472da146d12bc6f9163a495ab797f845630bcd2ef8286241bf1000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000007101111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000002ef510291d079858dc3f0f156911b34929b39c2c9eaf0ca40bf9f20a5d26d822000000000000000100000000000000012dd91964006f883b751c45f92d264088dbb338355c05ef5a500182b2913b4d09000000000000000100000000000000011e38849e261beea70de7052b98b67998033d7ec06c2fceb2ddaa4806b641a3d00000000000000001000000000000000124bf60aa2ac2d4500188122dde95a9ee7e3b52f6eaba4c2a5666b71802f81ef3000000000000000100000000000000003ca55dd4e43f5a3d40a1f1874445424009c4522ee5d8045b8e629b3d8e8923620000000000000001000000000000000005e603da4b71c6223d2ea56e8de57dd773cd4f377192b66a42c7d4d3b841ed85000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000003130193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000002dc3c510113d77f10d2648f5f7b259fd21887da4b4deba3d5d69d8a3ddce2bdf000000000000000100000000000000011fe2256bcdb530721429aab6b860015129e78e884b9239631800f72f335b3a830000000000000001000000000000000129998cfb669b9e27c180c8e03ef8cf25a24440a1eabf13ed6e472018863e4f01000000000000000100000000000000013cc0589924693fb71be9fcad991f9c01c709d4589e84faf8ebd9348e0f7db1a7000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc593230000000000000011168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000000ce45dcf2cf0ddf10ff06373ab757a0b66f627870251144a3539980559b7745d00000000000000010000000000000001227c31cf90da32d6fce61cf46be98ea8f694b9926fa6849e5a80a7d2f3b8b5830000000000000001000000000000000139507992e986a491f2534d3dca8acf66c66a8c343c9ef7d22286880f378d7efb0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000001328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000001c5675554502108ad9db25b21338f8209442f30fa156b86d01833f0936e90f7d0000000000000001000000000000000128ba9c5a1b072e92873cfb3a9b932186cfc77285a078c0985a069f070a5b03480000000000000001000000000000000103bd687b49ebfd40ab80ddabb59fe40cb319484c5385cd704f72a3790b53af73000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000010af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000003cbd69854fff4563e3f658bd3dac272b47eac32adb9aac22c4ca4e880f19f32c0000000000000001000000000000000119cf15b83ff2024bc003e3daf89d828dc6386880bd775fc62ef85544d13cd3d0000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000002080b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000114a21f3c0b513ec999252d54e5c325d0b4d4c5cc4c2e12d2f2c9462b9873699500000000000000010000000000000001135178c04dc63312d1256dee5a0c7a61f2c2499441058ce294e9c659e9acc6ea0000000000000001000000000000000133272c985690c812d139bd76979fc48056f11e02873a42e1e3d1396632a4d68d00000000000000010000000000000000070737d0c81c619ad0cdc619525dac51cee710b37ef4d848035c14ffe9ca6ce9000000000000000100000000000000013e1342ff699f38118a1c7d410f964dd5bfb5bf82bf5887714b352ed65cb547a6000000000000000100000000000000012ba74d476a37b11effca98e5a1b0edbde4955b30239ebc607276bfc5bf969e4600000000000000010000000000000001013c2c8e56c6ef61c6c35b16e78eed58d0cd2f838c09a6c32c39f7c82965e9bc0000000000000001000000000000000117c617d6cd76595b8396a03633d137791828190842015a26c868e22e7373118e000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000000081ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000010dcd13341490b1948a0fd12147cc17a0832af0de0fd15f028bb5af695228c8ab000000000000000100000000000000013059eb3d9299e059024fb7d6153c49fdd38bee32551b143567989817669a4ffc0000000000000001000000000000000115ceb4e26f82ac3e22b0705b619bfc07946031053204ca2e10285cda1965b1be00000000000000010000000000000000276a12abdc2deecf4315f00f9c7d2a8c66737ca14ce5584a1fac820d1cadbeb20000000000000001000000000000000125eba54ed6e6210682263c51373bfc0d35e13859b32d35f2d97a76015e54e4e60000000000000001000000000000000105a5d40fbbff6ce2e7406938b91972d40756bdfcd3f007505adad1a1f78a5712000000000000000100000000000000013fa10dbb04e29d2e55150c9a53fc97cbc1db1768e7d46037360ace55030e35b4000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d590000000000000008070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c3000000000000000800000000000000010000000000000001305461aaaa6e288274c8968a36c21b1c28a9e2a54bde925f93c83a49374c4782000000000000000100000000000000010a85c3caf0485db95f7961012a519987589804be48d977de4ac10b7ac5684db600000000000000010000000000000001292d2d34d11a1d8cf13482009b3d6c0b73fafff2c1b404d6071d9410ab6dcd6400000000000000010000000000000000380fc34dca0c0c24443c5637112f9b2e3f6722c35cabf909495cda21aaa0836a00000000000000010000000000000001339431fdc34412d3c0f82b4c982f5ebfdd01db5bd0c237ee0d257fc0beddbe62000000000000000100000000000000011f551aaf89d55d8cb083318f77fe1377563e457155f20d794e8970c84e84c4eb000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000000801111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c00000000000000070000000000000001000000000000000101ebe9184e34b05bc53a4ed971f635313e53970376f8cb8feb4bd2293c685246000000000000000100000000000000010a9d0dfb62007c3257b43cf375b98edb4bde05fd1f98de67c12cfe7d2ae04ecf000000000000000100000000000000013434b470bccb25c4bd1851b6d755ad552e25ef4b1aaae96f0f192461562c1cd2000000000000000100000000000000001f19eb40eb785ef88494c74e7964e71edda6b6f20a57e8ea40cf403c6b9850b700000000000000010000000000000001385f47556d7f2e2b1815a940a6ab8f8dbf012abea3f2976afc8560ed0295c1c2000000000000000100000000000000010982eab0946956ec87034893e8c3bf7b8f206c65a9e455c14573686eaa80c268000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000000830193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b600000000000000060000000000000001000000000000000138fe1bc82e030061d63ab12815b0bc5572047823e38f8c9a56769c790104c9cf000000000000000100000000000000010a6d671ce0beadcfec5ca9b4908e208cb4f05eccbe2f06745621df9a602e5652000000000000000100000000000000011e8e5c074cc4945507af2f1bd241626988fb1b1bb4f81b23ed3c9f35e6203a250000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000008168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b000000000000000500000000000000010000000000000001176e41b38b34552de1f903cd051d629423d033aa0bf39e7c297fdac8add8ffdb0000000000000001000000000000000102456e910e45f0614463a941b29777d284d74dac773de9e3bc1772f4c13784180000000000000001000000000000000120440f8b110e92f0292fa2a28b36ec26f0df52c20eeaa387108aef9499694daf000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a0000000000000008328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001147ac9df64e19372615e96f13cd5974323cdd36c1ca92ba7768b815c5b6a642100000000000000010000000000000001326e84fe0ebf9a6da665dbf080a3528eb773022cdf498603579bb6148054afce000000000000000100000000000000010be9e65e8c8ac04e702d63135f34fdb8f27e6bce927a9467ae47b5e982c1b8df0000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000000af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000011e3dcf2a810c5dc050b5019b5a9cfb6e2defd3d67e476cf7b385945cd444d2640000000000000001000000000000000119cf15b83ff2024bc003e3daf89d828dc6386880bd775fc62ef85544d13cd3d0000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000000ce0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000013ad8fb5c8b3b26b1e27df88dda1a9cba7c6b07800d8b1ad665337b331fb482c8000000000000000100000000000000000fad7b7303a269770a24d19a942d0838b4f392269828249876a5673a253d0b530000000000000001000000000000000033441efe4dd210192cc1338d4f9c25eed88c9ea2f3d320f2d4f30f3752a330500000000000000001000000000000000018199e68dd6d47f365816fc9f8b61b56d080d8a48f8a086406b3c000a3c0e3a9000000000000000100000000000000013d91eb2f0455245c423338f0f22f99dcced3c30bf5a123f8c4bc2b94b75cf508000000000000000100000000000000013a63eb3ceeb7c0e6a918fe93e1ce7fc613d667892ea8f58aa114c7624a7ba8940000000000000001000000000000000037f0145e3b470f650c4a8459c6f734c4f11c0819bc7c8e51d7ace2f90a484f0b000000000000000100000000000000001d7259738c80273a823ca5d329f89b9d0f51a92e0bd9117414c51cab14992dc50000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000000ce1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000012516aa894030be1ac11ee203a4afaa6edb13add219835a66c295b7e21e3f7c04000000000000000100000000000000001226899c8ebcf1e75acd51e2bb01567534899039bd317e2ef7fc42bda2a17ae2000000000000000100000000000000003a9b344f95e65e2e473dd344963a2d2c67fb51bd22d98e96bb7e13fde74430070000000000000001000000000000000002d2efe1190ce34d0cf02ad05dc3bbf057dcc831aadafd12114be3248017bfde000000000000000100000000000000011ae3af7d96ca266e42967f7fa40a897ac78fe64f4aec22511448beff928729660000000000000001000000000000000117857ef5a99912e454d96e63a01e534bdc9bc2f6072bcb0f0333c2aff9df6c4c000000000000000100000000000000002ade54bf391d828df094d24ef9ea8439b6fce7ca1091f942806f1435b196efd2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d5900000000000000ce070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c300000000000000080000000000000001000000000000000128dd15aa8970a0b513b31dacfba1b338b8c068296f178a1e6f90e59998e439bf0000000000000001000000000000000005460408775bf715c89894c4633c3fce1eee2656c86809ac5be853b9a94586420000000000000001000000000000000038d1ee8a09f6f3fc371f74ca23a32cadbd8077bb20685c6c8827bc398e78a306000000000000000100000000000000000de6a0cf81f1735b3f1e1e5c481e2616cf881973f11b1ec54eda1ff56a5723f6000000000000000100000000000000012e2d22e5264bd4671a96e3c21ece563157e226a725d9d0b25cc672d1069b26e2000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000004e01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c000000000000000700000000000000010000000000000001045ff1568f1263899b2aca22f9ff0b88910bc73abf9eb1089e69760643baf457000000000000000100000000000000003c0f4527992e25aaeb1e242a97436dffb09ee632e3967d4df69f064322651dab0000000000000001000000000000000020339c53f4ba8f58c233b2ddd67d0d3f084716a713edf531cf76508ef5aa2e74000000000000000100000000000000003c7ee22981a176f07369929732497d393084af2e1f03328ad9faafb0626d2eb6000000000000000100000000000000011f0d82c86423e2dc3b65791258066b26c550c16204e40f554262d6476a741d6a000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000000e30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b6000000000000000600000000000000010000000000000001387d2b208d94bebc933043869d577de20c3d508eb46870e6bb78952e58b030c900000000000000010000000000000000238a3e8dce97bf4346149a9d5e4219d77cbfcec04c9f1b6091f3bea9a993915b000000000000000100000000000000003691fbfa0c2b33f8e31ce405962c381b6f891b121ca646e2f66a0c1a4dc1cb5d0000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d79000000000000000e168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000013181db0cd27f916e6a9211daf51877894298e56e280d835d2be09a43f1b0846700000000000000010000000000000000341a2b8a1b49734fcd7bbb4d75c03c067c33e82c9be1897d1f0578853f27b05b000000000000000100000000000000000f5b449679d27b00de38caa5c0dea420d83f888fee146e3af60fec3b88637b43000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a000000000000000e328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001080f2cb993bad5050ce59c1dfe9a5a35094e6da53383430e4441f6d0188dfd4d0000000000000001000000000000000011a90741a4cee5a5769213df86a17ee8cba21e1284becb7d919522073613bc62000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000060af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd000000000000000300000000000000010000000000000001343551a2eda24f9d8d6b54e02a884f603e8787bc28cf7cb0b5a26d54f4bb4750000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000000060b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000012abb16ccc279ba01749ad1faa1082e916b3cf603d85dfc6eaad146997076cdb90000000000000001000000000000000020a26b2c98c8f15e685cb98fd3d67ad89654ff1de6df4fd64832a1a4d7a32836000000000000000100000000000000001da04a998dd8a7596d40e28c427993116363cfd0aeb8c020a75f32854c99a8e9000000000000000100000000000000013fd36c234b46466a59c097ef2dd65c9c6b47696917b0951c365907bc68a44b1e00000000000000010000000000000001302bbfbebf3d35ad0beba3fec8d4558b0ce8b10aac6ae0abfb9f161f7e2a9a2d0000000000000001000000000000000139f2807b23fbc4c1f3a08eb8a888ca1df9c4c3a823892e518b6a41eef90f11e20000000000000001000000000000000125a84d670f7f0f120700b42feda3088440ef6cefb8c56ff7b019882e72011f2c0000000000000001000000000000000100d520b007248f767523293c387c2a20ce35f76655d1cf1d47a9e5f1c5d0e64e0000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000000061ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000012c667be9956da6f18f556fb5d8201bb72a4265c9374045d57f9e8b696bb780eb000000000000000100000000000000001ecb8eb2621572c5d3b8237381d874c45c8cc306c94b47b11b26c20e7c25f95e000000000000000100000000000000001ce1bbf1a1bbbe9fb947df611ef6cbf7d0d23160e22e023dbf52d162a2dd7f6a000000000000000100000000000000011eadb2a46f5ffd08ab0e346248f5ed0bdcbb4f8b12f195d38c1794c84a7adc0d0000000000000001000000000000000125eba54ed6e6210682263c51373bfc0d35e13859b32d35f2d97a76015e54e4e60000000000000001000000000000000105a5d40fbbff6ce2e7406938b91972d40756bdfcd3f007505adad1a1f78a5712000000000000000100000000000000013fa10dbb04e29d2e55150c9a53fc97cbc1db1768e7d46037360ace55030e35b4000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d590000000000000006070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c300000000000000080000000000000001000000000000000128704ea23ef719f48f95025f0ae188bf28a24808eb1169e1319987da1c04683100000000000000010000000000000000327168a821c5d8686925341b439b0f1ac6751123c66ca987b9dae154d80e6192000000000000000100000000000000000af96abb2096800f2f42be526e2bdb7e5d459c3dd6a5f5db91eefed1ca97fd63000000000000000100000000000000010c200407fc15e7707d0905900d8985135966ac81ddc81dbf0951eb09764567c500000000000000010000000000000001339431fdc34412d3c0f82b4c982f5ebfdd01db5bd0c237ee0d257fc0beddbe62000000000000000100000000000000011f551aaf89d55d8cb083318f77fe1377563e457155f20d794e8970c84e84c4eb000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000000601111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000010434514dfefb1def6b8dce8ae1ccad849a22da4bf828254703a5f2231098771d000000000000000100000000000000002a234b186f3b7d8f25376338171c5f60875c8ba8aedc088f57dac8d1af2f88f80000000000000001000000000000000030050cbfbe15bd94132089b80df061c274c29340ed58fb9825db2c31d4dec83600000000000000010000000000000001329ac44dedd5db0b874cb76f42c8c1a034eb84ce461f91588e0f1427419f9e0d00000000000000010000000000000001385f47556d7f2e2b1815a940a6ab8f8dbf012abea3f2976afc8560ed0295c1c2000000000000000100000000000000010982eab0946956ec87034893e8c3bf7b8f206c65a9e455c14573686eaa80c268000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000000630193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000012f41656fc4f9eb08c74ad4fe217aa20be78b0d8ba215a9ba6370edcec5dbdd8e000000000000000100000000000000000c756c83b81a106779e91fd74475e38d534b07e5f1fa739387ee49b9435c2f9f00000000000000010000000000000000273dc8a86e1d9e4887891bf6d0b929f3984a382cf0cdbb2b8c60b865a0d47fc70000000000000001000000000000000130b86a37bb53a344dc98b999424f501b3c323d808b081e0ccf15db9626cdd72d000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000006168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000011c25deb0875f839d4c5af6e84b6af3c16c2d523702ad0a201c66266f33e46284000000000000000100000000000000003ea66052e378bd0837956cf62e80f8ae5b0035356cc89d2cb50e79ac73c06b9900000000000000010000000000000000053dea0b078f18e76498d0f2663c0acdd9f4cd4f73d7cb9b8312c000346b39c6000000000000000100000000000000012e33a9d6cf3b0a93ec8e1a06d0ec991ed601477864623eefa49beb11ac24a8bc000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a0000000000000006328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001174a3b141514ed08aa06bd76e8b70e9739003efd24e8625df06061f9c17fd7710000000000000001000000000000000020b40adf8a19a6a1b3ddcbe792f1245aa674f47481365d0844ab2a5fc9fd4346000000000000000100000000000000002ecc5879d18568b10a5a4079bba13d9cc30403dc16a789a67e36ed735dffef8b000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000060af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd000000000000000300000000000000010000000000000001343551a2eda24f9d8d6b54e02a884f603e8787bc28cf7cb0b5a26d54f4bb4750000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d000000000000003e0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000010592a5c4787b0541bf381f1e80bfb9b50a127f3909d8de1fcfa0efa727852b5000000000000000010000000000000000310db29d201704fc94bfb60d63215dfa33bfaaeaded20e7912f8aa79506737110000000000000001000000000000000011bad44fa8c9581532dbadeee1e7c44ce64ccf06174ded5cc9cb959fcdca91b5000000000000000100000000000000003ae585e00fd65f86e0901dd059d8786014b3a9700cb354f404f5df1ab552eccc00000000000000010000000000000000397e4792aa19ba9a288d8da2c714e827b993d67e05237f234c27ab559fad1b250000000000000001000000000000000038c80ce71a12ba0ad61b0f46fcf76a4a9f66b5ea96a8ac6f3cc52988443d1f0b0000000000000001000000000000000125a84d670f7f0f120700b42feda3088440ef6cefb8c56ff7b019882e72011f2c0000000000000001000000000000000100d520b007248f767523293c387c2a20ce35f76655d1cf1d47a9e5f1c5d0e64e0000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a000000000000003e1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000010ad15790d4b8bed6fffd3cf7c174f56039bfa5fe58d0fd53ffa5eb38feff7c2f000000000000000100000000000000002e19718b821e7201ff4421b526ecb53076b4df92f26c3f464581e9825f143eb7000000000000000100000000000000003a598a4dc313545cb94455a187811eb5dba7e1f15f102055a394838ded40d3d90000000000000001000000000000000016b8f470077d339bb774de48a4d7b91ada55fef6953884dbaeb2262876642f04000000000000000100000000000000001aebeee00c03b18ef162bcac5c9b9a1299c76cbbbc2da88394cf203c594badc1000000000000000100000000000000003656939e26156db4291629f39fe2a2501f17e3eef5c4c1c2dc1ecfef56427180000000000000000100000000000000013fa10dbb04e29d2e55150c9a53fc97cbc1db1768e7d46037360ace55030e35b4000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d59000000000000003e070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c3000000000000000800000000000000010000000000000001235bd3db5a4a1b86a74f7c254da22079b0874cfd990d6f2a9afbc6ce12cc0e6d0000000000000001000000000000000008a4ec74c63c589be18798845b19000d3a2c6f0b5f2f5dba5de54e4124fc0be90000000000000001000000000000000022bfac860142e36e85b449e189f2bae08e626377d4c365b0becc4a615acf27a2000000000000000100000000000000001fc9561f8dac3532e7435426c39dbc191bed732c62ddc7ed1640d601c43ba02200000000000000010000000000000000319f8576925bb57d78ca608078d678d249a8971e008b9318a1aedb1af0ce20c9000000000000000100000000000000000b0f9797b7fc01e0f99e19fac202cf8e9f7d22a66f5f34b0dd54dbe91d8ae7ee000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000003e01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000013f72e54399552958a8a8e6cf960dce4136184951766ef8a621f979009c0110be000000000000000100000000000000000e5749ac1e98ed78adfd215d323f3256279b9391bcec8f3ec1001f4faf13ce8c000000000000000100000000000000001fd665ed2a69f2456075e2b9f0e7815ba536fd265fdd1b03f67c4978242feda7000000000000000100000000000000000873e6bf9506b1ad77620854fd2ec64f785fc86d89d91e582bd88c020637f48b0000000000000001000000000000000014e3e42c208ed7f12b863776f1f3362ac5893adf769836f84a1880bcd5fbb82f00000000000000010000000000000000125e7372803fb22d1929104ba7f6bb623f18bfbb32de6f06821b37ab7a94181c000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000003e30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b6000000000000000600000000000000010000000000000001285240016dd8cbc2fec9458a2d85b212e2d6addec46b8218689ce898002152a10000000000000001000000000000000030f8cf25196e72ba4a2953e2c5139095c40a01db90917259f99cab746e187d3600000000000000010000000000000000238b3a2f271a014633f4170ac01dc2033d706f27b31c89434c1de1c81050d2a100000000000000010000000000000000080adef57096b64f93f6fcaf10762b774b49a78ea6fd74fcb894144a9d2d1a62000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc59323000000000000001e168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000118e0ba09d1c059c99bdc0228dc96dc327e7d8e7d2b8b294fa014dea53cf559190000000000000001000000000000000027bd480a6b38e4d8207f1ec48b18ba3084ec00ce46384ca2e1ec8ab89b7bfdf00000000000000001000000000000000002ff6ecd20a354eb7e7f3113f5494a50512422acab4db02936777a1124b56655000000000000000100000000000000002d069ae31f4d343cc289900447ba6a652ca3e68f0d805b7631400c38eb019adb0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d2000000000000000e328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001080f2cb993bad5050ce59c1dfe9a5a35094e6da53383430e4441f6d0188dfd4d0000000000000001000000000000000011a90741a4cee5a5769213df86a17ee8cba21e1284becb7d919522073613bc62000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000060af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd000000000000000300000000000000010000000000000001343551a2eda24f9d8d6b54e02a884f603e8787bc28cf7cb0b5a26d54f4bb4750000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d000000000000003b0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000001cfae0a1827d552d5f9454d053e3f07b525ec35a58c93fd62172db8270c2d99600000000000000010000000000000000021367086d4381440b3e7e751781ebedbfffb4e6413ec9683e110c6fcd948d9c0000000000000001000000000000000122e12e04ed4e56e22324efec3efa2316a47c093bdcb9ed40a05dc8e6ffdc9bfc000000000000000100000000000000003ae585e00fd65f86e0901dd059d8786014b3a9700cb354f404f5df1ab552eccc00000000000000010000000000000000397e4792aa19ba9a288d8da2c714e827b993d67e05237f234c27ab559fad1b250000000000000001000000000000000038c80ce71a12ba0ad61b0f46fcf76a4a9f66b5ea96a8ac6f3cc52988443d1f0b0000000000000001000000000000000125a84d670f7f0f120700b42feda3088440ef6cefb8c56ff7b019882e72011f2c0000000000000001000000000000000100d520b007248f767523293c387c2a20ce35f76655d1cf1d47a9e5f1c5d0e64e0000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a000000000000003b1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000002f1640112d012248e1f6cafc32f34d3407e26ee84807fb4571b8418b471f0b1d000000000000000100000000000000000f8edab6811864898b0c83f4067ca2d27327ba299fd4bfbbd0f97976f4799b6d000000000000000100000000000000010ea1232ef56b7d3eb694afb8a134f41c46fa39d4505cabd6baa1c51d7c4ac6f70000000000000001000000000000000016b8f470077d339bb774de48a4d7b91ada55fef6953884dbaeb2262876642f04000000000000000100000000000000001aebeee00c03b18ef162bcac5c9b9a1299c76cbbbc2da88394cf203c594badc1000000000000000100000000000000003656939e26156db4291629f39fe2a2501f17e3eef5c4c1c2dc1ecfef56427180000000000000000100000000000000013fa10dbb04e29d2e55150c9a53fc97cbc1db1768e7d46037360ace55030e35b4000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d59000000000000003b070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c300000000000000080000000000000001000000000000000012b4fc3449aaf882fb6687e54886fbcc5941a79340f9309b8d62009f0399a73e00000000000000010000000000000000326cc6023db36dfdfaedfb53a842d78daec3c29e243411067af7474580693a10000000000000000100000000000000013c2095d1f1e8a6de0d49919b5a162641515c266c74d1ed3a64cae362c666e1a6000000000000000100000000000000001fc9561f8dac3532e7435426c39dbc191bed732c62ddc7ed1640d601c43ba02200000000000000010000000000000000319f8576925bb57d78ca608078d678d249a8971e008b9318a1aedb1af0ce20c9000000000000000100000000000000000b0f9797b7fc01e0f99e19fac202cf8e9f7d22a66f5f34b0dd54dbe91d8ae7ee000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000003b01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c00000000000000070000000000000001000000000000000002102078ea5212041b368ef2057b9676066c44d22d8b6d7839e7a1b5a9c93d2c00000000000000010000000000000000074dad56551750de60433d6cd4109fab55704720c1c6ccfaf740ec02e61bd0c100000000000000010000000000000001271d3431df899117e8c047b2d7da42ad7864d345e5503c9cf28aebb922382240000000000000000100000000000000000873e6bf9506b1ad77620854fd2ec64f785fc86d89d91e582bd88c020637f48b0000000000000001000000000000000014e3e42c208ed7f12b863776f1f3362ac5893adf769836f84a1880bcd5fbb82f00000000000000010000000000000000125e7372803fb22d1929104ba7f6bb623f18bfbb32de6f06821b37ab7a94181c000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000003b30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000002bc86cda10228ff5cc4eb3254ab9a974f421360f87c6d5ecf196e8e7fa3ce53e00000000000000010000000000000000223d75d9fa638dedbb5a6b9e8b62db582761b7a74d4e5f829b13b64ce43331b30000000000000001000000000000000121e58edb629756d342eb44ceabcabce0a03c1cd83f2321360045e3341bb7a3bd00000000000000010000000000000000080adef57096b64f93f6fcaf10762b774b49a78ea6fd74fcb894144a9d2d1a62000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc59323000000000000001b168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000023f1a657c762ca36e238cb6d1142fd19e105eff6d54f17a20c66525f072885540000000000000001000000000000000028c62434e05ee52fc20c840909edeb8eb6dfdeb0918d1b0a32561e4fb8144a58000000000000000100000000000000012ff366689fde744479aa397eae4d5a9e20e735605782584c945f515ce7e5898b000000000000000100000000000000002d069ae31f4d343cc289900447ba6a652ca3e68f0d805b7631400c38eb019adb0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d2000000000000000b328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000000024aaf3e90b415ddb8104d6d4c8819a64b4c7cb4817348ca77bbb0dbc2314d2c00000000000000010000000000000000318e7ff4146f9dd8b543f4317201a8244a89c3555652f8c6914a5ba045862651000000000000000100000000000000010be9e65e8c8ac04e702d63135f34fdb8f27e6bce927a9467ae47b5e982c1b8df0000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000030af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd00000000000000030000000000000001000000000000000032559649b9dd4c00d1b0a9fda4fe0425cc64652e7e36b838bd18422c55be46e400000000000000010000000000000000114898ab1790b60a81364775bb889beaf6adb7e668c5f50b87ebdf435ff7e717000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000000d20b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000115874e3df39bcb3f3cf38f6e28207d52f8959611675f95d99bd8b49d4b219ed1000000000000000100000000000000003641ed498bacd68ee7b528ab297d5cdb89420e7f980b63cab124c1b672215a0d000000000000000100000000000000011d88c94b6d52e99827d910a63dbfd0ec286c6118497c4ee319ee3cd24517ba78000000000000000100000000000000012c6fa058d1b3b9c29e370ee0aee1c5a72f834f6acd5a7e6e258517cb1f422adf0000000000000001000000000000000039cb1484264170128521b4e52fef5a3242c61480a5c78de528de7d3a493238ba000000000000000100000000000000013a63eb3ceeb7c0e6a918fe93e1ce7fc613d667892ea8f58aa114c7624a7ba8940000000000000001000000000000000037f0145e3b470f650c4a8459c6f734c4f11c0819bc7c8e51d7ace2f90a484f0b000000000000000100000000000000001d7259738c80273a823ca5d329f89b9d0f51a92e0bd9117414c51cab14992dc50000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000000d21ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000011f8cff3d839bb8db4f8183e2b3a0ed6078f33bcf68664e68d2a389d1ba6560ef00000000000000010000000000000000267578d225efea5e0620f6f73016206082f92a89c750919d13401f72f1392bdf000000000000000100000000000000011708984ecf2929a82a5581cabbec50e5e1529ce2fd6da2c0052ea41964500205000000000000000100000000000000010c525694206ab4ce89583183c70b17b9cb68bef6c319d41bc5108eb173fb74d2000000000000000100000000000000000aaabd4ac9b10b26d985f63148873ff2322c79348a33e53875e51f01d4452de30000000000000001000000000000000117857ef5a99912e454d96e63a01e534bdc9bc2f6072bcb0f0333c2aff9df6c4c000000000000000100000000000000002ade54bf391d828df094d24ef9ea8439b6fce7ca1091f942806f1435b196efd2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d5900000000000000d2070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c3000000000000000800000000000000010000000000000001197747011fb391060244ec22ad81a6de8aa0bc9e892a6acedb9e8270a86902400000000000000001000000000000000014348167200bd79d55cb9eda1e30199c2c64819ebb53c909c894d63d19703c0900000000000000010000000000000001215d9adfa096853525d47306dd5554ae146831a51d1b5cb49242df815be8ce410000000000000001000000000000000127102d633c2272d9f18ca399aa68f7b90ca205a8668bccaa0376f720861ab4780000000000000001000000000000000005be30474d69373fb6e0292cbdde998e7a892c35dab4115f0cd9f93b70143e6f000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000005201111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000010a49db0ada26b3aa19f0e1e81f0f7aa71e2863bf2dbd7719bee34e9e1537d2120000000000000001000000000000000018eeef941c42c7d7547c8e4d2b730436d0594b6997aa60c4abf4b082600e8c6b000000000000000100000000000000010ea5d804cc3c6e9bf32a069d94baf3785632e8b4f8fa79d6a61d87cc2b20998800000000000000010000000000000001267767242dd3f73d8955b1a14440dbd85c0efdef0394ba60b718375422e104de000000000000000100000000000000002a6c75d446856c1602dcb0f9e48cae1722fa7750ceb88f7d5ed8d2fe2655df3f000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000001230193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b600000000000000060000000000000001000000000000000128ec1017a1009a15da6137d4ce5b5b3df6bdb8a893392dd699608a48dd25986a0000000000000001000000000000000000278f18127e69a593a71e2b15596d5625aec1995a0d6d5994294713b77cc8ba000000000000000100000000000000013c70fd0479bca1bad293c19a2d3ce0e542911599efa9e8c7e4e4c015a413b88b000000000000000100000000000000010c4af2fb47ab2ede2c78bfef909f170edf1347554f7619e46879ab782f16a6220000000000000001000000000000000007bd7fdf329b200bd8f58aa027b3e4da8cfd98c6fa207cc856dd5af628aac2a6000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000012168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000135d2a2e79e9ac3b424c80103e045952d4fe483e44bf2926882214ca4b977d10d00000000000000010000000000000000274d96f3962c79fa75d870dae4034052ab15a3cf0f6bc631a4d8224d9d2d945e0000000000000001000000000000000139507992e986a491f2534d3dca8acf66c66a8c343c9ef7d22286880f378d7efb0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000002328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000010c8f71494f390d0995dec2b11cb331231dcc1846a42c3ab89bdecea56ce3693200000000000000010000000000000000203c09d8af4e0d778510b5513d6799e058d34e51473c25f2bb9b197f94b669bd0000000000000001000000000000000103bd687b49ebfd40ab80ddabb59fe40cb319484c5385cd704f72a3790b53af73000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000020af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000010724e9a997b5bda94917daa6be96435879bc6c72df5ae2f6ff320c9494c186a800000000000000010000000000000000114898ab1790b60a81364775bb889beaf6adb7e668c5f50b87ebdf435ff7e717000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000003d00b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000126b999835f9d26e7f85d5c82a2bdb8c7c8f8e6a9179b29e6f559746beb4b6fd30000000000000001000000000000000114a8a9bff003cb5777b4e1709b9104aacb06d2525c56dc76a038a6bd158759500000000000000001000000000000000123829d75af31336e23d2ecae2c0a8b5bf4f9a62a08e7480d4307172104810d71000000000000000100000000000000010d3c6a8b380efcd8bda64390bf43f87452fc980a6ec4fb2f2ad2f855036111f10000000000000001000000000000000036b8c7cfafc95db9bd588bd886e00b7d4d54afe119bca0e48040355b209e88bf000000000000000100000000000000012a077a3728c6157ee3f7e8330d938a6a092fe41c8bb55ab8bb236cb40b46691400000000000000010000000000000000247baff5ab03a8cc57dd58caac50a037ad8fa0c00f42c012ddf9dacaf838c8d9000000000000000100000000000000000e06b4e1033f6851960bd438f83f21fd1fec62f13c36d56db39ca7e641129ad1000000000000000100000000000000001d87165ddde6d54f54eab67360861795670b48edf536440c746b5071d1e7536700000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000001d01ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b05836188900000000000000090000000000000001000000000000000119797d12580984ea0dbea09872831d3c62ad111d2772c50b6e243d717cea8c1700000000000000010000000000000001374b91a4bc33e7618cf34eec507a6c3e763396321432b9e7755fc32031ac3b82000000000000000100000000000000011c0a8e49b0950d35ffd3cc21d34208135a1eda12e59aac676eee2ca2c9ea39400000000000000001000000000000000102699ecc3c159cab083b031d6d6f4c0a74728df675396b2ae4aac6994fef1ddd000000000000000100000000000000001b94ec12ceda3dfcc6ca666f1330a1a51fc7502978d4f972bd1f1daf037f72cd000000000000000100000000000000013294e2e6947bcfbfc65ae3dc2ff2b8baee0d5f347f8703e01f29c70b664b3c38000000000000000100000000000000000b6f57216ee6dacdb1293b892715e859545bedd9fcf29ec9005c73f95a3ed0830000000000000001000000000000000035163a28439443291e9afb63d08793c2dcaf1fce85499e2bac14751001ba95da0000000000000001000000000000000034e8f7e0bebc4e6ceac176e58697aa811375831824b908ae505b1c454c9495bb00000000000000d0070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000013aff29dcd8f7d1ab67f78ff49ca222645373833b7d5d77caa8e7e68b629d6242000000000000000100000000000000013affd6261c946fa2040deccd0f87c28bd45337a62ca55296b0e7232a2a4f203500000000000000010000000000000001215d9adfa096853525d47306dd5554ae146831a51d1b5cb49242df815be8ce410000000000000001000000000000000127102d633c2272d9f18ca399aa68f7b90ca205a8668bccaa0376f720861ab4780000000000000001000000000000000005be30474d69373fb6e0292cbdde998e7a892c35dab4115f0cd9f93b70143e6f000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000005001111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000012e656693897b7747d28dcb037b365ca3ac978bdb1a28ecc0ffc52842d31e9a4f000000000000000100000000000000010d208671d56884924e06e5eb6ee5a424de945ac4e900f016352b5bf390cdac9e000000000000000100000000000000010ea5d804cc3c6e9bf32a069d94baf3785632e8b4f8fa79d6a61d87cc2b20998800000000000000010000000000000001267767242dd3f73d8955b1a14440dbd85c0efdef0394ba60b718375422e104de000000000000000100000000000000002a6c75d446856c1602dcb0f9e48cae1722fa7750ceb88f7d5ed8d2fe2655df3f000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000001030193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000010ab5a450e62bc08c8bb4a4292c13d9fab85a6f4541dba9b9c0a9a4f2f2cbf8ee0000000000000001000000000000000119e2b22bcb290c66b9451d25dba1d1b31eb647069ae9abc3af3e8918b4fd5f6b000000000000000100000000000000013c70fd0479bca1bad293c19a2d3ce0e542911599efa9e8c7e4e4c015a413b88b000000000000000100000000000000010c4af2fb47ab2ede2c78bfef909f170edf1347554f7619e46879ab782f16a6220000000000000001000000000000000007bd7fdf329b200bd8f58aa027b3e4da8cfd98c6fa207cc856dd5af628aac2a6000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000010168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000010fa7311baa29cc40ba2eb2bcfcd1b2f0486c5c8bfd65e463d592fcd6465e7b2c00000000000000010000000000000001227c31cf90da32d6fce61cf46be98ea8f694b9926fa6849e5a80a7d2f3b8b5830000000000000001000000000000000139507992e986a491f2534d3dca8acf66c66a8c343c9ef7d22286880f378d7efb0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000000328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000012470ef12783dc2ef467bdb15d0f6c2fb08c97ca3c7d6f17e4b60d22bd31d0d1e0000000000000001000000000000000128ba9c5a1b072e92873cfb3a9b932186cfc77285a078c0985a069f070a5b03480000000000000001000000000000000103bd687b49ebfd40ab80ddabb59fe40cb319484c5385cd704f72a3790b53af73000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000000af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000011e3dcf2a810c5dc050b5019b5a9cfb6e2defd3d67e476cf7b385945cd444d2640000000000000001000000000000000119cf15b83ff2024bc003e3daf89d828dc6386880bd775fc62ef85544d13cd3d0000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000000330b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a00000000000000010000000000000000133968973019ed491cf09438b16a0ecbd4d317a0bb5d32f86883189ab48c762a000000000000000100000000000000003166f867805ac4588c045157d7db3a9fd95c2b8e368b80bbbb0c138c695bc02f0000000000000001000000000000000121fa57982fc8a0f9618c729d1a86026d7e6b2763c9f01e0ce872b2e86317edc2000000000000000100000000000000010a4c27a14a2a086e993e0bde4ceae363f0babef3811a724f8d1414f89857fe7700000000000000010000000000000000397e4792aa19ba9a288d8da2c714e827b993d67e05237f234c27ab559fad1b250000000000000001000000000000000038c80ce71a12ba0ad61b0f46fcf76a4a9f66b5ea96a8ac6f3cc52988443d1f0b0000000000000001000000000000000125a84d670f7f0f120700b42feda3088440ef6cefb8c56ff7b019882e72011f2c0000000000000001000000000000000100d520b007248f767523293c387c2a20ce35f76655d1cf1d47a9e5f1c5d0e64e0000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000000331ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b058361889000000000000000900000000000000010000000000000000326c897713c2de168c874ae24a790290f0222911e017be232b9aa2c8bec6a20200000000000000010000000000000000360e58cae8a5e257ffa72b7abc5abc0e4d725b8f09158909e4a45c1b4b64b267000000000000000100000000000000012019428ccbd0b891951c1b1ae852e8639021d208a65dcb453d82ef05a2e3c4b3000000000000000100000000000000013f3dbdf89093305344e9d2ed8a24fe24543f81363acf6fed41df05a3f7495e7c000000000000000100000000000000001aebeee00c03b18ef162bcac5c9b9a1299c76cbbbc2da88394cf203c594badc1000000000000000100000000000000003656939e26156db4291629f39fe2a2501f17e3eef5c4c1c2dc1ecfef56427180000000000000000100000000000000013fa10dbb04e29d2e55150c9a53fc97cbc1db1768e7d46037360ace55030e35b4000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d590000000000000033070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000002c5fc525f61ad81ec62b5c44c0e796e484bbd777347a51579bde3bfd0c760fdc00000000000000010000000000000000144f93c33a3dc8774009ac9b6ee886d370b6a1d88c893847e816ec0474777eaf0000000000000001000000000000000133324b1fc3841667cd45743c3e83e7950157db733647a3f255f3df641e67bc98000000000000000100000000000000011ac29b44a33a62d2107180580ae3a5764b6f4b6a573c229d81bea778b13e0c2800000000000000010000000000000000319f8576925bb57d78ca608078d678d249a8971e008b9318a1aedb1af0ce20c9000000000000000100000000000000000b0f9797b7fc01e0f99e19fac202cf8e9f7d22a66f5f34b0dd54dbe91d8ae7ee000000000000000100000000000000012c9aa9cfbe6a352e0df5a75f2777cde6c4dbacb016df34b11350930c11542df4000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000003301111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000002e751dae7c829ea6b036b8662f835dd5e76b8a614c14ad28d4803b0e07c89ce400000000000000010000000000000000252658d8397b9fdbf8d6da2a132b32e17ad5b114fcb85fa0585fb9efb50b15df00000000000000010000000000000001128fd4ea15befa84e2e845d0a414dc1e2193b610ad5f18d3c542439b2e0b27f50000000000000001000000000000000104b7782a10d61ab567baf6fa63cc1c4ee192375a3376796475f7a35454ee9edc0000000000000001000000000000000014e3e42c208ed7f12b863776f1f3362ac5893adf769836f84a1880bcd5fbb82f00000000000000010000000000000000125e7372803fb22d1929104ba7f6bb623f18bfbb32de6f06821b37ab7a94181c000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000003330193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000001f059d0d822fb7fdde8f716ce33839224b031de0c1b9ee6dd485b41fb35647920000000000000001000000000000000031b2af18b8c17bb5d304f2b41bcb85ba65dd3a276d63f2058fee36b0720fe22d0000000000000001000000000000000129998cfb669b9e27c180c8e03ef8cf25a24440a1eabf13ed6e472018863e4f01000000000000000100000000000000013cc0589924693fb71be9fcad991f9c01c709d4589e84faf8ebd9348e0f7db1a7000000000000000100000000000000003eeca8244f12c25f96d6cd59e7f96cb8d3cb4f97dc3174c6379946b381b653ad000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc593230000000000000013168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000006c1a1396a3d6e3e0baaa11bf66bb5f1c0c9af0830dcc8da1f99ee0a05f13a8300000000000000010000000000000000274d96f3962c79fa75d870dae4034052ab15a3cf0f6bc631a4d8224d9d2d945e0000000000000001000000000000000139507992e986a491f2534d3dca8acf66c66a8c343c9ef7d22286880f378d7efb0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000003328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000003eae36b2c30156ab30a8473944f348acc401331e509125116a661186f59cdcd500000000000000010000000000000000203c09d8af4e0d778510b5513d6799e058d34e51473c25f2bb9b197f94b669bd0000000000000001000000000000000103bd687b49ebfd40ab80ddabb59fe40cb319484c5385cd704f72a3790b53af73000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000030af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd00000000000000030000000000000001000000000000000032559649b9dd4c00d1b0a9fda4fe0425cc64652e7e36b838bd18422c55be46e400000000000000010000000000000000114898ab1790b60a81364775bb889beaf6adb7e668c5f50b87ebdf435ff7e717000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000002d40b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a00000000000000010000000000000001069f2d8698da2b0ebfa9eec6df71acf7341f6ff9c23824c42f78db668177d71f00000000000000010000000000000001232888c70aa5ce15883fb72149d74862ae31195de17db6f243bb60fae0e180570000000000000001000000000000000004f531ec1f66283efe30b2c828769c5d74ebe13cce655750f647f77dda07e0f6000000000000000100000000000000012a2a78334a3d1015bc25d49ab88b2d944d130a46fb7b7526c85f515a554d60820000000000000001000000000000000034ab6ee0e0550b0a8a46cbf4d7426a15ec9a3ca0a71ddfaac3fd5db609b683d40000000000000001000000000000000136c308593f3e476423148961b61f49ad69f39eeb38e6809cfccebf8603e3c98b000000000000000100000000000000001a2e05aa8185136862995e5ec9f2927063d2dff30a733396ca19f76689aa133b000000000000000100000000000000002997304c67ce3176a517bd359d9b0c8e1ce7045e1cb1080a27ea3aa30e8a16d4000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000000d41ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000011edcddf3d7310929af863c6dafa0de3aebad35bc367eeb90cfafa7bf94ebc656000000000000000100000000000000013ccbdabe3f1b1094af13cb16ffaa989ea3d2c5da72f9647e6a599a6c1685a2290000000000000001000000000000000027ec35cb006ce41b6529697459c52c1e564e1af7eb50896986f6c8dcf5faad75000000000000000100000000000000010c525694206ab4ce89583183c70b17b9cb68bef6c319d41bc5108eb173fb74d2000000000000000100000000000000000aaabd4ac9b10b26d985f63148873ff2322c79348a33e53875e51f01d4452de30000000000000001000000000000000117857ef5a99912e454d96e63a01e534bdc9bc2f6072bcb0f0333c2aff9df6c4c000000000000000100000000000000002ade54bf391d828df094d24ef9ea8439b6fce7ca1091f942806f1435b196efd2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d5900000000000000d4070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000013b2b1e9de8c2b895f09cbd7b9ae5055e86f4d59adefe7573cf3581948643a92d00000000000000010000000000000001165301436349dcb3c5e167c974182341112c9ce3b5a461735ce697236d5b6283000000000000000100000000000000001b98acec691c93f17859729aa158007dd4192147810fb154bbfb9f4b086f205e0000000000000001000000000000000127102d633c2272d9f18ca399aa68f7b90ca205a8668bccaa0376f720861ab4780000000000000001000000000000000005be30474d69373fb6e0292cbdde998e7a892c35dab4115f0cd9f93b70143e6f000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000005401111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c00000000000000070000000000000001000000000000000116dfb882ed00f51ea27d52a4e8ff7cd75154f29779b30e7895756646b18efcdf000000000000000100000000000000011712be839f9410e98562a5c8ef452615bb0079204adf1628a5da952c059bc0fe000000000000000100000000000000000bd95bcbce4b7326162307cee92d943ae0132052be0561cb0ff5bc61a0b3d28200000000000000010000000000000001267767242dd3f73d8955b1a14440dbd85c0efdef0394ba60b718375422e104de000000000000000100000000000000002a6c75d446856c1602dcb0f9e48cae1722fa7750ceb88f7d5ed8d2fe2655df3f000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000001430193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b600000000000000060000000000000001000000000000000105d5fcb566a5219f64d9416ad43e0d8afb626fe0f9efc09eaaf122da3aea451a000000000000000100000000000000012df9cf61ae21f98d5191472b6d1dcf9b3524490761a6acf46e31f07f58f630af0000000000000001000000000000000025eb52613116b045e112679f92ae4002ce6c12668944c083ea77f7247af72090000000000000000100000000000000010c4af2fb47ab2ede2c78bfef909f170edf1347554f7619e46879ab782f16a6220000000000000001000000000000000007bd7fdf329b200bd8f58aa027b3e4da8cfd98c6fa207cc856dd5af628aac2a6000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000014168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000011b9694936d0e7ddbaffbf60460b926cae68df80d8c7be7bd311ea0996cfd0fd30000000000000001000000000000000103563a5953258ec918ef310a34d833cd33a6c5a0c323893c0bb36d42185cbb4c0000000000000001000000000000000035b06fe68180b11ce8c6cca7dd6225d107e41aaf844e9e7c08f41defb39a9a7a0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000004328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa100000000000000040000000000000001000000000000000116311d2c1d458e687fb30effe6b71a3a418cf1bb3b450998265baac224cb7e13000000000000000100000000000000012ba5533feeb77466d68d3177e712d1374476a8116dab6d557ad9ddb04638aaa8000000000000000100000000000000002ecc5879d18568b10a5a4079bba13d9cc30403dc16a789a67e36ed735dffef8b000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000040af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000011aea55ec4fa23801eb579b79f0172980e491af816ea6cb6b06a493fff1af6de900000000000000010000000000000001023d495543e407e6376a45335b72abe709e25b55e8c72d3dcf10b9c6a92ea62300000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d000000000000029e0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000011edbb45a5dab9efd643cfd7b3cd84ba16c1196f357da96e3d528716fb92bf6bf00000000000000010000000000000000017da97f63b8478546828d7a86a01248952a9ffcfa8b83a2f8ccdf49cf3598620000000000000001000000000000000000d528594928c35e7030fadfcb0fbeade78167ccc3da4c699adcb3221431a974000000000000000100000000000000000c9083504dcc82170efe6671d7adacfad9b5366a7b12f96f0d8d6d8f6f97781600000000000000010000000000000000110f926b435cb40c0a12b7509169642512650884dab75a00302783f3dca19eb6000000000000000100000000000000010e5ad09311531e8b29e9b80ef3e17f4f0369f1191918c67c5c2ba10303d013a100000000000000010000000000000001315cbb678ce14a6ed5c5d6f87589ef23c278577bc878bf8671dd3fa016e8e511000000000000000100000000000000002997304c67ce3176a517bd359d9b0c8e1ce7045e1cb1080a27ea3aa30e8a16d4000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a000000000000009e1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000013bd5e6ee5b7bd832b12254f6ac7f26b0e2b41fee33e0e81abdc21ebcdefabdf3000000000000000100000000000000003c222c85bc6c72d80cdf49db44c4782c7cf8d37924de3bdd9b9084538d29e1100000000000000001000000000000000004afdbd1b094d4cdea4d8e8a6905aee7bba4383a73a6e1f7e6ea31073be80fcd000000000000000100000000000000002f20511647091c22134a3cd95bfa45cd87f261c61d6c59dadf1053d412d88ffe000000000000000100000000000000000d4027e15e8857298135db5c3533f77663958c2a2e4d2012f668481734187f0300000000000000010000000000000001380fab801ea69ccd54ae7b2928bdd7c506c053135851552beaa132f577b690ec0000000000000001000000000000000130c79da9b080ad82e77544933e508d30c5e62d8f6d17dcfc1af0bcf0b0f416e2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d59000000000000009e070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000012793cd14f0c36521d13cc3de2a8e07a33e4d98b068657e5841857494ca13d69a000000000000000100000000000000003c25e46310fbb3f89522bfb669852537bb091f976142deaff5762b095b7a2716000000000000000100000000000000002df3a839e0f003304cd32574337b68b093eb0e2ff42173e11c1de78363a2988b000000000000000100000000000000002cc9b45d03dde2e2e986b7e7c685771d1968169a778027b2c8d1187cabebbe83000000000000000100000000000000001a6ff00f76e8926c7fa117b37b1bf46cb56df5a5d380f34f0e87b36f9d488aac000000000000000100000000000000011b47168e88b10d36560ec9609b38a1f7f2fc579c302e6341edf246920547a1b20000000000000001000000000000000111da9f4e5c4795bfaf0ebc7bc2f905f8802c356278a5153c39275b4138eb952f000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000001e01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000012e748b27efdce38217d9a99ca9902fc05d10f00f2ff4ed4b016fe3c7caf74a7c0000000000000001000000000000000010f93ecf721b1a67c24aa44a46d8ec5417fcba0fce34c18c6416bb3229846741000000000000000100000000000000002fb236ebc546e47f00bae61e1eb747869d6f956cb46e0e235b1fd6ce12a7988b000000000000000100000000000000003cb2f38c8929e4dda9b679c34fc23ab67f2de862f287b02e6a58391e12fd59f30000000000000001000000000000000029597ab7794d39f373ad8c40935e3671c95d263f79266858b37fc8e7bdfa8dbd000000000000000100000000000000010982eab0946956ec87034893e8c3bf7b8f206c65a9e455c14573686eaa80c268000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000001e30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000013a2136f77db57a1737f963c7cea5b7b0f848fca1c608a48b1462cf77fb3979bb000000000000000100000000000000002eae76f23c200119d9bc2528d5bd4ea025e6832468153a24e5f0fcf65153b160000000000000000100000000000000002dbc0afd38ce520f08754e2fbdd9ae881631f8fd15e91b325867f913ccc4c3c40000000000000001000000000000000010671a6a32bdf5c34d549df26323619b3be22bd79c274b57ea3e50de195527fd0000000000000001000000000000000007bd7fdf329b200bd8f58aa027b3e4da8cfd98c6fa207cc856dd5af628aac2a6000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d79000000000000001e168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000118e0ba09d1c059c99bdc0228dc96dc327e7d8e7d2b8b294fa014dea53cf559190000000000000001000000000000000027bd480a6b38e4d8207f1ec48b18ba3084ec00ce46384ca2e1ec8ab89b7bfdf00000000000000001000000000000000002ff6ecd20a354eb7e7f3113f5494a50512422acab4db02936777a1124b56655000000000000000100000000000000002d069ae31f4d343cc289900447ba6a652ca3e68f0d805b7631400c38eb019adb0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d2000000000000000e328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001080f2cb993bad5050ce59c1dfe9a5a35094e6da53383430e4441f6d0188dfd4d0000000000000001000000000000000011a90741a4cee5a5769213df86a17ee8cba21e1284becb7d919522073613bc62000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000060af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd000000000000000300000000000000010000000000000001343551a2eda24f9d8d6b54e02a884f603e8787bc28cf7cb0b5a26d54f4bb4750000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d000000000000006e0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a00000000000000010000000000000001297144af1a7dc5c981b0f383bb5b8532d6ad6b5c65ce89e5bfcffdbbe84708d3000000000000000100000000000000001455ca2ebf52b1165d15c76204b9778c4a2074d66aa6254bebbf1e47f22c314000000000000000010000000000000000079d90b71707ff7b65e980c9214e6fb0f414e7bc54af0ab1a2e0f79f94fa55f60000000000000001000000000000000026d98a29e71d685e299faee07a8270a90cb1c2724329c61d993dd587ad8d8c9a0000000000000001000000000000000109c063e92c71f33cbdf34443a290af4b47dfa4f47960bfe218e948e8e1328e79000000000000000100000000000000000e5046be245530e67a4ae8772f15a9ae3bb37fe36e83d4760e1164ce761158290000000000000001000000000000000030fd6f710f5f58e7b527335b0f2f5142acb0ac5dc9074ba6a21d5289eced077e0000000000000001000000000000000100d520b007248f767523293c387c2a20ce35f76655d1cf1d47a9e5f1c5d0e64e0000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a000000000000006e1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b05836188900000000000000090000000000000001000000000000000138f86fbb90b18b3870aeaf7b3e9965572f1dd8662c6d3837611a125dabb778a90000000000000001000000000000000024468ceb51e1b3a38015c2996ff0dca4d83ec69595b1886f92d949d2f07c68a20000000000000001000000000000000028a208addf3161c499fe1fe2bb0607189d343dcaafa7b4b3f9dc89cdacc4b252000000000000000100000000000000000bc1dba8ae65f3b8ffa704ca5f6dfafcada1be84b6bea75f5123c894905387a9000000000000000100000000000000012835ffa0a4bea54603313f92fdcc88b4620baaca9150f838337b07d7ba7fefd100000000000000010000000000000000166617bfd0ead8bf097d082e55711f7f9d4a33a8fcb113b94ef42cfc80705560000000000000000100000000000000002597be56c8553c2f0aec7f6eead4988440f231aa5dc89fa25f1fa07e517f4a00000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d59000000000000006e070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c3000000000000000800000000000000010000000000000001387cb1ef5dd26ed325e0bb035567abbd461ed259e3dbba3ad5abeff843e5baf5000000000000000100000000000000002bdcdf5ca171d0c20686f349e7df093bb9090e4a8833d7c993c6da1ae2b227f1000000000000000100000000000000002caf9567c50a8d4aacb18be70751f8557dfab88b7bd501284f4792c4e42774ee00000000000000010000000000000000363a0a454514b1f210fa076a3e1a9a3d67ad4f3eaa6587af4322374f3e8b7e820000000000000001000000000000000109761955a165c95c3cfa61530c6219e92df4befa3bf22ae7f432bbf060b8af30000000000000000100000000000000003e418307403fe6fe1bce279648b95a52998f64e4d2d1ec5d9ce71760f437c761000000000000000100000000000000002167607703c840ea4670ffd89100e17258197af01fd9154f758c8a4925ccb0c0000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000006e01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000013fd38a41c1c46fdac4d96e6e5eeed480066c50b4734ae26db53c5db1fe6a0f770000000000000001000000000000000005b482cdc93ab11f2491f779e1d7abd7c1c7a2813872e2eb97855db9a8179683000000000000000100000000000000000402533de8443d03084b258d869376da92624135f4141006e1ad79b46e448b650000000000000001000000000000000010d2cdba06917742d7acf8a5f66676e741ad78eed3d102f37b05a0eda51dbd76000000000000000100000000000000010ce23376a1b7df9a294f51c190c0890cc513e20e0fcfec75aab480f6c6c455cd0000000000000001000000000000000005e603da4b71c6223d2ea56e8de57dd773cd4f377192b66a42c7d4d3b841ed85000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000002e30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000010654cacf2fe8bf098bb2bb8d3cb6de6fb88512a2099fe43ac3741064f1f023610000000000000001000000000000000034978b901d918f538ad133f00628a36f67aa17567f3330f34dd37215a2925c98000000000000000100000000000000003c9a3507b792044793e4d69f180b8f5e84a0c0ab16e20d25190481bd6350dc69000000000000000100000000000000003fa2801a234255b480529451917cb439116b90e40346c02d8c4d6c5ed57d655400000000000000010000000000000001194d633f3059d1ac950f86b7775296c791f7ad97904b3b6dc7ffe864f7ea2cea000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc59323000000000000000e168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000013181db0cd27f916e6a9211daf51877894298e56e280d835d2be09a43f1b0846700000000000000010000000000000000341a2b8a1b49734fcd7bbb4d75c03c067c33e82c9be1897d1f0578853f27b05b000000000000000100000000000000000f5b449679d27b00de38caa5c0dea420d83f888fee146e3af60fec3b88637b43000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a000000000000000e328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000001080f2cb993bad5050ce59c1dfe9a5a35094e6da53383430e4441f6d0188dfd4d0000000000000001000000000000000011a90741a4cee5a5769213df86a17ee8cba21e1284becb7d919522073613bc62000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000060af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd000000000000000300000000000000010000000000000001343551a2eda24f9d8d6b54e02a884f603e8787bc28cf7cb0b5a26d54f4bb4750000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000002c90b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000000c015a6b98db893bdf06fe52c35ef846bebc8774f732b4caf9a22a38de4811c6000000000000000100000000000000011c0e5750c8a2979864ecba0a336f18f1c0d29c8ee5f0450669f5eaa78b5b7067000000000000000100000000000000013dbb9452514622c80097f86751930533e7fd125f3dd82cd4c8e93dbe7f16b80a00000000000000010000000000000000066114e4619540bef3c050f85ce1113a4d9addd2b24e7d94686903bf7e8cf7f80000000000000001000000000000000110ed63c4b41ffcb717167fe027aae6093cde8b42deb51b9d74ea9dddaa5046210000000000000001000000000000000136c308593f3e476423148961b61f49ad69f39eeb38e6809cfccebf8603e3c98b000000000000000100000000000000001a2e05aa8185136862995e5ec9f2927063d2dff30a733396ca19f76689aa133b000000000000000100000000000000002997304c67ce3176a517bd359d9b0c8e1ce7045e1cb1080a27ea3aa30e8a16d4000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000000c91ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000000da9236fe7cf5cefb863602167aae07b7bde4b9b5f1fe6b677c54cb6e7b0ae13000000000000000100000000000000013a789f43f99f9800b474c336437dc25fef3eb829f1553458b8658fea2b6ffd650000000000000001000000000000000133a68d123338d80416a3424dd5a31787d6411fdeb5bfbb9d9657a3a030cae7920000000000000001000000000000000002d2efe1190ce34d0cf02ad05dc3bbf057dcc831aadafd12114be3248017bfde000000000000000100000000000000011ae3af7d96ca266e42967f7fa40a897ac78fe64f4aec22511448beff928729660000000000000001000000000000000117857ef5a99912e454d96e63a01e534bdc9bc2f6072bcb0f0333c2aff9df6c4c000000000000000100000000000000002ade54bf391d828df094d24ef9ea8439b6fce7ca1091f942806f1435b196efd2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d5900000000000000c9070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000002396e3503ece8457e36d54b4c8aa752c17161121796be8bfda94396197dd3c3f000000000000000100000000000000013d29b0083911aca7951eee40f13fcf822cdc910874b493024d5aecc4208baef5000000000000000100000000000000012ee7c12e57071606a0e341a4bacac08e31f978a740f5e63770d468082b8e2a87000000000000000100000000000000000de6a0cf81f1735b3f1e1e5c481e2616cf881973f11b1ec54eda1ff56a5723f6000000000000000100000000000000012e2d22e5264bd4671a96e3c21ece563157e226a725d9d0b25cc672d1069b26e2000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000004901111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000003b48409a093becba1e74b88641d4ce53573bde804f41dec9f53771781ec6203800000000000000010000000000000001083b059bf8e67eac2a3d350784ac73ca645cded007cbd94e26c5d9e375875586000000000000000100000000000000011660ce71d423135e6daff0bc7542ca4bf5eb8cabe9d25531ccd408d2c9cf35f8000000000000000100000000000000003c7ee22981a176f07369929732497d393084af2e1f03328ad9faafb0626d2eb6000000000000000100000000000000011f0d82c86423e2dc3b65791258066b26c550c16204e40f554262d6476a741d6a000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000000930193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b6000000000000000600000000000000010000000000000000253741a85dfc0e8057f07337262b448b0fd9805c1d00d00ba3f82fe9b9e3c3ca000000000000000100000000000000010a6d671ce0beadcfec5ca9b4908e208cb4f05eccbe2f06745621df9a602e5652000000000000000100000000000000011e8e5c074cc4945507af2f1bd241626988fb1b1bb4f81b23ed3c9f35e6203a250000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000009168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b00000000000000050000000000000001000000000000000029bae6fd1d8e8a0867c8f1d1aff688c3fd4a0d352d24e60d36061958f60ea0830000000000000001000000000000000102456e910e45f0614463a941b29777d284d74dac773de9e3bc1772f4c13784180000000000000001000000000000000120440f8b110e92f0292fa2a28b36ec26f0df52c20eeaa387108aef9499694daf000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a0000000000000009328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000001988234a64c2bf7e4801a4408ed239278eac29c9bac34ce3da5d00ca99e52ac800000000000000010000000000000001326e84fe0ebf9a6da665dbf080a3528eb773022cdf498603579bb6148054afce000000000000000100000000000000010be9e65e8c8ac04e702d63135f34fdb8f27e6bce927a9467ae47b5e982c1b8df0000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000010af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000003cbd69854fff4563e3f658bd3dac272b47eac32adb9aac22c4ca4e880f19f32c0000000000000001000000000000000119cf15b83ff2024bc003e3daf89d828dc6386880bd775fc62ef85544d13cd3d0000000000000000100000000000000012f606c943b33ff34eef857f5685262ee56a8833cfccc025d61c621a06f19aaf500000000000000950b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000037e36f27e539f206a9e0848af0e272b62933b629a8efb0c4a18fc141d2d589b0000000000000000100000000000000010e07e97e38ff010b6da36e574191e1e0cfecc7f7a47ae9a88cda2fc32d643c340000000000000001000000000000000009f19973499a37f4e893092c8030461698890c915a21115263584e192736054a00000000000000010000000000000001269696ce334e34ba7f501ccc3e792b1f11fbe1ab6de47283c148f7bbb98418b100000000000000010000000000000000078467c777143067922d574c74ed5d8c32956f3f6378aefbd7967555cfb06954000000000000000100000000000000011bb29f650c5e5275268e6210cf6146679b4df0bf3631040df50b61339b0f4f3a000000000000000100000000000000011bf32d536c4676058d95458e6ef8f26b830282362e31869605460d80361eb5d5000000000000000100000000000000001d7259738c80273a823ca5d329f89b9d0f51a92e0bd9117414c51cab14992dc50000000000000001000000000000000127e3a35e643a261eaed0ce5e1d7814b2f551a5734fd0185e29547df56cc707830000000000000001000000000000000111735e6d5e280ad2e12efbddb91af9c38fdb31e2dc659127362de179915a057a00000000000000951ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000001ae497696313a6f9e25cc189e25cbd5fbc922e6af38a5b5293df90c00f6d3dd50000000000000001000000000000000113aadc2dbdb07db1353407b995b906b67cd79ac030ea681afda1aac0d5e28fbf000000000000000100000000000000000b0cd754fdae78979d843303bf1d17a217a79ab21e80932d7e35c8d1c72fe6c0000000000000000100000000000000010517499483e671f3d4417d0765113db7d62610c63b73ffe35a575bc5300b7fdc000000000000000100000000000000000d4027e15e8857298135db5c3533f77663958c2a2e4d2012f668481734187f0300000000000000010000000000000001380fab801ea69ccd54ae7b2928bdd7c506c053135851552beaa132f577b690ec0000000000000001000000000000000130c79da9b080ad82e77544933e508d30c5e62d8f6d17dcfc1af0bcf0b0f416e2000000000000000100000000000000000afae524714a44139d058ac38af9228470e20df7ffd676cc797abc29cec1342a0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d590000000000000095070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000000343d5a293d5b99196bf333bf3bfce4d9c7d29b427ae16c710d856ca7f8e66590000000000000001000000000000000128e9e51c0548c5398a228dfac66b59f6736bb6435f3d160850f76fb265f632b3000000000000000100000000000000001e58319cafc1d809e873ec8fc12d3b71e4afaee1f75dcd074aefbb614bea682c000000000000000100000000000000012178ca2f0666ad59671724557f23ff5356a4921a06a4b994d96a3c31fa79e032000000000000000100000000000000001a6ff00f76e8926c7fa117b37b1bf46cb56df5a5d380f34f0e87b36f9d488aac000000000000000100000000000000011b47168e88b10d36560ec9609b38a1f7f2fc579c302e6341edf246920547a1b20000000000000001000000000000000111da9f4e5c4795bfaf0ebc7bc2f905f8802c356278a5153c39275b4138eb952f000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000001501111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c00000000000000070000000000000001000000000000000016d4942efc7d3227e901e048de93a90c37b22a5d36def7701b8986d5b39d45c80000000000000001000000000000000123844eab25983288777591c43638474c86cff6a93b8951c9b3d955cf76461f12000000000000000100000000000000003efe391cc4701de41cdfe4b5d15b96b30e51abefe844aae5d5aa76c6857cca01000000000000000100000000000000012e55e60fab8bdd349acb33b885a91e28e83a7812e479c14e40c48b67aea563590000000000000001000000000000000029597ab7794d39f373ad8c40935e3671c95d263f79266858b37fc8e7bdfa8dbd000000000000000100000000000000010982eab0946956ec87034893e8c3bf7b8f206c65a9e455c14573686eaa80c268000000000000000100000000000000012c1f925d76037a58f0fad789f3c5f5188eddb5cee7695aea6b12f885027eb24f000000000000001530193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000001ae33b8ebdbf1d1adbb631d530004ef70a42fec0ee46f843400ee47162a4cc58000000000000000100000000000000012df9cf61ae21f98d5191472b6d1dcf9b3524490761a6acf46e31f07f58f630af0000000000000001000000000000000025eb52613116b045e112679f92ae4002ce6c12668944c083ea77f7247af72090000000000000000100000000000000010c4af2fb47ab2ede2c78bfef909f170edf1347554f7619e46879ab782f16a6220000000000000001000000000000000007bd7fdf329b200bd8f58aa027b3e4da8cfd98c6fa207cc856dd5af628aac2a6000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d790000000000000015168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b000000000000000500000000000000010000000000000000326cbb38639c48fd2b67706ad674af1fc1774978013b5c3040bd6bd710edb2590000000000000001000000000000000103563a5953258ec918ef310a34d833cd33a6c5a0c323893c0bb36d42185cbb4c0000000000000001000000000000000035b06fe68180b11ce8c6cca7dd6225d107e41aaf844e9e7c08f41defb39a9a7a0000000000000001000000000000000100289b442157f3141828627b0f630c4a3c2a7e07f8fa5247eea37207e197951e0000000000000001000000000000000038b7503c39bca1750a7f3ab4fba340571cc87c153a00fbf6561f199c36f168d20000000000000005328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000000313cb6f68f9d4165b793ff1c04639a36fca12b0ef99feaab26164c62f682f53d000000000000000100000000000000012ba5533feeb77466d68d3177e712d1374476a8116dab6d557ad9ddb04638aaa8000000000000000100000000000000002ecc5879d18568b10a5a4079bba13d9cc30403dc16a789a67e36ed735dffef8b000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000050af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd00000000000000030000000000000001000000000000000005d5bbf01900c7ffd7a4f1c857b2fc1358af18ec3eac6c4d49e5bb5e2ea9f4cf00000000000000010000000000000001023d495543e407e6376a45335b72abe709e25b55e8c72d3dcf10b9c6a92ea62300000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000002670b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a000000000000000100000000000000002e691562f02aab2255d48d448177ca35f8121d6d49a1cf3f6e996cd7095b537d000000000000000100000000000000002acf129a1947402d35183e3d87e05de315402af47e789a0eba068029bf94826a000000000000000100000000000000003c0af5bd0acd5555537a1c536980e8dad8e55ba0958931f8dce53c06db77cbd800000000000000010000000000000001364a43286af801f9e018ba88827ee2daceac4084d334175c24bf8be9364081ac0000000000000001000000000000000100a413f38db8bd2b9dbe4c8c3a4c9e2708fcd2000b7f318730267514f0923de10000000000000001000000000000000010b328505a4dacc2df0e34b8836d44a043dec9ef8916dd88f8af05286186ea87000000000000000100000000000000000d63cba247168e95cf9aeb8f1df5bd77e39fa0565f18b2099ee86888056a70bc0000000000000001000000000000000117c617d6cd76595b8396a03633d137791828190842015a26c868e22e7373118e000000000000000100000000000000012ab7b87c2de2b7026fca3b4414e88dddccafd6d35d7f2d6bf696f29c6281270500000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000000671ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000001dc9ebf966ba0810aefd5340bf76eca225b90cb000b0a995102f785506950bfd000000000000000100000000000000003a15f38f71837afd54efbcaf8bd1ee5e37c42d08cd3f1d8a257da8614b60782d000000000000000100000000000000000ddf4da5f07aa8c1ff008b57d0f00cbc8f3cc902a09aa19e5f9fbbf077f2b1df000000000000000100000000000000011b8b159f78c87540c9d88becd57348480ef123a6bab483a616e7ec8d1aa208bf000000000000000100000000000000012835ffa0a4bea54603313f92fdcc88b4620baaca9150f838337b07d7ba7fefd100000000000000010000000000000000166617bfd0ead8bf097d082e55711f7f9d4a33a8fcb113b94ef42cfc80705560000000000000000100000000000000002597be56c8553c2f0aec7f6eead4988440f231aa5dc89fa25f1fa07e517f4a00000000000000000100000000000000012f7a5d4b7486a7658cffd8c14fa7828396ab45405c5f0a0841afc731fca4a62c0000000000000001000000000000000106b8c734a5924aafb0a75443758c4f5fed8f527d94856151af42aacce0202d590000000000000067070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c30000000000000008000000000000000100000000000000000b9dacfbf105f6fc855336cec3f24a547d189caa552722878351589da235b3c4000000000000000100000000000000000541269b1dfa431157cae927d1a334e88eed6a6e6ab1c6640216f0979e8160e300000000000000010000000000000000316872d4e6245741d31149c55957b61bb4d7e8dc44006103038ca7e0538594fb000000000000000100000000000000013098fa89c78c74653c2f80211327ddad04144fdf4e3279245b99dd7c99bc4e310000000000000001000000000000000109761955a165c95c3cfa61530c6219e92df4befa3bf22ae7f432bbf060b8af30000000000000000100000000000000003e418307403fe6fe1bce279648b95a52998f64e4d2d1ec5d9ce71760f437c761000000000000000100000000000000002167607703c840ea4670ffd89100e17258197af01fd9154f758c8a4925ccb0c0000000000000000100000000000000010024d63f2e75192cb8ea8b4896792d6200a3f27d7107bf8397b03ed485d291b4000000000000006701111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000003a1e77de6b0d18ca7e53c29d33ce4885b77a65366fc0ed28401b5f4fab12f590000000000000000100000000000000001cf0f498afae637ba49276d83c20122ef8ee559f6f987a0226e009f01c4909ea000000000000000100000000000000000a7a98336158c836e48dd3c558dd5177a9f7f99b57e9e877bf099edf9f965a9400000000000000010000000000000001286852828a5455fb4500d64cfafae36211234e4b58ed68e0dc058216864337e0000000000000000100000000000000010ce23376a1b7df9a294f51c190c0890cc513e20e0fcfec75aab480f6c6c455cd0000000000000001000000000000000005e603da4b71c6223d2ea56e8de57dd773cd4f377192b66a42c7d4d3b841ed85000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000002730193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000000edd778bbbecdd1e890d87e69a08a1ab77f895d67de1cdb25e6477b0adc4d31c000000000000000100000000000000000c11aa66576e74b26484efdac940b3d1a11f8f934f5893898ec5e95ad366e390000000000000000100000000000000000e1288c6f712dc3895ea2c605960dbb4329ef31e5a59573ef13121ac3004b9e200000000000000010000000000000001270c72f3bdc9a84f397dd8a6414dee37b9f96dbf52b42b5575a81841c4a21a1800000000000000010000000000000001194d633f3059d1ac950f86b7775296c791f7ad97904b3b6dc7ffe864f7ea2cea000000000000000100000000000000003bdf2e83b1ec85ea7544f8cb5ae611f47ddf0970909eac5d92c2bf877dc593230000000000000007168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000002e33fc549796713ce1256dae7fbd0f9b6d0e149010476b2177fe8045d75bf1b2000000000000000100000000000000003ea66052e378bd0837956cf62e80f8ae5b0035356cc89d2cb50e79ac73c06b9900000000000000010000000000000000053dea0b078f18e76498d0f2663c0acdd9f4cd4f73d7cb9b8312c000346b39c6000000000000000100000000000000012e33a9d6cf3b0a93ec8e1a06d0ec991ed601477864623eefa49beb11ac24a8bc000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a0000000000000007328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa10000000000000004000000000000000100000000000000003b8bfafe905c25b254ffe4e3faf940c8d54956eb0bb5a581260772eead8646fc0000000000000001000000000000000020b40adf8a19a6a1b3ddcbe792f1245aa674f47481365d0844ab2a5fc9fd4346000000000000000100000000000000002ecc5879d18568b10a5a4079bba13d9cc30403dc16a789a67e36ed735dffef8b000000000000000100000000000000012617df8391070dac9476796a6b2951c827ea19a5d3ee5a765e79ad38dc43195a00000000000000070af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000001043287c3ea483495fb79ee4e66b2ed5121ccab3f5ae7f4d13ec05a43842744d000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d00000000000003cf0b9e1f7dc7ee1fdca8301f4ade1ac8806ec76b1e2258484b5ad56cd5f0e1d1d3000000000000000a0000000000000001000000000000000022820e6d34f5b19159672539f875d95f6ad2a33289f4b85209bd6f25437ebeca0000000000000001000000000000000028053adc38efeb8364fd9553112c738d81be276c15589db4e5c3998596385bbf0000000000000001000000000000000003a96b47b490cfa888abc471ba62a34e909ed5c9be2580ca11560cba2d1ec135000000000000000100000000000000003ff57edb4b10a5505849e257b5cb2917f65a93f0a74a9a23a568bc2203d753c9000000000000000100000000000000012558337efb45a857a744071e1b65c5c28f7f871b04050f18b8a1e52ff5d40134000000000000000100000000000000012a077a3728c6157ee3f7e8330d938a6a092fe41c8bb55ab8bb236cb40b46691400000000000000010000000000000000247baff5ab03a8cc57dd58caac50a037ad8fa0c00f42c012ddf9dacaf838c8d9000000000000000100000000000000000e06b4e1033f6851960bd438f83f21fd1fec62f13c36d56db39ca7e641129ad1000000000000000100000000000000001d87165ddde6d54f54eab67360861795670b48edf536440c746b5071d1e7536700000000000000010000000000000000245011f5a6ddb662ce07cc396dbc2b74929ea340017ac8651c2591bc4719d17a00000000000001cf1ecfd0256b15c815800e6ea09e395fd90f27e213f613137b488e96b0583618890000000000000009000000000000000100000000000000001229ea6204b3ed74528bf15dda1c5d1ca1af60378c348f621a315519b5d60586000000000000000100000000000000003bd7ddef4a0c4cb8328a342b57adf6c3e3b8137ff7df15bad83fbfba37ffc908000000000000000100000000000000001473c0b55ecb131c7fac89c98801294a4b5cdb1a50f495d0e6fb20c4d86c978b000000000000000100000000000000001e7987af199affbb6a7ac24e7293ce84f44f3cc9e46435f777f085164dfb33ac000000000000000100000000000000011d90962810ec603d730ecfb2223cf76ee502d19feb368a909e60cd8f3a18aaaf000000000000000100000000000000013294e2e6947bcfbfc65ae3dc2ff2b8baee0d5f347f8703e01f29c70b664b3c38000000000000000100000000000000000b6f57216ee6dacdb1293b892715e859545bedd9fcf29ec9005c73f95a3ed0830000000000000001000000000000000035163a28439443291e9afb63d08793c2dcaf1fce85499e2bac14751001ba95da0000000000000001000000000000000034e8f7e0bebc4e6ceac176e58697aa811375831824b908ae505b1c454c9495bb00000000000000cf070942901333d78acd0f530034ca6c1802c7c6f1163d42c51d52caa5084475c300000000000000080000000000000001000000000000000009bdd36f9d32e3a7e338bb3801cb2bf0ea37a19fa02bd8a83fa1ef3501edd9bb0000000000000001000000000000000005460408775bf715c89894c4633c3fce1eee2656c86809ac5be853b9a94586420000000000000001000000000000000038d1ee8a09f6f3fc371f74ca23a32cadbd8077bb20685c6c8827bc398e78a306000000000000000100000000000000000de6a0cf81f1735b3f1e1e5c481e2616cf881973f11b1ec54eda1ff56a5723f6000000000000000100000000000000012e2d22e5264bd4671a96e3c21ece563157e226a725d9d0b25cc672d1069b26e2000000000000000100000000000000011d854f893ae63156ee94a8e7479e3804dc8729bc5d96f1c57dba8e28a6b733b8000000000000000100000000000000003187b8a2b7bddee61d0d63e330e78ac7be3ddf052ec0ca589163b671af4e244b000000000000000100000000000000002e6f2cb203fe528ef1b498002b771924bf10609a2bbeb9f4adfec141ee479c43000000000000004f01111ea37366c9a97bdc0f46b9459bac9f79f0b43966611333337bace1e7596c0000000000000007000000000000000100000000000000003f36e2f3f22aef320363dee534deb8763dbb2b5a34081c16a34a702f4273610f000000000000000100000000000000003c0f4527992e25aaeb1e242a97436dffb09ee632e3967d4df69f064322651dab0000000000000001000000000000000020339c53f4ba8f58c233b2ddd67d0d3f084716a713edf531cf76508ef5aa2e74000000000000000100000000000000003c7ee22981a176f07369929732497d393084af2e1f03328ad9faafb0626d2eb6000000000000000100000000000000011f0d82c86423e2dc3b65791258066b26c550c16204e40f554262d6476a741d6a000000000000000100000000000000011b6a6cbcacd410b213f545901a7cc5ee061366e8638b63dc209624f2a556e231000000000000000100000000000000001081945b05963fe10230562a3ca69e87ac1ca0a66926bb51ea82596c4bf1f4a7000000000000000f30193b0ecb9b5babe9a7567ea74e4792f275c6e71b505b6a37422e6aa47852b60000000000000006000000000000000100000000000000003b82e43f44d5dbb797a795d700695d6ffa603cb8153aff40e9852930df2966f600000000000000010000000000000000238a3e8dce97bf4346149a9d5e4219d77cbfcec04c9f1b6091f3bea9a993915b000000000000000100000000000000003691fbfa0c2b33f8e31ce405962c381b6f891b121ca646e2f66a0c1a4dc1cb5d0000000000000001000000000000000033c4eda6645e49b289acf49ae8c8184abc624ac2a4ddad0bf6304435315aaa14000000000000000100000000000000013f9842b0930fa721a102b232ac7812b9f0998c7d4e4b24d2e2b0c9e25b03ff7b000000000000000100000000000000010d20cf754d68931517db579ce3b0fe0aee8ba96a1f2a3bd297fff298bec03d79000000000000000f168ab4e3221cdd0d250857898e1221f0e68e62abb81f046396ae6ceed67cf20b0000000000000005000000000000000100000000000000000caa658ce2a7eca7dfbea9aa154e710bce04e4f48ca50e66d360d6e7c85ad72400000000000000010000000000000000341a2b8a1b49734fcd7bbb4d75c03c067c33e82c9be1897d1f0578853f27b05b000000000000000100000000000000000f5b449679d27b00de38caa5c0dea420d83f888fee146e3af60fec3b88637b43000000000000000100000000000000000da8b778e4fa653975e146fc6a71cd7986ddc0f433aad56e78f1488f713ff273000000000000000100000000000000011aa306ed0e8c9f2e57f669a09704d609e6e7b4093620ba8d6bd56b3f005be51a000000000000000f328ccf82508ec63a37c4dd23315514cad3d0431616dc0c8b57d074a618f8daa1000000000000000400000000000000010000000000000000005541d0287f7a76b538652b24b76ad9b891601d8b4e46de1a2219feb1f346410000000000000001000000000000000011a90741a4cee5a5769213df86a17ee8cba21e1284becb7d919522073613bc62000000000000000100000000000000002e4c17b3bc44c81bce9603cf3d7fb67a6f0124aaf19c16e6ef76a73e7ff0daa30000000000000001000000000000000033f191bf6ea6d2cd461abff69b641c6ac601d758114bf090f90f9c6ee2357b9e00000000000000070af87db27242fcbcda22fbdad69edbba79a94bf6141ec9b75359a8eb9c0026cd0000000000000003000000000000000100000000000000001043287c3ea483495fb79ee4e66b2ed5121ccab3f5ae7f4d13ec05a43842744d000000000000000100000000000000003089ffa7bafc5bcafe962e4752f79f3a1a2b869bf27209f80a0ebf9ec65779a900000000000000010000000000000000007fe4e249e3da3d273e5f83f9a5db8097048011e07c43e3302fad96e78bc10d0000000000000002155e1432c0d1430b8190eed3cd647c654212db64f245b1feb1cde5a65e75ec313658e6357f995e72c0af15863b617690c0c93f48c6111d80e6909cd2e39e453bffffffff \ No newline at end of file diff --git a/libs/blueprint/test/verifiers/placeholder/expression_evaluation_component.cpp b/libs/blueprint/test/verifiers/placeholder/expression_evaluation_component.cpp new file mode 100644 index 000000000..676bb97f0 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/expression_evaluation_component.cpp @@ -0,0 +1,156 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_expression_evaluation_component_test + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + std::unordered_map, + crypto3::zk::snark::plonk_variable> var_map, + crypto3::zk::snark::plonk_constraint &constraint) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::detail::expression_evaluation_component; + + std::array witnesses; + std::iota(witnesses.begin(), witnesses.end(), 0); + component_type component_instance(witnesses, std::array(), std::array(), + constraint); + + std::function get_var_value = [&var_map, &public_input](const var &v) -> const value_type& { + BOOST_ASSERT(var_map.count(v) > 0); + const var input_var = var_map[v]; + BOOST_ASSERT(input_var.type == var::column_type::public_input); + BOOST_ASSERT(input_var.index == 0); + return public_input[input_var.rotation]; + }; + expression_evaluator evaluator(constraint, get_var_value); + value_type expected_res = evaluator.evaluate(); + + typename component_type::input_type instance_input = {var_map}; + + auto result_check = [&expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + BOOST_ASSERT(var_value(assignment, real_res.output) == expected_res); + }; + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + constraint); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_expression_evaluation_component_basic_test) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = field_type::value_type; + using var = crypto3::zk::snark::plonk_variable; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + constraint_type example_constraint = var(0, 0) * var(1, 1) - var(2, 3); + std::unordered_map var_map = { + {var(0, 0), var(0, 0, false, var::column_type::public_input)}, + {var(1, 1), var(0, 1, false, var::column_type::public_input)}, + {var(2, 3), var(0, 2, false, var::column_type::public_input)} + }; + std::vector public_input = {value_type(2), value_type(3), value_type(4)}; + test(public_input, var_map, example_constraint); + constraint_type example_constraint_2 = var(0, 0).pow(4); + std::vector public_input_2 = {value_type(2)}; + std::unordered_map var_map_2 = { + {var(0, 0), var(0, 0, false, var::column_type::public_input)}, + }; + test(public_input_2, var_map_2, example_constraint_2); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_expression_evaluation_component_random_tests) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + using value_type = field_type::value_type; + using var = crypto3::zk::snark::plonk_variable; + + boost::random::mt19937 gen(1444); + nil::crypto3::random::algebraic_engine random_engine(gen); + + constexpr std::size_t WitnessAmount = 15; + blueprint::assignment> tmp_assignment( + WitnessAmount, 0, 1, 0); + for (std::size_t i = 0; i < 10; i++) { + auto constraint = nil::blueprint::generate_random_constraint(tmp_assignment, 7, 3, gen); + std::set variable_set; + std::function variable_extractor = + [&variable_set](var variable) { variable_set.insert(variable); }; + nil::crypto3::math::expression_for_each_variable_visitor visitor(variable_extractor); + visitor.visit(constraint); + std::unordered_map var_map; + std::size_t rotation = 0; + for (auto &variable : variable_set) { + var_map[variable] = var(0, rotation++, false, var::column_type::public_input); + } + std::vector public_input; + public_input.reserve(variable_set.size()); + for (std::size_t i = 0; i < variable_set.size(); i++) { + public_input.push_back(random_engine()); + } + test(public_input, var_map, constraint); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/f1_loop.cpp b/libs/blueprint/test/verifiers/placeholder/f1_loop.cpp new file mode 100644 index 000000000..d0771e559 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/f1_loop.cpp @@ -0,0 +1,296 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_permutation_loop_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + typename BlueprintFieldType::value_type &expected_res) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = WitnessAmount + (WitnessAmount - 1) / 3; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::detail::f1_loop; + + std::size_t m = (public_input.size() - 2) / 2; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array(), m); + + std::vector> gates; + std::vector selectors; + std::size_t ctr = 0; + var beta = var(0, ctr++, false, var::column_type::public_input); + var gamma = var(0, ctr++, false, var::column_type::public_input); + std::vector si, ti; + for (std::uint32_t i = 0; i < m; i++) { + si.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + for (std::uint32_t i = 0; i < m; i++) { + ti.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + typename component_type::input_type instance_input = {beta, gamma, si, ti}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + std::cout << "F: 0x" << std::hex << var_value(assignment, real_res.output).data << std::endl; + assert(var_value(assignment, real_res.output) == expected_res); + }; + + crypto3::test_component( + component_instance, + desc, + public_input, + result_check, + instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + m); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test0) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x343841a32c928eb4e2ae534f59cc5cf1e25c53e307a5b81b75c131c73b6fc7a0_cppui_modular255, // gamma + 0x69e9e35f0c0f9c2c99fa7d570a5c269a886544f6708a4d2bb1e6f227c44ac62_cppui_modular255, // beta + 0x3ed0f74ff54a53257fc6836fec09caef8293a302ea145f6aa536b1c1eea3ab46_cppui_modular255, + 0x1acd7d04aa7b58b4eece036b22952608b6d36426fb7b6886580f0b94fba78027_cppui_modular255, + 0xff593d0141cbe02fec2f5a6423c83388c61787ac53ba0bf30c7176b21e93004_cppui_modular255, + 0x27a5ffbc960919dd52e5e701d1cbf1b34bca1178031bc6a669c4d569234397d7_cppui_modular255, + 0x3e1bdecdd496459ee5a11c2665460a832d084d28c68f98eeb035f2549e994be4_cppui_modular255, + 0x39786922cdb8e0f0e8338bd6796833d3c653e5ef7b22478a01b24f3c0ff43402_cppui_modular255, + }; + + typename BlueprintFieldType::value_type expected_res = + 0x29edab3fc33b0e6d6a75f53dac8612ac902a363340da6f1e5f0f91af80ff9e5e_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x13d38859e00f79df76e547b36dee3c0d19c5a4c6b7bc33ae284ec653e2db0e66_cppui_modular255, // gamma + 0x2d05f8356617f5060a8c5593d0bcbf5e15c74eb5fd681140b018139c0b453e48_cppui_modular255, // beta + 0xf7436a0e17af4814bd5da359d8b3c3c01bd2dd85d67ba4eb66e73a6852f694b_cppui_modular255, + 0x3fd11cbab87d551cc8b10411f1ee2abfbc68cc27e9fe275912670a794ebc6b06_cppui_modular255, + 0x366217783833274a413583bb6fcfaa3de8dfee3c2885526255a28302bb93231_cppui_modular255, + 0x3b5e28af60706486205ddb4f197e3b8923199c89e043392d36489b299a5ac600_cppui_modular255, + 0xec45e2ee30419aa67682743019246bb630a2d35abcc64ef51295b1dabc9cc4f_cppui_modular255, + 0x1a0f8fdb5e646f277cd13d360a1238a0bcfc13b2dc1acb89dc4cbe90a0e296b9_cppui_modular255, + 0x3f479ebb49bb54c6e7bedf53b04ab68682de35c188ab61096fc433991c567186_cppui_modular255, + 0x2ad86697004fb86c9ff21eefb5a5302ee93a5af6d66e9177039070e0d9008b08_cppui_modular255, + 0x2fbb8c6fa08d8deff7dede25f772a7660e7f3d6214a9924ea6401086b218c21a_cppui_modular255, + 0x2c7bcd2773ec55c8f5833ff46e1542c5390746e05185b379f19d00c5248adb56_cppui_modular255, + 0xf4c65e93df2d11107677b1096c1177c0acb9a3b373cda815b5c5b739862abf2_cppui_modular255, + 0xf163e52958ab4026cc78a067a636cd7c9c358354c747829e0706a77267fe32a_cppui_modular255, + 0x3147dc26cd071216a5cceb16291d35c68ce3e01505adff83b690bd3f82655ae3_cppui_modular255, + 0x39354121b4b606762eb088e4fed35a3aedd44feecfaebd6aecb0e508da13f0f3_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x10fdfb2f515ec48c32c7b31b7e3039739bb22cd7bee475b5a74327ccd0dd0f6d_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test2) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*beta: */ + 0x181227226c1423739b9ac923321ed2f09ed6b194314f01fa63cf06a993087d47_cppui_modular255, + /*gamma: */ + 0x2e42a7ce383a60b4a57ead1d59609e3c77d54efdbc3fb3b78df45906b61fa26c_cppui_modular255, + 0x3d08d7113ec5ad138d4d720b44f3a8839a8541ee8f677bd6b688a821e34c2df6_cppui_modular255, + 0x130b302c3079f281cef9b0082567fb0a5504ae46546dbe46a6a0c1a217148bd1_cppui_modular255, + 0x1601b0e7bc9d588bfd28ebc94bc310654ab24669f26645d575cbfb1423cb0f90_cppui_modular255, + 0x3cd9714e6b5440733518b80c64a3aaa050516c848b3a09c456d2c99112f396c3_cppui_modular255, + 0x20e9e41aa6d693d979e31d4cd3426d1ba1138f59b18f3e354758343ba6e857d9_cppui_modular255, + 0x3f48aa78bdb07bc9dea0e8b9c5050f653a3e883061fb3e7242975c11cfafa9bc_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + 0x0_cppui_modular255, 0x18582466937834d434ceff70fa27ea4a1486d5c835080ebc13cb1f7b5bbd4850_cppui_modular255, + 0x1979a1d3b8e9bfc29212140b1f38ea9120aa47febf6a0df823348102f783be15_cppui_modular255, + 0x7d60e5c26d15b2aa293d72a00194e20e533fc3f38aa259ac899b4e82b0b3a3b_cppui_modular255, + 0x3a3c06a35de362be982b6bd008c05efce84f297d4893cb61faee0e230e465502_cppui_modular255, + 0x51a74265a7a5c53a24d9d461403d93aba9c5151e77a4da184dab3c417f687f6_cppui_modular255, + 0xae1137ab4cd9d26666d9dbc0ee2f21a1f1692730311f89f97a5f825992e0f26_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + 0x0_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x1fe0cedb4028c10c6fbb7984040bacd33a3644a3df6c157a7d253af03168ee8b_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test3) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*beta: */ + 0x815e5816cf879a118c629f6f724e75486494f53371ad59a031e8acf03199b88_cppui_modular255, + /*gamma: */ + 0x257158c4ad84265ed8011284ea62f8d24834881034b1d1bf2bdf6dfd0d87aab6_cppui_modular255, + 0x1b5ed497136f6e508532fd859a0cb4fdad54c84549a665d5d580943fa88bb5de_cppui_modular255, + 0x45d83c89ccb02eceedf188623a5cc960fe523678bb70567bcfcada4a8f1d7aa_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x3df91b519c56c1661226abe86c59cd26f3a63c2dbaac2cdeecb286d4735dcbf2_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test4) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*beta: */ + 0x815e5816cf879a118c629f6f724e75486494f53371ad59a031e8acf03199b88_cppui_modular255, + /*gamma: */ + 0x257158c4ad84265ed8011284ea62f8d24834881034b1d1bf2bdf6dfd0d87aab6_cppui_modular255, + 0x1b5ed497136f6e508532fd859a0cb4fdad54c84549a665d5d580943fa88bb5de_cppui_modular255, + 0x45d83c89ccb02eceedf188623a5cc960fe523678bb70567bcfcada4a8f1d7aa_cppui_modular255, + 0x1601b0e7bc9d588bfd28ebc94bc310654ab24669f26645d575cbfb1423cb0f90_cppui_modular255, + 0x3cd9714e6b5440733518b80c64a3aaa050516c848b3a09c456d2c99112f396c3_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x3368f12ac6c75aceaed6f7f2e9af9926674370ddb3d12c00c83c225cc6b88380_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test5) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*beta: */ + 0x181227226c1423739b9ac923321ed2f09ed6b194314f01fa63cf06a993087d47_cppui_modular255, + /*gamma: */ + 0x2e42a7ce383a60b4a57ead1d59609e3c77d54efdbc3fb3b78df45906b61fa26c_cppui_modular255, + 0x29566d61a92beabbe4124f3c1140e1621e6dbf40685efcdd3654ebb4a120936f_cppui_modular255, + 0x371ed596cbfbef3fb137c07f7387f0694fb9df229725ecbfa7275460091e5292_cppui_modular255, + 0x5b880339bcbe06aad36ac71638d5d3e831f1dec5ca6f97a456f9936b6690c4c_cppui_modular255, + 0x29566d61a92beabbe4124f3c1140e1621e6dbf40685efcdd3654ebb4a120936f_cppui_modular255, + 0x371ed596cbfbef3fb137c07f7387f0694fb9df229725ecbfa7275460091e5292_cppui_modular255, + 0x5b880339bcbe06aad36ac71638d5d3e831f1dec5ca6f97a456f9936b6690c4c_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x37b68c62a05782ebfe610897109adc5e0c343a4468372c305e050b4a133860d5_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test6) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*beta: */ + 0x181227226c1423739b9ac923321ed2f09ed6b194314f01fa63cf06a993087d47_cppui_modular255, + /*gamma: */ + 0x2e42a7ce383a60b4a57ead1d59609e3c77d54efdbc3fb3b78df45906b61fa26c_cppui_modular255, + 0x3a7580268e270952e70bf455392b34d33608d4291f37c778edf18a1df79b3d02_cppui_modular255, + 0x12cc7a905f5ba0f349772eccf0a4f893255895eab99b4654971beb32c3d18e4c_cppui_modular255, + 0x233de57bb49faf3747f8065836f6e150326ffbcec7f5ff84ab3ed517dff9f782_cppui_modular255, + 0x46ac9a0a0a61d85811114d1b5a59055a1476d224220100ea28aef3ff5057b36_cppui_modular255, + 0x19527a8b49c446bf75b243da6ac78d7c48e4b60c1071a4e5d369c3b46ab184d8_cppui_modular255, + 0xbfdd514782dd17a99538aab6f7c447bb39607098ff684d907d05c4ee6b47364_cppui_modular255, + 0x3cfd22f42d7f4bb00884f12035ac3b507bb19e482dd2e7787754e5cf67dd72a1_cppui_modular255, + 0x1e0fb613aca0c659711bd4c889a691aa78953f8738d6005673123c2667c3cd50_cppui_modular255, + 0x218014cf84950736b0d9c62ccb68ee90f755fc6dd0ed60a208d09da39e64bfd2_cppui_modular255, + 0x237428f92e0be38a22c6220ea2cf7b010a2eae8670f062647bebcab8506fdddc_cppui_modular255, + 0xc0af31fd4848be3dd24df1eb1681e13cb4de4395df9a3c1c8835270e4ae1699_cppui_modular255, + 0x2708b8c59416e906abec12349b0aa4bd8e4e22dfc216b1a78f82daf83aa219df_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x3a204699d97747058b03b75350f8969b7022230a7b6a46bb764050759b6a7363_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/f3_loop.cpp b/libs/blueprint/test/verifiers/placeholder/f3_loop.cpp new file mode 100644 index 000000000..5ec0601ea --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/f3_loop.cpp @@ -0,0 +1,191 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_permutation_loop_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + typename BlueprintFieldType::value_type &expected_res) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = WitnessAmount + 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::detail::f3_loop; + + std::size_t m = public_input.size() / 3; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array(), m); + + std::vector> gates; + std::vector selectors; + std::size_t ctr = 0; + std::vector si, ti, alphai; + for (std::uint32_t i = 0; i < m; i++) { + alphai.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + for (std::uint32_t i = 0; i < m; i++) { + si.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + for (std::uint32_t i = 0; i < m; i++) { + ti.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + typename component_type::input_type instance_input = {alphai, si, ti}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + std::cout << "F: 0x" << std::hex << var_value(assignment, real_res.output).data << std::endl; + assert(var_value(assignment, real_res.output) == expected_res); + }; + + crypto3::test_component( + component_instance, + desc, + public_input, + result_check, + instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + m); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test0) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x343841a32c928eb4e2ae534f59cc5cf1e25c53e307a5b81b75c131c73b6fc7a0_cppui_modular255, + 0x69e9e35f0c0f9c2c99fa7d570a5c269a886544f6708a4d2bb1e6f227c44ac62_cppui_modular255, + 0x3ed0f74ff54a53257fc6836fec09caef8293a302ea145f6aa536b1c1eea3ab46_cppui_modular255, + 0x1acd7d04aa7b58b4eece036b22952608b6d36426fb7b6886580f0b94fba78027_cppui_modular255, + 0xff593d0141cbe02fec2f5a6423c83388c61787ac53ba0bf30c7176b21e93004_cppui_modular255, + 0x27a5ffbc960919dd52e5e701d1cbf1b34bca1178031bc6a669c4d569234397d7_cppui_modular255, + 0x3e1bdecdd496459ee5a11c2665460a832d084d28c68f98eeb035f2549e994be4_cppui_modular255, + 0x39786922cdb8e0f0e8338bd6796833d3c653e5ef7b22478a01b24f3c0ff43402_cppui_modular255, + 0x29edab3fc33b0e6d6a75f53dac8612ac902a363340da6f1e5f0f91af80ff9e5e_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x10f04580466164e2155d833e6489f63f259071438925993668473cf36d4e3826_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x13d38859e00f79df76e547b36dee3c0d19c5a4c6b7bc33ae284ec653e2db0e66_cppui_modular255, + 0x2d05f8356617f5060a8c5593d0bcbf5e15c74eb5fd681140b018139c0b453e48_cppui_modular255, + 0xf7436a0e17af4814bd5da359d8b3c3c01bd2dd85d67ba4eb66e73a6852f694b_cppui_modular255, + 0x3fd11cbab87d551cc8b10411f1ee2abfbc68cc27e9fe275912670a794ebc6b06_cppui_modular255, + 0x366217783833274a413583bb6fcfaa3de8dfee3c2885526255a28302bb93231_cppui_modular255, + 0x3b5e28af60706486205ddb4f197e3b8923199c89e043392d36489b299a5ac600_cppui_modular255, + 0xec45e2ee30419aa67682743019246bb630a2d35abcc64ef51295b1dabc9cc4f_cppui_modular255, + 0x1a0f8fdb5e646f277cd13d360a1238a0bcfc13b2dc1acb89dc4cbe90a0e296b9_cppui_modular255, + 0x3f479ebb49bb54c6e7bedf53b04ab68682de35c188ab61096fc433991c567186_cppui_modular255, + 0x2ad86697004fb86c9ff21eefb5a5302ee93a5af6d66e9177039070e0d9008b08_cppui_modular255, + 0x2fbb8c6fa08d8deff7dede25f772a7660e7f3d6214a9924ea6401086b218c21a_cppui_modular255, + 0x2c7bcd2773ec55c8f5833ff46e1542c5390746e05185b379f19d00c5248adb56_cppui_modular255, + 0xf4c65e93df2d11107677b1096c1177c0acb9a3b373cda815b5c5b739862abf2_cppui_modular255, + 0xf163e52958ab4026cc78a067a636cd7c9c358354c747829e0706a77267fe32a_cppui_modular255, + 0x3147dc26cd071216a5cceb16291d35c68ce3e01505adff83b690bd3f82655ae3_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x3627e4bec2ef12578906bbc3d1820ad3e351949cbff1a8cf4783e82caaa4a225_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_loop_test2) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x3ac916e994eb085e73928ffcf7349bd78232597abe727e5419efca5da3352762_cppui_modular255, + 0x28c5329493df6684237fe29b099bd63b8e308890d824cc22e1cae645ca74d16e_cppui_modular255, + 0x20850cee63944d67e20b3a7bbb287445d56d896b21dfad679371f92d7c8cfdde_cppui_modular255, + 0x733d8c425ee853f86e19b732693e23fb0b3455e4716461232558a95c507edc7_cppui_modular255, + 0x10107a265a3e1c84a7bfba1098d21f8c454e7b924dbd1da5ac8b3aab636c6a0c_cppui_modular255, + 0x350d9495311fef9a4b0209edb4a3c4bddd5ffb56ce2f191ef970ab9c0ed0ea89_cppui_modular255, + 0x4f4a9468effe3b8b593752aa6ca4076d9548e7ad0db0dd4db34775c4806d44f_cppui_modular255, + 0x3f3c910aa039f6a9c696978cffd810099023bac4778c6a25df3e7cc4882e249f_cppui_modular255, + 0x362a6fe7cda76313effd4f422c2aa30517af2e8cf75cbd6cacd18592f8504465_cppui_modular255, + 0x2a6d49e1c07f61f3356cde688fb9807c7efa8120c1357b768292038c29c33ca1_cppui_modular255, + 0x2fc44c30d66a005631bab5e36186f6f2df3d82695bb41025d12706204a8e98fa_cppui_modular255, + 0x34aa8f0b44149c23df98d0a7950f11945bcaf1588a589d8442fcf6a4551ff475_cppui_modular255, + 0x384d6cead91ac3c0ae1a8acc9f4d753121afe02066774dd10a381ce3f30649f9_cppui_modular255, + 0x181e07ef1cacc19be824f2d02109b1863ab1740bdc82e0e184b3246af28f8773_cppui_modular255, + 0x17a9cdd5c4c1fe3143318316c5a9b18d879813ebda0c56f2b3ab5faaf8e530af_cppui_modular255, + 0x1df2aafa1674d30190d7e9fc28bb72263c0e73ff16f4558736d45836e45de71a_cppui_modular255, + 0x1c397b8d8e0b71e74a95b05b6769850aa0cff9c3d29bf0e48c7df205c571f7ab_cppui_modular255, + 0x3fc816df5e71c581a52cdbbd4cee972b9b56cb08b5d838dfe317b93e0243e11a_cppui_modular255}; + + typename BlueprintFieldType::value_type expected_res = + 0x2cc334d4e4f1ba642074f6700758d854b5ae4862b5a9cb4b8f8e1fb814b767df_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/final_polynomial_check.cpp b/libs/blueprint/test/verifiers/placeholder/final_polynomial_check.cpp new file mode 100644 index 000000000..546177246 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/final_polynomial_check.cpp @@ -0,0 +1,159 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include "nil/blueprint/components/systems/snark/plonk/verifier/final_polynomial_check.hpp" +#include +#include +#define BOOST_TEST_MODULE plonk_final_polynomial_check_component_test + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + bool expected_to_pass) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 3; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t TestLambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::final_polynomial_check; + + std::array witnesses; + std::iota(witnesses.begin(), witnesses.end(), 0); + component_type component_instance(witnesses, std::array(), std::array(), + Power, Lambda); + + typename component_type::input_type instance_input; + std::size_t rotation = 0; + for (std::size_t i = 0; i < Lambda; i++) { + instance_input.points.push_back(var(0, rotation++, false, var::column_type::public_input)); + } + for (std::size_t i = 0; i < 2 * Lambda; i++) { + instance_input.values.push_back(var(0, rotation++, false, var::column_type::public_input)); + } + for (std::size_t i = 0; i < Power + 1; i++) { + instance_input.coefficients.push_back(var(0, rotation++, false, var::column_type::public_input)); + } + + auto result_check = [](AssignmentType &assignment, typename component_type::result_type &real_res) {}; + + if (expected_to_pass) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + Power, Lambda); + } else { + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + Power, Lambda); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +template +void test_random_polynomials(boost::random::mt19937 &gen) { + using value_type = typename BlueprintFieldType::value_type; + nil::crypto3::random::algebraic_engine random_engine(gen); + boost::random::uniform_int_distribution<> dist(0, 2 * Lambda - 1); + // test case generation doesn't work otherwise + BOOST_ASSERT(2 * Lambda == Power + 1); + for (std::size_t i = 0; i < 15; i++) { + std::vector public_input; + std::vector points_with_m; + std::vector values; + for (std::size_t j = 0; j < Lambda; j++) { + value_type point = random_engine(); + public_input.push_back(point); + points_with_m.push_back(point); + points_with_m.emplace_back(-point); + } + for (std::size_t j = 0; j < 2 * Lambda; j++) { + value_type value = random_engine(); + public_input.push_back(value); + values.push_back(value); + } + std::vector> points_values; + for (std::size_t j = 0; j < 2 * Lambda; j += 2) { + points_values.emplace_back(std::make_pair(points_with_m[j], values[j])); + points_values.emplace_back(std::make_pair(points_with_m[j + 1], values[j + 1])); + } + // now we use lagrange interpolation to create a polynomial which would be y at all the (s; -s) + auto polynomial = nil::crypto3::math::lagrange_interpolation(points_values); + BOOST_ASSERT(polynomial.size() == 2 * Lambda); + std::vector coefficients; + for (auto val : polynomial) { + coefficients.push_back(val); + } + BOOST_ASSERT(coefficients.size() == Power + 1); + std::reverse(coefficients.begin(), coefficients.end()); + public_input.insert(public_input.end(), coefficients.begin(), coefficients.end()); + test(public_input, true); + // randomly try to break a constraint + std::size_t rand_index = dist(gen); + public_input[Lambda + rand_index] = random_engine(); + test(public_input, false); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_final_polynomial_check_component_random_tests) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + boost::random::mt19937 gen(1444); + test_random_polynomials(gen); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/fri_array_swap.cpp b/libs/blueprint/test/verifiers/placeholder/fri_array_swap.cpp new file mode 100644 index 000000000..bbe9772ab --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/fri_array_swap.cpp @@ -0,0 +1,145 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_plonk_fri_array_swap_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_array_swap( + const std::vector &array, + const typename BlueprintFieldType::value_type &t){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::fri_array_swap; + + BOOST_ASSERT(array.size() == 2 * ArraySize); + + typename component_type::input_type instance_input; + instance_input.t = var(0, 0, false, var::column_type::public_input); + instance_input.arr.reserve(2 * ArraySize); + for (std::size_t i = 0; i < 2 * ArraySize; i++) { + instance_input.arr.emplace_back(var(0, i + 1, false, var::column_type::public_input)); + } + + std::vector public_input = {t}; + std::copy(array.begin(), array.end(), std::back_inserter(public_input)); + + auto result_check = [&t, &array](AssignmentType &assignment, + typename component_type::result_type &real_res) { + BOOST_ASSERT(real_res.output.size() == 2 * ArraySize); + for (std::size_t i = 0; i < ArraySize; i++) { + BOOST_ASSERT(var_value(assignment, real_res.output[2 * i]) == + (t == 1 ? array[2 * i + 1] : array[2 * i])); + BOOST_ASSERT(var_value(assignment, real_res.output[2 * i + 1]) == + (t == 1 ? array[2 * i] : array[2 * i + 1])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, ArraySize); + // I thought this component would be an example of where the ::WEAK connectedness check is required + // I was wrong + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, ArraySize); +} + +template +void fri_array_swap_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + boost::random::uniform_int_distribution<> t_dist(0, 1); + + for (std::size_t i = 0; i < RandomTestsAmount; i++) { + test_array_swap( + {generate_random(), generate_random()}, + t_dist(seed_seq)); + test_array_swap( + {generate_random(), generate_random(), generate_random(), generate_random(), + generate_random(), generate_random(), generate_random(), generate_random(), + generate_random(), generate_random(), generate_random(), generate_random(), + generate_random(), generate_random(), generate_random(), generate_random()}, + t_dist(seed_seq)); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + fri_array_swap_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + fri_array_swap_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + fri_array_swap_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/fri_cosets.cpp b/libs/blueprint/test/verifiers/placeholder/fri_cosets.cpp new file mode 100644 index 000000000..0d2c301cb --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/fri_cosets.cpp @@ -0,0 +1,150 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_fri_cosets_test + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_fri_cosets(std::vector public_input, + std::size_t n, + typename FieldType::value_type omega){ + using BlueprintFieldType = FieldType; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 5; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::fri_cosets; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input)}; + + typename BlueprintFieldType::integral_type pi_num = typename BlueprintFieldType::integral_type(public_input[0].data); + + std::vector> expected_res = {}; + + typename BlueprintFieldType::value_type w_powers = omega; + typename BlueprintFieldType::value_type w_pow_x = 1; + expected_res.resize(n); + for(std::size_t i = 0; i < n; i++) { + w_pow_x *= (pi_num % 2 == 1) ? w_powers : 1; + // (n-1-i) because the bits in the result are in reverse order + expected_res[n-1-i][2] = typename BlueprintFieldType::value_type(pi_num % 2); + pi_num /= 2; + w_powers *= w_powers; + } + for(std::size_t i = 0; i < n; i++) { + expected_res[i][0] = w_pow_x; + expected_res[i][1] = expected_res[i][0]*(-1); + w_pow_x *= w_pow_x; + } + + auto result_check = [&expected_res, public_input, n, omega](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "fri_cosets test: " << "\n"; + std::cout << "input : " << std::hex << public_input[0].data << " " << std::hex << omega.data << "\n"; + std::cout << "expected: {" << std::hex < witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, // witnesses + std::array{0}, // constants + std::array{}, // public inputs + n, omega); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, n, omega); +} + +template +void field_operations_test() { +// Format: test_fri_cosets(public_input, n, omega) + for (int i = 14; i < 25; i++){ + test_fri_cosets({i}, 3, 2); + } + test_fri_cosets({46744073709551615}, 4, 2); + test_fri_cosets({46744073709551615}, 3, 2); + test_fri_cosets({46744073709551615}, 5, 2); +// more realistic data + test_fri_cosets({0xa53a16c34fb833b5_cppui_modular255}, 4, + 0x1ff2863fd35bfc59e51f3693bf37e2d841d1b5fbed4138f755a638bec8750abd_cppui_modular255 + ); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fri_cosets_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + field_operations_test(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/fri_lin_inter.cpp b/libs/blueprint/test/verifiers/placeholder/fri_lin_inter.cpp new file mode 100644 index 000000000..ee17ebb22 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/fri_lin_inter.cpp @@ -0,0 +1,121 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_verifiers_plonk_fri_lin_inter_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_fri_lin_inter(const std::vector &public_input){ + constexpr std::size_t WitnessColumns = 5; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::fri_lin_inter; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + value_type s = public_input[0], + y0 = public_input[1], + y1 = public_input[2], + alpha = public_input[3]; + value_type expected_res = y0 + (y1 - y0) * (s - alpha) * (2 * s).inversed(); + + auto result_check = [&expected_res](AssignmentType &assignment, + typename component_type::result_type &real_res) { + + BOOST_ASSERT(var_value(assignment, real_res.output) == expected_res); + }; + + component_type component_instance({0, 1, 2, 3, 4}, {}, {}); + + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input); +} + +template +void fri_lin_inter_tests() { + static boost::random::mt19937 seed_seq; + static nil::crypto3::random::algebraic_engine generate_random(seed_seq); + + for (std::size_t i = 0; i < RandomTestsAmount; i++){ + test_fri_lin_inter( + {generate_random(), generate_random(), generate_random(), generate_random()}); + } +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; + + fri_lin_inter_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_field_operations_test_pallas) { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + fri_lin_inter_tests(); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_bls12) { + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; + + fri_lin_inter_tests(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/gate_argument_verifier.cpp b/libs/blueprint/test/verifiers/placeholder/gate_argument_verifier.cpp new file mode 100644 index 000000000..c8fee7131 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/gate_argument_verifier.cpp @@ -0,0 +1,211 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_gate_argument_verifier_test + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + typename BlueprintFieldType::value_type &expected_res, std::vector signature) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 2 * WitnessAmount + 1 + 3; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::basic_constraints_verifier; + + std::size_t m = signature.size(); + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array(), + signature); + + std::vector constraints; + std::vector selectors; + std::size_t ctr = 0; + var theta = var(0, ctr++, false, var::column_type::public_input); + for (std::uint32_t i = 0; i < m; i++) { + for (std::uint32_t j = 0; j < signature[i]; j++) { + constraints.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + selectors.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + typename component_type::input_type instance_input = {theta, constraints, selectors}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + std::cout << "F: 0x" << std::hex << var_value(assignment, real_res.output).data << std::endl; + assert(var_value(assignment, real_res.output) == expected_res); + // std::cout << "expected F: " << expected_res.data << std::endl; + }; + if (signature.size() == 1 && signature[0] == 1) { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::NONE, signature); + } else { + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, signature); + } +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x3c670eabac71e05f3e29255748e080b2ec288a774fdf4c3b6b7685183f2186c0_cppui_modular255, // theta + 0x6e152a2ee7cd62e55993e72b0c32aeb48241c792f48d789cbe35606a72f3c45_cppui_modular255, // C_1_0 + 0x30457a793c861dd0044f6f5bcfb8775b99dd2313c7686f120f1f334997fcbea0_cppui_modular255}; // q_2 + + typename BlueprintFieldType::value_type expected_res = + 0x274f55a187ab99ed7946f85953dc499dee73941bbfeefe80a74c0fe42d254fb4_cppui_modular255; // F + + test(public_input, expected_res, {1}); + test(public_input, expected_res, {1}); + test(public_input, expected_res, {1}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test0) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x3b68a611d0a4896cf54a3130a166367ce9e3c02584842d117ceca4e7e5100e00_cppui_modular255, // theta + 0x775559d0e51d93ff23e78cfb5b51b797225769e058e37729fb8ed291234d62_cppui_modular255, // C_1_0 + 0x1c454f840f62d7deb28c1161f420930ccc521572e68af58150fe9fdd42246b7c_cppui_modular255, // q_1 + 0x3713eddbd6e7723d5ec49d4865af9a764ba0877d4d54659b1ad2ee3ae0efe344_cppui_modular255, // C_2_0 + 0x1e251c5521af2a481096841a65deb34bfac8a09a05096b6f83db060bbf823217_cppui_modular255}; // q_2 + + typename BlueprintFieldType::value_type expected_res = + 0x1196613de39bdeef57744700e540ba900d8be069a0dc963bce25ae3b550c2a37_cppui_modular255; // F + + test(public_input, expected_res, {1, 1}); + test(public_input, expected_res, {1, 1}); + test(public_input, expected_res, {1, 1}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x107af4ffd5e6a759be7c279d5eac2d5e893a89d703e52a3184b2331d3acbdfc1_cppui_modular255, // theta + 0x815ea6af6ba30435bfe42b04942a42354b7ac5bcb970d2c7fd29600afba4c76_cppui_modular255, // C_3_0 + 0xca85be77a54d63ae5d08aa369c21df7cc559803fa92d44cf06ad801165ba0eb_cppui_modular255, // C_3_1 + 0x11ee26b867cf7bce6b41152282cadb4bf8044b9e2722acf91c8fa01fa634bafb_cppui_modular255, // C_3_2 + 0x32df974641fa733bfafde92e7f86e0f967af2744d6b1d239530419921f8460be_cppui_modular255, // q_3 + 0x2c3c7fd7ad540677c20238e931ab15eec3f30b0d19c0465dcbb2c79500f52959_cppui_modular255, // C_1_0 + 0x39d9d3ce89500f9bbaeefb254fff8d0c25c17cebe2a1c4586630b3c168622e8c_cppui_modular255, // q_1 + 0x37fb8feeafe97cc91109beeda57125d20266990bbbeff2d0f206e51fa447d72_cppui_modular255, // C_2_0 + 0x2b673c52209b43e4735ecb7fe0ba59f4b5cce80ccc1643f56a067ecbbeeba8c3_cppui_modular255 // q_2 + }; + + typename BlueprintFieldType::value_type expected_res = + 0x33ceac2f8ef925e1f95a6d60334a8e4e3a9c5a925ecd73d6f8a676a5e56efc6c_cppui_modular255; // F + + test(public_input, expected_res, {3, 1, 1}); + test(public_input, expected_res, {3, 1, 1}); + test(public_input, expected_res, {3, 1, 1}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test2) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x107af4ffd5e6a759be7c279d5eac2d5e893a89d703e52a3184b2331d3acbdfc1_cppui_modular255, // theta + 0x2c3c7fd7ad540677c20238e931ab15eec3f30b0d19c0465dcbb2c79500f52959_cppui_modular255, // C_1_0 + 0x39d9d3ce89500f9bbaeefb254fff8d0c25c17cebe2a1c4586630b3c168622e8c_cppui_modular255, // q_1 + 0x37fb8feeafe97cc91109beeda57125d20266990bbbeff2d0f206e51fa447d72_cppui_modular255, // C_2_0 + 0x2b673c52209b43e4735ecb7fe0ba59f4b5cce80ccc1643f56a067ecbbeeba8c3_cppui_modular255, // q_2 + 0x815ea6af6ba30435bfe42b04942a42354b7ac5bcb970d2c7fd29600afba4c76_cppui_modular255, // C_3_0 + 0xca85be77a54d63ae5d08aa369c21df7cc559803fa92d44cf06ad801165ba0eb_cppui_modular255, // C_3_1 + 0x11ee26b867cf7bce6b41152282cadb4bf8044b9e2722acf91c8fa01fa634bafb_cppui_modular255, // C_3_2 + 0x32df974641fa733bfafde92e7f86e0f967af2744d6b1d239530419921f8460be_cppui_modular255 // q_3 + }; + + typename BlueprintFieldType::value_type expected_res = + 0x1c347e1b881df3bce20b36de4249336d1e650ec283955b9d5ec16c0ab319e51a_cppui_modular255; // F + + test(public_input, expected_res, {1, 1, 3}); + test(public_input, expected_res, {1, 1, 3}); + test(public_input, expected_res, {1, 1, 3}); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test3) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x107af4ffd5e6a759be7c279d5eac2d5e893a89d703e52a3184b2331d3acbdfc1_cppui_modular255, // theta + 0x2c3c7fd7ad540677c20238e931ab15eec3f30b0d19c0465dcbb2c79500f52959_cppui_modular255, // C_1_0 + 0x39d9d3ce89500f9bbaeefb254fff8d0c25c17cebe2a1c4586630b3c168622e8c_cppui_modular255, // q_1 + 0x815ea6af6ba30435bfe42b04942a42354b7ac5bcb970d2c7fd29600afba4c76_cppui_modular255, // C_3_0 + 0xca85be77a54d63ae5d08aa369c21df7cc559803fa92d44cf06ad801165ba0eb_cppui_modular255, // C_3_1 + 0x11ee26b867cf7bce6b41152282cadb4bf8044b9e2722acf91c8fa01fa634bafb_cppui_modular255, // C_3_2 + 0x32df974641fa733bfafde92e7f86e0f967af2744d6b1d239530419921f8460be_cppui_modular255, // q_3 + 0x37fb8feeafe97cc91109beeda57125d20266990bbbeff2d0f206e51fa447d72_cppui_modular255, // C_2_0 + 0x2b673c52209b43e4735ecb7fe0ba59f4b5cce80ccc1643f56a067ecbbeeba8c3_cppui_modular255 // q_2 + }; + + typename BlueprintFieldType::value_type expected_res = + 0x160e3212397bc43fab22922a46f77280c614ab11fc9567c4659b035d46c29827_cppui_modular255; // F + + test(public_input, expected_res, {1, 3, 1}); + test(public_input, expected_res, {1, 3, 1}); + test(public_input, expected_res, {1, 3, 1}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/gate_component.cpp b/libs/blueprint/test/verifiers/placeholder/gate_component.cpp new file mode 100644 index 000000000..85acc301d --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/gate_component.cpp @@ -0,0 +1,188 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_gate_compoent_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + typename BlueprintFieldType::value_type &expected_res) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 2 * WitnessAmount + 1; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::detail::gate_component; + + std::size_t m = public_input.size() - 3; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array(), m); + + std::vector> gates; + std::vector selectors; + std::size_t ctr = 0; + var theta = var(0, ctr++, false, var::column_type::public_input); + std::vector constraints; + for (std::uint32_t i = 0; i <= m; i++) { + constraints.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + var selector = var(0, ctr++, false, var::column_type::public_input); + + typename component_type::input_type instance_input = {theta, constraints, selector}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + std::cout << "F: 0x" << std::hex << var_value(assignment, real_res.output).data << std::endl; + assert(var_value(assignment, real_res.output) == expected_res); + }; + + nil::crypto3::test_component( + component_instance, + desc, + public_input, + result_check, + instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + m); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_component_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, // theta + 0x38e1a856aae5cf012d142449cfe878b1a827f08fec4ac2d1724b7ebf37d3a637_cppui_modular255, // c0 + 0x854a9d175f7eece4dd7bb82babe799b2369571e75d2386264b512bc4f049ee6_cppui_modular255, // c1 + 0x393d97b04bc5d4490ae53903974c1d0aa65e11b4e9ae487a1d3aede2f6edec92_cppui_modular255}; // q + + typename BlueprintFieldType::value_type expected_res = + 0xf60c8e3799f676371137e184244aaf6859123322600128b05aed7e26223cfd1_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_component_test2) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, // theta + 0x38e1a856aae5cf012d142449cfe878b1a827f08fec4ac2d1724b7ebf37d3a637_cppui_modular255, // c0 + 0x854a9d175f7eece4dd7bb82babe799b2369571e75d2386264b512bc4f049ee6_cppui_modular255, // c1 + 0xf60c8e3799f676371137e184244aaf6859123322600128b05aed7e26223cfd1_cppui_modular255, // c2 + 0x393d97b04bc5d4490ae53903974c1d0aa65e11b4e9ae487a1d3aede2f6edec92_cppui_modular255}; // q + + typename BlueprintFieldType::value_type expected_res = + 0x1ab9e0ab4db80e2649fe1c44791b231a165329cb8e1cb3186fd42311dfb96ba7_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_component_test3) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, // theta + 0x38e1a856aae5cf012d142449cfe878b1a827f08fec4ac2d1724b7ebf37d3a637_cppui_modular255, // c0 + 0x854a9d175f7eece4dd7bb82babe799b2369571e75d2386264b512bc4f049ee6_cppui_modular255, // c1 + 0xf60c8e3799f676371137e184244aaf6859123322600128b05aed7e26223cfd1_cppui_modular255, // c2 + 0x42d09cbf0dbb3ec8e566f3835b8c70cdc6ffb4ee160b7e974174cb84b656c94_cppui_modular255, // c3 + 0x393d97b04bc5d4490ae53903974c1d0aa65e11b4e9ae487a1d3aede2f6edec92_cppui_modular255}; // q + + typename BlueprintFieldType::value_type expected_res = + 0x1d8aaff35b7c1a8afe535c508bda43c907bc059ced7720df45cb83fcce35d632_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_component_test4) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + 0x1f91750ec43107c824e1b79cb0e5b0ce2d5a99ee4d931726955dd619926b3ac8_cppui_modular255, // theta + 1, // c0 + 0x1b058d4ad7a64a076158339af0f28f31b92d727545c3d63fc2d682721d7849fc_cppui_modular255, // c1 + 0x13c8cbb50c4a86600d432cc5340ef1bfa78bb5a35c9381e67b1fe23584a5c12_cppui_modular255, // c2 + 0x3628c1c830ae70a28bf4129c6a01662df74ded3b0529e820fdc3ccf8377ee0e2_cppui_modular255, // c3 + 0x1f36ae1c91b093f88ab05c97230c85bdd79c4d791ac2b0e8bf5c7889bb80aafd_cppui_modular255, // c4 + 0x27b1ece6c803fcf6de3fd9aa5207378466f574a6e9b30e188b1158962fec34cf_cppui_modular255, // c5 + 0x31acc41a65db47a663c27d691157e2f9dcf92de98d482f347c6fa0a78e67d988_cppui_modular255, // c6 + 0x2f971ec81c0309f69e82434a3c6596a509f586fa36712e7fd965ab31ce83b8c2_cppui_modular255, // c7 + 0xcb0e17a777c9ade431b8751afd8057cdd15f74a6795dedd6c1f56bdcdfcff41_cppui_modular255}; // q + + typename BlueprintFieldType::value_type expected_res = + 0xa98684e2e2f94ea94934ca0cf06778ccda845b247f2eb226eff63171181a160_cppui_modular255; + + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/lookup_argument_verifier.cpp b/libs/blueprint/test/verifiers/placeholder/lookup_argument_verifier.cpp new file mode 100644 index 000000000..6625952b4 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/lookup_argument_verifier.cpp @@ -0,0 +1,733 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_lookup_argument_verifier_test + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + std::array &expected_res, std::size_t num_gates, + std::vector gate_constraints_sizes, std::vector gate_constraint_lookup_input_sizes, + std::size_t num_tables, std::vector lookup_table_lookup_options_sizes, + std::vector lookup_table_columns_number) { + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 10 * WitnessAmount; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::lookup_verifier; + + std::size_t num_constraints = std::accumulate(gate_constraints_sizes.begin(), gate_constraints_sizes.end(), 0); + std::size_t num_lu_options = 0; + for (std::size_t i = 0; i < num_tables; i++) { + num_lu_options += lookup_table_lookup_options_sizes[i] * lookup_table_columns_number[i]; + } + + std::size_t num_lu_ops = + std::accumulate(lookup_table_lookup_options_sizes.begin(), lookup_table_lookup_options_sizes.end(), 0); + std::size_t num_lu_inputs = + std::accumulate(gate_constraint_lookup_input_sizes.begin(), gate_constraint_lookup_input_sizes.end(), 0); + + std::size_t m = num_constraints + num_lu_ops; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + component_type component_instance(witnesses, std::array(), std::array(), + num_gates, gate_constraints_sizes, gate_constraint_lookup_input_sizes, num_tables, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + + std::size_t ctr = 0; + var theta = var(0, ctr++, false, var::column_type::public_input); + var beta = var(0, ctr++, false, var::column_type::public_input); + var gamma = var(0, ctr++, false, var::column_type::public_input); + std::vector alphas; + for (std::uint32_t i = 0; i < m - 1; i++) { + alphas.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::array V_L_values; + V_L_values[0] = var(0, ctr++, false, var::column_type::public_input); + V_L_values[1] = var(0, ctr++, false, var::column_type::public_input); + + std::array q_last; + q_last[0] = var(0, ctr++, false, var::column_type::public_input); + q_last[1] = var(0, ctr++, false, var::column_type::public_input); + + std::array q_blind; + q_blind[0] = var(0, ctr++, false, var::column_type::public_input); + q_blind[1] = var(0, ctr++, false, var::column_type::public_input); + + var L0 = var(0, ctr++, false, var::column_type::public_input); + + std::vector lookup_gate_selectors; + for (std::uint32_t i = 0; i < num_gates; i++) { + lookup_gate_selectors.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector lookup_gate_constraints_table_ids; + for (std::uint32_t i = 0; i < num_constraints; i++) { + lookup_gate_constraints_table_ids.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector lookup_gate_constraints_lookup_inputs; + for (std::uint32_t i = 0; i < num_lu_inputs; i++) { + lookup_gate_constraints_lookup_inputs.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector lookup_table_selectors; + for (std::uint32_t i = 0; i < num_tables; i++) { + lookup_table_selectors.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector lookup_table_lookup_options; + for (std::uint32_t i = 0; i < num_lu_options; i++) { + lookup_table_lookup_options.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector shifted_lookup_table_selectors; + for (std::uint32_t i = 0; i < num_tables; i++) { + shifted_lookup_table_selectors.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector shifted_lookup_table_lookup_options; + for (std::uint32_t i = 0; i < num_lu_options; i++) { + shifted_lookup_table_lookup_options.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + std::vector sorted; + for (std::uint32_t i = 0; i < 3 * m - 1; i++) { + sorted.push_back(var(0, ctr++, false, var::column_type::public_input)); + } + + typename component_type::input_type instance_input = {theta, + beta, + gamma, + alphas, + V_L_values, + q_last, + q_blind, + L0, + lookup_gate_selectors, + lookup_gate_constraints_table_ids, + lookup_gate_constraints_lookup_inputs, + lookup_table_selectors, + lookup_table_lookup_options, + shifted_lookup_table_selectors, + shifted_lookup_table_lookup_options, + sorted}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + for (int i = 0; i < 4; i++) { + std::cout << "F[" << i << "]: 0x" << std::hex << var_value(assignment, real_res.output[i]).data + << std::endl; + } + + for (int i = 0; i < 4; i++) { + assert(var_value(assignment, real_res.output[i]) == expected_res[i]); + } + + // std::cout << "expected F: " << expected_res.data << std::endl; + }; + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, num_gates, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, num_tables, lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 3; + std::size_t lookup_tables_size = 3; + std::vector gate_constraints_sizes = {1, 1, 1}; + std::vector gate_constraint_lookup_input_sizes = {7, 2, 1}; + std::vector lookup_table_lookup_options_sizes = {1, 2, 3}; + std::vector lookup_table_columns_number = {7, 2, 1}; + + std::vector public_input = { + /*theta: */ + 0x1f91750ec43107c824e1b79cb0e5b0ce2d5a99ee4d931726955dd619926b3ac8_cppui_modular255, + /*beta: */ + 0x181227226c1423739b9ac923321ed2f09ed6b194314f01fa63cf06a993087d47_cppui_modular255, + /*gamma: */ + 0x2e42a7ce383a60b4a57ead1d59609e3c77d54efdbc3fb3b78df45906b61fa26c_cppui_modular255, + /*alpha: */ + 0x12c35108408002e011a77b1385fafa519b0c049dda69edeec76a5582aefdf679_cppui_modular255, + 0x3c43857ea745aebffbf5b5a15c1f19f6c544fc6f4c058200e8c1fcfc94be97a2_cppui_modular255, + 0xed5489c9d40c4d8a7214f245430ca28651cf789957e34a192e5a25bbe99a36f_cppui_modular255, + 0x24a490b07a66d4d3c7889869f50b54b7d45e049c959d2c7b3908127cc0166736_cppui_modular255, + 0x20082215dffd067489b93a647a77e2d063a0a020b1d1c833fac1d38127789a52_cppui_modular255, + 0x2535b195a0c7d604ab78762b644abe5eb3ce5bb9bf637954b073753c08b913ed_cppui_modular255, + 0xb4959a45c71c0be90a8a6df21b547679746452337f85337087f18498e96b39f_cppui_modular255, + 0x1009604828f0b005c222820f19d2586ce28ff73d888558ea1bae36dc10923fe4_cppui_modular255, + /*V_L: */ + 0x2f067360ee454281ed80c4bfd37b9e0140071ed383651858abb37d0d868238a2_cppui_modular255, + 0xd079c9f0f66462d4dcf74077bcf5d4e1fb62214198a22d789637d6651d840de_cppui_modular255, + /*q_last_0: */ + 0x2fe713a78f776121b2d68cfe5d35db3f34ca42b4973228a520617b5d92e8e449_cppui_modular255, + 0x36041a6ee4c9d83ca3b8f282a8212e5ecd3a1304f01aeb6fc81dd055c30a115d_cppui_modular255, + /*q_blind_0: */ + 0x143ef00c6cd28361b04c9c9e3782c1cb5e4543f1faea52c9b22a93803bed7f9d_cppui_modular255, + 0x2fe713a78f776121b2d68cfe5d35db3f34ca42b4973228a520617b5d92e8e449_cppui_modular255, + /*L_0: */ + 0x3c8519a916c0ddd81a0992eeb1fc91bc33674d02900ba9977478c89af7e5fd37_cppui_modular255, + /*gate selectors: */ + 0x31269dc016cbeeb0720063dcd5494f9eb9348c5dd1d03c98a7ea90340c6cf4e3_cppui_modular255, + 0x3bd9fc4c03b61b7c9cdcd6636b4762f5b17dab51807d76c85fce52fc31299c1d_cppui_modular255, + 0x3f54e2a2ecf53da482d34374b94ad139a05cf74af9bec64c8482bb4e39439ee7_cppui_modular255, + /*table_ids: */ + 1, 2, 3, + /*lookup gate constraint lookup inputs: */ + 0x124971c6cce767192245d6688a4af4482b1c5e935bbdec7825da26763c93e6fc_cppui_modular255, + 0x3a57f30b24c9aec7984dd8bed4f52cf9497665344886c51d9bb9821309c4623b_cppui_modular255, + 0x3ebc055241d36e5d1c8afb9ef7466f8e5d01ac3d16ce2c707d78a92820e61a1c_cppui_modular255, + 0x32da319a20e51a7808c32e35c73434b5f72e0f5031e8c10cdce4b218fc87781a_cppui_modular255, + 0x186be39596fdb9314fbb1f081dcd7056eefe8beec42a1cf745a42863028325c2_cppui_modular255, + 0x9ea111bf6543ac23f0cb51d9ec6a57265709bf3d294df922d7b6841348c255c_cppui_modular255, + 0x2d93a78ddc155e05f452fbf35778c89283dc93503bed266bda527150a440a17b_cppui_modular255, + 0x32da319a20e51a7808c32e35c73434b5f72e0f5031e8c10cdce4b218fc87781a_cppui_modular255, + 0x10e3d915ddd49ca40f1011a754ef3e1e3fa146fed50413be1d8b0c2c9b1a6484_cppui_modular255, + 0x2fddfd79e3de586bd5a03b21b6592cae2ef255e993a07c77dd51d26a6afbfaa8_cppui_modular255, + /*lookup tables selcetors: */ + 0xcb0e17a777c9ade431b8751afd8057cdd15f74a6795dedd6c1f56bdcdfcff41_cppui_modular255, + 0x32a401287578a2c63fb7bc230972cbbcc34700009228e76f186364906b469fa6_cppui_modular255, + 0x32a401287578a2c63fb7bc230972cbbcc34700009228e76f186364906b469fa6_cppui_modular255, + /* lookup tables lookup option: */ + 0x1b058d4ad7a64a076158339af0f28f31b92d727545c3d63fc2d682721d7849fc_cppui_modular255, + 0x13c8cbb50c4a86600d432cc5340ef1bfa78bb5a35c9381e67b1fe23584a5c12_cppui_modular255, + 0x3628c1c830ae70a28bf4129c6a01662df74ded3b0529e820fdc3ccf8377ee0e2_cppui_modular255, + 0x1f36ae1c91b093f88ab05c97230c85bdd79c4d791ac2b0e8bf5c7889bb80aafd_cppui_modular255, + 0x27b1ece6c803fcf6de3fd9aa5207378466f574a6e9b30e188b1158962fec34cf_cppui_modular255, + 0x31acc41a65db47a663c27d691157e2f9dcf92de98d482f347c6fa0a78e67d988_cppui_modular255, + 0x2f971ec81c0309f69e82434a3c6596a509f586fa36712e7fd965ab31ce83b8c2_cppui_modular255, + 0x1b058d4ad7a64a076158339af0f28f31b92d727545c3d63fc2d682721d7849fc_cppui_modular255, + 0x13c8cbb50c4a86600d432cc5340ef1bfa78bb5a35c9381e67b1fe23584a5c12_cppui_modular255, + 0x3628c1c830ae70a28bf4129c6a01662df74ded3b0529e820fdc3ccf8377ee0e2_cppui_modular255, + 0x1f36ae1c91b093f88ab05c97230c85bdd79c4d791ac2b0e8bf5c7889bb80aafd_cppui_modular255, + 0x27b1ece6c803fcf6de3fd9aa5207378466f574a6e9b30e188b1158962fec34cf_cppui_modular255, + 0x31acc41a65db47a663c27d691157e2f9dcf92de98d482f347c6fa0a78e67d988_cppui_modular255, + 0x2f971ec81c0309f69e82434a3c6596a509f586fa36712e7fd965ab31ce83b8c2_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0xb9481d2c72ffdac988d67079b8eeb9a17a0883db91507b650af1fb2992cdd7d_cppui_modular255, + 0x3a41600a57bc459360967cd9279748fceee9a90ae09a7cbde02e93e0d4f2ad44_cppui_modular255, + 0x3a41600a57bc459360967cd9279748fceee9a90ae09a7cbde02e93e0d4f2ad44_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x1db8ba9b27956295bdf26c4b9502d384ce2447fa7d596fc62263314822fec898_cppui_modular255, + 0x342b0f9fb517871584711bfbefe1ffa3e79d7bdf47980a15f6954402d5e7d8a2_cppui_modular255, + 0x1ed54bb4ec015e30fe0bef1cfc7e702391b1d73dc45f4289b67b04acd2dcf2e9_cppui_modular255, + 0x139c294a0d36efa023e4a91d157774ce8e78f8da9172c90915184b683eaa93d2_cppui_modular255, + 0x4e7594148540be9d7608b428fc578288bb480844be045c03bf67bb63a6425df_cppui_modular255, + 0x14d6b4ef9917aa59be762b845f2befa11a438bc9b52548b1e1a50649c0f3be7a_cppui_modular255, + 0x6042e08d361d89c0d0344212ae9043ba052d5708398387d0e911392a2f3d340_cppui_modular255, + 0x1db8ba9b27956295bdf26c4b9502d384ce2447fa7d596fc62263314822fec898_cppui_modular255, + 0x342b0f9fb517871584711bfbefe1ffa3e79d7bdf47980a15f6954402d5e7d8a2_cppui_modular255, + 0x1ed54bb4ec015e30fe0bef1cfc7e702391b1d73dc45f4289b67b04acd2dcf2e9_cppui_modular255, + 0x139c294a0d36efa023e4a91d157774ce8e78f8da9172c90915184b683eaa93d2_cppui_modular255, + 0x4e7594148540be9d7608b428fc578288bb480844be045c03bf67bb63a6425df_cppui_modular255, + 0x14d6b4ef9917aa59be762b845f2befa11a438bc9b52548b1e1a50649c0f3be7a_cppui_modular255, + 0x6042e08d361d89c0d0344212ae9043ba052d5708398387d0e911392a2f3d340_cppui_modular255, + /* sorted :*/ + 0x3d08d7113ec5ad138d4d720b44f3a8839a8541ee8f677bd6b688a821e34c2df6_cppui_modular255, + 0x130b302c3079f281cef9b0082567fb0a5504ae46546dbe46a6a0c1a217148bd1_cppui_modular255, + 0x1601b0e7bc9d588bfd28ebc94bc310654ab24669f26645d575cbfb1423cb0f90_cppui_modular255, + 0x3cd9714e6b5440733518b80c64a3aaa050516c848b3a09c456d2c99112f396c3_cppui_modular255, + 0x20e9e41aa6d693d979e31d4cd3426d1ba1138f59b18f3e354758343ba6e857d9_cppui_modular255, + 0x3f48aa78bdb07bc9dea0e8b9c5050f653a3e883061fb3e7242975c11cfafa9bc_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + 0x0_cppui_modular255, 0x18582466937834d434ceff70fa27ea4a1486d5c835080ebc13cb1f7b5bbd4850_cppui_modular255, + 0x1979a1d3b8e9bfc29212140b1f38ea9120aa47febf6a0df823348102f783be15_cppui_modular255, + 0x7d60e5c26d15b2aa293d72a00194e20e533fc3f38aa259ac899b4e82b0b3a3b_cppui_modular255, + 0x3a3c06a35de362be982b6bd008c05efce84f297d4893cb61faee0e230e465502_cppui_modular255, + 0x51a74265a7a5c53a24d9d461403d93aba9c5151e77a4da184dab3c417f687f6_cppui_modular255, + 0xae1137ab4cd9d26666d9dbc0ee2f21a1f1692730311f89f97a5f825992e0f26_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + 0x0_cppui_modular255, 0x2187d355d7ebd8be30402d16877e181cca486f51fad291095b33096277e13e_cppui_modular255, + 0x1ef8272e914c2ccd659341031794f603a21dcf3ac35d2e0a9c26bf8881c1ea18_cppui_modular255, + 0x36954d713e9209b0975b87b6b3d51d89ddf76338178177a0b5a503dad5bebd9c_cppui_modular255, + 0x169244a31230525091fa126d82a977e8a7c65705d7aacf73cc28732ed1447f71_cppui_modular255, + 0x28395c6c442a250e83922de639e4296d8925f96f2f0ac1fcd6c129391c76b423_cppui_modular255, + 0x189d2fbb6f71ef06571eeed614cbd374257fb5205ca63b28a5414d3997c1ae4e_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255}; + + std::array expected_res = { + 0x3fb663ded7ac8eb4b399cf60ec20e4a5e51e1d2fe3fc28759f012f13fac238a7_cppui_modular255, + 0x5f511600d82c0f86b86133984c868d32994819f499e0403b5eb708129de72ae_cppui_modular255, + 0x19278da4bf5d19e98a445ebadc0910733d4ad48527b3417af89b9571a7b886c4_cppui_modular255, + 0x20faeea92ea55a96f57dea87d5ce112778185cb59ef6a1252242d571d1a3f3aa_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 2; + std::size_t lookup_tables_size = 2; + std::vector gate_constraints_sizes = {2, 1}; + std::vector gate_constraint_lookup_input_sizes = {1, 1, 1}; + std::vector lookup_table_lookup_options_sizes = {1, 3}; + std::vector lookup_table_columns_number = {1, 1}; + + std::vector public_input = { + /*theta: */ + 0x1bfbff1c2f23e10837c97d97f72c907607f1bcfd3b4700ee2b6781dfc732fbca_cppui_modular255, + /*beta: */ + 0x314d7748efea79faaa93c1b943ce278d30e412a8a293df7f21674ac0da207ddf_cppui_modular255, + /*gamma: */ + 0xc4b0a8e57586aeb49fa3282f7d4584da1b8c127ae316a599a37654c83577aac_cppui_modular255, + /*alpha: */ + 0x3ac916e994eb085e73928ffcf7349bd78232597abe727e5419efca5da3352762_cppui_modular255, + 0x28c5329493df6684237fe29b099bd63b8e308890d824cc22e1cae645ca74d16e_cppui_modular255, + 0x20850cee63944d67e20b3a7bbb287445d56d896b21dfad679371f92d7c8cfdde_cppui_modular255, + 0x733d8c425ee853f86e19b732693e23fb0b3455e4716461232558a95c507edc7_cppui_modular255, + 0x10107a265a3e1c84a7bfba1098d21f8c454e7b924dbd1da5ac8b3aab636c6a0c_cppui_modular255, + 0x350d9495311fef9a4b0209edb4a3c4bddd5ffb56ce2f191ef970ab9c0ed0ea89_cppui_modular255, + /*V_L: */ + 0x2f9124776b548d7c11e42c640a80bac1629a259098c027712ac5c8d03861f9a1_cppui_modular255, + 0x26e20a54731d9020cd3a49f4f93647a9fc6c4220c04c12252381b38822bde8ff_cppui_modular255, + /*q_last_0: */ + 0x3e4be0f60a0892de53ce64e30dbeeb2ad97a4a9f48f8d0de90179dff9b655ee_cppui_modular255, + 0x55b0e97fa86bdb951a6f982ebb8e625911af5d675ae321772de8859ef16759_cppui_modular255, + /*q_blind_0: */ + 0x15cb10dc31dd9dd99205932dc89d5f4cc9d516dd93c82c005e49eb276a565fc3_cppui_modular255, + 0x3e4be0f60a0892de53ce64e30dbeeb2ad97a4a9f48f8d0de90179dff9b655ee_cppui_modular255, + /*L_0: */ + 0x3e480b88f7e13e682fbd0e71b75ae9b7e779f1e5513868152c6cacece130159_cppui_modular255, + /*gate selectors: */ + 0x265031146d81d8f888bd86840686b200aad9dd7480f5400d51e1cbe59bf34a51_cppui_modular255, + 0x226bb05bde03c51205c1b59ceb1103652c623e562be1b98bff1b0116cde048f8_cppui_modular255, + /*table_ids: */ + 1, 2, 2, + /*lookup gate constraint lookup inputs: */ + 0x38cf0677a1def0c0aebaa6d9df9a338e213824e22eaae2279d8e824189bcc666_cppui_modular255, + 0x1bc6cc84db93e32cdcb39fcf0b737ce973ec99e2139941a1eaf10e1f4358f9bb_cppui_modular255, + 0x277581609fba694550ec7d87c8d3c92fdaf392f7303ff5e4d26ad9e187c2a4af_cppui_modular255, + /*lookup tables selcetors: */ + 0x226bb05bde03c51205c1b59ceb1103652c623e562be1b98bff1b0116cde048f8_cppui_modular255, + 0x226bb05bde03c51205c1b59ceb1103652c623e562be1b98bff1b0116cde048f8_cppui_modular255, + /* lookup tables lookup option: */ + 0x13b142d74d5d6ce3355ef3c65e9809d91eb69e0ad2deea4197761e3e3361dc16_cppui_modular255, + 0x13b142d74d5d6ce3355ef3c65e9809d91eb69e0ad2deea4197761e3e3361dc16_cppui_modular255, + 0x3fcbb4a2a370463d52277fd6f5ed1ad2b814a3c19bad97c660a2c1d638c348ec_cppui_modular255, + 0x1d0c444e682d3cd84514835b04cc28bd46f35621d6c6dbea5e34e783a6836b9a_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0x25fa802aedd96d1cf3a316ebd7cb239e51c82e17199a5cebdab3e35ffd01e2f8_cppui_modular255, + 0x25fa802aedd96d1cf3a316ebd7cb239e51c82e17199a5cebdab3e35ffd01e2f8_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x3b8e1e41ccc266c12d5f3e08397385c4d88c4f0fd56b7ae5f9dd594b44ce5451_cppui_modular255, + 0x3b8e1e41ccc266c12d5f3e08397385c4d88c4f0fd56b7ae5f9dd594b44ce5451_cppui_modular255, + 0x39729f1872018851ef8eb0a3706b37dc0aa16a8f3988602e73d9376435d7c326_cppui_modular255, + 0x7be020326311d5b6ba5130e1d85ab6ae573fa30f2218b4d10325204dc16a399_cppui_modular255, + /* sorted :*/ + 0x144cf6b94befcde04ca399aa28f7c19b9c7abe4516b149d51c979578c26132aa_cppui_modular255, + 0x4f4a9468effe3b8b593752aa6ca4076d9548e7ad0db0dd4db34775c4806d44f_cppui_modular255, + 0x3f3c910aa039f6a9c696978cffd810099023bac4778c6a25df3e7cc4882e249f_cppui_modular255, + 0x362a6fe7cda76313effd4f422c2aa30517af2e8cf75cbd6cacd18592f8504465_cppui_modular255, + 0x2a6d49e1c07f61f3356cde688fb9807c7efa8120c1357b768292038c29c33ca1_cppui_modular255, + 0x2fc44c30d66a005631bab5e36186f6f2df3d82695bb41025d12706204a8e98fa_cppui_modular255, + 0x34aa8f0b44149c23df98d0a7950f11945bcaf1588a589d8442fcf6a4551ff475_cppui_modular255, + 0x2be0a5cb81a428769c7a00a5c286d190daa41649ce8cc80dd48da8ddb6541ff4_cppui_modular255, + 0x37822189c5f080b02ce93fd126be0cd976149faf2c698a05f8e67716c95fff2c_cppui_modular255, + 0x3c107fd152981cf471acbcec2bc52731776ee7572a0358668751955be7ebe5e3_cppui_modular255, + 0x20a347ef86111d08804823c1e81f6c7bffa4c768533e19f86bbd4dacb9539fb5_cppui_modular255, + 0x22e3fb00764626ac8e7f5765869236b03c60e073c00bfd01e63a662ab7a66199_cppui_modular255, + 0x3f4bda0c68abd98d7b543381553ac4523dc3510eb347a1072a08246a7a454491_cppui_modular255, + 0x44ac8e5824fe8aa17842a4a4fe23aadcbbc09f07b02c60df78e22fcb4bcec46_cppui_modular255, + 0x384d6cead91ac3c0ae1a8acc9f4d753121afe02066774dd10a381ce3f30649f9_cppui_modular255, + 0x181e07ef1cacc19be824f2d02109b1863ab1740bdc82e0e184b3246af28f8773_cppui_modular255, + 0x17a9cdd5c4c1fe3143318316c5a9b18d879813ebda0c56f2b3ab5faaf8e530af_cppui_modular255, + 0x1df2aafa1674d30190d7e9fc28bb72263c0e73ff16f4558736d45836e45de71a_cppui_modular255, + 0x1c397b8d8e0b71e74a95b05b6769850aa0cff9c3d29bf0e48c7df205c571f7ab_cppui_modular255, + 0x3fc816df5e71c581a52cdbbd4cee972b9b56cb08b5d838dfe317b93e0243e11a_cppui_modular255}; + + std::array expected_res = { + 0x35e69c5ddd12ba21112c825dbece7df98c7e250b0c4e4517a8aa79f0925499b0_cppui_modular255, + 0x246009173489a2c898257f798838fbbf15099b5e311b765e0114cbb017472baa_cppui_modular255, + 0x1f4800182b9b832ceaca69367a60809745976e7793157079f305358ecc0d1034_cppui_modular255, + 0x3784c3bbe15ea3db50780f6d73698cd40e98f82aafdc6dfd62f7d3f29aa4f1ff_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test2) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 1; + std::size_t lookup_tables_size = 1; + std::vector gate_constraints_sizes = {1}; + std::vector gate_constraint_lookup_input_sizes = {3}; + std::vector lookup_table_lookup_options_sizes = {1}; + std::vector lookup_table_columns_number = {3}; + + std::vector public_input = { + /*theta: */ + 0x1d7f68089e58314bd0fd09de62903aa5ec3268b574f97bf7cad8e1af15952f05_cppui_modular255, + /*beta: */ + 0x815e5816cf879a118c629f6f724e75486494f53371ad59a031e8acf03199b88_cppui_modular255, + /*gamma: */ + 0x257158c4ad84265ed8011284ea62f8d24834881034b1d1bf2bdf6dfd0d87aab6_cppui_modular255, + /*alpha: */ + 0xc5f37c144b3d9a3727c15cb2860927aa1852b09741fa40473fc9eb1787dc89e_cppui_modular255, + /*V_L: */ + 0x3d71d8822355d24ec28a4689490af52a0edec77b1a1e1a336b00136ca5b2aa55_cppui_modular255, + 0x39bfbccba576b46aadd07a1269e2167a8af30ee2f5b63d18da633976f8d39b90_cppui_modular255, + /*q_last_0: */ + 0x289716269efe8d211ed1c4b73cf3a1a8a92c4f35b60c88610e2a2274a9462e4d_cppui_modular255, + 0x3938ee6f665648111b1fb38692685fc58d13dd96eb282fa132df606fc13c24ff_cppui_modular255, + /*q_blind_0: */ + 0x2ce4873d86986fd9fd9f80e3d17f77086d77fa3600ffcc14a5b151735026659_cppui_modular255, + 0x1d9d793626a197fb1ca1b02a9283241f3de2f6291d8e3009ef5c5a2f90ccb87b_cppui_modular255, + /*L_0: */ + 0x3c64fd8ec24abf24f8ff563202680a359bc5d73caac1096da418e429324017db_cppui_modular255, + /*gate selectors: */ + 0x3c64fd8ec24abf24f8ff563202680a359bc5d73caac1096da418e429324017db_cppui_modular255, + /*table_ids: */ + 1, + /*lookup gate constraint lookup inputs: */ + 0x3c64fd8ec24abf24f8ff563202680a359bc5d73caac1096da418e429324017db_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + /*lookup tables selcetors: */ + 0x1835a3d6c64d2cbc4854ed08838c5cb178c38be251bc63a735bc4624ef775382_cppui_modular255, + /* lookup tables lookup option: */ + 0x3d3c325d1c415801ca4b9f57fe82a5e5ee1dd4f0cd544d2bac86cf6507d1411b_cppui_modular255, + 0x1af97179aa0bd4ba7e094db08509b6cbacec4fed8db50f972262a7ace7a61268_cppui_modular255, + 0x40343edb5eb0ff0af2bebd16c1a46206109f759e22c1d8a79a76ef546951c1c_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0x1b61b2f62241a3d026348fb3f38c07218775858811553d73a6f5c9de607b465e_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x175e6f086c5693df7708a3e28771c101266b8e2e2f291fe92d4e5ae919e62a42_cppui_modular255, + 0x40343edb5eb0ff0af2bebd16c1a46206109f759e22c1d8a79a76ef546951c1c_cppui_modular255, + 0x3c64fd8ec24abf24f8ff563202680a359bc5d73caac1096da418e429324017db_cppui_modular255, + /* sorted :*/ + 0xaae87d4c1a9c5f67fd8d128e52c19df71cae8ae988616ccdf64469b1f622167_cppui_modular255, + 0x88ac88639b105ff6b4c74b858b9a6956b54ec13dc00aa5db193d934078a6744_cppui_modular255, + 0x22cf6baa70e28b08de79a41bffe3ba33dff26ef9269dee9488688f53652a3f6c_cppui_modular255, + 0x3ad0b47143a7797996a7583b7b976d1d65dd0dfb82c24e3095d5ad597451ebd8_cppui_modular255, + 0x2882c884e7588cb74da62fb359b02d5143f2eff9d3e475112caee5f237f5265c_cppui_modular255}; + + std::array expected_res = { + 0x1d38affafcaea87529bfbd1eecfb8bdfec63c8477ef34d81ed7524a4b3dfd0ee_cppui_modular255, + 0x1517c78abe81de6b6ab6cea789d87b139ae198de67059be4da160d4eef47a120_cppui_modular255, + 0x1c6d84769c65a19b6291825efc7e31c970629504b4c88816a90e7d339ebe2252_cppui_modular255, + 0x3c74bf9104f97f4019d75b317c1df2629f1f421002b7abc02344d53707b21de2_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test3) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 1; + std::size_t lookup_tables_size = 1; + std::vector gate_constraints_sizes = {1}; + std::vector gate_constraint_lookup_input_sizes = {3}; + std::vector lookup_table_lookup_options_sizes = {1}; + std::vector lookup_table_columns_number = {3}; + + std::vector public_input = { + /*theta: */ + 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, + /*beta: */ + 0x234beb2f67e10c0b52a8162be56be6d0b441bfb15b18ea41167ff3eb6f07160e_cppui_modular255, + /*gamma: */ + 0x1855e317848ad8aa18904176c2af32d853dfc7698d97dc19b608992e6bdaa052_cppui_modular255, + /*alpha: */ + 0x17f36241082dc5dc4e12968736300a389c2cbab471ace605aa7eb8d1cc91016f_cppui_modular255, + /*V_L: */ + 0x2aad0ea7357d6d6bc3e696cb9f53a669b36ef1888fd3daefda299547d5222873_cppui_modular255, + 0x20d25dca706ce16e69797886802260ca4dbfbde1dbc488d97c44af1b752dc2cb_cppui_modular255, + /*q_last_0: */ + 0x30895ee54f09f37d02e99a406c6c8af394e4dd638575bb48bb97498bea211940_cppui_modular255, + 0x2afc6ee2bd64a76ce5d5a2345bed6c99dd4452d7d1712321e497c67b3906a86c_cppui_modular255, + /*q_blind_0: */ + 0x31849db7e2ccf46d25b15a478e4da18972e88efdeccd4525110e636f945a2b3c_cppui_modular255, + 0x16c598e3d182c4be8e8c706fd77a79e03918b7cd335d74768864041a54a195eb_cppui_modular255, + /*L_0: */ + 0x2342576902db29a6082f737d90dd8ceca2aa402444b829d177859040ca028f33_cppui_modular255, + /*gate selectors: */ + 0x2342576902db29a6082f737d90dd8ceca2aa402444b829d177859040ca028f33_cppui_modular255, + /*table_ids: */ + 1, + /*lookup gate constraint lookup inputs: */ + 0x2342576902db29a6082f737d90dd8ceca2aa402444b829d177859040ca028f33_cppui_modular255, 0x0_cppui_modular255, 0x0_cppui_modular255, + /*lookup tables selcetors: */ + 0x3aafabf9cb4dee6fcf3597fa74684696bc5c1e6e64ebc113875c558ab7822c55_cppui_modular255, + /* lookup tables lookup option: */ + 0x334125c610355fc7c5f126ca247e9a5233590241e3050c6a97c4f3c942db3c7a_cppui_modular255, + 0x76e8633bb188ea8094471304fe9ac4489031c2c81e6b4a8ef9761c174a6efdb_cppui_modular255, + 0x844b6e352d0b85ae01b8495c8912db85614af6a1193e948b32d2d4e09d4940e_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0x32f5948010c470a8f18f6943a95866e981c20bbad832c7c31a4a1f50487e131c_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x2ab0dd9cbdf3b84e1173e4ade0c739312bad5c50c69ede7a671cf2023ea97f0e_cppui_modular255, + 0x844b6e352d0b85ae01b8495c8912db85614af6a1193e948b32d2d4e09d4940e_cppui_modular255, + 0x2342576902db29a6082f737d90dd8ceca2aa402444b829d177859040ca028f33_cppui_modular255, + /* sorted :*/ + 0x309c3f5b27a9decbc857a77c4de0dcf924ab52beef5f1fe1478b4d5f0cdea83d_cppui_modular255, + 0x2f84548fee17235fa8b337133a77b4325accc997229273a513d7eeb2c26d8c72_cppui_modular255, + 0x3c3a027f046e121eb28f3f3a8c595f722b5711aca301ac25563a0bc13f600a8c_cppui_modular255, + 0x3aa7d1881a22a4408949c2b9e4ad31bba1329e386882f6454c8070c3673f2783_cppui_modular255, + 0xf47f8092f969b60daabd4cb1ef68b595a76c42fdc9e217912e4ced2f7dcad3a_cppui_modular255}; + + std::array expected_res = { + 0xed0fd5a4c7353bbe05c6b605c83fbec891cdf62c9e137b630aea8fc23eba213_cppui_modular255, + 0x2730aed0d40e486fefa62d17dceffdd430ddd4e1129876f1be75b0440887f59a_cppui_modular255, + 0x509b68f48912d1abc42fead48c7aae0adc49dfc05aafcabbcd1b4a84a81822b_cppui_modular255, + 0x126cb3995ab0d911bb988e50df2ece7ba3d22d3fd272421c29579d7c091bbea4_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test4) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 1; + std::size_t lookup_tables_size = 1; + std::vector gate_constraints_sizes = {1}; + std::vector gate_constraint_lookup_input_sizes = {3}; + std::vector lookup_table_lookup_options_sizes = {1}; + std::vector lookup_table_columns_number = {3}; + + std::vector public_input = { + /*theta: */ + 0x3570e3894c1215e257f32b60a2506ab1bb838dd71e5bdd070aeea75eef5c04e1_cppui_modular255, + /*beta: */ + 0x4110a75d7bb3b6119d90555bef2ef98a5cc6d02b863d166a8478e7db7d097b7_cppui_modular255, + /*gamma: */ + 0xd8c6a785dd97b0298dec35c05b9a644434fb8ffb8fd571b0efa28269166320f_cppui_modular255, + /*alpha: */ + 0x22c705f52e10b6f5c581348c05068b813972db6286d46fe377bc0e5cd43b6986_cppui_modular255, + /*V_L: */ + 0xfed12cbd566c5b8f2643428652eb43c7bc927475b69c7a4e24deacb830e18b2_cppui_modular255, + 0x19e5f35744d2e897dc93a79eb0ad4840205abcb9f75a7bbc25c82c09d7d12b86_cppui_modular255, + /*q_last_0: */ + 0x25f7509bea7b2d56c9ff4f491e4414772c978dca237a1fae98d9f51bf31ae22d_cppui_modular255, + 0x349f44e2ae4743a2763ccf01c567c9b869a176070df81d96a490c496cad8c2c4_cppui_modular255, + /*q_blind_0: */ + 0x23b533ff1c373758f4acbd22122f1c0262a368e2b7fe16da8407f08650e476b3_cppui_modular255, + 0x36b42752f807b80ecc9b48c46f7af88581f8a1d01062d7b6599c88e22cfde5d1_cppui_modular255, + /*L_0: */ + 0x3b34f6cecb7a7e839ae09301afc44b95407a20905b8a14be97abd9f4d8d9173e_cppui_modular255, + /*gate selectors: */ + 0x2b041988e0bcf922cb877fb2c7b9c78f089b71c49697abfb91c120c7c2eba5a3_cppui_modular255, + /*table_ids: */ + 1, + /*lookup gate constraint lookup inputs: */ + 0x216a008a9b4cb540a650f5b8ce4ffa32ed13b0b36d772aa4b44c34e7089b30ec_cppui_modular255, + 0xf34378a7a7d68ec55dc448195fe4d8230e3e586f8d33e253fa06b55c9bb2c81_cppui_modular255, + 0x3f89dc06cbc9ed36f7c9eb31a5ba50c08c81b20e3cc6df6392088d3e70560457_cppui_modular255, + /*lookup tables selcetors: */ + 0x3b1e84962dd31ccca67360931fc883f1971eb3b6e4e4a00b16f9d32fe3278fe6_cppui_modular255, + /* lookup tables lookup option: */ + 0x2270b42c8a18543699ab4604e78e1fa874b8bbd2921a33b51ec991ce9917dc3c_cppui_modular255, + 0x3feea6bec6d7e5cfec0942e3cd3ad1b016583f8dae822d4928482006c3edc444_cppui_modular255, + 0x349f44e2ae4743a2763ccf01c567c9b869a176070df81d96a490c496cad8c2c4_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0x1b436824b0657adcb1724930a2505ce4bb0c54429299e1770e7b7a0f127e45f_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x3920d125f461b2c1993aeae529f95de7da14a84e2df91eec971d5994c7541af9_cppui_modular255, + 0x3b2fddd766fb36fcba6a1daf528db241a30d0d253faf6bdd87dee4161f39cba3_cppui_modular255, + 0x2dd16f49dbd11094236e7703222655f02d5ddec78d6f0f3a1365fe24ce3f1979_cppui_modular255, + /* sorted :*/ + 0x29caaf2bb1ed8d76799585de1615640b5e860512b975eddc1ccc07f3c98b7020_cppui_modular255, + 0x3dec9024d47503f3c555ecc92ff68f5341be354cfb6f996d3891259b01f9b447_cppui_modular255, + 0x2f5a6a904d8e435c78790bc85d1451d353480bbca304a67878b4d130098d7faa_cppui_modular255, + 0x3a5fb8b401914e92a328bc1f72fb8671d39432059ed0b4b17e457899ae244e47_cppui_modular255, + 0x561e67687c658bf77f8a1bac00def678dcd280cd0e94cb52cb4870589541c9e_cppui_modular255}; + + std::array expected_res = { + 0x304cec1d36435a3e6b2b0d8a03d7ca470fe20f1e0664b0a87b330020abeb7790_cppui_modular255, + 0xc87379013fea37f144c3272f082dd7687a98f7531d6ecd8030e58ebe38bb21d_cppui_modular255, + 0x2db7784e371e44e5a2c8bd73f52edf1a0e24801444f312c67a41054f4367cfe4_cppui_modular255, + 0x1300ba94cabf85c2f178f39aa3322edf9db9fb27e284fd75f03fdc0f5ba3711a_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_gate_argument_verifier_test5) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::size_t lookup_gates_size = 1; + std::size_t lookup_tables_size = 1; + std::vector gate_constraints_sizes = {1}; + std::vector gate_constraint_lookup_input_sizes = {3}; + std::vector lookup_table_lookup_options_sizes = {1}; + std::vector lookup_table_columns_number = {3}; + + std::vector public_input = { + /*theta: */ + 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, + /*beta: */ + 0x3e13fbff8a3e86fd9f2008fad7038a4cd7f131714459e26db3dcd6d4ade21176_cppui_modular255, + /*gamma: */ + 0x3812204b2d8a8231ea5b286ba560cb5f81d79413f41da18e1e46864c4b4f8d91_cppui_modular255, + /*alpha: */ + 0x248372a47839f741d202d4c85de0073259242acc0d885a2d6bde0962fa8a6a10_cppui_modular255, + /*V_L: */ + 0x4e37933c31782d66c0562d7d9cb454160e1978332bd5bd21e5ea353b5683e17_cppui_modular255, + 0xe6354934aa089780f37b58f2a1e06d3e86abe87b914e9fd570874bb50b7d824_cppui_modular255, + /*q_last_0: */ + 0x27e2f5b264645452ea918502e707d6bfbd367964a9941a43c60b4c3a1cf07a6d_cppui_modular255, + 0x185ff7f1d1095cb5afd3453b60b94979e86bae25d109aebe649b37f686a5166e_cppui_modular255, + /*q_blind_0: */ + 0x207dc1c6703c47f4d2677a9d22b5067a0286f863e7d111d36141ab7c2c47cf95_cppui_modular255, + 0x640e57a8467b4ac8c5f28df2de4c22e9bfe4ebe2a38d9463c88af718fe8eff9_cppui_modular255, + /*L_0: */ + 0x216c498e3d3ac1475457bdf32d60392f8c5ba174e750103cdecad98748c8f76f_cppui_modular255, + /*gate selectors: */ + 0x1be57e68b61a28070b21d2c655643b1ba0f3ac40f1b420dae429069f370284f4_cppui_modular255, + /*table_ids: */ + 1, + /*lookup gate constraint lookup inputs: */ + 0x286d7641832338fb96156dc92b252e9373ebf6467885af29d34af6eeaa3f4d0f_cppui_modular255, + 0x172b41321a52e33fbc0dc04bafc25ddd93b28d1dafa4769e9a2c3e17bddbf593_cppui_modular255, + 0x66e1ce661dd815a11bad6d33497ff7c90a77ab56c90fc777203287884102451_cppui_modular255, + /*lookup tables selcetors: */ + 0x1632fef8ee24a270eeaf426cc8e2e996f8741eba99e4b5e32c42909c6dfebe92_cppui_modular255, + /* lookup tables lookup option: */ + 0x3827f3dafcadebcc13addd89f6e1fb57f23d5371cc843d18dfea03bde8f33af5_cppui_modular255, + 0x3419c210464e9866e7b872d501983124cc47c214608a54038b7f9b7b066a477b_cppui_modular255, + 0x185ff7f1d1095cb5afd3453b60b94979e86bae25d109aebe649b37f686a5166e_cppui_modular255, + /*lookup tables shifted selcetors: */ + 0x1f3f50955a5607029333bb249589d94c9c641209b02b1761a672322d30229f93_cppui_modular255, + /* lookup tables shifted lookup option: */ + 0x3b81c607a0e9cac79bbfc5e837079988edadb93a8afb339fa2332f4be2135594_cppui_modular255, + 0x22193ce8a7d60a0a06f6cf97c74ab8724e72f5a242a75afb39f0260e67947718_cppui_modular255, + 0x1fc7fbe92ba48f1663da984e9628b1de09d1a54bfb7a8e5a7b4ecbc7624e2487_cppui_modular255, + /* sorted :*/ + 0x1ad4c042735d793d01537bad9f89a0f1dacacdc47495cd16a638e9ddf74b7f90_cppui_modular255, + 0xeef4260e7f2f40ebcf749089fb3bcc22a0d504a0c4b0b0bb581832fb8597e3d_cppui_modular255, + 0x263a119ae0c496c4a6a289302d2eb77f1acbb3cf11c71b7594b8102060192c5_cppui_modular255, + 0x3c6db664b0ff67f755a19001fdd120088a06e162e3ef0cb16dfe35095040147b_cppui_modular255, + 0x3d35e0079956b46536464e7edd6e72c1cfd8c58c07d9127ef7c79b03776e5ee3_cppui_modular255}; + + std::array expected_res = { + 0x2413f12ba244014bb6d17827fdf209681c9f3a27fbce3708282075180377399d_cppui_modular255, + 0x1b8893815550e740989d0307c537ee686917e97aeedc6373cf074bb385d4b1bc_cppui_modular255, + 0x3b6b19adb84d93d44f6d20a4d2e7318130609cf9d0dbcf032265e31b92dd20ec_cppui_modular255, + 0x39ee0a3ffd9389e182c7918896bc78ba74b7af4fc8e2ae5e585b4566b0ea183_cppui_modular255}; // F + + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); + test(public_input, expected_res, lookup_gates_size, gate_constraints_sizes, + gate_constraint_lookup_input_sizes, lookup_tables_size, + lookup_table_lookup_options_sizes, lookup_table_columns_number); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/permutation_argument_verifier.cpp b/libs/blueprint/test/verifiers/placeholder/permutation_argument_verifier.cpp new file mode 100644 index 000000000..d094704e5 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/permutation_argument_verifier.cpp @@ -0,0 +1,173 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE plonk_permutation_argument_verifier_test + +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../../test_plonk_component.hpp" + +using namespace nil; + +template +void test(std::vector &public_input, + std::array &expected_res) { + + constexpr std::size_t WitnessColumns = 6; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 4; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 1; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::permutation_verifier; + + std::size_t m = (public_input.size() - 7) / 3; + component_type component_instance({0, 1, 2, 3, 4, 5}, {}, {}, m); + + std::vector f, Se, Ssigma; + var L0, V, V_zeta, q_last, q_pad; + std::array thetas; + for (std::uint32_t i = 0; i < m; i++) { + f.push_back(var(0, i, false, var::column_type::public_input)); + } + for (std::uint32_t i = 0; i < m; i++) { + Se.push_back(var(0, m + i, false, var::column_type::public_input)); + } + for (std::uint32_t i = 0; i < m; i++) { + Ssigma.push_back(var(0, 2 * m + i, false, var::column_type::public_input)); + } + L0 = var(0, 3 * m, false, var::column_type::public_input); + V = var(0, 3 * m + 1, false, var::column_type::public_input); + V_zeta = var(0, 3 * m + 2, false, var::column_type::public_input); + q_last = var(0, 3 * m + 3, false, var::column_type::public_input); + q_pad = var(0, 3 * m + 4, false, var::column_type::public_input); + thetas[0] = var(0, 3 * m + 5, false, var::column_type::public_input); + thetas[1] = var(0, 3 * m + 6, false, var::column_type::public_input); + typename component_type::input_type instance_input = {f, Se, Ssigma, L0, V, V_zeta, q_last, q_pad, thetas}; + + auto result_check = [expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + for (std::size_t i = 0; i < 3; i++) { + std::cout << "F_" << i << ": " << std::hex << var_value(assignment, real_res.output[i]).data << std::endl; + } + for (std::size_t i = 0; i < 3; i++) { + assert(var_value(assignment, real_res.output[i]) == expected_res[i]); + } + }; + + crypto3::test_component( + component_instance, + desc, + public_input, + result_check, + instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + m); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_argument_verifier_test0) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*.f = */ 0xad2bcf3656123a451e5ae737a10600efd60eb61a019329f54336e570a35ab9_cppui_modular255, + 0x3a229abbe186e216f2c24d215d3a14ec65f213fda941f5d4ee6894ea0f20331e_cppui_modular255, + 0x314564d4bb2dc124ebe6977105d2a16a70e5bc0a100095e28a9931be6a37770a_cppui_modular255, + 0x20b742463ddb6422a9638a1e1e024b97f67786fded20e091254f2d6a0476f847_cppui_modular255, + /*.Se = */ 0xd76a2e28c1a0d640b40187154c48effa28452984730b0a7a0eb15e5ce281546_cppui_modular255, + 0x3512e6cbc8242f438407a36a7d6cafe0a4f03fd5aa67a2a8b6a3c9006c86a5d_cppui_modular255, + 0x1095e81fae8b4ec5194263114731f6f6338b13f2c54062d4b9132ed021ea13d1_cppui_modular255, + 0x12ed889e68b889d97e4bef5663f9d2cedf70cac1d0f4f50c0432b923a9926314_cppui_modular255, + /*.Ssigma = */ 0xd76a2e28c1a0d640b40187154c48effa28452984730b0a7a0eb15e5ce281546_cppui_modular255, + 0x740745f98a84b4a006f567611871547c31fc448c6a32fd0b31e2ae2cb614ef6_cppui_modular255, + 0x2a966cffe76f3bf716c6f0abfb15f48472f34b3284e716a4c52108f4e1a9365b_cppui_modular255, + 0x3c814ce925e32a65c269304816c7e0114dfe0cf6a234ca8140fd133f2c19cf2c_cppui_modular255, + /*.L0_y = */ 0x22e9429d6b3f5e7b775dab62879dbaf184cbd89c713ee99e165040d7052d550d_cppui_modular255, + /*.Vsigma_y = */ 0xb69213d83fd8da544645b1bcf69e827f5327ee15437632222676104ad1b08a3_cppui_modular255, + /*.Vsigma_zetay = */ 0x1d4e3ecd39d89a37045c909602e88968d376bc444b6c8976ff0d2d0d407a4ac5_cppui_modular255, + /*.q_last_y = */ 0x22e9429d6b3f5e7b775dab62879dbaf184cbd89c713ee99e165040d7052d550d_cppui_modular255, + /*.q_pad_y = */ 0xeae8e9652b38b988bfcd8e9ef1a418990021cd4fa6981461d0e1c50049b76f2_cppui_modular255, + /*.theta = {*/ 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, + 0x3301d234398523d96772d81b9b06b066888f67fcc3f1d0af919bbc7b856cf854_cppui_modular255}; + + std::array expected_res = { + 0x164cd4eb4883a25e30db9cf84de858ae429a9e8bc1cc6afa78610548c0455b69_cppui_modular255, + 0x644e8375bddf7d18597aab619542335c0767f3398461c70b3ca1ad73c9a89c1_cppui_modular255, + 0x34bbaf6e1e85fef1a66b63e96f343e34c02cbcfc8531d7f18dea0e1c9425ca24_cppui_modular255, + }; + + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_CASE(blueprint_plonk_permutation_argument_verifier_test1) { + + using BlueprintFieldType = typename crypto3::algebra::curves::pallas::base_field_type; + + std::vector public_input = { + /*.f = */ 0x15d6ac6a26ba0bf2d81357fc2009ca5c8df2ee41a3442fdb40f9ef7d31d2968f_cppui_modular255, + 0x0_cppui_modular255, + 0x15d6ac6a26ba0bf2d81357fc2009ca5c8df2ee41a3442fdb40f9ef7d31d2968f_cppui_modular255, + /*.Se = */ 0x3b96bf475105236e888b56379c4a37e56e694e4778d96480b2bff231b3448fd8_cppui_modular255, + 0x29f1bc649519b128aab8af160d73177a9ef42375370b1215190af7448056cf34_cppui_modular255, + 0x11b8adf6e98075cb559b6b6e433f7564b3f0e655f7506f16b1af418f81b20c01_cppui_modular255, + /*.Ssigma = */ 0x3b96bf475105236e888b56379c4a37e56e694e4778d96480b2bff231b3448fd8_cppui_modular255, + 0x29f1bc649519b128aab8af160d73177a9ef42375370b1215190af7448056cf34_cppui_modular255, + 0x11b8adf6e98075cb559b6b6e433f7564b3f0e655f7506f16b1af418f81b20c01_cppui_modular255, + /*.L0_y = */ 0x2a37d8915b58f641a50e3d4e43869c2987f7988f0ff7dac56a285ee684e53af3_cppui_modular255, + /*.Vsigma_y = */ 0x1_cppui_modular255, + /*.Vsigma_zetay = */ 0x1_cppui_modular255, + /*.q_last_y = */ 0x2a37d8915b58f641a50e3d4e43869c2987f7988f0ff7dac56a285ee684e53af3_cppui_modular255, + /*.q_pad_y = */ 0x362c09c9697f3d477eee786396c2e690cc2f73d44a5650e351b2b844dba3fb73_cppui_modular255, + /*.theta = {*/ 0xc51d84f8427d67ce47566fb043b6415f91196129cb6fd0ea3362f213a0e8cc8_cppui_modular255, + 0x3301d234398523d96772d81b9b06b066888f67fcc3f1d0af919bbc7b856cf854_cppui_modular255}; + + std::array expected_res = { + 0x0_cppui_modular255, + 0x0_cppui_modular255, + 0x0_cppui_modular255, + }; + + test(public_input, expected_res); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/placeholder/verifier.cpp b/libs/blueprint/test/verifiers/placeholder/verifier.cpp new file mode 100644 index 000000000..9e2f75734 --- /dev/null +++ b/libs/blueprint/test/verifiers/placeholder/verifier.cpp @@ -0,0 +1,424 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +#define BOOST_TEST_MODULE flexible_placeholder_verifier_test + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../test_plonk_component.hpp" + +using namespace nil; + +bool read_buffer_from_file(std::ifstream &ifile, std::vector &v) { + char c; + char c1; + uint8_t b; + + ifile >> c; + if (c != '0') + return false; + ifile >> c; + if (c != 'x') + return false; + while (ifile) { + std::string str = ""; + ifile >> c >> c1; + if (!isxdigit(c) || !isxdigit(c1)) + return false; + str += c; + str += c1; + b = stoi(str, 0, 0x10); + v.push_back(b); + } + return true; +} + +struct default_zkllvm_params { + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + using constraint_system_type = + nil::crypto3::zk::snark::plonk_constraint_system; + using table_description_type = + nil::crypto3::zk::snark::plonk_table_description; + using Endianness = nil::marshalling::option::big_endian; + using TTypeBase = nil::marshalling::field_type; + + using ColumnType = nil::crypto3::zk::snark::plonk_column; + using assignment_table_type = + nil::crypto3::zk::snark::plonk_table; + + using ColumnsRotationsType = std::vector>; + using poseidon_policy = nil::crypto3::hashes::detail::mina_poseidon_policy; + using Hash = nil::crypto3::hashes::poseidon; + using transcript_hash_type = Hash; + using circuit_params_type = nil::crypto3::zk::snark::placeholder_circuit_params; + + using lpc_params_type = nil::crypto3::zk::commitments::list_polynomial_commitment_params< + Hash, + Hash, + 2 + >; + using lpc_type = nil::crypto3::zk::commitments::list_polynomial_commitment; + using commitment_scheme_type = typename nil::crypto3::zk::commitments::lpc_commitment_scheme; + using commitment_scheme_params_type = typename commitment_scheme_type::fri_type::params_type; + using placeholder_params = nil::crypto3::zk::snark::placeholder_params; + using policy_type = nil::crypto3::zk::snark::detail::placeholder_policy; + + using circuit_marshalling_type = + nil::crypto3::marshalling::types::plonk_constraint_system; + using table_marshalling_type = + nil::crypto3::marshalling::types::plonk_assignment_table; + static table_description_type load_table_description(std::string filename){ + std::ifstream iassignment; + iassignment.open(filename, std::ios_base::binary | std::ios_base::in); + BOOST_ASSERT(iassignment.is_open()); + std::vector v; + iassignment.seekg(0, std::ios_base::end); + const auto fsize = iassignment.tellg(); + v.resize(fsize); + iassignment.seekg(0, std::ios_base::beg); + iassignment.read(reinterpret_cast(v.data()), fsize); + BOOST_ASSERT(iassignment); + iassignment.close(); + + table_marshalling_type marshalled_table_data; + auto read_iter = v.begin(); + auto status = marshalled_table_data.read(read_iter, v.size()); + auto [table_description, assignment_table] = + nil::crypto3::marshalling::types::make_assignment_table( + marshalled_table_data + ); + + return table_description; + } + + static constraint_system_type load_circuit(std::string filename){ + constraint_system_type constraint_system; + { + std::ifstream ifile; + ifile.open(filename, std::ios_base::binary | std::ios_base::in); + BOOST_ASSERT(ifile.is_open()); + + std::vector v; + ifile.seekg(0, std::ios_base::end); + const auto fsize = ifile.tellg(); + v.resize(fsize); + ifile.seekg(0, std::ios_base::beg); + ifile.read(reinterpret_cast(v.data()), fsize); + BOOST_ASSERT(ifile); + ifile.close(); + + circuit_marshalling_type marshalled_data; + auto read_iter = v.begin(); + auto status = marshalled_data.read(read_iter, v.size()); + constraint_system = nil::crypto3::marshalling::types::make_plonk_constraint_system( + marshalled_data + ); + } + return constraint_system; + } +}; + +// TODO(martun): consider moving these functions to some shared location so other tests can re-use them. +template + static nil::crypto3::zk::snark::placeholder_proof load_proof(std::string filename) { + std::cout << "Loading proof from " << filename << std::endl; + + using Endianness = nil::marshalling::option::big_endian; + using TTypeBase = nil::marshalling::field_type; + + std::ifstream iproof; + iproof.open(filename); + BOOST_ASSERT(iproof.is_open()); + std::vector v; + if (!read_buffer_from_file(iproof, v)) + throw "Unable to read proof from file."; + iproof.close(); + + using proof_type = nil::crypto3::zk::snark::placeholder_proof; + using proof_marshalling_type = + nil::crypto3::marshalling::types::placeholder_proof; + + proof_marshalling_type marshalled_proof_data; + auto read_iter = v.begin(); + auto status = marshalled_proof_data.read(read_iter, v.size()); + if (status != nil::marshalling::status_type::success) { + std::cerr << "Status is " << make_error_code(status) << std::endl; + throw "Reading a marshalled object from buffer failed."; + } + return nil::crypto3::marshalling::types::make_placeholder_proof( + marshalled_proof_data); +} + +template +static typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type load_common_data(std::string filename){ + std::ifstream ifile; + ifile.open(filename, std::ios_base::binary | std::ios_base::in); + BOOST_ASSERT(ifile.is_open()); + + std::vector v; + ifile.seekg(0, std::ios_base::end); + const auto fsize = ifile.tellg(); + v.resize(fsize); + ifile.seekg(0, std::ios_base::beg); + ifile.read(reinterpret_cast(v.data()), fsize); + BOOST_ASSERT(ifile); + ifile.close(); + + using common_data_type = typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type; + + nil::crypto3::marshalling::types::placeholder_common_data marshalled_data; + auto read_iter = v.begin(); + auto status = marshalled_data.read(read_iter, v.size()); + return nil::crypto3::marshalling::types::make_placeholder_common_data( + marshalled_data + ); +} + +template +struct dst_params{ + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + + static constexpr std::size_t WitnessColumns = Witnesses; + static constexpr std::size_t PublicInputColumns = 1; + static constexpr std::size_t ConstantColumns = 2; + static constexpr std::size_t SelectorColumns = 35; + + using constraint_system_type = + nil::crypto3::zk::snark::plonk_constraint_system; + using table_description_type = + nil::crypto3::zk::snark::plonk_table_description; + using Endianness = nil::marshalling::option::big_endian; + using TTypeBase = nil::marshalling::field_type; + + using ColumnType = nil::crypto3::zk::snark::plonk_column; + using assignment_table_type = + nil::crypto3::zk::snark::plonk_table; + + using ColumnsRotationsType = std::vector>; + static const std::size_t Lambda = 9;//ParametersPolicy::lambda; + using poseidon_policy = nil::crypto3::hashes::detail::mina_poseidon_policy; + using Hash = nil::crypto3::hashes::poseidon; + using circuit_params = nil::crypto3::zk::snark::placeholder_circuit_params; +}; + +inline std::vector generate_random_step_list(const std::size_t r, const int max_step) { + using dist_type = std::uniform_int_distribution; + static std::random_device random_engine; + + std::vector step_list; + std::size_t steps_sum = 0; + while (steps_sum != r) { + if (r - steps_sum <= max_step) { + while (r - steps_sum != 1) { + step_list.emplace_back(r - steps_sum - 1); + steps_sum += step_list.back(); + } + step_list.emplace_back(1); + steps_sum += step_list.back(); + } else { + step_list.emplace_back(dist_type(1, max_step)(random_engine)); + steps_sum += step_list.back(); + } + } + return step_list; +} + +template +std::tuple +gen_test_proof( + typename SrcParams::constraint_system_type constraint_system, + typename SrcParams::table_description_type table_description, + typename SrcParams::assignment_table_type assignment_table +){ + using src_placeholder_params = typename SrcParams::placeholder_params; + using field_type = typename SrcParams::field_type; + using fri_params_type = typename SrcParams::lpc_type::fri_type::params_type; + + fri_params_type fri_params(0, std::ceil(std::log2(table_description.rows_amount)), + src_placeholder_params::lambda, 4 /*expand_factor*/); + typename SrcParams::commitment_scheme_type lpc_scheme(fri_params); + + std::cout <<"Preprocess public data" << std::endl; + typename nil::crypto3::zk::snark::placeholder_public_preprocessor< + field_type, src_placeholder_params>::preprocessed_data_type public_preprocessed_data = + nil::crypto3::zk::snark::placeholder_public_preprocessor::process( + constraint_system, assignment_table.move_public_table(), table_description, lpc_scheme + ); + + std::cout <<"Preprocess private data" << std::endl; + typename nil::crypto3::zk::snark::placeholder_private_preprocessor< + field_type, src_placeholder_params>::preprocessed_data_type private_preprocessed_data = + nil::crypto3::zk::snark::placeholder_private_preprocessor::process( + constraint_system, assignment_table.move_private_table(), table_description + ); + + std::cout <<"Generate proof" << std::endl; + typename SrcParams::proof_type proof = nil::crypto3::zk::snark::placeholder_prover::process( + public_preprocessed_data, private_preprocessed_data, table_description, constraint_system, lpc_scheme + ); + + bool verification_result = + nil::crypto3::zk::snark::placeholder_verifier::process( + public_preprocessed_data, proof, table_description, constraint_system, lpc_scheme + ); + std::cout <<"Proof verified" << std::endl; + + BOOST_ASSERT(verification_result); + + return std::make_tuple(public_preprocessed_data.common_data, fri_params, proof); +} + +template +void test_flexible_verifier( + const typename SrcParams::constraint_system_type &constraint_system, + const typename nil::crypto3::zk::snark::placeholder_public_preprocessor::preprocessed_data_type::common_data_type &common_data, + const typename nil::crypto3::zk::snark::placeholder_proof &proof, + const typename SrcParams::commitment_scheme_params_type &fri_params +){ + std::cout << "****************** Test flexible verifier with " << DstParams::WitnessColumns <<" witness rows ******************" << std::endl; + using src_placeholder_params = typename SrcParams::placeholder_params; + using field_type = typename SrcParams::field_type; + using value_type = typename field_type::value_type; + + std::array witnesses; + for (std::uint32_t i = 0; i < DstParams::WitnessColumns; i++) { + witnesses[i] = i; + } + using component_type = nil::blueprint::components::plonk_flexible_verifier; + using var = crypto3::zk::snark::plonk_variable; + + bool expected_res = true; + auto result_check = [&expected_res]( + typename DstParams::assignment_table_type &assignment, + typename component_type::result_type &real_res) { + return true; + }; + + nil::blueprint::components::detail::placeholder_proof_input_type full_instance_input(common_data, constraint_system, fri_params); + nil::blueprint::components::detail::placeholder_proof_wrapper proof_ext(common_data, proof); + + std::size_t value_vector_size = proof_ext.vector().size(); + std::cout << "value vector size = " << value_vector_size << std::endl; + std::cout << "var vector size = " << full_instance_input.vector().size() << std::endl; + BOOST_ASSERT(proof_ext.vector().size() == full_instance_input.vector().size()); + + std::vector public_input = proof_ext.vector(); + typename component_type::input_type instance_input(full_instance_input); + + std::array witness_ids; + for (std::uint32_t i = 0; i < DstParams::WitnessColumns; i++) { + witness_ids[i] = i; + } + component_type component_instance( + witness_ids, std::array({0}), std::array(), + SrcParams(), constraint_system, common_data, fri_params + ); + + zk::snark::plonk_table_description desc( + DstParams::WitnessColumns, DstParams::PublicInputColumns, DstParams::ConstantColumns, DstParams::SelectorColumns); + std::cout << "desc = " << desc.rows_amount << " " << desc.witness_columns << " " << desc.public_input_columns << " " << desc.constant_columns << " " << desc.selector_columns << std::endl; + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, + instance_input, nil::blueprint::connectedness_check_type::type::NONE, + SrcParams(), constraint_system, common_data, fri_params + ); + std::cout << "desc = " << desc.rows_amount << " " << desc.witness_columns << " " << desc.public_input_columns << " " << desc.constant_columns << " " << desc.selector_columns << std::endl; + + +// auto r_circuit0 = component_instance.generate_circuit(constraint_system, common_data); +// auto [r_table_description0, r_asignment0] = component_instance.generate_assignment(constraint_system, common_data, assignment_table.public_inputs(), proof); +} + +template +void test_multiple_arithmetizations(std::string folder_name){ +// auto table_description = SrcParams::load_table_description(folder_name + "/assignment.tbl"); + std::cout << "Start loading" << std::endl; + auto constraint_system = SrcParams::load_circuit(folder_name + "/circuit.crct"); + std::cout << "Loaded the constraint system" << std::endl; + + auto common_data = load_common_data(folder_name + "/common.dat"); + std::cout << "Loaded the common data" << std::endl; + + auto proof = load_proof(folder_name + "/proof.bin"); + std::cout << "Loaded the proof" << std::endl; + auto table_description = common_data.desc; + auto fri_params = common_data.commitment_params; + + std::cout << "Usable rows = " << table_description.usable_rows_amount << std::endl; + std::cout << "Rows amount = " << table_description.rows_amount << std::endl; + std::cout << "Witness amount = " << table_description.witness_columns << std::endl; + std::cout << "Public input amount = " << table_description.public_input_columns << std::endl; + std::cout << "Constant amount = " << table_description.constant_columns << std::endl; + std::cout << "Selector amount = " << table_description.selector_columns << std::endl; + std::cout << "Lambda = " << fri_params.lambda << std::endl; + +// auto [common_data, fri_params, proof] = gen_test_proof(constraint_system, table_description, assignment_table); + + test_flexible_verifier>(constraint_system, common_data, proof, fri_params); + test_flexible_verifier>(constraint_system, common_data, proof, fri_params); + test_flexible_verifier>(constraint_system, common_data, proof, fri_params); + test_flexible_verifier>(constraint_system, common_data, proof, fri_params); +} + +BOOST_AUTO_TEST_SUITE(blueprint_pallas_test_suite) + +BOOST_AUTO_TEST_CASE(basic_test) { + test_multiple_arithmetizations("../test/verifiers/placeholder/data/merkle_tree_poseidon"); +} + +// TODO: add vesta tests +// Cannot add bls12 tests because poseidon circuit is not implemented for it. + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verifiers/r1cs_ppzksnark.cpp b/libs/blueprint/test/verifiers/r1cs_ppzksnark.cpp new file mode 100644 index 000000000..f90d73e67 --- /dev/null +++ b/libs/blueprint/test/verifiers/r1cs_ppzksnark.cpp @@ -0,0 +1,459 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE r1cs_ppzksnark_test + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +//#include + +#include +#include +#include +#include +#include +#include + +#include "../pairing/weierstrass_miller_loop.hpp" +#include "../r1cs_examples.hpp" + +using namespace nil::crypto3::zk; +using namespace nil::crypto3::zk::snark; +using namespace nil::crypto3::algebra; + +template +void test_verifier() { + typedef typename ppT_A::scalar_field_type FieldT_A; + typedef typename ppT_B::scalar_field_type FieldT_B; + + const std::size_t num_constraints = 50; + const std::size_t primary_input_size = 3; + + r1cs_example example = + generate_r1cs_example_with_field_input(num_constraints, primary_input_size); + BOOST_CHECK(example.primary_input.size() == primary_input_size); + + BOOST_CHECK(example.constraint_system.is_satisfied(example.primary_input, example.auxiliary_input)); + const r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); + const r1cs_ppzksnark_proof pi = + r1cs_ppzksnark_prover(keypair.first, example.primary_input, example.auxiliary_input); + bool bit = r1cs_ppzksnark_verifier_strong_input_consistency(keypair.second, example.primary_input, pi); + BOOST_CHECK(bit); + + const std::size_t elt_size = FieldT_A::size_in_bits(); + const std::size_t primary_input_size_in_bits = elt_size * primary_input_size; + const std::size_t vk_size_in_bits = + r1cs_ppzksnark_verification_key_variable::size_in_bits(primary_input_size); + + blueprint bp; + nil::crypto3::zk::detail::blueprint_variable_vector vk_bits; + vk_bits.allocate(bp, vk_size_in_bits); + + nil::crypto3::zk::detail::blueprint_variable_vector primary_input_bits; + primary_input_bits.allocate(bp, primary_input_size_in_bits); + + r1cs_ppzksnark_proof_variable proof(bp); + + r1cs_ppzksnark_verification_key_variable vk(bp, vk_bits, primary_input_size); + + nil::crypto3::zk::detail::blueprint_variable result; + result.allocate(bp); + + r1cs_ppzksnark_verifier_component verifier(bp, vk, primary_input_bits, elt_size, proof, result); + + proof.generate_gates(); + verifier.generate_gates(); + + std::vector input_as_bits; + for (const FieldT_A &el : example.primary_input) { + std::vector v = algebra::convert_field_element_to_bit_vector(el, elt_size); + input_as_bits.insert(input_as_bits.end(), v.begin(), v.end()); + } + + primary_input_bits.fill_with_bits(bp, input_as_bits); + + vk.generate_assignments(keypair.second); + proof.generate_assignments(pi); + verifier.generate_assignments(); + bp.val(result) = FieldT_B::one(); + + std::cout << "positive test:\n" << std::endl; + BOOST_CHECK(bp.is_satisfied()); + + bp.val(primary_input_bits[0]) = FieldT_B::one() - bp.val(primary_input_bits[0]); + verifier.generate_assignments(); + bp.val(result) = FieldT_B::one(); + + std::cout << "negative test:" << std::endl; + BOOST_CHECK(!bp.is_satisfied()); + std::cout << "number of constraints for verifier:" << bp.num_constraints() << std::endl; +} + +template +void test_hardcoded_verifier() { + typedef typename ppT_A::scalar_field_type FieldT_A; + typedef typename ppT_B::scalar_field_type FieldT_B; + + const std::size_t num_constraints = 50; + const std::size_t primary_input_size = 3; + + r1cs_example example = + generate_r1cs_example_with_field_input(num_constraints, primary_input_size); + BOOST_CHECK(example.primary_input.size() == primary_input_size); + + BOOST_CHECK(example.constraint_system.is_satisfied(example.primary_input, example.auxiliary_input)); + const r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); + const r1cs_ppzksnark_proof pi = + r1cs_ppzksnark_prover(keypair.first, example.primary_input, example.auxiliary_input); + bool bit = r1cs_ppzksnark_verifier_strong_input_consistency(keypair.second, example.primary_input, pi); + BOOST_CHECK(bit); + + const std::size_t elt_size = FieldT_A::size_in_bits(); + const std::size_t primary_input_size_in_bits = elt_size * primary_input_size; + + blueprint bp; + r1cs_ppzksnark_preprocessed_r1cs_ppzksnark_verification_key_variable hardcoded_vk(bp, keypair.second); + nil::crypto3::zk::detail::blueprint_variable_vector primary_input_bits; + primary_input_bits.allocate(bp, primary_input_size_in_bits); + + r1cs_ppzksnark_proof_variable proof(bp); + + nil::crypto3::zk::detail::blueprint_variable result; + result.allocate(bp); + + r1cs_ppzksnark_online_verifier_component online_verifier(bp, hardcoded_vk, primary_input_bits, elt_size, + proof, result); + + proof.generate_gates(); + online_verifier.generate_gates(); + + std::vector input_as_bits; + for (const FieldT_A &el : example.primary_input) { + std::vector v = algebra::convert_field_element_to_bit_vector(el, elt_size); + input_as_bits.insert(input_as_bits.end(), v.begin(), v.end()); + } + + primary_input_bits.fill_with_bits(bp, input_as_bits); + + proof.generate_assignments(pi); + online_verifier.generate_assignments(); + bp.val(result) = FieldT_B::one(); + + printf("positive test:\n"); + BOOST_CHECK(bp.is_satisfied()); + + bp.val(primary_input_bits[0]) = FieldT_B::one() - bp.val(primary_input_bits[0]); + online_verifier.generate_assignments(); + bp.val(result) = FieldT_B::one(); + + printf("negative test:\n"); + BOOST_CHECK(!bp.is_satisfied()); + std::cout << "number of constraints for verifier: " << bp.num_constraints(); +} + +template class VarT, template class MulT> +void test_mul() { + typedef typename FpExtT::my_Fp FieldType; + + blueprint bp; + VarT x(bp); + VarT y(bp); + VarT xy(bp); + MulT mul(bp, x, y, xy); + mul.generate_gates(); + + for (size_t i = 0; i < 10; ++i) { + const typename FpExtT::value_type x_val = algebra::random_element(); + const typename FpExtT::value_type y_val = algebra::random_element(); + x.generate_assignments(x_val); + y.generate_assignments(y_val); + mul.generate_assignments(); + const typename FpExtT::value_type res = xy.get_element(); + BOOST_CHECK(res == x_val * y_val); + BOOST_CHECK(bp.is_satisfied()); + } + std::cout << "number of constraints:" << bp.num_constraints() << std::endl; +} + +template class VarT, template class SqrT> +void test_sqr() { + typedef typename FpExtT::my_Fp FieldType; + + blueprint bp; + VarT x(bp); + VarT xsq(bp); + SqrT sqr(bp, x, xsq); + sqr.generate_gates(); + + for (size_t i = 0; i < 10; ++i) { + const typename FpExtT::value_type x_val = algebra::random_element(); + x.generate_assignments(x_val); + sqr.generate_assignments(); + const typename FpExtT::value_type res = xsq.get_element(); + BOOST_CHECK(res == x_val.squared()); + BOOST_CHECK(bp.is_satisfied()); + } + std::cout << "number of constraints: " << bp.num_constraints() << std::endl; +} + +template class VarT, template class CycloSqrT> +void test_cyclotomic_sqr() { + typedef algebra::Fqk FpExtT; + typedef typename FpExtT::my_Fp FieldType; + + blueprint bp; + VarT x(bp); + VarT xsq(bp); + CycloSqrT sqr(bp, x, xsq); + sqr.generate_gates(); + + for (size_t i = 0; i < 10; ++i) { + FpExtT::value_type x_val = algebra::random_element(); + x_val = final_exponentiation(x_val); + + x.generate_assignments(x_val); + sqr.generate_assignments(); + const typename FpExtT::value_type res = xsq.get_element(); + BOOST_CHECK(res == x_val.squared()); + BOOST_CHECK(bp.is_satisfied()); + } + std::cout << "number of constraints: " << bp.num_constraints() << std::endl; +} + +template class VarT> +void test_Frobenius() { + typedef typename FpExtT::my_Fp FieldType; + + for (size_t i = 0; i < 100; ++i) { + blueprint bp; + VarT x(bp); + VarT x_frob = x.Frobenius_map(i); + + const typename FpExtT::value_type x_val = algebra::random_element(); + x.generate_assignments(x_val); + x_frob.evaluate(); + const typename FpExtT::value_type res = x_frob.get_element(); + BOOST_CHECK(res == x_val.Frobenius_map(i)); + BOOST_CHECK(bp.is_satisfied()); + } +} + +template +void test_full_pair() { + typedef typename CurveType::scalar_field_type FieldType; + typedef typename pairing::CurveType::pairing::pair_curve_type::pairing pairing_policy; + + blueprint bp; + pairing::CurveType::pairing::pair_curve_type::template g1_type<>::value_type P_val = + algebra::random_element() * + pairing::CurveType::pairing::pair_curve_type::template g1_type<>::value_type::one(); + pairing::CurveType::pairing::pair_curve_type::template g2_type<>::value_type Q_val = + algebra::random_element() * + pairing::CurveType::pairing::pair_curve_type::template g2_type<>::value_type::one(); + + element_g1 P(bp); + element_g2 Q(bp); + g1_precomputation prec_P; + g2_precomputation prec_Q; + + precompute_G1_component compute_prec_P(bp, P, prec_P); + precompute_G2_component compute_prec_Q(bp, Q, prec_Q); + + Fqk_variable miller_result(bp); + mnt_miller_loop_component miller(bp, prec_P, prec_Q, miller_result); + components::blueprint_variable result_is_one; + result_is_one.allocate(bp); + final_exp_component finexp(bp, miller_result, result_is_one); + + compute_prec_P.generate_gates(); + compute_prec_Q.generate_gates(); + miller.generate_gates(); + finexp.generate_gates(); + + P.generate_assignments(P_val); + compute_prec_P.generate_assignments(); + Q.generate_assignments(Q_val); + compute_prec_Q.generate_assignments(); + miller.generate_assignments(); + finexp.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename pairing_policy::affine_ate_g1_precomp native_prec_P = pairing_policy::affine_ate_precompute_g1(P_val); + typename pairing_policy::affine_ate_g2_precomp native_prec_Q = pairing_policy::affine_ate_precompute_g2(Q_val); + typename pairing_policy::Fqk native_miller_result = + pairing_policy::affine_ate_miller_loop(native_prec_P, native_prec_Q); + + typename pairing_policy::Fqk native_finexp_result = pairing_policy::final_exponentiation(native_miller_result); + printf("Must match:\n"); + finexp.result->get_element().print(); + native_finexp_result.print(); + + BOOST_CHECK(finexp.result->get_element() == native_finexp_result); + + std::cout << "number of constraints for full pairing: " << bp.num_constraints() << std::endl; +} + +template +void test_full_precomputed_pair() { + typedef typename CurveType::scalar_field_type FieldType; + typedef typename pairing::CurveType::pairing::pair_curve_type::pairing pairing_policy; + + blueprint bp; + pairing::CurveType::pairing::pair_curve_type::template g1_type<>::value_type P_val = + algebra::random_element() * + pairing::CurveType::pairing::pair_curve_type::template g1_type<>::value_type::one(); + pairing::CurveType::pairing::pair_curve_type::template g2_type<>::value_type Q_val = + algebra::random_element() * + pairing::CurveType::pairing::pair_curve_type::template g2_type<>::value_type::one(); + + g1_precomputation prec_P(bp, P_val); + g2_precomputation prec_Q(bp, Q_val); + + Fqk_variable miller_result(bp); + mnt_miller_loop_component miller(bp, prec_P, prec_Q, miller_result); + components::blueprint_variable result_is_one; + result_is_one.allocate(bp); + final_exp_component finexp(bp, miller_result, result_is_one); + + miller.generate_gates(); + finexp.generate_gates(); + + miller.generate_assignments(); + finexp.generate_assignments(); + BOOST_CHECK(bp.is_satisfied()); + + typename pairing_policy::affine_ate_g1_precomp native_prec_P = pairing_policy::affine_ate_precompute_g1(P_val); + typename pairing_policy::affine_ate_g2_precomp native_prec_Q = pairing_policy::affine_ate_precompute_g2(Q_val); + typename pairing_policy::Fqk native_miller_result = + pairing_policy::affine_ate_miller_loop(native_prec_P, native_prec_Q); + + typename pairing_policy::Fqk native_finexp_result = pairing_policy::final_exponentiation(native_miller_result); + printf("Must match:\n"); + finexp.result->get_element().print(); + native_finexp_result.print(); + + BOOST_CHECK(finexp.result->get_element() == native_finexp_result); + + std::cout << "number of constraints for full precomputed pairing: " << bp.num_constraints() << std::endl; +} + +BOOST_AUTO_TEST_SUITE(benes_components_test_suite) + +BOOST_AUTO_TEST_CASE(benes_components_mnt4_test) { + + std::cout << "Benes components test for mnt4-298 started" << std::endl; + using curve_type = typename algebra::curves::mnt4<298>; + using fq2_type = typename curve_type::template g2_type<>::field_type; + using fq4_type = typename curve_type::gt_type; + + test_mul(); + test_sqr(); + + test_mul(); + test_sqr(); + test_cyclotomic_sqr(); + test_exponentiation_component( + curve_type::pairing::final_exponent_last_chunk_abs_of_w0); + test_Frobenius(); + + test_element_g2_is_well_formed(); + + test_element_g1_precomp(); + + test_element_g2_precomp(); + + test_mnt_miller_loop(); + + test_mnt_e_over_e_miller_loop(); + + test_mnt_e_times_e_over_e_miller_loop(); + + test_full_pairing(); + + test_full_precomputed_pairing(); + + test_verifier(); + + test_hardcoded_verifier(); +} + +BOOST_AUTO_TEST_CASE(benes_components_mnt6_test) { + + std::cout << "Benes components test for mnt6-298 started" << std::endl; + + using curve_type = typename algebra::curves::mnt6<298>; + using fq3_type = typename curve_type::template g2_type<>::field_type; + using fq6_2over3_type = typename curve_type::gt_type; + + test_mul(); + test_sqr(); + + test_mul(); + test_sqr(); + test_cyclotomic_sqr(); + test_exponentiation_component( + curve_type::pairing::final_exponent_last_chunk_abs_of_w0); + test_Frobenius(); + + test_element_g2_is_well_formed(); + + test_element_g1_precomp(); + + test_element_g2_precomp(); + + test_mnt_miller_loop(); + + test_mnt_e_over_e_miller_loop(); + + test_mnt_e_times_e_over_e_miller_loop(); + + test_full_pairing(); + + test_full_precomputed_pairing(); + + test_verifier(); + + test_hardcoded_verifier(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/verify_r1cs_scheme.hpp b/libs/blueprint/test/verify_r1cs_scheme.hpp new file mode 100644 index 000000000..b821fd137 --- /dev/null +++ b/libs/blueprint/test/verify_r1cs_scheme.hpp @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_VERIFY_R1CS_SCHEME_COMPONENT_TEST_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_VERIFY_R1CS_SCHEME_COMPONENT_TEST_HPP + +#include + +#include +#include +#include + +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template> +bool verify_component(blueprint::blueprint bp) { + + if (bp.num_variables() == 0x00) { + std::cout << "Empty blueprint!" << std::endl; + return false; + } + + using field_type = typename CurveType::scalar_field_type; + using scheme_type = SchemeType; + + const snark::r1cs_constraint_system constraint_system = bp.get_constraint_system(); + + auto begin = std::chrono::high_resolution_clock::now(); + const typename scheme_type::keypair_type keypair = generate(constraint_system); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Key generation finished, time: " << elapsed.count() * 1e-9 << std::endl; + + begin = std::chrono::high_resolution_clock::now(); + const typename scheme_type::proof_type proof = + prove(keypair.first, bp.primary_input(), bp.auxiliary_input()); + end = std::chrono::high_resolution_clock::now(); + elapsed = std::chrono::duration_cast(end - begin); + std::cout << "Proving finished, time: " << elapsed.count() * 1e-9 << std::endl; + + begin = std::chrono::high_resolution_clock::now(); + bool verified = verify(keypair.second, bp.primary_input(), proof); + end = std::chrono::high_resolution_clock::now(); + elapsed = std::chrono::duration_cast(end - begin); + + std::cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << std::endl; + std::cout << "Verification finished, time: " << elapsed.count() * 1e-9 << std::endl; + std::cout << "Verification status: " << verified << std::endl; + + return verified; +} + +template<> +bool verify_component, snark::r1cs_gg_ppzksnark>>( + blueprint::blueprint::scalar_field_type> bp) { + std::cout << "Warning! r1cs_gg_ppzksnark for Edwards-183 is not implemented yet" << std::endl; + + return false; +} + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_VERIFY_R1CS_SCHEME_COMPONENT_TEST_HPP diff --git a/libs/blueprint/test/voting/r1cs/encrypted_input_voting.cpp b/libs/blueprint/test/voting/r1cs/encrypted_input_voting.cpp new file mode 100644 index 000000000..d284224ba --- /dev/null +++ b/libs/blueprint/test/voting/r1cs/encrypted_input_voting.cpp @@ -0,0 +1,361 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2021 Mikhail Komarov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE voting_encrypted_input_component_test + +#include + +#include + +#include + +#include + +#include + +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::zk; +using namespace nil::crypto3::algebra; + +template +std::vector calculate_hash_via_component(const std::vector &in_bits) { + using field_type = typename HashComponent::field_type; + + blueprint bp_bits; + components::block_variable in_block(bp_bits, in_bits.size()); + in_block.generate_assignments(in_bits); + + HashComponent hash_comp_bits(bp_bits, in_block); + hash_comp_bits.generate_assignments(); + hash_comp_bits.generate_gates(); + return hash_comp_bits.result.get_digest(); +} + +void test_jubjub_pedersen_encrypted_input_voting_component() { + using curve_type = curves::jubjub; + using bp_generator_hash_type = hashes::sha2<256>; + using hash_params = hashes::find_group_hash_default_params; + using hash_component = components::pedersen; + using hash_type = typename hash_component::hash_type; + using merkle_hash_component = hash_component; + using merkle_hash_type = typename merkle_hash_component::hash_type; + using field_type = typename hash_component::field_type; + constexpr std::size_t arity = 2; + using voting_component = + components::encrypted_input_voting; + using merkle_proof_component = typename voting_component::merkle_proof_component; + using merkle_validate_component = typename voting_component::merkle_proof_validating_component; + + /* prepare test */ + const std::size_t tree_depth = 16; + // TODO: use merkle_proof from container module + std::vector> path(tree_depth); + + const std::size_t sk_len = 128; + std::vector sk(sk_len); + std::generate(sk.begin(), sk.end(), [&]() { return std::rand() % 2; }); + auto sk_wrong = sk; + sk_wrong[0] = !sk_wrong[0]; + + std::vector pk = hash(sk); + std::vector pk_leaf = hash(pk); + BOOST_CHECK(pk_leaf.size() == merkle_hash_component::digest_bits); + + std::vector prev_hash = pk_leaf; + std::vector leaf = pk_leaf; + + std::vector address_bits; + + std::size_t address = 0; + for (long level = tree_depth - 1; level >= 0; --level) { + const bool computed_is_right = (std::rand() % 2); + address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0); + address_bits.push_back(computed_is_right); + std::vector other(merkle_hash_component::digest_bits); + std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; }); + + std::vector block = prev_hash; + block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end()); + std::vector h = hash(block); + + path[level] = other; + + prev_hash = h; + } + std::vector root = prev_hash; + auto root_wrong = root; + root_wrong[0] = !root_wrong[0]; + auto path_wrong = path; + path_wrong[0][0] = !path_wrong[0][0]; + auto address_bits_wrong = address_bits; + address_bits_wrong[0] = !address_bits_wrong[0]; + + std::vector m = {0, 1, 0, 0, 0, 0, 0}; + auto m_wrong = m; + m_wrong[0] = !m_wrong[0]; + + const std::size_t eid_size = 64; + std::vector eid(eid_size); + std::generate(eid.begin(), eid.end(), [&]() { return std::rand() % 2; }); + + std::vector eid_sk; + std::copy(std::cbegin(eid), std::cend(eid), std::back_inserter(eid_sk)); + std::copy(std::cbegin(sk), std::cend(sk), std::back_inserter(eid_sk)); + std::vector sn = hash(eid_sk); + auto sn_wrong = sn; + sn_wrong[0] = !sn_wrong[0]; + + /* execute test */ + blueprint bp; + nil::crypto3::zk::detail::blueprint_variable_vector address_bits_va; + address_bits_va.allocate(bp, tree_depth); + components::block_variable m_block(bp, m.size()); + components::block_variable eid_block(bp, eid.size()); + components::block_variable sk_block(bp, sk.size()); + components::digest_variable sn_digest(bp, hash_component::digest_bits); + components::digest_variable root_digest(bp, merkle_hash_component::digest_bits); + merkle_hash_component path_var(bp, tree_depth); + voting_component vote_var(bp, m_block, eid_block, sn_digest, root_digest, address_bits_va, path_var, sk_block, + nil::crypto3::zk::detail::blueprint_variable(0)); + + path_var.generate_gates(); + vote_var.generate_gates(); + + address_bits_va.fill_with_bits(bp, address_bits); + BOOST_CHECK(address_bits_va.get_field_element_from_bits(bp) == address); + m_block.generate_assignments(m); + eid_block.generate_assignments(eid); + sk_block.generate_assignments(sk); + path_var.generate_assignments(address, path); + + /* make sure that read checker didn't accidentally overwrite anything */ + address_bits_va.fill_with_bits(bp, address_bits); + vote_var.generate_assignments(root, sn); + BOOST_CHECK(bp.is_satisfied()); + + // false positive test with wrong root + root_digest.generate_assignments(root_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + root_digest.generate_assignments(root); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong sk + sk_block.generate_assignments(sk_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + sk_block.generate_assignments(sk); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong path + path_var.generate_assignments(address, path_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + path_var.generate_assignments(address, path); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong address + address_bits_va.fill_with_bits(bp, address_bits_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + address_bits_va.fill_with_bits(bp, address_bits); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong sn + sn_digest.generate_assignments(sn_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + sn_digest.generate_assignments(sn); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong m + m_block.generate_assignments(m_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + m_block.generate_assignments(m); + BOOST_CHECK(bp.is_satisfied()); + + // const std::size_t num_constraints = bp.num_constraints(); + // const std::size_t expected_constraints = + // components::merkle_tree_check_read_component::expected_constraints(tree_depth); + // BOOST_CHECK(num_constraints == expected_constraints); +} + +template +typename std::enable_if::value, std::vector>>::type + generate_random_data(std::size_t leaf_number) { + std::vector> v; + for (std::size_t i = 0; i < leaf_number; ++i) { + std::array leaf; + std::generate(std::begin(leaf), std::end(leaf), + [&]() { return std::rand() % (std::numeric_limits::max() + 1); }); + v.emplace_back(leaf); + } + return v; +} + +void test_jubjub_merkle_container_pedersen_encrypted_input_voting_component() { + using curve_type = curves::jubjub; + using bp_generator_hash_type = hashes::sha2<256>; + using hash_params = hashes::find_group_hash_default_params; + using hash_component = components::pedersen; + using hash_type = typename hash_component::hash_type; + using merkle_hash_component = hash_component; + using merkle_hash_type = typename merkle_hash_component::hash_type; + using field_type = typename hash_component::field_type; + constexpr std::size_t arity = 2; + using voting_component = + components::encrypted_input_voting; + using merkle_proof_component = typename voting_component::merkle_proof_component; + using merkle_validate_component = typename voting_component::merkle_proof_validating_component; + + /* prepare test */ + constexpr std::size_t tree_depth = 4; + constexpr std::size_t leafs_number = 1 << tree_depth; + auto secret_keys = generate_random_data(leafs_number); + std::vector> public_keys; + for (const auto &sk : secret_keys) { + std::array pk; + hash(sk, std::begin(pk)); + public_keys.emplace_back(pk); + } + nil::crypto3::containers::merkle_tree tree(std::cbegin(public_keys), + std::cend(public_keys)); + std::size_t proof_idx = std::rand() % leafs_number; + nil::crypto3::containers::merkle_proof proof(tree, proof_idx); + nil::crypto3::containers::merkle_proof proof_wrong(tree, (proof_idx + 1) % leafs_number); + + auto tree_pk_leaf = tree[proof_idx]; + std::vector pk_leaf = hash(public_keys[proof_idx]); + + BOOST_ASSERT(tree_pk_leaf.size() == pk_leaf.size()); + for (auto i = 0; i < pk_leaf.size(); ++i) { + BOOST_ASSERT(tree_pk_leaf[i] == pk_leaf[i]); + } + + auto sk_wrong = secret_keys[proof_idx]; + sk_wrong[0] = !sk_wrong[0]; + + auto root = tree.root(); + auto root_wrong = root; + root_wrong[0] = !root_wrong[0]; + + std::vector m = {0, 1, 0, 0, 0, 0, 0}; + auto m_wrong = m; + m_wrong[0] = !m_wrong[0]; + + const std::size_t eid_size = 64; + std::vector eid(eid_size); + std::generate(eid.begin(), eid.end(), [&]() { return std::rand() % 2; }); + + std::vector eid_sk; + std::copy(std::cbegin(eid), std::cend(eid), std::back_inserter(eid_sk)); + std::copy(std::cbegin(secret_keys[proof_idx]), std::cend(secret_keys[proof_idx]), std::back_inserter(eid_sk)); + std::vector sn = hash(eid_sk); + auto sn_wrong = sn; + sn_wrong[0] = !sn_wrong[0]; + + /* execute test */ + blueprint bp; + nil::crypto3::zk::detail::blueprint_variable_vector address_bits_va; + address_bits_va.allocate(bp, tree_depth); + components::block_variable m_block(bp, m.size()); + components::block_variable eid_block(bp, eid.size()); + components::block_variable sk_block(bp, secret_keys[proof_idx].size()); + components::digest_variable sn_digest(bp, hash_component::digest_bits); + components::digest_variable root_digest(bp, merkle_hash_component::digest_bits); + merkle_proof_component path_var(bp, tree_depth); + voting_component vote_var(bp, m_block, eid_block, sn_digest, root_digest, address_bits_va, path_var, sk_block, + nil::crypto3::zk::detail::blueprint_variable(0)); + + path_var.generate_gates(); + vote_var.generate_gates(); + + std::cout << "Constraints number: " << bp.num_constraints() << std::endl; + + path_var.generate_assignments(proof); + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address); + auto address = path_var.address; + BOOST_CHECK(address_bits_va.get_field_element_from_bits(bp) == path_var.address); + m_block.generate_assignments(m); + eid_block.generate_assignments(eid); + sk_block.generate_assignments(secret_keys[proof_idx]); + vote_var.generate_assignments(root, sn); + BOOST_CHECK(bp.is_satisfied()); + + // false positive test with wrong root + root_digest.generate_assignments(root_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + root_digest.generate_assignments(root); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong sk + sk_block.generate_assignments(sk_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + sk_block.generate_assignments(secret_keys[proof_idx]); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong address + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address - 1); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + address_bits_va.fill_with_bits_of_ulong(bp, path_var.address); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong sn + sn_digest.generate_assignments(sn_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + sn_digest.generate_assignments(sn); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong m + m_block.generate_assignments(m_wrong); + BOOST_CHECK(!bp.is_satisfied()); + + // reset blueprint in the correct state + m_block.generate_assignments(m); + BOOST_CHECK(bp.is_satisfied()); + // false positive test with wrong path + path_var.generate_assignments(proof_wrong, true); + BOOST_CHECK(!bp.is_satisfied()); +} + +BOOST_AUTO_TEST_SUITE(voting_component_test_suite) + +BOOST_AUTO_TEST_CASE(voting_component_jubjub_pedersen_test) { + test_jubjub_pedersen_encrypted_input_voting_component(); +} + +BOOST_AUTO_TEST_CASE(voting_component_jubjub_merkle_container_pedersen_test) { + test_jubjub_merkle_container_pedersen_encrypted_input_voting_component(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libs/blueprint/test/zkevm/bytecode.cpp b/libs/blueprint/test/zkevm/bytecode.cpp new file mode 100644 index 000000000..b354cab0f --- /dev/null +++ b/libs/blueprint/test/zkevm/bytecode.cpp @@ -0,0 +1,256 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_zkevm_bytecode_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "../test_plonk_component.hpp" + +using namespace nil; + +std::string bytecode_for = "0x60806040523480156100195760008061001661001f565b50505b5061008d565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561005a57808601518160408401015260208101905061003c565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6103188061009c6000396000f3fe6080604052348015610019576000806100166100bb565b50505b50600436106100345760003560e01c806347b0b31c14610042575b60008061003f6100bb565b50505b61005c600480360381019061005791906101a3565b610072565b60405161006991906101f7565b60405180910390f35b60006001905060005b828110156100a457838261008f9190610212565b9150808061009c90610276565b91505061007b565b5080600081906100b2610129565b50505092915050565b632a2a7adb598160e01b8152600481016020815285602082015260005b868110156100f65780860151816040840101526020810190506100d8565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b60408110156101895760008183015260208101905061016f565b505050565b60008135905061019d816102f8565b92915050565b600080604083850312156101bf576000806101bc6100bb565b50505b60006101cd8582860161018e565b92505060206101de8582860161018e565b9150509250929050565b6101f18161026c565b82525050565b600060208201905061020c60008301846101e8565b92915050565b600061021d8261026c565b91506102288361026c565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610261576102606102bf565b5b828202905092915050565b6000819050919050565b60006102818261026c565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156102b4576102b36102bf565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000006000526011600452602460006102f46100bb565b5050565b6103018161026c565b8114610315576000806103126100bb565b50505b5056"; +std::string bytecode_addition = "0x60806040523480156100195760008061001661001f565b50505b5061008d565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561005a57808601518160408401015260208101905061003c565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6102b38061009c6000396000f3fe6080604052348015610019576000806100166100a3565b50505b50600436106100345760003560e01c8063f080118c14610042575b60008061003f6100a3565b50505b61005c6004803603810190610057919061018b565b610072565b60405161006991906101df565b60405180910390f35b6000818361008091906101fa565b6000819061008c610111565b505050818361009b91906101fa565b905092915050565b632a2a7adb598160e01b8152600481016020815285602082015260005b868110156100de5780860151816040840101526020810190506100c0565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b604081101561017157600081830152602081019050610157565b505050565b60008135905061018581610293565b92915050565b600080604083850312156101a7576000806101a46100a3565b50505b60006101b585828601610176565b92505060206101c685828601610176565b9150509250929050565b6101d981610250565b82525050565b60006020820190506101f460008301846101d0565b92915050565b600061020582610250565b915061021083610250565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102455761024461025a565b5b828201905092915050565b6000819050919050565b7f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526024600061028f6100a3565b5050565b61029c81610250565b81146102b0576000806102ad6100a3565b50505b5056"; + +std::vector hex_string_to_bytes(std::string const &hex_string) { + std::vector bytes; + for (std::size_t i = 2; i < hex_string.size(); i += 2) { + std::string byte_string = hex_string.substr(i, 2); + bytes.push_back(std::stoi(byte_string, nullptr, 16)); + } + return bytes; +} + +template +void test_zkevm_bytecode( + std::vector> bytecodes +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 6; + constexpr std::size_t SelectorColumns = 3; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 5; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::zkevm_bytecode; + + std::vector> bytecode_vars; + std::size_t cur = 0; + for (std::size_t i = 0; i < bytecodes.size(); i++) { + std::vector bytecode; + bytecode.push_back(var(0, cur, false, var::column_type::public_input)); // length + cur++; + for (std::size_t j = 0; j < bytecodes[i].size(); j++, cur++) { + bytecode.push_back(var(0, cur, false, var::column_type::public_input)); + } + bytecode_vars.push_back(bytecode); + } + std::vector> bytecode_hash_vars; + for( std::size_t i = 0; i < bytecodes.size(); i++, cur+=2){ + bytecode_hash_vars.push_back({var(0, cur, false, var::column_type::public_input), var(0, cur+1, false, var::column_type::public_input)}); + } + var rlc_challenge_var = var(0, cur, false, var::column_type::public_input); + typename component_type::input_type instance_input(bytecode_vars, bytecode_hash_vars, rlc_challenge_var); + + std::vector public_input; + cur = 0; + for( std::size_t i = 0; i < bytecodes.size(); i++){ + public_input.push_back(bytecodes[i].size()); + cur++; + for( std::size_t j = 0; j < bytecodes[i].size(); j++, cur++){ + public_input.push_back(bytecodes[i][j]); + } + } + for( std::size_t i = 0; i < bytecodes.size(); i++){ + std::string hash = nil::crypto3::hash>(bytecodes[i].begin(), bytecodes[i].end()); + std::string str_hi = hash.substr(0, hash.size()-32); + std::string str_lo = hash.substr(hash.size()-32, 32); + value_type hash_hi; + value_type hash_lo; + for( std::size_t j = 0; j < str_hi.size(); j++ ){hash_hi *=16; hash_hi += str_hi[j] >= '0' && str_hi[j] <= '9'? str_hi[j] - '0' : str_hi[j] - 'a' + 10;} + for( std::size_t j = 0; j < str_lo.size(); j++ ){hash_lo *=16; hash_lo += str_lo[j] >= '0' && str_lo[j] <= '9'? str_lo[j] - '0' : str_lo[j] - 'a' + 10;} + public_input.push_back(hash_hi); + public_input.push_back(hash_lo); + } + nil::crypto3::random::algebraic_engine rnd(0); + value_type rlc_challenge = rnd(); + public_input.push_back(rlc_challenge); + + auto result_check = [](AssignmentType &assignment, + typename component_type::result_type &real_res) { + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, 2046); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::NONE, 2046); +} + +template +void test_zkevm_bytecode_dynamic_table( + std::vector> bytecodes +){ + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 4; + constexpr std::size_t SelectorColumns = 6; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 5; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename BlueprintFieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::bytecode_table_tester; + + std::vector> bytecode_vars; + std::size_t cur = 0; + for (std::size_t i = 0; i < bytecodes.size(); i++) { + std::vector bytecode; + bytecode.push_back(var(0, cur, false, var::column_type::public_input)); // length + cur++; + for (std::size_t j = 0; j < bytecodes[i].size(); j++, cur++) { + bytecode.push_back(var(0, cur, false, var::column_type::public_input)); + } + bytecode_vars.push_back(bytecode); + } + std::vector> bytecode_hash_vars; + for( std::size_t i = 0; i < bytecodes.size(); i++, cur+=2){ + bytecode_hash_vars.push_back({var(0, cur, false, var::column_type::public_input), var(0, cur+1, false, var::column_type::public_input)}); + } + typename component_type::input_type instance_input(bytecode_vars, bytecode_hash_vars); + + std::vector public_input; + cur = 0; + for( std::size_t i = 0; i < bytecodes.size(); i++){ + public_input.push_back(bytecodes[i].size()); + cur++; + for( std::size_t j = 0; j < bytecodes[i].size(); j++, cur++){ + public_input.push_back(bytecodes[i][j]); + } + } + for( std::size_t i = 0; i < bytecodes.size(); i++){ + std::string hash = nil::crypto3::hash>(bytecodes[i].begin(), bytecodes[i].end()); + std::string str_hi = hash.substr(0, hash.size()-32); + std::string str_lo = hash.substr(hash.size()-32, 32); + value_type hash_hi; + value_type hash_lo; + for( std::size_t j = 0; j < str_hi.size(); j++ ){hash_hi *=16; hash_hi += str_hi[j] >= '0' && str_hi[j] <= '9'? str_hi[j] - '0' : str_hi[j] - 'a' + 10;} + for( std::size_t j = 0; j < str_lo.size(); j++ ){hash_lo *=16; hash_lo += str_lo[j] >= '0' && str_lo[j] <= '9'? str_lo[j] - '0' : str_lo[j] - 'a' + 10;} + public_input.push_back(hash_hi); + public_input.push_back(hash_lo); + } + nil::crypto3::random::algebraic_engine rnd(0); + value_type rlc_challenge = rnd(); + public_input.push_back(rlc_challenge); + + auto result_check = [](AssignmentType &assignment, + typename component_type::result_type &real_res) { + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, 2046); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::NONE, 2046); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; +BOOST_AUTO_TEST_CASE(one_small_contract){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition)}); +} +BOOST_AUTO_TEST_CASE(two_small_contracts){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition), hex_string_to_bytes(bytecode_for)}); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_pallas_test_suite) + using field_type = typename crypto3::algebra::curves::pallas::base_field_type; +BOOST_AUTO_TEST_CASE(one_small_contract){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition)}); +} +BOOST_AUTO_TEST_CASE(two_small_contracts){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition), hex_string_to_bytes(bytecode_for)}); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_bls_test_suite) + using field_type = typename crypto3::algebra::fields::bls12_fr<381>; +BOOST_AUTO_TEST_CASE(one_small_contract){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition)}); +} +BOOST_AUTO_TEST_CASE(two_small_contracts){ + test_zkevm_bytecode({hex_string_to_bytes(bytecode_addition),hex_string_to_bytes(bytecode_for)}); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(dynamic_table_test_suite) + using field_type = typename crypto3::algebra::curves::vesta::base_field_type; +BOOST_AUTO_TEST_CASE(one_small_contract){ + test_zkevm_bytecode_dynamic_table({hex_string_to_bytes(bytecode_addition)}); +} +BOOST_AUTO_TEST_CASE(two_small_contracts){ + test_zkevm_bytecode_dynamic_table({hex_string_to_bytes(bytecode_addition), hex_string_to_bytes(bytecode_for)}); +} +BOOST_AUTO_TEST_SUITE_END() + diff --git a/libs/blueprint/test/zkevm/opcode_tester.hpp b/libs/blueprint/test/zkevm/opcode_tester.hpp new file mode 100644 index 000000000..c5f6464cb --- /dev/null +++ b/libs/blueprint/test/zkevm/opcode_tester.hpp @@ -0,0 +1,39 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + zkevm_machine_interface get_empty_machine() { + return zkevm_machine_interface(); + } + } // namespace blueprint +} // namespace nil \ No newline at end of file diff --git a/libs/blueprint/test/zkevm/opcodes/add_sub.cpp b/libs/blueprint/test/zkevm/opcodes/add_sub.cpp new file mode 100644 index 000000000..8b09896be --- /dev/null +++ b/libs/blueprint/test/zkevm/opcodes/add_sub.cpp @@ -0,0 +1,76 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include "nil/crypto3/algebra/fields/pallas/base_field.hpp" +#define BOOST_TEST_MODULE zkevm_add_test + +#include + +#include +#include "nil/blueprint/zkevm/zkevm_word.hpp" + +#include +#include + +#include +#include "../opcode_tester.hpp" + +#include + +using namespace nil::blueprint; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(zkevm_add_test_suite) + +BOOST_AUTO_TEST_CASE(zkevm_add_test) { + using field_type = fields::pallas_base_field; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using zkevm_machine_type = zkevm_machine_interface; + assignment_type assignment(0, 0, 0, 0); + circuit_type circuit; + zkevm_circuit zkevm_circuit(assignment, circuit); + zkevm_machine_type machine = get_empty_machine(); + // incorrect test logic, but we have no memory operations so + machine.stack.push(zwordc(0x1234567890_cppui_modular257)); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::ADD, machine); + zkevm_circuit.assign_opcode(zkevm_opcode::SUB, machine); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::ADD, machine); + zkevm_circuit.assign_opcode(zkevm_opcode::SUB, machine); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(zwordc(0x1234567890_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::ADD, machine); + zkevm_circuit.assign_opcode(zkevm_opcode::SUB, machine); + zkevm_circuit.finalize_test(); + // assignment.export_table(std::cout); + // circuit.export_circuit(std::cout); + nil::crypto3::zk::snark::basic_padding(assignment); + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/opcodes/div.cpp b/libs/blueprint/test/zkevm/opcodes/div.cpp new file mode 100644 index 000000000..f23b43c28 --- /dev/null +++ b/libs/blueprint/test/zkevm/opcodes/div.cpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE zkevm_div_test + +#include + +#include +#include + +#include +#include + +#include +#include "../opcode_tester.hpp" + +#include + +using namespace nil::blueprint; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(zkevm_mul_test_suite) + +BOOST_AUTO_TEST_CASE(zkevm_mul_test) { + using field_type = curves::alt_bn128<254>::base_field_type; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using zkevm_machine_type = zkevm_machine_interface; + assignment_type assignment(0, 0, 0, 0); + circuit_type circuit; + zkevm_circuit zkevm_circuit(assignment, circuit); + zkevm_machine_type machine = get_empty_machine(); + // incorrect test logic, but we have no memory operations so + // check all overflows for chunks + machine.stack.push(zwordc(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular257)); + machine.stack.push(zwordc(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::DIV, machine); + machine.stack.push(1234567890); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::DIV, machine); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::DIV, machine); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(1234567890); + zkevm_circuit.assign_opcode(zkevm_opcode::DIV, machine); + machine.stack.push(0); + machine.stack.push(1234567890); + zkevm_circuit.assign_opcode(zkevm_opcode::DIV, machine); + zkevm_circuit.finalize_test(); + // assignment.export_table(std::cout); + // circuit.export_circuit(std::cout); + nil::crypto3::zk::snark::basic_padding(assignment); + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/opcodes/iszero.cpp b/libs/blueprint/test/zkevm/opcodes/iszero.cpp new file mode 100644 index 000000000..8ae2fbd3e --- /dev/null +++ b/libs/blueprint/test/zkevm/opcodes/iszero.cpp @@ -0,0 +1,67 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE zkevm_iszero_test + +#include + +#include + +#include +#include + +#include +#include "../opcode_tester.hpp" + +#include + +using namespace nil::blueprint; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(zkevm_iszero_test_suite) + +BOOST_AUTO_TEST_CASE(zkevm_iszero_test) { + using field_type = fields::goldilocks64; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using zkevm_machine_type = zkevm_machine_interface; + assignment_type assignment(0, 0, 0, 0); + circuit_type circuit; + zkevm_circuit zkevm_circuit(assignment, circuit); + zkevm_machine_type machine = get_empty_machine(); + // incorrect test logic, but we have no memory operations so + machine.stack.push(1234567890); + machine.stack.push(0); + zkevm_circuit.assign_opcode(zkevm_opcode::ISZERO, machine); + machine.stack.pop(); + zkevm_circuit.assign_opcode(zkevm_opcode::ISZERO, machine); + zkevm_circuit.finalize_test(); + // assignment.export_table(std::cout); + // circuit.export_circuit(std::cout); + nil::crypto3::zk::snark::basic_padding(assignment); + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/opcodes/mul.cpp b/libs/blueprint/test/zkevm/opcodes/mul.cpp new file mode 100644 index 000000000..4f7fa4a0a --- /dev/null +++ b/libs/blueprint/test/zkevm/opcodes/mul.cpp @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE zkevm_mul_test + +#include + +#include +#include + +#include +#include + +#include +#include "../opcode_tester.hpp" + +#include + +using namespace nil::blueprint; +using namespace nil::crypto3::algebra; + +BOOST_AUTO_TEST_SUITE(zkevm_mul_test_suite) + +BOOST_AUTO_TEST_CASE(zkevm_mul_test) { + using field_type = curves::alt_bn128<254>::base_field_type; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using zkevm_machine_type = zkevm_machine_interface; + assignment_type assignment(0, 0, 0, 0); + circuit_type circuit; + zkevm_circuit zkevm_circuit(assignment, circuit); + zkevm_machine_type machine = get_empty_machine(); + // incorrect test logic, but we have no memory operations so + // check all overflows for chunks + machine.stack.push(zwordc(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular257)); + machine.stack.push(zwordc(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::MUL, machine); + machine.stack.push(1234567890); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::MUL, machine); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(zwordc(0xFb70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + zkevm_circuit.assign_opcode(zkevm_opcode::MUL, machine); + machine.stack.push(zwordc(0x1b70726fb8d3a24da9ff9647225a18412b8f010425938504d73ebc8801e2e016_cppui_modular257)); + machine.stack.push(1234567890); + zkevm_circuit.assign_opcode(zkevm_opcode::MUL, machine); + zkevm_circuit.finalize_test(); + + // assignment.export_table(std::cout); + // circuit.export_circuit(std::cout); + nil::crypto3::zk::snark::basic_padding(assignment); + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/state_selector.cpp b/libs/blueprint/test/zkevm/state_selector.cpp new file mode 100644 index 000000000..854398a62 --- /dev/null +++ b/libs/blueprint/test/zkevm/state_selector.cpp @@ -0,0 +1,96 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_zkevm_state_selector_test + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "../test_plonk_component.hpp" + +using namespace nil; + +template +void test_state_selector(std::size_t options_amount, std::size_t option){ + BOOST_ASSERT(option < options_amount); + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + const std::size_t WitnessColumns = (options_amount + 1) / 2 + 2; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment>; + + using value_type = typename BlueprintFieldType::value_type; + using var = typename crypto3::zk::snark::plonk_variable; + using component_type = blueprint::components::state_selector; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input) + }; + + std::vector public_input = {value_type(option)}; + auto result_check = [](AssignmentType &assignment, + typename component_type::result_type &real_res) { + return true; + }; + + std::vector witnesses(WitnessColumns); + std::iota(witnesses.begin(), witnesses.end(), 0); + + component_type component_instance = component_type(witnesses, std::array{0}, + std::array{0}, options_amount); + nil::crypto3::test_component + (component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, options_amount); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_equality_flag_test_vesta) { + using field_type = typename crypto3::algebra::fields::goldilocks64_base_field; + boost::random::mt19937 gen(1444); + + for (std::size_t i = 1; i < 30; i++) { + boost::uniform_int<> distrib(0, i - 1); + test_state_selector(i, distrib(gen)); + test_state_selector(i, distrib(gen)); + test_state_selector(i, distrib(gen)); + test_state_selector(i, distrib(gen)); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/state_transition.cpp b/libs/blueprint/test/zkevm/state_transition.cpp new file mode 100644 index 000000000..46499496f --- /dev/null +++ b/libs/blueprint/test/zkevm/state_transition.cpp @@ -0,0 +1,124 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE zkevm_state_transition_test + +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace nil::blueprint; +using namespace nil::crypto3::algebra; + +template +void fill_empty_state(zkevm_state& state) { + using var = typename nil::crypto3::zk::snark::plonk_variable; + std::size_t witness_index = 0; + #define X(name) state.name.selector = witness_index++; \ + state.name.type = var::column_type::witness; \ + state.name.value = 0; + zkevm_STATE_LIST_FOR_TRANSITIONS(X) + #undef X + + state.step_selection.selector = witness_index++; + state.rows_until_next_op.selector = witness_index++; + state.rows_until_next_op_inv.selector = witness_index++; + + state.step_selection.type = var::column_type::witness; + state.rows_until_next_op.type = var::column_type::witness; + state.rows_until_next_op_inv.type = var::column_type::witness; + + state.step_selection.value = 0; + state.rows_until_next_op.value = 0; + state.rows_until_next_op_inv.value = 0; +} + +BOOST_AUTO_TEST_SUITE(zkevm_state_transition_test_suite) + +BOOST_AUTO_TEST_CASE(zkevm_state_transition_basic_test) { + using field_type = fields::goldilocks64; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using state_type = zkevm_state; + assignment_type assignment(20, 0, 0, 1); + circuit_type circuit; + + std::size_t row = 0; + state_type state; + fill_empty_state(state); + zkevm_state_transition transition; + auto constraints = generate_transition_constraints(state, transition); + auto selector = circuit.add_gate(constraints); + assignment.enable_selector(selector, row); + state.assign_state(assignment, row++); + state.pc.value += 1; + state.assign_state(assignment, row++); + + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); + state.pc.value = 0; + state.assign_state(assignment, row - 1); + BOOST_ASSERT(is_satisfied(circuit, assignment) == false); +} + +BOOST_AUTO_TEST_CASE(zkevm_state_transition_other_test) { + using field_type = fields::goldilocks64; + using arithmentization_type = nil::crypto3::zk::snark::plonk_constraint_system; + using assignment_type = assignment; + using circuit_type = circuit; + using state_type = zkevm_state; + assignment_type assignment(20, 0, 0, 1); + circuit_type circuit; + + std::size_t row = 0; + state_type state; + fill_empty_state(state); + zkevm_state_transition transition; + transition.curr_gas.t = transition_type::NEW_VALUE; + transition.curr_gas.value = 100; + auto constraints = generate_transition_constraints(state, transition); + auto selector = circuit.add_gate(constraints); + assignment.enable_selector(selector, row); + state.assign_state(assignment, row++); + state.pc.value += 1; + state.curr_gas.value = 100; + state.assign_state(assignment, row++); + + BOOST_ASSERT(is_satisfied(circuit, assignment) == true); + state.curr_gas.value = 0; + state.assign_state(assignment, row - 1); + BOOST_ASSERT(is_satisfied(circuit, assignment) == false); + state.curr_gas.value = -1; + state.assign_state(assignment, row - 1); + BOOST_ASSERT(is_satisfied(circuit, assignment) == false); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/test/zkevm/zkevm_word.cpp b/libs/blueprint/test/zkevm/zkevm_word.cpp new file mode 100644 index 000000000..e266c9818 --- /dev/null +++ b/libs/blueprint/test/zkevm/zkevm_word.cpp @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_zkevm_word_utils_test + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace nil; +using namespace nil::blueprint; + +BOOST_AUTO_TEST_SUITE(blueprint_zkevm_word_utils_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_zkevm_word_goldilocks64_test, * boost::unit_test::disabled()) { + using field_type = crypto3::algebra::fields::goldilocks64; + using value_type = field_type::value_type; + using word_type = zkevm_word_type; + + word_type word = 0x123456789abcdef0; + std::vector chunks = zkevm_word_to_field_element(word); + BOOST_CHECK_EQUAL(chunks.size(), 16); + BOOST_CHECK_EQUAL(chunks[0], 0xdef0); + BOOST_CHECK_EQUAL(chunks[1], 0x9abc); + BOOST_CHECK_EQUAL(chunks[2], 0x5678); + BOOST_CHECK_EQUAL(chunks[3], 0x1234); + for (std::size_t i = 4; i < 16; ++i) { + BOOST_CHECK_EQUAL(chunks[i], 0); + } +} + +BOOST_AUTO_TEST_CASE(blueprint_zkevm_word_pallas_test) { + using field_type = crypto3::algebra::fields::pallas_base_field; + using value_type = field_type::value_type; + using word_type = zkevm_word_type; + + word_type word = 0x123456789abcdef0; + std::vector chunks = zkevm_word_to_field_element(word); + BOOST_CHECK_EQUAL(chunks.size(), 16); + BOOST_CHECK_EQUAL(chunks[0], 0xdef0); + BOOST_CHECK_EQUAL(chunks[1], 0x9abc); + BOOST_CHECK_EQUAL(chunks[2], 0x5678); + BOOST_CHECK_EQUAL(chunks[3], 0x1234); + for (std::size_t i = 4; i < 16; ++i) { + BOOST_CHECK_EQUAL(chunks[i], 0); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/blueprint/zkllvm-blueprint.nix b/libs/blueprint/zkllvm-blueprint.nix new file mode 100644 index 000000000..c6f0059c6 --- /dev/null +++ b/libs/blueprint/zkllvm-blueprint.nix @@ -0,0 +1,57 @@ +{ lib, + stdenv, + src_repo, + ninja, + pkg-config, + cmake, + boost183, + # We'll use boost183 by default, but you can override it + boost_lib ? boost183, + gdb, + cmake_modules, + crypto3, + enableDebugging, + enableDebug ? false, + runTests ? false, + }: +let + inherit (lib) optional; +in stdenv.mkDerivation rec { + name = "blueprint"; + + src = src_repo; + + nativeBuildInputs = [ cmake ninja pkg-config ] ++ (lib.optional (!stdenv.isDarwin) gdb); + + # enableDebugging will keep debug symbols in boost + propagatedBuildInputs = [ (if enableDebug then (enableDebugging boost_lib) else boost_lib) ]; + + buildInputs = [cmake_modules crypto3]; + + cmakeFlags = + [ + (if runTests then "-DBUILD_TESTS=TRUE" else "") + (if runTests then "-DCMAKE_ENABLE_TESTS=TRUE" else "") + (if enableDebug then "-DCMAKE_BUILD_TYPE=Debug" else "-DCMAKE_BUILD_TYPE=Release") + (if enableDebug then "-DCMAKE_CXX_FLAGS=-ggdb" else "") + (if enableDebug then "-DCMAKE_CXX_FLAGS=-O0" else "") + "-G Ninja" + ]; + + doBuild = false; + doCheck = runTests; + dontInstall = true; + + buildPhase = '' + echo "skip build" + ''; + + checkPhase = '' + bash ../run_tests.sh + ''; + + shellHook = '' + PS1="\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ " + echo "Welcome to Blueprint development environment!" + ''; +}