From c912111c134b6bbefe223bb61ba22f864036e9ba Mon Sep 17 00:00:00 2001 From: sripwoud Date: Wed, 25 Sep 2024 18:09:59 +0200 Subject: [PATCH 1/6] ci: update triggers for docs workflow --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4925c6db..3258f991 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,16 +1,19 @@ name: docs on: + workflow_dispatch: push: branches: - main paths: - packages/**/src/*.{ts,js} - "**/README.md" + - .github/workflows/docs.yml pull_request: paths: - packages/**/src/*.{ts,js} - "**/README.md" + - .github/workflows/docs.yml jobs: gh-pages: From f9d07cea4ac509a26bf0bae2fd5838c050273e23 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 15 Oct 2024 10:40:04 +0100 Subject: [PATCH 2/6] build(eddsa-poseidon): move poseidon-lite package to dependencies (#345) --- packages/eddsa-poseidon/package.json | 4 +- packages/eddsa-poseidon/rollup.config.ts | 10 +- yarn.lock | 118 +++++++++++++++++++---- 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/packages/eddsa-poseidon/package.json b/packages/eddsa-poseidon/package.json index 3bd13c3b..b456c12a 100644 --- a/packages/eddsa-poseidon/package.json +++ b/packages/eddsa-poseidon/package.json @@ -41,7 +41,6 @@ "@rollup/plugin-typescript": "^11.1.6", "circomlibjs": "0.0.8", "ffjavascript": "0.2.38", - "poseidon-lite": "0.2.0", "rimraf": "^5.0.5", "rollup": "^4.12.0", "rollup-plugin-cleanup": "^3.2.1", @@ -51,6 +50,7 @@ "dependencies": { "@zk-kit/baby-jubjub": "1.0.3", "@zk-kit/utils": "1.2.1", - "buffer": "6.0.3" + "buffer": "6.0.3", + "poseidon-lite": "0.3.0" } } diff --git a/packages/eddsa-poseidon/rollup.config.ts b/packages/eddsa-poseidon/rollup.config.ts index 1e54692e..bb11577a 100644 --- a/packages/eddsa-poseidon/rollup.config.ts +++ b/packages/eddsa-poseidon/rollup.config.ts @@ -32,14 +32,10 @@ export default [ "@zk-kit/utils/f1-field", "@zk-kit/utils/scalar", "@zk-kit/utils/error-handlers", - "@zk-kit/utils/type-checks" + "@zk-kit/utils/type-checks", + "poseidon-lite/poseidon5" ], - plugins: [ - typescript({ tsconfig: "./build.tsconfig.json" }), - nodeResolve(), - commonjs(), - cleanup({ comments: "jsdoc" }) - ] + plugins: [typescript({ tsconfig: "./build.tsconfig.json" }), cleanup({ comments: "jsdoc" })] }, { input: "src/index.ts", diff --git a/yarn.lock b/yarn.lock index 13f9ee55..b284c674 100644 --- a/yarn.lock +++ b/yarn.lock @@ -87,7 +87,17 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.24.7": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13": + version: 7.24.6 + resolution: "@babel/code-frame@npm:7.24.6" + dependencies: + "@babel/highlight": "npm:^7.24.6" + picocolors: "npm:^1.0.0" + checksum: 10/e9b70af2a9c7c734ac36c2e6e1da640a6e0a483bfba7cf620226a1226a2e6d64961324b02d786e06ce72f0aa329e190dfc49128367a2368b69e2219ffddcdcc5 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.24.7": version: 7.24.7 resolution: "@babel/code-frame@npm:7.24.7" dependencies: @@ -200,6 +210,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.24.6": + version: 7.24.6 + resolution: "@babel/helper-validator-identifier@npm:7.24.6" + checksum: 10/7e725ef0684291ca3306d5174a5d1cd9072ad58ba444cfa50aaf92a5c59dd723fa15031733ac598bb6b066cb62c2472e14cd82325522348977a72e99aa21b97a + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-validator-identifier@npm:7.24.7" @@ -224,6 +241,18 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.24.6": + version: 7.24.6 + resolution: "@babel/highlight@npm:7.24.6" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.24.6" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10/e11cd39ceb01c9b5e4f2684a45caefe7b2d7bb74997c30922e6b4063a6f16aff88356091350f0af01f044e1a198579a6b5c4161a84d0a6090e63a41167569daf + languageName: node + linkType: hard + "@babel/highlight@npm:^7.24.7": version: 7.24.7 resolution: "@babel/highlight@npm:7.24.7" @@ -1590,6 +1619,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.15": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: 10/89960ac087781b961ad918978975bcdf2051cd1741880469783c42de64239703eab9db5230d776d8e6a09d73bb5e4cb964e07d93ee6e2e7aea5a7d726e865c09 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -2664,11 +2700,11 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 22.5.5 - resolution: "@types/node@npm:22.5.5" + version: 20.14.0 + resolution: "@types/node@npm:20.14.0" dependencies: - undici-types: "npm:~6.19.2" - checksum: 10/172d02c8e6d921699edcf559c28b3805616bd6481af1b3cb0299f89ad9a6f33b71050434c06ce7b503166054a26275344187c443f99f745d0b12601372452f19 + undici-types: "npm:~5.26.4" + checksum: 10/49b332fbf8aee4dc4f61cc1f1f6e130632510f795dd7b274e55894516feaf4bec8a3d13ea764e2443e340a64ce9bbeb006d14513bf6ccdd4f21161eccc7f311e languageName: node linkType: hard @@ -3059,7 +3095,7 @@ __metadata: buffer: "npm:6.0.3" circomlibjs: "npm:0.0.8" ffjavascript: "npm:0.2.38" - poseidon-lite: "npm:0.2.0" + poseidon-lite: "npm:0.3.0" rimraf: "npm:^5.0.5" rollup: "npm:^4.12.0" rollup-plugin-cleanup: "npm:^3.2.1" @@ -3290,7 +3326,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.0, acorn@npm:^8.11.3, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": +"acorn@npm:^8.11.0": version: 8.12.1 resolution: "acorn@npm:8.12.1" bin: @@ -3299,6 +3335,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.11.3, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" + bin: + acorn: bin/acorn + checksum: 10/b688e7e3c64d9bfb17b596e1b35e4da9d50553713b3b3630cf5690f2b023a84eac90c56851e6912b483fe60e8b4ea28b254c07e92f17ef83d72d78745a8352dd + languageName: node + linkType: hard + "adm-zip@npm:^0.4.16": version: 0.4.16 resolution: "adm-zip@npm:0.4.16" @@ -5699,15 +5744,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.6": - version: 4.3.7 - resolution: "debug@npm:4.3.7" +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": + version: 4.3.5 + resolution: "debug@npm:4.3.5" dependencies: - ms: "npm:^2.1.3" + ms: "npm:2.1.2" peerDependenciesMeta: supports-color: optional: true - checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + checksum: 10/cb6eab424c410e07813ca1392888589972ce9a32b8829c6508f5e1f25f3c3e70a76731610ae55b4bbe58d1a2fffa1424b30e97fa8d394e49cd2656a9643aedd2 languageName: node linkType: hard @@ -5732,6 +5777,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.3.5, debug@npm:~4.3.6": + version: 4.3.7 + resolution: "debug@npm:4.3.7" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -11172,7 +11229,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.10, magic-string@npm:^0.30.3": +"magic-string@npm:^0.30.10": version: 0.30.11 resolution: "magic-string@npm:0.30.11" dependencies: @@ -11181,6 +11238,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.3": + version: 0.30.10 + resolution: "magic-string@npm:0.30.10" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + checksum: 10/9f8bf6363a14c98a9d9f32ef833b194702a5c98fb931b05ac511b76f0b06fd30ed92beda6ca3261d2d52d21e39e891ef1136fbd032023f6cbb02d0b7d5767201 + languageName: node + linkType: hard + "make-dir@npm:^4.0.0": version: 4.0.0 resolution: "make-dir@npm:4.0.0" @@ -12778,10 +12844,10 @@ __metadata: languageName: node linkType: hard -"poseidon-lite@npm:0.2.0": - version: 0.2.0 - resolution: "poseidon-lite@npm:0.2.0" - checksum: 10/63c7668b480ee3d57aaca0eda7e56d563ab2bfcc40bbce0e4bccdc9deed4c0d68255749356e328b622ce8715b1f1ba689fe1a86ca78eb1056a51a18daa252ee1 +"poseidon-lite@npm:0.3.0": + version: 0.3.0 + resolution: "poseidon-lite@npm:0.3.0" + checksum: 10/1e7294f7fed91e1cdc3aee7bd0380461bfdba74ba34d100c2fc4e3d5434d09af0644f6c889b749b9511fd3867216a1aa6d575f070b716c6756b1f56c0067b71d languageName: node linkType: hard @@ -12934,7 +13000,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.13.0, qs@npm:^6.5.2": +"qs@npm:6.13.0": version: 6.13.0 resolution: "qs@npm:6.13.0" dependencies: @@ -12943,6 +13009,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.5.2": + version: 6.12.1 + resolution: "qs@npm:6.12.1" + dependencies: + side-channel: "npm:^1.0.6" + checksum: 10/035bcad2a1ab0175bac7a74c904c15913bdac252834149ccff988c93a51de02642fe7be10e43058ba4dc4094bb28ce9b59d12b9e91d40997f445cfde3ecc1c29 + languageName: node + linkType: hard + "qs@npm:~6.5.2": version: 6.5.3 resolution: "qs@npm:6.5.3" @@ -15472,6 +15547,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10/0097779d94bc0fd26f0418b3a05472410408877279141ded2bd449167be1aed7ea5b76f756562cb3586a07f251b90799bab22d9019ceba49c037c76445f7cddd + languageName: node + linkType: hard + "undici-types@npm:~6.19.2": version: 6.19.8 resolution: "undici-types@npm:6.19.8" From c0b99e5fca6468c784634fa762bba1c25186562d Mon Sep 17 00:00:00 2001 From: cedoor Date: Tue, 15 Oct 2024 10:43:57 +0100 Subject: [PATCH 3/6] chore(eddsa-poseidon): v1.0.4 --- packages/eddsa-poseidon/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eddsa-poseidon/package.json b/packages/eddsa-poseidon/package.json index b456c12a..f29720d8 100644 --- a/packages/eddsa-poseidon/package.json +++ b/packages/eddsa-poseidon/package.json @@ -1,6 +1,6 @@ { "name": "@zk-kit/eddsa-poseidon", - "version": "1.0.3", + "version": "1.0.4", "description": "A JavaScript EdDSA library for secure signing and verification using Poseidon the Baby Jubjub elliptic curve.", "type": "module", "license": "MIT", From 91789964d407bc1a7b24a088d426dc527458a2e4 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Thu, 17 Oct 2024 13:16:40 +0200 Subject: [PATCH 4/6] docs: add paper download link to readme files (#346) * docs: add paper download link to readme files Now it is easier to download the paper pdf. * chore: remove yarn from the command --- README.md | 4 ++++ papers/leanimt/README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 844f9bcd..5ecaa737 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,10 @@ - Rust: [privacy-scaling-explorations/zk-kit.rust](https://github.com/privacy-scaling-explorations/zk-kit.rust) - Solidity: [privacy-scaling-explorations/zk-kit.solidity](https://github.com/privacy-scaling-explorations/zk-kit.solidity) +## 📄 Papers + +- LeanIMT ([Download PDF](https://github.com/privacy-scaling-explorations/zk-kit/raw/main/papers/leanimt/paper/leanimt-paper.pdf)) + ## 📦 Packages diff --git a/papers/leanimt/README.md b/papers/leanimt/README.md index 928f4662..ba92f392 100644 --- a/papers/leanimt/README.md +++ b/papers/leanimt/README.md @@ -1,6 +1,6 @@ # LeanIMT Paper -This folder contains the LeanIMT Paper. +This folder contains the LeanIMT Paper ([Download PDF](https://github.com/privacy-scaling-explorations/zk-kit/raw/main/papers/leanimt/paper/leanimt-paper.pdf)). ## Related work From 25f30b18ba0fb2428bb28f077dc69d91eb465dc7 Mon Sep 17 00:00:00 2001 From: hannahredler <72608366+hannahredler@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:56:03 +0200 Subject: [PATCH 5/6] feat(eddsa-poseidon): adds Blake2s hashing for eddsa and conditional imports (#329) * feat(eddsa-poseidon): adds Blake2s hashing for eddsa and conditional imports Currently EdDSA Poseidon hashes using Blake 1, which is now outdated. This commit swaps the default hashing algorithm for Blake2, whilst also introducing conditional imports so the user can specify the underlying hashing algorithm if required. BREAKING CHANGE: n * feat(eddsa-poseidon): clean up code re 152 * feat(eddsa-poseidon): fix exports * feat(eddsa-poseidon): update comments * refactor(eddsa-poseidon): makes the blake algorithms implement the hash function interface * feat(eddsa-poseidon): removes implementation of blake2b in favour of the blakejs library BREAKING CHANGE: n * refactor(eddsa-poseidon): simplifies the input types for the hash functions re #152 * refactor(eddsa-poseidon): removes to hex function in favour of zk-kit bufferToHexadecimal re #152 * fix(eddsa-poseidon): change the default import back to blake1 and fix the export files re 152 * docs(eddsa-poseidon): update comments re 152 * refactor(eddsa-poseidon): unifies both algorithms to use default exports re 152 * refactor(eddsa-poseidon): use imported buffer type instead of the default re #152 * fix(eddsa-poseidon): throw an error if unsupported algorithm is chosen" BREAKING CHANGE: n re #51 * refactor(eddsa-poseidon): rename test file re #152 --------- Co-authored-by: Cedoor --- packages/eddsa-poseidon/package.json | 11 + packages/eddsa-poseidon/rollup.config.ts | 42 +++ packages/eddsa-poseidon/src/HashFunction.ts | 6 + packages/eddsa-poseidon/src/blake.ts | 3 +- packages/eddsa-poseidon/src/blake2b.ts | 51 +++ .../src/eddsa-poseidon-blake-1.ts | 15 + .../src/eddsa-poseidon-blake-2b.ts | 15 + .../src/eddsa-poseidon-factory.ts | 313 ++++++++++++++++++ packages/eddsa-poseidon/src/eddsa-poseidon.ts | 297 ----------------- packages/eddsa-poseidon/src/index.ts | 3 +- packages/eddsa-poseidon/src/utils.ts | 31 +- ....test.ts => eddsa-poseidon-blake1.test.ts} | 7 +- .../tests/eddsa-poseidon-blake2.test.ts | 61 ++++ .../tests/generated-test-vectors.txt | 100 ++++++ yarn.lock | 3 +- 15 files changed, 645 insertions(+), 313 deletions(-) create mode 100644 packages/eddsa-poseidon/src/HashFunction.ts create mode 100644 packages/eddsa-poseidon/src/blake2b.ts create mode 100644 packages/eddsa-poseidon/src/eddsa-poseidon-blake-1.ts create mode 100644 packages/eddsa-poseidon/src/eddsa-poseidon-blake-2b.ts create mode 100644 packages/eddsa-poseidon/src/eddsa-poseidon-factory.ts delete mode 100644 packages/eddsa-poseidon/src/eddsa-poseidon.ts rename packages/eddsa-poseidon/tests/{index.test.ts => eddsa-poseidon-blake1.test.ts} (99%) create mode 100644 packages/eddsa-poseidon/tests/eddsa-poseidon-blake2.test.ts create mode 100644 packages/eddsa-poseidon/tests/generated-test-vectors.txt diff --git a/packages/eddsa-poseidon/package.json b/packages/eddsa-poseidon/package.json index f29720d8..920583a6 100644 --- a/packages/eddsa-poseidon/package.json +++ b/packages/eddsa-poseidon/package.json @@ -14,6 +14,16 @@ "types": "./dist/index.d.ts", "require": "./dist/index.cjs", "default": "./dist/index.js" + }, + "./blake-1": { + "types": "./dist/index.d.ts", + "require": "./dist/lib.commonjs/eddsa-poseidon-blake-1.cjs", + "default": "./dist/esm/eddsa-poseidon-blake-1.js" + }, + "./blake-2b": { + "types": "./dist/index.d.ts", + "require": "./dist/lib.commonjs/eddsa-poseidon-blake-2b.cjs", + "default": "./dist/esm/eddsa-poseidon-blake-2b.js" } }, "files": [ @@ -50,6 +60,7 @@ "dependencies": { "@zk-kit/baby-jubjub": "1.0.3", "@zk-kit/utils": "1.2.1", + "blakejs": "^1.2.1", "buffer": "6.0.3", "poseidon-lite": "0.3.0" } diff --git a/packages/eddsa-poseidon/rollup.config.ts b/packages/eddsa-poseidon/rollup.config.ts index bb11577a..984a87d0 100644 --- a/packages/eddsa-poseidon/rollup.config.ts +++ b/packages/eddsa-poseidon/rollup.config.ts @@ -66,5 +66,47 @@ export default [ input: "src/index.ts", output: [{ file: "dist/index.d.ts", format: "es" }], plugins: [dts()] + }, + { + input: "src/eddsa-poseidon-blake-1.ts", + output: [ + { + dir: "./dist/lib.commonjs", + format: "cjs", + banner, + entryFileNames: "[name].cjs" + }, + { dir: "./dist/lib.esm", format: "es", banner } + ], + external: [ + ...Object.keys(pkg.dependencies), + "@zk-kit/utils/conversions", + "@zk-kit/utils/f1-field", + "@zk-kit/utils/scalar", + "@zk-kit/utils/error-handlers", + "@zk-kit/utils/type-checks" + ], + plugins: [typescript({ tsconfig: "./build.tsconfig.json", declaration: false, declarationDir: undefined })] + }, + { + input: "src/eddsa-poseidon-blake-2b.ts", + output: [ + { + dir: "./dist/lib.commonjs", + format: "cjs", + banner, + entryFileNames: "[name].cjs" + }, + { dir: "./dist/lib.esm", format: "es", banner } + ], + external: [ + ...Object.keys(pkg.dependencies), + "@zk-kit/utils/conversions", + "@zk-kit/utils/f1-field", + "@zk-kit/utils/scalar", + "@zk-kit/utils/error-handlers", + "@zk-kit/utils/type-checks" + ], + plugins: [typescript({ tsconfig: "./build.tsconfig.json", declaration: false, declarationDir: undefined })] } ] diff --git a/packages/eddsa-poseidon/src/HashFunction.ts b/packages/eddsa-poseidon/src/HashFunction.ts new file mode 100644 index 00000000..f776d912 --- /dev/null +++ b/packages/eddsa-poseidon/src/HashFunction.ts @@ -0,0 +1,6 @@ +import { Buffer } from "buffer" + +export interface HashFunction { + update(data: Buffer): HashFunction + digest(): Buffer +} diff --git a/packages/eddsa-poseidon/src/blake.ts b/packages/eddsa-poseidon/src/blake.ts index d9b8169f..aad3d889 100644 --- a/packages/eddsa-poseidon/src/blake.ts +++ b/packages/eddsa-poseidon/src/blake.ts @@ -20,6 +20,7 @@ */ import { Buffer } from "buffer" +import { HashFunction } from "./HashFunction" const zo = Buffer.from([0x01]) const oo = Buffer.from([0x81]) @@ -156,7 +157,7 @@ function lengthCarry(arr: number[]) { * hashing, allowing data to be added in chunks. */ /* eslint-disable import/prefer-default-export */ -export class Blake512 { +export default class Blake512 implements HashFunction { private _h: number[] private _s: number[] private _block: Buffer diff --git a/packages/eddsa-poseidon/src/blake2b.ts b/packages/eddsa-poseidon/src/blake2b.ts new file mode 100644 index 00000000..c90d420f --- /dev/null +++ b/packages/eddsa-poseidon/src/blake2b.ts @@ -0,0 +1,51 @@ +import { blake2bInit, blake2bUpdate, blake2bFinal, Blake2bCTX } from "blakejs" +import { HashFunction } from "./HashFunction" + +/** + * @module Blake2b + * Implements the Blake2b cryptographic hash function. + * Blake2b is a second iteration of the blake algorithm + * + * This code is a wrapper around the "blakeJS" JavaScript library. + * It supports hashing with optional keys, or output length for enhanced security in certain contexts. + */ + +export default class Blake2b implements HashFunction { + key: Uint8Array | null = null + outlen: number = 64 + context: Blake2bCTX + /** + * Constructor of the Blake2b engine + * @param outlen The fixed output length of the generated hash + * @param key Optional key parameter if keyed hashes are required + * @returns This instance, to allow method chaining. + */ + constructor(outlen: number = 64, key?: Uint8Array) { + if (key) this.key = key + if (outlen <= 0 || outlen > 64) throw new Error("Illegal output length, expected 0 < length <= 64") + else this.outlen = outlen + + this.context = blake2bInit(this.outlen, key) + } + + /** + * Updates the hash with new data. This method can be called multiple + * times to incrementally add data to the hash computation. + * @param input The data to add to the hash. + * @returns The instance, to allow method chaining. + */ + update(input: Buffer) { + blake2bUpdate(this.context, input) + return this + } + + /** + * Completes the hash computation and returns the final hash value. + * This method applies the necessary padding, performs the final compression, + * and returns the output. + * @returns The Blake2b hash of the input data. + */ + digest() { + return Buffer.from(blake2bFinal(this.context)) + } +} diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon-blake-1.ts b/packages/eddsa-poseidon/src/eddsa-poseidon-blake-1.ts new file mode 100644 index 00000000..61e77d6d --- /dev/null +++ b/packages/eddsa-poseidon/src/eddsa-poseidon-blake-1.ts @@ -0,0 +1,15 @@ +import { EdDSAPoseidonFactory, SupportedHashingAlgorithms } from "./eddsa-poseidon-factory" + +export const { + EdDSAPoseidon, + derivePublicKey, + deriveSecretScalar, + packPublicKey, + packSignature, + signMessage, + unpackPublicKey, + unpackSignature, + verifySignature +} = EdDSAPoseidonFactory(SupportedHashingAlgorithms.BLAKE1) + +export * from "./types" diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon-blake-2b.ts b/packages/eddsa-poseidon/src/eddsa-poseidon-blake-2b.ts new file mode 100644 index 00000000..cac69205 --- /dev/null +++ b/packages/eddsa-poseidon/src/eddsa-poseidon-blake-2b.ts @@ -0,0 +1,15 @@ +import { EdDSAPoseidonFactory, SupportedHashingAlgorithms } from "./eddsa-poseidon-factory" + +export const { + EdDSAPoseidon, + derivePublicKey, + deriveSecretScalar, + packPublicKey, + packSignature, + signMessage, + unpackPublicKey, + unpackSignature, + verifySignature +} = EdDSAPoseidonFactory(SupportedHashingAlgorithms.BLAKE2b) + +export * from "./types" diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon-factory.ts b/packages/eddsa-poseidon/src/eddsa-poseidon-factory.ts new file mode 100644 index 00000000..5cbf6b15 --- /dev/null +++ b/packages/eddsa-poseidon/src/eddsa-poseidon-factory.ts @@ -0,0 +1,313 @@ +import { + Base8, + Fr, + Point, + addPoint, + inCurve, + mulPointEscalar, + packPoint, + subOrder, + unpackPoint +} from "@zk-kit/baby-jubjub" +import type { BigNumberish } from "@zk-kit/utils" +import { crypto, requireBuffer } from "@zk-kit/utils" +import { bigNumberishToBigInt, leBigIntToBuffer, leBufferToBigInt } from "@zk-kit/utils/conversions" +import { requireBigNumberish } from "@zk-kit/utils/error-handlers" +import F1Field from "@zk-kit/utils/f1-field" +import * as scalar from "@zk-kit/utils/scalar" +import { Buffer } from "buffer" +import { poseidon5 } from "poseidon-lite/poseidon5" +import { Signature } from "./types" +import { checkMessage, checkPrivateKey, hashInput, isPoint, isSignature, pruneBuffer } from "./utils" + +export enum SupportedHashingAlgorithms { + BLAKE1 = "blake-1", + BLAKE2b = "blake-2b" +} + +export const EdDSAPoseidonFactory = (algorithm: SupportedHashingAlgorithms) => { + /** + * Derives a public key from a given private key using the + * {@link https://eips.ethereum.org/EIPS/eip-2494|Baby Jubjub} elliptic curve. + * This function utilizes the Baby Jubjub elliptic curve for cryptographic operations. + * The private key should be securely stored and managed, and it should never be exposed + * or transmitted in an unsecured manner. + * + * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to + * generate entropy and there is no limit in size. + * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. + * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. + * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. + * + * @param privateKey The private key used for generating the public key. + * @returns The derived public key. + */ + const deriveSecretScalar = (privateKey: Buffer | Uint8Array | string): bigint => { + // Convert the private key to buffer. + privateKey = checkPrivateKey(privateKey) + + let hash = hashInput(privateKey, algorithm) + + hash = hash.slice(0, 32) + hash = pruneBuffer(hash) + + return scalar.shiftRight(leBufferToBigInt(hash), BigInt(3)) % subOrder + } + + /** + * Derives a public key from a given private key using the + * {@link https://eips.ethereum.org/EIPS/eip-2494|Baby Jubjub} elliptic curve. + * This function utilizes the Baby Jubjub elliptic curve for cryptographic operations. + * The private key should be securely stored and managed, and it should never be exposed + * or transmitted in an unsecured manner. + * + * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to + * generate entropy and there is no limit in size. + * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. + * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. + * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. + * + * @param privateKey The private key used for generating the public key. + * @returns The derived public key. + */ + function derivePublicKey(privateKey: Buffer | Uint8Array | string): Point { + const s = deriveSecretScalar(privateKey) + + return mulPointEscalar(Base8, s) + } + + /** + * Signs a message using the provided private key, employing Poseidon hashing and + * EdDSA with the Baby Jubjub elliptic curve. + * + * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to + * generate entropy and there is no limit in size. + * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. + * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. + * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. + * + * @param privateKey The private key used to sign the message. + * @param message The message to be signed. + * @returns The signature object, containing properties relevant to EdDSA signatures, such as 'R8' and 'S' values. + */ + function signMessage(privateKey: Buffer | Uint8Array | string, message: BigNumberish): Signature { + // Convert the private key to buffer. + privateKey = checkPrivateKey(privateKey) + + // Convert the message to big integer. + message = checkMessage(message) + + const hash = hashInput(privateKey, algorithm) + + const sBuff = pruneBuffer(hash.slice(0, 32)) + const s = leBufferToBigInt(sBuff) + const A = mulPointEscalar(Base8, scalar.shiftRight(s, BigInt(3))) + + const msgBuff = leBigIntToBuffer(message, 32) + + const rBuff = hashInput(Buffer.concat([hash.slice(32, 64), msgBuff]), algorithm) + + const Fr = new F1Field(subOrder) + const r = Fr.e(leBufferToBigInt(rBuff)) + + const R8 = mulPointEscalar(Base8, r) + const hm = poseidon5([R8[0], R8[1], A[0], A[1], message]) + const S = Fr.add(r, Fr.mul(hm, s)) + + return { R8, S } + } + + /** + * Verifies an EdDSA signature using the Baby Jubjub elliptic curve and Poseidon hash function. + * @param message The original message that was be signed. + * @param signature The EdDSA signature to be verified. + * @param publicKey The public key associated with the private key used to sign the message. + * @returns Returns true if the signature is valid and corresponds to the message and public key, false otherwise. + */ + function verifySignature(message: BigNumberish, signature: Signature, publicKey: Point): boolean { + if ( + !isPoint(publicKey) || + !isSignature(signature) || + !inCurve(signature.R8) || + !inCurve(publicKey) || + BigInt(signature.S) >= subOrder + ) { + return false + } + + // Convert the message to big integer. + message = checkMessage(message) + + // Convert the signature values to big integers for calculations. + const _signature: Signature = { + R8: [BigInt(signature.R8[0]), BigInt(signature.R8[1])], + S: BigInt(signature.S) + } + // Convert the public key values to big integers for calculations. + const _publicKey: Point = [BigInt(publicKey[0]), BigInt(publicKey[1])] + + const hm = poseidon5([signature.R8[0], signature.R8[1], publicKey[0], publicKey[1], message]) + + const pLeft = mulPointEscalar(Base8, BigInt(signature.S)) + let pRight = mulPointEscalar(_publicKey, scalar.mul(hm, BigInt(8))) + + pRight = addPoint(_signature.R8, pRight) + + // Return true if the points match. + return Fr.eq(pLeft[0], pRight[0]) && Fr.eq(pLeft[1], pRight[1]) + } + + /** + * Converts a given public key into a packed (compressed) string format for efficient transmission and storage. + * This method ensures the public key is valid and within the Baby Jubjub curve before packing. + * @param publicKey The public key to be packed. + * @returns A string representation of the packed public key. + */ + function packPublicKey(publicKey: Point): bigint { + if (!isPoint(publicKey) || !inCurve(publicKey)) { + throw new Error("Invalid public key") + } + + // Convert the public key values to big integers for calculations. + const _publicKey: Point = [BigInt(publicKey[0]), BigInt(publicKey[1])] + + return packPoint(_publicKey) + } + + /** + * Unpacks a public key from its packed string representation back to its original point form on the Baby Jubjub curve. + * This function checks for the validity of the input format before attempting to unpack. + * @param publicKey The packed public key as a bignumberish. + * @returns The unpacked public key as a point. + */ + function unpackPublicKey(publicKey: BigNumberish): Point { + requireBigNumberish(publicKey, "publicKey") + + const unpackedPublicKey = unpackPoint(bigNumberishToBigInt(publicKey)) + + if (unpackedPublicKey === null) { + throw new Error("Invalid public key") + } + + return unpackedPublicKey + } + + /** + * Packs an EdDSA signature into a buffer of 64 bytes for efficient storage. + * Use {@link unpackSignature} to reverse the process without needing to know + * the details of the format. + * + * The buffer contains the R8 point packed int 32 bytes (via + * {@link packSignature}) followed by the S scalar. All encodings are + * little-endian. + * + * @param signature the signature to pack + * @returns a 64 byte buffer containing the packed signature + */ + function packSignature(signature: Signature): Buffer { + if (!isSignature(signature) || !inCurve(signature.R8) || BigInt(signature.S) >= subOrder) { + throw new Error("Invalid signature") + } + + const numericSignature: Signature = { + R8: signature.R8.map((c) => BigInt(c)) as Point, + S: BigInt(signature.S) + } + + const packedR8 = packPoint(numericSignature.R8) + const packedBytes = Buffer.alloc(64) + packedBytes.set(leBigIntToBuffer(packedR8, 32), 0) + packedBytes.set(leBigIntToBuffer(numericSignature.S, 32), 32) + return packedBytes + } + + /** + * Unpacks a signature produced by {@link packSignature}. See that function + * for the details of the format. + * + * @param packedSignature the 64 byte buffer to unpack + * @returns a Signature with numbers in string form + */ + function unpackSignature(packedSignature: Buffer): Signature { + requireBuffer(packedSignature, "packedSignature") + if (packedSignature.length !== 64) { + throw new Error("Packed signature must be 64 bytes") + } + + const sliceR8 = packedSignature.subarray(0, 32) + const sliceS = packedSignature.subarray(32, 64) + const unpackedR8 = unpackPoint(leBufferToBigInt(sliceR8)) + if (unpackedR8 === null) { + throw new Error(`Invalid packed signature point ${sliceS.toString("hex")}.`) + } + return { + R8: unpackedR8, + S: leBufferToBigInt(sliceS) + } + } + + /** + * Represents a cryptographic entity capable of signing messages and verifying signatures + * using the EdDSA scheme with Poseidon hash and the Baby Jubjub elliptic curve. + */ + class EdDSAPoseidon { + // Private key for signing, stored securely. + privateKey: Buffer | Uint8Array | string + // The secret scalar derived from the private key to compute the public key. + secretScalar: bigint + // The public key corresponding to the private key. + publicKey: Point + // A packed (compressed) representation of the public key for efficient operations. + packedPublicKey: bigint + + /** + * Initializes a new instance, deriving necessary cryptographic parameters from the provided private key. + * If the private key is not passed as a parameter, a random 32-byte hexadecimal key is generated. + * + * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to + * generate entropy and there is no limit in size. + * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. + * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. + * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. + * + * @param privateKey The private key used for signing and public key derivation. + */ + constructor(privateKey: Buffer | Uint8Array | string = crypto.getRandomValues(32)) { + this.privateKey = privateKey + this.secretScalar = deriveSecretScalar(privateKey) + this.publicKey = derivePublicKey(privateKey) + this.packedPublicKey = packPublicKey(this.publicKey) + } + + /** + * Signs a given message using the private key and returns the signature. + * @param message The message to be signed. + * @returns The signature of the message. + */ + signMessage(message: BigNumberish): Signature { + return signMessage(this.privateKey, message) + } + + /** + * Verifies a signature against a message and the public key stored in this instance. + * @param message The message whose signature is to be verified. + * @param signature The signature to be verified. + * @returns True if the signature is valid for the message and public key, false otherwise. + */ + verifySignature(message: BigNumberish, signature: Signature): boolean { + return verifySignature(message, signature, this.publicKey) + } + } + + return { + deriveSecretScalar, + derivePublicKey, + signMessage, + verifySignature, + packPublicKey, + unpackPublicKey, + packSignature, + unpackSignature, + EdDSAPoseidon + } +} diff --git a/packages/eddsa-poseidon/src/eddsa-poseidon.ts b/packages/eddsa-poseidon/src/eddsa-poseidon.ts deleted file mode 100644 index fe7c7b58..00000000 --- a/packages/eddsa-poseidon/src/eddsa-poseidon.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { - Base8, - Fr, - Point, - addPoint, - inCurve, - mulPointEscalar, - packPoint, - subOrder, - unpackPoint -} from "@zk-kit/baby-jubjub" -import type { BigNumberish } from "@zk-kit/utils" -import { crypto, requireBuffer } from "@zk-kit/utils" -import { bigNumberishToBigInt, leBigIntToBuffer, leBufferToBigInt } from "@zk-kit/utils/conversions" -import { requireBigNumberish } from "@zk-kit/utils/error-handlers" -import F1Field from "@zk-kit/utils/f1-field" -import * as scalar from "@zk-kit/utils/scalar" -import { Buffer } from "buffer" -import { poseidon5 } from "poseidon-lite/poseidon5" -import { Signature } from "./types" -import { hash as blake, checkMessage, checkPrivateKey, isPoint, isSignature, pruneBuffer } from "./utils" - -/** - * Derives a secret scalar from a given EdDSA private key. - * - * This process involves hashing the private key with Blake1, pruning the resulting hash to retain the lower 32 bytes, - * and converting it into a little-endian integer. The use of the secret scalar streamlines the public key generation - * process by omitting steps 1, 2, and 3 as outlined in RFC 8032 section 5.1.5, enhancing circuit efficiency and simplicity. - * This method is crucial for fixed-base scalar multiplication operations within the correspondent cryptographic circuit. - * For detailed steps, see: {@link https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5}. - * For example usage in a circuit, see: {@link https://github.com/semaphore-protocol/semaphore/blob/2c144fc9e55b30ad09474aeafa763c4115338409/packages/circuits/semaphore.circom#L21} - * - * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to - * generate entropy and there is no limit in size. - * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. - * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. - * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. - * - * @param privateKey The EdDSA private key for generating the associated public key. - * @returns The derived secret scalar to be used to calculate public key and optimized for circuit calculations. - */ -export function deriveSecretScalar(privateKey: Buffer | Uint8Array | string): bigint { - // Convert the private key to buffer. - privateKey = checkPrivateKey(privateKey) - - let hash = blake(privateKey) - - hash = hash.slice(0, 32) - hash = pruneBuffer(hash) - - return scalar.shiftRight(leBufferToBigInt(hash), BigInt(3)) % subOrder -} - -/** - * Derives a public key from a given private key using the - * {@link https://eips.ethereum.org/EIPS/eip-2494|Baby Jubjub} elliptic curve. - * This function utilizes the Baby Jubjub elliptic curve for cryptographic operations. - * The private key should be securely stored and managed, and it should never be exposed - * or transmitted in an unsecured manner. - * - * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to - * generate entropy and there is no limit in size. - * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. - * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. - * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. - * - * @param privateKey The private key used for generating the public key. - * @returns The derived public key. - */ -export function derivePublicKey(privateKey: Buffer | Uint8Array | string): Point { - const s = deriveSecretScalar(privateKey) - - return mulPointEscalar(Base8, s) -} - -/** - * Signs a message using the provided private key, employing Poseidon hashing and - * EdDSA with the Baby Jubjub elliptic curve. - * - * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to - * generate entropy and there is no limit in size. - * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. - * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. - * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. - * - * @param privateKey The private key used to sign the message. - * @param message The message to be signed. - * @returns The signature object, containing properties relevant to EdDSA signatures, such as 'R8' and 'S' values. - */ -export function signMessage(privateKey: Buffer | Uint8Array | string, message: BigNumberish): Signature { - // Convert the private key to buffer. - privateKey = checkPrivateKey(privateKey) - - // Convert the message to big integer. - message = checkMessage(message) - - const hash = blake(privateKey) - - const sBuff = pruneBuffer(hash.slice(0, 32)) - const s = leBufferToBigInt(sBuff) - const A = mulPointEscalar(Base8, scalar.shiftRight(s, BigInt(3))) - - const msgBuff = leBigIntToBuffer(message, 32) - - const rBuff = blake(Buffer.concat([hash.slice(32, 64), msgBuff])) - - const Fr = new F1Field(subOrder) - const r = Fr.e(leBufferToBigInt(rBuff)) - - const R8 = mulPointEscalar(Base8, r) - const hm = poseidon5([R8[0], R8[1], A[0], A[1], message]) - const S = Fr.add(r, Fr.mul(hm, s)) - - return { R8, S } -} - -/** - * Verifies an EdDSA signature using the Baby Jubjub elliptic curve and Poseidon hash function. - * @param message The original message that was be signed. - * @param signature The EdDSA signature to be verified. - * @param publicKey The public key associated with the private key used to sign the message. - * @returns Returns true if the signature is valid and corresponds to the message and public key, false otherwise. - */ -export function verifySignature(message: BigNumberish, signature: Signature, publicKey: Point): boolean { - if ( - !isPoint(publicKey) || - !isSignature(signature) || - !inCurve(signature.R8) || - !inCurve(publicKey) || - BigInt(signature.S) >= subOrder - ) { - return false - } - - // Convert the message to big integer. - message = checkMessage(message) - - // Convert the signature values to big integers for calculations. - const _signature: Signature = { - R8: [BigInt(signature.R8[0]), BigInt(signature.R8[1])], - S: BigInt(signature.S) - } - // Convert the public key values to big integers for calculations. - const _publicKey: Point = [BigInt(publicKey[0]), BigInt(publicKey[1])] - - const hm = poseidon5([signature.R8[0], signature.R8[1], publicKey[0], publicKey[1], message]) - - const pLeft = mulPointEscalar(Base8, BigInt(signature.S)) - let pRight = mulPointEscalar(_publicKey, scalar.mul(hm, BigInt(8))) - - pRight = addPoint(_signature.R8, pRight) - - // Return true if the points match. - return Fr.eq(pLeft[0], pRight[0]) && Fr.eq(pLeft[1], pRight[1]) -} - -/** - * Converts a given public key into a packed (compressed) string format for efficient transmission and storage. - * This method ensures the public key is valid and within the Baby Jubjub curve before packing. - * @param publicKey The public key to be packed. - * @returns A string representation of the packed public key. - */ -export function packPublicKey(publicKey: Point): bigint { - if (!isPoint(publicKey) || !inCurve(publicKey)) { - throw new Error("Invalid public key") - } - - // Convert the public key values to big integers for calculations. - const _publicKey: Point = [BigInt(publicKey[0]), BigInt(publicKey[1])] - - return packPoint(_publicKey) -} - -/** - * Unpacks a public key from its packed string representation back to its original point form on the Baby Jubjub curve. - * This function checks for the validity of the input format before attempting to unpack. - * @param publicKey The packed public key as a bignumberish. - * @returns The unpacked public key as a point. - */ -export function unpackPublicKey(publicKey: BigNumberish): Point { - requireBigNumberish(publicKey, "publicKey") - - const unpackedPublicKey = unpackPoint(bigNumberishToBigInt(publicKey)) - - if (unpackedPublicKey === null) { - throw new Error("Invalid public key") - } - - return unpackedPublicKey -} - -/** - * Packs an EdDSA signature into a buffer of 64 bytes for efficient storage. - * Use {@link unpackSignature} to reverse the process without needing to know - * the details of the format. - * - * The buffer contains the R8 point packed int 32 bytes (via - * {@link packSignature}) followed by the S scalar. All encodings are - * little-endian. - * - * @param signature the signature to pack - * @returns a 64 byte buffer containing the packed signature - */ -export function packSignature(signature: Signature): Buffer { - if (!isSignature(signature) || !inCurve(signature.R8) || BigInt(signature.S) >= subOrder) { - throw new Error("Invalid signature") - } - - const numericSignature: Signature = { - R8: signature.R8.map((c) => BigInt(c)) as Point, - S: BigInt(signature.S) - } - - const packedR8 = packPoint(numericSignature.R8) - const packedBytes = Buffer.alloc(64) - packedBytes.set(leBigIntToBuffer(packedR8, 32), 0) - packedBytes.set(leBigIntToBuffer(numericSignature.S, 32), 32) - return packedBytes -} - -/** - * Unpacks a signature produced by {@link packSignature}. See that function - * for the details of the format. - * - * @param packedSignature the 64 byte buffer to unpack - * @returns a Signature with numbers in string form - */ -export function unpackSignature(packedSignature: Buffer): Signature { - requireBuffer(packedSignature, "packedSignature") - if (packedSignature.length !== 64) { - throw new Error("Packed signature must be 64 bytes") - } - - const sliceR8 = packedSignature.subarray(0, 32) - const sliceS = packedSignature.subarray(32, 64) - const unpackedR8 = unpackPoint(leBufferToBigInt(sliceR8)) - if (unpackedR8 === null) { - throw new Error(`Invalid packed signature point ${sliceS.toString("hex")}.`) - } - return { - R8: unpackedR8, - S: leBufferToBigInt(sliceS) - } -} - -/** - * Represents a cryptographic entity capable of signing messages and verifying signatures - * using the EdDSA scheme with Poseidon hash and the Baby Jubjub elliptic curve. - */ -export class EdDSAPoseidon { - // Private key for signing, stored securely. - privateKey: Buffer | Uint8Array | string - // The secret scalar derived from the private key to compute the public key. - secretScalar: bigint - // The public key corresponding to the private key. - publicKey: Point - // A packed (compressed) representation of the public key for efficient operations. - packedPublicKey: bigint - - /** - * Initializes a new instance, deriving necessary cryptographic parameters from the provided private key. - * If the private key is not passed as a parameter, a random 32-byte hexadecimal key is generated. - * - * The private key must be an instance of Buffer, Uint8Array or a string. The input will be used to - * generate entropy and there is no limit in size. - * The string is used as a set of raw bytes (in UTF-8) and is typically used to pass passwords or secret messages. - * If you want to pass a bigint, a number or a hexadecimal, be sure to convert them to one of the supported types first. - * The 'conversions' module in @zk-kit/utils provides a set of functions that may be useful in case you need to convert types. - * - * @param privateKey The private key used for signing and public key derivation. - */ - constructor(privateKey: Buffer | Uint8Array | string = crypto.getRandomValues(32)) { - this.privateKey = privateKey - this.secretScalar = deriveSecretScalar(privateKey) - this.publicKey = derivePublicKey(privateKey) - this.packedPublicKey = packPublicKey(this.publicKey) - } - - /** - * Signs a given message using the private key and returns the signature. - * @param message The message to be signed. - * @returns The signature of the message. - */ - signMessage(message: BigNumberish): Signature { - return signMessage(this.privateKey, message) - } - - /** - * Verifies a signature against a message and the public key stored in this instance. - * @param message The message whose signature is to be verified. - * @param signature The signature to be verified. - * @returns True if the signature is valid for the message and public key, false otherwise. - */ - verifySignature(message: BigNumberish, signature: Signature): boolean { - return verifySignature(message, signature, this.publicKey) - } -} diff --git a/packages/eddsa-poseidon/src/index.ts b/packages/eddsa-poseidon/src/index.ts index 1ce36676..8b4f94db 100644 --- a/packages/eddsa-poseidon/src/index.ts +++ b/packages/eddsa-poseidon/src/index.ts @@ -1,2 +1 @@ -export * from "./eddsa-poseidon" -export * from "./types" +export * from "./eddsa-poseidon-blake-1" diff --git a/packages/eddsa-poseidon/src/utils.ts b/packages/eddsa-poseidon/src/utils.ts index 01dee09c..7ffc3dea 100644 --- a/packages/eddsa-poseidon/src/utils.ts +++ b/packages/eddsa-poseidon/src/utils.ts @@ -4,9 +4,11 @@ import { bigNumberishToBigInt, bufferToBigInt } from "@zk-kit/utils/conversions" import { requireTypes } from "@zk-kit/utils/error-handlers" import { isArray, isBigNumber, isBigNumberish, isObject } from "@zk-kit/utils/type-checks" import { Buffer } from "buffer" -import { Blake512 } from "./blake" +import Blake512 from "./blake" +import Blake2b from "./blake2b" import { Signature } from "./types" - +import { SupportedHashingAlgorithms } from "./eddsa-poseidon-factory" +import { HashFunction } from "./HashFunction" /** * Prunes a buffer to meet the specific requirements for using it as a private key * or part of a signature. @@ -72,16 +74,27 @@ export function checkMessage(message: BigNumberish): bigint { } /** - * Computes the Blake512 hash of the input message. - * Blake512 is a cryptographic hash function that produces a hash value of 512 bits, - * commonly used for data integrity checks and other cryptographic applications. + * Computes a hash of an input, given a specified hashing algorithm. * @param message The input data to hash, provided as a Buffer. - * @returns A Buffer containing the 512-bit hash result. + * @param algorithm The selected algorithm + * @returns A Buffer containing the result */ -export function hash(message: Buffer | Uint8Array): Buffer { - const engine = new Blake512() +export function hashInput(message: Buffer | Uint8Array, algorithm?: SupportedHashingAlgorithms) { + let engine: HashFunction + switch (algorithm) { + case SupportedHashingAlgorithms.BLAKE1: { + engine = new Blake512() + break + } + case SupportedHashingAlgorithms.BLAKE2b: { + engine = new Blake2b() + break + } + default: { + throw new Error("Unsupported algorithm. Cannot hash input.") + } + } engine.update(Buffer.from(message)) - return engine.digest() } diff --git a/packages/eddsa-poseidon/tests/index.test.ts b/packages/eddsa-poseidon/tests/eddsa-poseidon-blake1.test.ts similarity index 99% rename from packages/eddsa-poseidon/tests/index.test.ts rename to packages/eddsa-poseidon/tests/eddsa-poseidon-blake1.test.ts index 6f5ca504..f0f13c81 100644 --- a/packages/eddsa-poseidon/tests/index.test.ts +++ b/packages/eddsa-poseidon/tests/eddsa-poseidon-blake1.test.ts @@ -5,7 +5,6 @@ import { utils } from "ffjavascript" import { r, packPoint, Point } from "@zk-kit/baby-jubjub" import { EdDSAPoseidon, - Signature, derivePublicKey, deriveSecretScalar, packPublicKey, @@ -14,7 +13,9 @@ import { unpackPublicKey, unpackSignature, verifySignature -} from "../src" +} from "../src/eddsa-poseidon-blake-1" + +import { Signature } from "../src/types" import { isPoint, isSignature } from "../src/utils" function stringifyPoint(publicKey: Point): Point { @@ -352,7 +353,7 @@ describe("EdDSAPoseidon", () => { const packedSignature = packSignature(signature) packedSignature.set([1], 35) - unpackSignature(packedSignature) + expect(() => unpackSignature(packedSignature)).not.toThrow() }) it("Should handle a signature with values smaller than 32 bytes", async () => { diff --git a/packages/eddsa-poseidon/tests/eddsa-poseidon-blake2.test.ts b/packages/eddsa-poseidon/tests/eddsa-poseidon-blake2.test.ts new file mode 100644 index 00000000..74dfb389 --- /dev/null +++ b/packages/eddsa-poseidon/tests/eddsa-poseidon-blake2.test.ts @@ -0,0 +1,61 @@ +import fs from "fs" +import path from "path" +import { bufferToHexadecimal, hexadecimalToBuffer } from "@zk-kit/utils" +import Blake2b from "../src/blake2b" + +describe("Blake2b Basic test", () => { + test("Basic case should return correctly", () => { + // From the example computation in the RFC + const instance = new Blake2b() + instance.update(Buffer.from("abc")) + + expect(bufferToHexadecimal(instance.digest())).toBe( + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923" + ) + }) + + test("Empty string should return correctly", () => { + // From the example computation in the RFC + const instance = new Blake2b() + instance.update(Buffer.from("")) + + expect(bufferToHexadecimal(instance.digest())).toBe( + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce" + ) + }) + test("Longer string should return correctly", () => { + // From the example computation in the RFC + const instance = new Blake2b() + instance.update(Buffer.from("The quick brown fox jumps over the lazy dog")) + + expect(bufferToHexadecimal(instance.digest())).toBe( + "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918" + ) + }) + + test("Passing an output length too high should throw", () => { + expect(() => new Blake2b(65)).toThrow("Illegal output length, expected 0 < length <= 64") + }) + + test("Passing an output length too low should throw", () => { + expect(() => new Blake2b(-1)).toThrow("Illegal output length, expected 0 < length <= 64") + }) +}) + +test("BLAKE2b generated test vectors2", () => { + const contents = fs.readFileSync(path.resolve(__dirname, "./generated-test-vectors.txt"), "utf8") + contents.split("\n").forEach((line) => { + if (line.length === 0) { + return + } + const parts = line.split("\t") + const inputHex = parts[0] + const keyHex = parts[1] + const outLen = Number(parts[2]) + const outHex = parts[3] + + const instance = new Blake2b(outLen, hexadecimalToBuffer(keyHex)) + instance.update(hexadecimalToBuffer(inputHex)) + expect(bufferToHexadecimal(instance.digest())).toBe(outHex) + }) +}) diff --git a/packages/eddsa-poseidon/tests/generated-test-vectors.txt b/packages/eddsa-poseidon/tests/generated-test-vectors.txt new file mode 100644 index 00000000..1d89d21c --- /dev/null +++ b/packages/eddsa-poseidon/tests/generated-test-vectors.txt @@ -0,0 +1,100 @@ +d3c32a9419c9874c0d240e203c0e2784742808fcc02518879c65f75b0ca07963ae3d765d6d6e2c18a74ddfabe84e0b703afe111deccba4bf39121531eb54271efc9a9d010eca12c9500216a4d8abc57ce730897209f3f5aef2b4dda332879ce4ba2a12093f3bbaccb36affffe69c9b30720e774556fcf19b84887b4a9be90656eed06a14f4fc012fcfe5c206cc93f20bf73eddfa3e223478506ea962389133728e55454b590eb4032b9e62b9e68a575c97cbcaee2bb3a840817da2ff03053a9d702d4aeceaef3e871a0d172720805da82f82799a712ee7302b6d76827bbf5e6ae4e719262e871e50c7f9cb9c5e65da1a0dd8d9ed9552d538672f2513a05dae121eee5ead6168ad780837e6dad09127b4eed74a69fa5ca80b97e30ec7a97f4ed06cc3ff3c4940ff806cdef0873d0a105935ac76fd16ae39ef92523c81fb7ac9877dd834171ebff47aa0f44cf2b640b27d655bbe174ecfde5fc313e5d203a79537690a1d45bbff85c0d77156eebc7e79d4ccc8fadd39afd2427dde389884f636d419fbcf89a9761637c25f3cc145b159bfb54376106fefed56759572bc05537f8852a3d63c4cc9105f3d86ca1591b6029aa40f1218e70928816ca54dec30af2d1c97cac704bcfb9cc89d4f074f4e1b38c00e52b27c83dff6a22a575d35a94405827e7b219ee761f64abe8ccdc1da319e1f5565b88f52764796d2415398a181a90742257d9c6cc9db3b71311e89e8ee5b476b690138771fc4a541764d955c0c35c05d5abb007df42dc83b2f0def5487eb0433a8c8f6571c42d9917688b941995a9ff968fa928435abf789382f7899822f1c6bb82ecbf60dbce71ddf9719a4cb48a567e14f473cee212d60e1793307ce9cabc3a1bbb89400553c0236fd0f083910ba30db50ef9b97f5 5c76e8ca30fcfd6d16b717ee89654f184027bbaa99a7c22c0f3797a32b5c5134359647393ba45611231928ea5d2a682a418a1ab64d834dcaf60c 48 af7a96c83a98e9fb636bbbd383dfa33a0831dd0773261105c06e527a5be398b5335c36e088c274be825178bd1c702523 +0581d66f43af761873012d1205329b0dbb1656aeff344a3a590c0302d4e313d820f76ca9cd30db9ec2330b00f5e2e939e9a477f4864127bdc365d6d590d17dad8267312ee40648a8f2b55bf92c87249814449519fe10dc5e378376909c21a8b1accdd189abdf8501baafe91a62dd1f4b9bde729aebf6e09070a4df87144f34193fd85eb438d8aa2938f1e109a22365bd3fce1a86df671e567342ef397f61e7f351458cbcc6639791edf245ab3620c8447f69739235ea18e0872eaf5227aa1abe884492a3cc0e77b12982cf2296de7c90a211c933cda6512284e2cf56661ec27b9f4757fbc80a09763b8f0e41f819793d0a7990744bdda8ee970d176e68237a35930e09456f433343a51e801bd55faa96fd560d75ed21d630a72d93c0d76066 ec74ff71f99ff481d9f176013f8511fdb8fa21406489248a2e92 43 005e5ab08cc7c35691ab705c2ddfc76482b40f1794028816591b0645661325029bfd7dc9007a786b08b53f +bf267d559dec13c127dcba0d2962f142b8516421a67de8a9a6e71d3bd70445964e764175f43f3e70a3b26bd8522b92ae86342fbfdac665d01c37e08ad94a56efec612678219b1c02dc45f97c189baf6c9973c79756d56f84cbd1b37f83ecb1171b15093c01727f19dc2f4e530da59b9d5871e7ffaae069201cd18d5d1f4a51e1cceb381b9a25a6d18f39d3ca3d25322605c1dc97201e6924773802ad87576489a9b6c6663f97d2dc24e1d37b03636b2442e19d4bee2b48b12de59a12df0addce75a2ac07bacee84eeb69df1b5e7d45209794cd7c77b1aff576ce8551320f26cae5ea1ecc469bf3fd62a6839a29a38e2566914f5b2dd51f7e9bc2d20ae8f941d5741ce45e29b33f021389f693964753a96b695fa51945625392ad1deb79f77e5ed97ec8f2826e855f4de8e350825429d741cf818fa0ffe34d8c89a458a2938903f81370af0deeb38d293a7bd73ec2eae65323ef51575d47de0b012315ae879efe04c5b38c066e9752d1b262361fd34a8d57e33b5819af500145569bc630f6c60117accb807d37fd2935f7208875bdd5707e6ae5449ecb769f7db94fb1b686eb40e8f5255bb6b0540d671a40e31c6086ccb83fbfe9dfe9279cd188837de755669ba94411277c76e1774076a9fe4c8c48f95fd46b0f9f4d57fdabc6a7cc44344aad2d350e078a3e0c927b75989c9aa389155e8f92869f6b14d019e61749cd189eeb424709322543834173e2134ad60d01210c1b286285 bebff6651767c1cb345bb53942b7f126d78830591953d3bb908d1a4d5928582143bf 54 8fd58157cb9b471d05d3147299dd6c6bebf3b4ed26007d94ccbdc8da8b3b1aaea7898c131de13be0850e89347fc9af64dcfb716e7bd3 +bf2da91f9932e5c0b33e9e8c7d8bbf3fdd3f8840ff924612da56a7814c0f56a1be5350c885ddfc203abd71014cef4f96a75ebdd140439d04807f5c6895c200a9ef3953e37933a9ea577839de0aeb2f861da4ded4210a64c38a61cfba3ff7bedeb142ac8110c3776ccc80f13ef7f38c520d68a279bfddc93693ed3b4e2b944773f3d6b79e0c2fce7c6a2741c22d6226fdb760765488a8ed4bd058dbed8c3410875e011632aa3321697ccbbe3e206106b2222e931cd1ff980cd79f76b442f74baaf11c2a7dca56912e772e94c8633c26fc833519a38d4ae543e4322d3849a3856bb655ac9b4669486814feb22ce3bd852b904b908758bf90639a587191650fea658765b5704817ec3c2ad05af4a86177f83c06b22c0abfbbcecc6060b381165a79199993adeda1f949d1849231eb08fc4d7f4f5cd67572415349cbbcfefda9991145314c23ff830bef677b6d64ab0c920a018f497f15b2bdaff81094bf2f7a66a34b0386efab6dd1dbfa8e9c561bb148f6c6ba52823fee3b336a1bcae2fe6acafe0e80db5f686a901eb7e22547a47cfda4142333de02e783366de45d06a0c522e1e0e69b3e5ab039ad762427f01c8dbc9e1a27b62e24c2b33ae3d3d7274a8fc6c55a6e826f6143283c 876adb584a86572a0abff5813e1bffc892320f72205278dd297ea3096552 58 593d7d604aa64687b27280b75813a4e44af62b4637fdf6cd763cb7227357fb8e03ccf7ffe3b109d6beb7ed3befd3293612652e3db6f0dc7c94c9 +c1e0a9973357062ad326e0283ef60d2eac7164fb1710c78f50ea9dde6cad0a9dee99c310eac07f23d097559b5fab611c4e632327f0225576bfbb91b36e69e9aa5ff50cd3ab6280aa36711b94953b653fc40422dffacb523ad53e6006044ea9a6b4ad59327eb3bb2e46c78b6fc5ad3b686a0379668f07e57ff6f9a6b8eab1ae09e677e5d561cf9809d0d19d6491076ad456ead57c0b5f5bf1ab9fc9744143eab29934e2941296d8471d 0285e7106d58aa6bed2e94d4144feed38f972eda27ed8b49d7fdaef67c665f72924bf251bbf2355f 43 e7a60021c13ab2cab8c7f2e625df33fb295387ff156b119c0a9e25dc2213825e20ef3d1352723ab0e6a152 +b6b7838f11aa5240892aeea4aa402bfa124c600a6deb2b0d8df15cf4427d3af347a5399d7be634dfbdb686984637207c20eb1630ccebdb205b0a0ca6037ef2c9c451ae02836eb5d41e1a55b5160a9d55b64bc481bdf70e57c55df953b5d84a0da1eda9f3aff9e5124f17ec714326734752a43a459dfeebcb98d162392cc7ec8e7bb2780d004362c029d70454fe387af21ba4cfe1868b006f8d87489eb460fe2c5386243898e24b25de7633eeef1c50b9bef978a7ebc6f6aef021c862766c95bc4de75b0f4e1bfeef8822946735576c4210d79945714f76953afa84793e2603ec2b3d20bbfcf7627146f6d346e4f71d934c89df77cbd2ec522a6eddfea034480a8a35d3fae7149f1b423bd944ea80fd5d3f72eadd45ceb09dcbca808348d9e2fdbdcd4a38f2ed05431c705350ffd2f45935a199cb27c54118e6db6fd6f5b5e1aeb8d7ac7bc6a397ad86ecc24bb57bf7ce9b4b546f66dd0021ca0e9485e862c520cb81d198fd8ededdc6d1288c628333b9ad43a4c2f0c8a9abcf6e33e5d004463c8cc0241e947dd7fed618bcabff22da47c8fda92f479381b23548b4d9375f7a849677090ef53684bff715837f6e52af54594a94dfe6d5cbdd6c20473e783321f160842e4edd225d72ce00cb4571446150481984d130364e0141f9e0f55f78f832d9798e5863edf86e285c3ec2518bb28145116a57431210d99c7b74f22c0e55f7000eeca58cd6c823eeca7c30d47c0bc2208218794d497b8dfd0502c309fd194fc7a6b082b0a3fda9322b7da7b9a0bcdc8437c1fa2fd423032d6fe04ea3670538b6953ed21a11a02486dd62d709ad08564d10119ca0c45608d9781945766a2aebdd80620b86ffbf9706aa8471b794f80cdf8f319ad643aeeb8acdc53dace71e7b52c4330ae134ec9e0c9bc5fac5092d37d6d378e56af9670770a7c747105bc3cf6e77e531015ee9714b647222330a51659aac49d269c437d77f3522ded11b34c3be688d91e708efc69589b5367cddc67cebbe9670d3da936d170f711560693380dcb70d7ef5fa33f5d91bad68ded7cf9fee382d75bb0e691435801677e9fe14f39a349b5af56e599eaac38598b170e26a59f5b080977bcc5b43d0ad1838a40e286c19a659adb0d8767be056db1446 540e6e533711695e9e9e01cf3ffdb6ba7a4545e4b6c2b3be742056d6b6ac159dc05fb78311542287cee3c15fd515e441b433e66d80485883 30 683f9aa0c469abdf72e50a86a344bd835b84823ca80db731d2e6f1c3ce16 +1c9380b8d55feeb8078b458c6cdada60bd33439d9ab1d4afb71d7a094af7bd2da7813df84e5ea15683ab6b747a6d51b5428e4cfb40fd1aac22cde153f4666dd0fe66d66457e28d68b06d8489cf44c0c1bf6cac607654d1c7ddd5495b9ad1c0a1d92e687f1b3df7b87ca206821d5d1d097250d63e44b26dcf29dff1cdb20140bda4aa0437db88a1dd57fa24a5090a3e92c499231d6e42d83805774e3738c1ed2ca7a9e351b347d32718fbdc679fd4d30860a8fcc652d0eee78055e808287cd3e742b45c3f98c3507815f64b8dc58e855f202b46965029a0451fdd55640afa2855472d92f2d160a434848bdea7bc5b77544337779d136673c8a98d02150b06a613101945ea3bab04fe03478f5f35533fff2053f94db9f24d13303f23520d270f306bea8451efc1de9dba38969c6dff9e027cbef6cac942e3fc43b2f2d1e57db94773bfe27ea1e35b500a53f5216ee639d9379674b9667729ab72f217d7978799bd2ac70505e1ba8102dced3cbd442605c4355be30d8a6901d6a343dc13586b599c99110a4ad60d817f72e75561bfa630ff97fd9fad27a6ee50f89b795796cabb83ff3407945eeabef1611eb0e631789d808d7cd2bc00cf3e1440e867a4ead39452f494c4d7b24c60035d07c5aa3fa18e6e37321bffa11fb69de8758812ec9544cfa16c49e58d8d0c8b73b3d1bd0e8d3268d80dabfcec46a1258e2a4ca2419dde 5e9a2f959716d14200bf2f2e8aa826f4cae8b5df1efbacbcb8352d1826af0f3269b447ac3e8667e4f496e88a2c5471e91f46cd4fe93d6489a3800ac84e 64 6450b247b2db2e36a03a619ea706ac0dcf1e091b9212e3b4019edb34040c51e74372e1b0e2c666f9f4098c8cfd14fa5f9a86ba24875d61771a74e311964869f6 +394cbe5daa1c8832428b97dc3c4f2f59d358426e71b48f407707466c5e0c7d71fdf4fecbda9d29fcf11dbcce052bcf96a1aede91afe064860aa57710e5a18ddc589c3e476ea4dcc9778df5a6d1588a52012e6e73be8c7b90f060bb2e9cace99b0be5663f717cc2e337c2ecaeaa1609beb7b734deba3533b313f9a89b953a663995a9abdbca4eda54526eb54f128cee44cc02d1c15cc41b46ee2153a15fb40ef4fbd882763d85dad8febfa1d5488f3f6668b482b5a2150294e995c69bd7fb86a7eb8bf5c87e0527b11db21f10ca105332bc6f3f7a151ae721c5d1c2790ee4e73b53be5370afba0e089f711503bf9383eab0d0bc157a29c72cbad1d895f7d5319f4d270fc2696288f4d139a8e62a486575960e40afada06742a60fa8c082336d08b37bbc733ee57329cc0de7c3fb6e46abe0a0039e2a291160a7dc172106e851a23a92e5b202b8cb164c1dd7be0a727ecc33bbb9412598f8ca3e5e6b17eca231689eaee0b34ffe27f70d7b701ea6ef918660a21a17ba05c3fc8057895a76342e2652733eda4e98a046b77ca83cf7bfd87338220d605ed5275f8ba72e4babfd6449465477f4019c81fed54cacd95d1f72c31c458e9d0ad9c9c30a6fc44497af2fc4d1cab364f5c24ec5ad2bb5fc766b7e913c9911a541d6e0536fe1eb99d131864bc16a96af16c362a8c03890ee6c0c9d486e0abdbde74e24b53016a44fc99b2ad70d17fecb19d4c16fbca677c3b177b3b29a36db92e4da23e7e24f6585fc562c6b82897ed7744b7fb64d210dac2b6ec2ebe8f99a1b6865e0dd7752a4b09f56bd595b13a23956ce7e04cef6d1f2e20b28ddf62185fc22d141635ffbe9132e3914ebac82cff784d792fe8428a03fb72363106dea af8aeaa5137f9bb3ad8c0dabcc84f344f0ab839157eceffd5734b3ecd0bc33dd61d3b8dca93e59bf58 25 76abbc6f563861c15cbd00a099d16350f0eb146f5d20715f66 +4f38d3aba6687fa2d009951bc5ca6ab8bdff60f206d90e431b32c899dedcc2ad2a0dc8e04947c28258680074cd20c88e8b95eadf1fa795bb1e7bd6c50c7642c135801552f9f683f54c09209e0e884a09fa a264c7c52253a6a04033ca74399c6a98360cee09d19ddbc741ea5623f870c431860bb0649cab9b8e33a7a5e25d0b291dcbffdd2b 36 c1cf33f49052810b1308732c2bce883001fc62c7b816008e3df2fd6e2cdb956fc717db1f +9d0917969db748cecb19089f8d2284a63a57553e6d0567f669ef20369a4641e417907c33d05764062bb41d8af5203685d0ae77fe2967b17313def03791d15d02a6b18498a1a47cc10a80c9489029663d56d4119b4e2bfe123aa59b8ef8270570c49afded56bb29fe14e8c53a4a333a83cd6c457e1552e8803c70f9c62657efbc4c3ef21361bf134163a6c420be27c2e33f027e393f8a9651eb280d91c4d8cf21a24356a59730fa949694e8a37aa8ec3f39d0200e1952573210e2bab949d3d8c09953b3a2792f03809ee1e23498b2c69f6220fbbe022f40dc5a9b68bcac29b8175e20c056848e02cd288d7c2468fedda26386887592845a954aefaebe4fece00f6ec2fabfd4f95a385f2b414614111d24c22a0e4aa249a91e472284db789c601203ffe1f98fc990f984ec301a602981bdd8353158e71753021bfe3efc2cf8adb7a5b828cd274d1763d0631457a5dca412949cb602acb3bbe13aa014a7ba863ebfdfa2239adbb05deb5c1669cfbc6bf9a7bf409e88ae23d2fb4bd10a5e21482373d2a0fefb822a5834bfff7845851535367d3b23b4401cdef68dd7bfa875c355e49fc54178c72aaa5e3516d1247ffee42203ceb92021ded353f889b88268ecd849620f5da279ca659cc0086f102847a72741810461d01659001020b48885d9e5ddd96141581529307e5ed28e0865ea417090c7a373ebc46a34c77aef20cba235feea6215b4523a32be2ee15174b09aef534ca90398940150ff868db9aee33c127603b7f8cf84cfe0b0da0b062823a78a1d2629fdfc6b5ddfbe3305f1260af48589f59368ec0994cdd441c2011b28e5fcf63516e46ae859fccc283ac423d47cd83cf84786cc329e6d69fdddd71caadedf295ce086b120f8c66c56e91505f128efc28086465a4a341a0a123938e4fbbec8d06b13a4e5817b2c0ae982033ddb0a0ffc2de4c3dd0ff63949a191f05214da7fa4318ea3487ca5dc5516ae4bf7247cdbd2326034f92664d6d38459f3237daad93982f402b139e1aa885ba4b9501bcdbda29ee92537fa1f637568bb08129cd56de333a4f3a93febcee0d77b988765a0900006651e00aa9468a59d18ba9a66f477c3f19fe2f775b3727b6880bbaf2c6720747908f2d030b4dcc28ae795cfd9fd3b88bf72b94d8bc83cbd354d62266b0fde7d552c564e33a9516114165eeafe4281f3a211a653ed0d3d475269b1dbc9740a2233d2d98847f4614a616994693e99c54f7dba896e2eaef325ca9daca5512ac3d2713908f4f6517e7473b6f59c16a36995050539ba704216d7329810e24c1dd076d560e99670c02f355dc0bf0bfba59e3390 89923501695e2d4978ddeda7c1a58ac986f54e493e263d6d8430a2d931afeccfd3711c750fb85535f62cc243dafbb494011b3c2e00e574e10dd6b696ff68b9 32 6c3141ddb56dc865a40f5c41341fcf531009c2cd2fbcf20aed7eb41fb3b48e81 +e33ae1f521b4dfb9c4c082bd00d33edb37120b0959e5adce23a65d194cdfcb690d825b91d37f5c81cb9a63e543d7037f9cfcc25daf1662c8eb05738dc0ed22a071805a124b3afc4f5d762c9437fe03e0d0fb1e54f61629f69054fea2c6f75d736a138f46cbbee47a154a1bc8c6df0b58b2a2f96fd0010a2953a914f8a9fc2fd7f2cf1c2fe0726c138590109029815004901ef032e7bcb90deb1339e34040f1451e98620d3ed13837892791140173abc99d5a24d8eb60d9d55b7a7f339e1c83604a971fdb2eb1ed2c725c60b503dfe1af91719a1c2a9a625688e3b88322358353e890f40cc8415dfc9cb0af761d2c5c4195d0a4776d389d6dcba38d2b34a22b73641f35ef880b91027fe8f171beefbd41f65cbd124f890ba2d05c130c287562154d2a6feecec63a43d32f614c3d5cf313e3df0aeb9170cfce3fa927c987924706f8662ca31e32998b78dd3f63ce8b89ddb891b2cc118d88cd6cccd4962e2b6ff13b4be0a5e1d199a6baa1bb4cf7b3e06d707092930b386482b858994bd4f03fe54ee7e198c56888c737eb870a4783afa9bfa8e00537a25c62186e4536fcc2c040564442ff9bed27fdbc8e94ebf8b2ff33cd515c1d726fbe263a7b06dc9f68707381748534c45da37ff7a98e71d73af0921b94eb1e51983fe4368a4ab667e875d55b7dc60d85daa82e67f9e25676a9a0ffcdfc4e276a11b63201556dca4e5ecaa0debb0a1b0ef7292f9e51817376d7e5218444a45870dd7fc80984adb7daf6bb32cc42f4dcc18158861dd81f9c73a8e4746cd27cffa42d58cacfb6057f9c3fd10c502e596a99304ad033c032291c1e7b24f126917fa73fd6d1f72ef5da26c5e16ad6e4b1b72b2290a6cc58a00e1d84c7488c86de4183af02924804839ce72b06af9cf275ee9ff3d45985dce3bbfdf25890d864c22679b09c5eef4e6452008f517aef737f8940868a3901e5cd966bdbc741cf21b87f3234e6d6949c468b0eda3b566f455c1bb1971996acbcdd2b415888e229229903a7c02029c790b001e1851e1ca95869751852111d8379935e33dd5e6693fe00e54bf1910e5b92e48878e1761100be958c1822999bad109e396ceaafbc332e80b5565cfe75cb2e25695b4f2371317802 6ea28091829c23769124728156d5d7f65c62244c5ae3019d35704d0d7f09c49334a2b9bed9122ac334129e2f32f35282edd910 55 224a07ff090e05e08c1fb2f9b27af87a4a6c1a2e1304295fea6772a4f1cdfa6e45a6cc5f94170f335aa9eea08808d75b17add0c6b60dfb +c6b5782d97501b3b9fab94d6d668a9914ee8473ba0983ab7c3e982fc5be7c51383ea602c0f33 b03fa4c35cba87e1b7c96ddc16040356396c88be43ec62ae08e5e0e09ee97cb6ce9802dfa6b3 54 d8a995a6b8413633009b2456d7a3d5202719edd61e1795dace9f440f50b6053bfb40bb69e9e114d345ed587528e52534f4f37430ce3d +b86522bd35f246651fc3a4423c79e1c8d4d8244b26fa38378102aeaff5f84ebd8691b5e22efd63699f073acff29f07b748b4e951550abef130a8611fe8e038ed26c00f8e2fc2e4ff715ff3dd2a17b314df892a2c62d979c33d1501790ecffb23600ded42fa3aef4ed556f7559a00d4169c00ed41c8da82e82a28ed53e5a7753a141de55272a069bb0a54547845309f27abda98fc7ca694dd439ca8c85705013e50b951f90884b5cc3bfac2f7453a452c3cb44fd17c047c091a25e58d03f7ed3b3a29858aa884eb8020401f7a829446a8e9db330fb2fdc15bbe1bf708c7e59e1e2628213f30388b43f06cdf5279999d54e6d71ad12b3c28fb60d2001c435c50e47583bec1a0ca1427b870db5bf423f28b49c7c12f6711660a7a7fa7bf6e590ec47e77c204ed6fc9d0ab6ed6e83634c89663ade410f0a894b73fd2948e758e2e5c5e8f1fbe59f4d726c0d9a930b7ac81d328f9634f4d134a537c239d7ba4d761ba9d37c65e2144082f494850f9978408b23ef2136c1df31b72a68682d64bfdf894c287b4c1bff32da204753ccb135729527ba5c5b0853da10b440814f057a3bc 70799db7f0203cc3b7d4ebc2039cad4a13ebab8c634820c510974cfbcf6c580891871ff6c310f85a09e3cf0ae17e16b1fab2d4adb0cb3629d2c8f6b164a5 32 378ab69e1ca74ab1345fd3e8b87a746ae33f85a6d3bfbd29531f5bd1a973c769 +8167c99983372b30ff9113dd83ee0c8b7fee45607e0113a20490796dfd1b3c50ad429408794149d6fbd1fc827db4fc8dae952c714a1a7748cacd0a897d4fd1abfdf76e0b37863959e2b5a3cd5456dc7bf289812f2894fe38102d236a6ec7f022e3ca5888f0d58ee678be7b0287edeb3db8efadfbedc44a6928e435d3ea67328621b1b1c229f02ea396eaab2ac3513a656b8c48725ba23334eaaaa4344b4464a59cce079392773ca7f17a84563a1d0576bd4f41435d380528700b455fd74dfab3417a08276a425695e2da53a253722a6181148e8fa7d91c9643e6d2bccae7e58f3c3484130acb0252354e91db61fdf7bd5d87bd4c7736ae375b215cd762a91db9a0dd72b37f4a76a33331091d90741e28a1ae28a7a472d9498c748189fb10f56a69ce0b771cfbfa81f4d4c3cda3f4165cc79f4d6c39b50e1b186a80316a54561ed939d3d3d3f1e551ef6e883b714f3fb56c900682e9319669576d72866fa9f393f22e69c54780319733e82a54b9c6fa1cb4fac5ddc4b7e9bef4bca565ac789577de1464e099f7c2e7447eae3b4788fb41305ed7496bbf64ba1e7452bdcf775d6a568f8f84a500261ddb20cfcdf8709333f871f656871f4f1f7e99ebb9af15be0cc16d42ad2b0210bb4cd8e5c522bf15cfb52d71d4ad5468427f4b5306f6dbd46f372b9efa7cabd3be1ab114d3c6455e01a8a73c0986bd828115b7ba755e7ca645b4dd8b11d393d27b1e2b78692271505bc9750948587a92bd825e03d3f1572760aacdbdd0319acd695b55e02619ad60ae054d154764df07ae0e86c5ce75c7b73d3c15294eadb182327584bb3bf42d1ad044dc992ec351f094697366b1758717091d22184fb1fc384f1e71839d1db69f2db4d376a666fd527221 a2b1ab458a84d03b50f58b8048845590d0c9813ff321f692f1e22582e8a4ebfd9ed20bd6 53 fb0d0ecda82aabff2de9eaceb51fb575a511433827b8ade783f082c72c3381bed9f801574b1e1af95c5a5764f6a2da7c8f10358821 +7d4786ac2e900ee4ab99e47d6fcdf673cbaa5dea15906824368b3a7a335299716444240047fd5c7bb10fac051e14ba3aaa5ccb2168c254e2a1d58bbfb6d809015a41d1d6045148f0205f0bcaa26a8ad37d900c80bce8f976b2b25cb3025677384fe5c697a16435a042d6f3ecf2c7bf2a03d0e438893945fab98fa35d200ab855be31006ca702bcf44197e73f38656d811d14eea7539ce54a73704226648a7071727e6e5e5cf91e07a22724fe701de3858551fb96d771da48a93a5ebe12673df56778036bd6ced0e9c49225fb973822bc1ca5f285d869dbb2112c6868f98448d56efd88aa9a1982e3275dfd14a2c070618701005ae4b134ad9a28deb48bb6ae4f32014af8a37ca2ba637c51fc79775da8a2a0e73ed2efdd307c9b536148537e092f259a4f048a0f3021b39ea1acfa53df90cf244688b643a6787c88fe21cdf33b9653c846a65e9af8fce8fda9fb6aad19d43341b3257c834bb5e4928612df832164bc030af20830f2353b6e760267100e51efbc132e9ff2e8101a33db7d3b9021564e386bb3a122afe0dc309152b0b234011be7133d4210c3c0ec5eef2de289d15a55b7b28dc8bcf2d43c6c6de2bbae6fd1f4ecb608d61d9e4c87cd607e220f78058acc54d1c09a027a10dbbf69242510969a3335f7009b226bae3a35035fbf584e0087290e5eee0e72558de1fa451239419a41fa326c0748f8b5b84e66a2e221d50b11d4c38d4d6bd7d54947b6f5730b26645693c1f1ba31c82f9885f850e6c95b124d88ac496ba7ff6fa402f930af8266bed449bddde8f32c97026d345b3a12476d4fd9b4c317b789ac646b0ec724cc5b632292bde94e18ad290d5ff8bd34b90cdefe05387d1c940061cb56bb8d13fd8060332ed4d0bbd66c06cad7d892816e5f9cbb966d43e17d56449a96b5d7f4194179e0349aedd38f75bcff16dbee36368c76688662ecf7773a60b0cf6f7d1fb531c515a953c27cafbdccf763489f3df62c069732824e24258772b6629ef9d1b9851ff5ea6187edee31a5039642567a0798cfe7b2174fad7def82384ea891324860d35365a277e5874ac48919570877b2866b21c6cf1dc40214c12db99056e25660653091f33307b311928dcfff83ec2dc126479a048912d9c2c15ccdc57e2656e0027c3d4db1f49866bd2ae1a2b25a65d784a0dc801f593c606ec0debc26511687d147ae86113c3c6ab1a8fa 901dc9cd10ee9b7cb46c56ffb7b7ef89997fe31d27be4d2abfa5be0d4e1118814e06166de05ce12b15ea65c798f7b2b415ea1ebe084bc1fd4dbe57 61 27e402e910db4a5118709521ffcc8858759ffc15cdd13f5847a68cb38d4a232cb14123736ba6ea4a1269b7bc2cd18b09efa7ceb9fc6c4199db2af96102 +c4db75e2663ede1eb9003bd3897d458336486cb1ba19bacfbf60958eb6e2c595c6e7f5f24a14704dc1ff9983a8c697c38d4e6b876c5d40878a6c1cf9ca4fa07a17dcbd19abe769ce8212f645b67f9c9804f53d33a6bc7e8043284f87d91e319183e7d4c6dd23c7d4414ca65dc969f630d5cb0486650267e830af0fc36d1ea95b8a4b4d1d96f8f06bfdd40cf9f2c989c2783484db916f66c5e74346df660171daf5c17dcd7e648bbd9e0295accc7e76afca272fffd14564fc60b12124d4b5de0df8b2255785f46e5e3ed038f817732638d6588edb9397530e32233a175db5ab3901a0533f4cf8ba44251cf16ab6fe914bc36424d5e164d47626f66dd28753ffc2944af862e0435d26e891d90e532e395c8f32e568e517b8cea667b7e494f3785e087e55997d1c2bdd9dcf6e8458027bcc0cab1b6a10f6a75864675d030629f2183a419f0cf76117fc3f90a23d28ea90d96f1dd838bcfaa75761fd8defa4024962ce4f7f3ae52fca204443c1bbecdcef66720fa41f9b0cdd3a53641d3744139441715bd9379ede6f1c5349c7b2c1fc7a03a45a9cef2e7f63573565978d1a784cefeedc1a23e4ddae179879fd1a5f13138176f4e699fe5f375e9122431120a24058d8635db99fe1b3046960429190ad8b2a9cad0e3b9a200f595ee2df26284b69ef8cf6dab25dc17bd0e42e096845d6978bcee7d5710b4d52563a2ca96036e9e26536301227c61b6327b6e25c8339da56f9b5f8ef41173cf9d63c382260e9b2e733e9d74d33e44d791e089a79aa4605127407d279bf78c116518848ccaf60e0e93b0960f4d6c565f5ae6d7134c215a814bb5136342e284779682ba755c42e0ab7503a8baf9181b707d7ff6b47a07687b30891aa8055dc6afa9a7f6ba065244f49121b98421ea635cc33657d63c0990e32bdd9cf62cfa0aeed33e2b348b36607935b472e3fb773fa92abc2d4d6d00dd3786e69e8117d6bb9ff356b8d0cba217290f4fa84dd7e08dd09c0bde5d1f29b7e4d6e7cb6c28825c26470bc0cfac601873c4d934a42b83b5a6a227c1d7d949796e6eea398654602804d9c732603c585f30ca60b42a3dd4a0aa94691d7f077fabb0bac240a01671d5542acb38499d975b1175218691a5a2a2aa4d5448f015167feafac403f6d757a6b507504371194c43700299e534ab857f44b7a091929d150fcd339d37bf6d5f90110d1cc5ee0618d3cbb15743dbe77f84b74e6bd74748e646c15df53940ae8db77078ea9bc7a7d0bd5e09461e2a3aee4b79e84c52840fbb1ae863215c703a2175e55206bdaf7a67cfa3b53a8896a15ee086e9ebfcbf69f8c03cd4b0326028abb67b83180e0a97ce1 9b1f474eed1208c60b361b9fa1ae748ac52e9ca1ffd08ef21a3b5f540cd780cd3ee03b 27 8d1c1f830f192a4f7033e1668431117e49c8108c68b6002222df2b +85103f18d0bf4906c4b5978700b3f36d0ef4cac23688a805b6c32be6b355aec6c037f1bb1771f353f51b09e19b51970fe43141424a63b9c4f7569cfa23282b2c92940038eefcf4774fae5dc8d1ed5436a0a4c79dda1542ce735f613e29c950a6bf2b1f5a2054a6e2f56c87e4a31acb722caf7e3ede956eb662c9f6ffff5152b87c83ea02ae5448a9ec5deb17b88db96f5622612b0987e880b360d072f3c9323fb5ef8873976f75b688bc94117a40914347e38f1b7b6825c3bd1882fc9d3b2ceec20d7c8a492a0333b4425c393af671b5f7e1a1191b04b18f4de98ff72ed7a1e68125aa67b640015ef3f32f18d213ce70c5bc53837b5e5b786981b9e5999a95f3ff9f8d9cf202ff7e7095384c1687c3bc5d48cd7f6e61832e7740acbe383c74bb584edf6424ddd4f1a7242196e12d0c3902cbc567e9e4ed838ae8f051c0ce210a745e6331abb44c2835db04616145b9772e130eb087e572ed8e7becd40ff8f5e5f5f9a1ec9b190ab89e571340500cd44c73c7426d79567280de2c3b27483cf6ee60af752f83c488c2fc726016c8c1b8bed3ed027e5ee80f8162d36efed6154116d14fa584a727694ca0e1ac38d257dd64ee58c4b94b5526bd913bb9393b3d4aa80ddbb7d84b43255d52c30a8c173a408b05bb6fce972b63617b714e36cd2606f05e739bb49334fa3c4b4116b7664762dbebc34862fdb02483ec84f74e841169cb9f61baefaa10cb6ddcc92dd5cfecae877b25b65a53fcfc3a220ab69df0b1edbd219e15ceb0696a9d3310d17afe9d0da6c86020782c1fc1cb4a138b677ba3bde1993888e4afcde66d50ab8f987126a258d8d3f92b695984aaea7e894cede163c573fc6578d652c87877921f1679ff01c496a20afd350f1f54eed33f537d14457f0ced4ab1fa2a109f96aea2af6d51de819a65655ecb321cb94cd9166f4af991656db923a8745469aad063d118114ddeee5deb54e4ea6990127b7b251c1319c5bc127f7a3796f213d05449ba8c4b816b130b3b5d7d11f1c2c5e32468d3e5821cd8525794730fd2bf5c30055ce574db9a5b76296a601032 514fea4c995761dca81422fa13944623384f8f6d7b6b1d36a133e23f3359fc11b6f58ebc16ea2cabf67d4152ee7618ff519ed3169a 58 2ba25a645f49e95709e1061220d82623427e4af51210571ed587a40f7f28f4f423a6d835a394e20e29ee4b7c37090768e02eff6915bc9b5ebb18 +26bd569b73f76be5a98406af16d2a4329f61028331bc43b5a8a53dd76611e0a40938209fc7f50a5aa9ce177e8c4dfea55252ece24867bb2efc667e8df85f7876be0bdc0943b6b9d3e21f661299ddcbc48c023b3af359d8b816e7a42048b80a01939d6eaff11251024e5dc7ed021c86e5729d82592fb36f7a6362539129da889ad40740a8977d3e4539781263bd4ee9035eaad9bb129c9e312d61592ab873b0b595c0e3d6292186cee79157258020738bfdaa2809662a4ed5e77cd740efa682d680bf1ff8004cfda320753095f00a0c03549069896e32d8b9aad6a3120d11b09d65d8aab8ec00e935fdf0cf281d9e09c4b45396ca91715234ceef52cd3ac4395b798de414a058b63aae67f85a057286adf4ee68a284486efa1038440526685ae8aa4328c62ef8cc42bf8da586eff040a5ad5da7c0095b59c51b4d0f056bfc8c19389e2b6af2f787ab66b610023b59a62e27f9ef5c205f267e91a4b3df4dfc27dd814f140cd859331fff9e666416db5bb3642dba8291b9b85588c186c40667ec4d480dbe4f39caa5835bb5a0d4bd567ba0d2bc76da635152a2cbe170c362afb7c524811d6329fe80515b3037f2a0ce08d9e7ad6d4352f6717cffe74ad90e637bf3e43685a7d2ceee328cf6a376683bc0d25c156714aede5571ce35a1f1bf66165d0773526704c41c5db190e900e3be405f1f733fb4503d40880fb8f486730c0ea99fa12b812b6a5e77f7590c1970c5 410899917816bdcf669bb7dc179d71caf2d48a3eb52e873bb7ad5a3ca0aeabed4e217928cb32324e 44 d47bbd668a015f96921f0d1d447942952a3ca8b7140558e7f2559769600babf05d085112fc11a4673e65114b +d0545748c870e728fccc1afad7d79be56c3cf2b0e146a4f4306de57069497b15e786b52b717279705956dac298bf5b414bd868a7220fcf83730ecd6c0c73a6a34f6f6eacb19318d710a366de85f66549e81df6194d76dfd732a510af981c02fe74b48a9d4f3cba8a9223f32e5ec43eec0f965152d1d296d9fcf21157ebb9403be0f59f16f6525d0787cf2c1fd9009e3d1b4c520204af289cb109ac453836324e598e05cf7c21e1d30bd1267f5804714537b26306a650ee922ae646dbca70805dae6a5f3b874ad12d131f680a4d13eef4b194dc3ae3cd71068020a49865d2a8b71675b50db980a32871f2a3a4eb9a85fc1506adfe4d64e404ec4601bff303c05f7655f418fed598a1b3afb89140320d44ab73ef074481f1f1b43701972c5d7f7757634f5350b65be1ff7eaba99f2d808b54f407868fe508e7edf39dd2daaeb1cc71a77f79cb693775d103280d22c2ce334663060ed2e3b611f5e74c1c6bb39ea101ffb94507a19e2df3ef848d8c40bd8cc8d788279cab381ae5192bfe1b2041eea9c646611abd1442fb55ac1cca29ed083fc2a4aded94fbaaaa3f1e45c7bf3dbc460dd23b661ca26de35275766e1069b3171f605055b1dd11f2e4b86816fe42ee8f0747a8321057763997c1a82e3541ab383eacda39d4b80b7aa68445c15b9f730aec2ca18f520605e20ba07ee8050d31c56c20c9ec980f7eb56890f97d68b21f7799a4d546914c92c182264f50f3f935184010349c63a0e328e509e66f15fe54bfdfc1da60ed2441e768cc46b83d9610fd0cd747d33eca03d2f10b479082090cd8ae39de412821a3ab3e0138cf609078bc1d7475f965cd5532256aaf3a0fead1a6607af7c6b444c43a7b1506e90047e925b7e07d23abf6820d30d8fe7e9057fc623098ee0049267e437a1a220b56a8afa64b4007865c1223e63e9bebb69020fbf817bd87395122adeec3f2218f9a4fde3b84a665735e2df3b461619b9931d6b10b823cb9acb38b556cb12147d3ca09eb1c50be1e954c3f4778f1a0e3b1318c2b6e96c6f331f27ef8a1781144e94205ee4385e412ffc4c0f7a6322233a2113c188fff270d8aeb7098eb2372d22ea6c400a16a61311a63affd36eebbac2c30812dd6919d8591d13f8207f7b89da55bc27c48a4a1ab14002dda384ed55c96bc23a9875e76b8008311063e459ec564e03ae9a52725c4632929a8a72697ae7a9a710f1c5f41745d85843329c48dc66e467af8bb9ada421c16f5278960b49c847a7b76f36c786df3f1a9b8395d7d75f037c479c299ba76b98e54b52c18c8fca3a4d3bdd3f63b81455045ce05ea53b56f31913f4bf20a24590200f50158a0de264b50085af4482887b8de4d3d4e9ef753b03a4b f318fa72be78626d8577c5d7c8f1d5da161dd8a2b6915209598d8e94 36 468c88c13d84efc5aa7143e39e84f4e400a526aa9acc5daa0dbc43bd7d5784ac4ee2d71f +e42ac8428d1ddf026c4eee4aa55463b78f1e28a795a3fb3f8c99029d5448cdc0ea8ae8a0112d2ddb8887b6200826d0b7e9d9c73bb581fefbf85f344bc830462507c9bee7e255024c6a4f88a518b310ab7c287a992ff9353085ae6ed95bf35bf777941975529155f31f4a13a518d41db0a8c58aff55ec21d6856da122f4e942be4f4a27edb68af324eece5c522bd732eedb12f7a35fc8de91660c7c2d1ee7a79faaf93e19cd9510a396cfd453b5a66672446464a73eb01bc964695cb30807eb108c8cbd3bb61f32f4198dabb928428632ff024dfa98cc19607d78e0fb02cb81305e07ac33de83e2e125214a535663494768230bd1307ea89b092cb137f4374054a280619e3efb21d45b6051ef49e13e26714fcd0711d33aa3e3eb21215eab80335473d6c0533cad7d9f3aedd1b4b83977afdb6743232e7cb5c754530eee299cdda0d487db9dbb5ab6cc0a9281545a63d85a9aeabd4a39c2e83fdd6e6a089b17402c9fe930d40c704a2e8866bc199deca80ed5e443b334d752c77cadd6bb3112eeca33b5e34c7f68081583b67817b1770a41ec17ea9f1ad44548de86cd7527aa1c7b19c8fc2d71d6ad137d2d34b72c1a97413891606ec44b9197485b25e43a0b8c526d0af66f428ec0d369e5390c03492cebf83265e3665d4b66d48b4fca8fc432d383d586c21ff36a0365adeac2048d7fdc9abfa505b293fd466ddbf8da09cae91d269f47fe368bdf20c35812f19b26c9d45923189350647bda5c083000dce3b3a817 2a36f186fe7ff39234bf32bce635f3de1c0eb25489f91e7828b534b6fa2987330c8ec3301465dcb723718b1afc8fdca205eb 39 392e0a2ea1effac6e4556584d468cef564decb0d12ee3a364cab9da6680ca517a3d72902692fca +1b881c4c2dddcf42c41313fc127eb17b86789cc912357d91c90cf72ed9d1336580909d6bd28c5a31ff1ac81a14ab4a647ae924bbab045ca25315cc86a2903d3828b738f9d7fcdc0a93fc8102ad36dcfe82fe468aa8f543cddff9a3b2c27c4327a63f153d5657e9c650b0410a1927467756e4481d343bc2daa80e43 26761e1e7ddc8bce508fbea2a5a0f3e197be079e27f41e67b6398e13e02fbae90b1c2b8edc3c979a5bf6d3 44 d6ec79bf7f2226c2e5da868e69f3b64018a41d47eb681307da3f0bb6b632fb202ae834981465601518decf24 +287363e561f701662456e97d0e38b853dee982f29af86c5c35854d632c2ad703a1666b410aa6634f7115b9aa8bb0b136dc362ae71e0c94cc5b2dca48aedca4464ed2dcbca48477fc5e13f8ab651f938946a209adc9d0bf71f81db30a525c8e3133b68338f18b7d9d8f81eb6c52643a55c8ea883f15ffea62cd3d17528f6d06fcd5e79e4722c95c2e9c2dcf04269970b9c3c32b084804f7dfcf305fc4be50ff68c2ac429cc2077da479ce686f33ff693b8c031917716ffbdf752d72f2e5288c7bbc35b22ef54cdef10c89eedac1f3c700a50358fe56c98a1e16508ea90b557a045eb30ad9e59703c3d182a1c483d0149e032ca23fad2587effe26f5c23781478221cf1fbef6c6e126d9443a7ab899b86e48ed49ea8e4f7664b7dff8c9ef3506376612a80cbf85e115c07c5a6d92abaf1014f5813a1df7da636cb83a547b9eb67d7112c48436902cdccdb59672ae61 87eeb50209010d29e9e4a8c6a52c510733b63491e7ba36c4d25fc621843312ffd31c71feac383a1c4b3581 25 3a0bc45eb55e364c4214212edc26b4aca5939865a9a3c1fec1 +6f3a3c93fd120a71b592a698f4943a513b52e3086f6cdf0986b0d801f85acd57c696694425be1361b5ac138dee889180d955fd965b369c01cc53d7bebca6a963e9de60ea56f6f4b8604a46ac0e236dcaa905d046e01e59108ac4601aef6777a485800cd34989f3f097709e14ef81fce72e95377831e4cb551babeb73b707bea416db5941ca526fb7677ed52ae92f404bc005a084362aaf5a2e6465fe619e703ffa3e533f1fc45623d748e083a9e8cc541146fdc1bd4f1087169e557befe7bf210604b16394db82e330f4ec4e2b03c0671a2b8ac5ebb3179dcf8dd900a5cc983ec3b7211452002027274a0f7a109753e28fc3f0fbaec422d4b2d93dc93d91db4c410eb96bf4252910a47b3ec2632e169c1e7f9a110781c8e854352d0f88bf1bd1ba43bf77bb8b688dd33c486d256d29965f2d4e37b2f540dc5b63e2430e24c2213c961fd511a6677c915e92aa0fbbfd57ce82037d36fb3eba388ca7d67b2b5102f1eb3df2f581708b9e52acff2a13549130a176e353 8c1a3bb0390c3e39fa632e18b812f0767caafb4ccdf9fef355 64 21c21d805949d798ca3231ca8528e9c3090fe198805fa8341feba842c966c0cb786c47cc8a4e497d0742c0351249d4d21c1cec78d606f1f6fefe9f1efe67f9f2 +ea99c227d9cc8b47278d2d712adcb88043c5f5f12a36cf45d6de01a74fbfd97cc10174133e170dad22007002f2cdc997e21764c6eb2ef6028d60cf56214e41e541980fdcb61a0d8fff6e6ac0cecbe98c996454784edea045cc552ffbcd2a69a5b4265155ffe1548655ae0da10067f36483299840e6ac82a3e017fde842f55322a79dffb67a05e6e7b2c6281bdb0cf96424df9bcaa9d3f7f83fc4532bc46773d2b1ea0a824588832d8597b6f17e7263b2943bb65b62dabb6ae75dc0cce4237e6ce5cb9519b8b9489132d248e102007db3441e9061b3b19d0b3af5b823ca986c3bdedb38840620abcfb525776a9f6b39b1310fc51dfd27ede85373d3f6e353c786ab6083272e19d8ccd353f45447b158505e26f018473cc808f798ace2f38aea7a5bd8e67ffbfb22b0300917076e4ab7d574379b5e18037a1f4258550eb5d807a17a41fdf596f705f3d502c2348b630870d51bc4e7a8c080cb112f324b3a7fc43eb6854b951e444361bacded0967f483d5b07b952d1571c584a6a3db1817501216056efc45093e296a1af92c730e131c4bc01cb32fc98e0074b2be79ef4adfe99eb8f2baa274c7a6662be0a71c65030e8f77375e7094bbacd8f3c05234044061869a2d5048d0796d53c3a8e550b4c41792b63528eb7cb4d90dae604468429dcbc3d9de07d80c7a59237ec744af57904a5d8dc824fa2a0afccfdf60f4c31ddc2a77b449b0ed18b2d87625a52213a1c362574f036b8633ae86ee39cc7465bb059a40c536e825814747f712778af7d6dc1e2f429beedfb0a1c38445206bbd21186d50a0ac457164a0d8750bbdccbb74ba4902c2365b27b8dcb8191298af549410e7119cc5f93e50c5faeecffe4342e13cbf990df9f0f75d32144d4b79d2237c42b83a44d40d59cb15ab75a0cf17c73c1ebeee5ac3668f30a25e1c860913021a95c281f52bef7616a817a3e2096a705665e0449ecaf0dcb71d40dce6f925a1df3aeff6e203768ca26da575b6f6899d89186ae31f263f20f6b50f02fef83543b9aad9fca6917fd136412e325180c3561bd73bee476fe55b4a6b034581d6d67955c4719bf64466371b54bc2a88f217176c3e706a8c6a2e2024a2390f693f5e01076101324cd5b32c06121a0a96a636d59c55281b7401403e8e984e7f58bc368fa17d1b9487605fc490af75a58ce56cc6c1a30b04093d06d62924e927e59840011e040c 3fc3e409c428411bf5a270dbe776cf7339acf8dcaf0904f169bcb8d5e3923e63bf739b162c9189 35 b6c4ee4e8658e39bfa715ada2f5b0ef755e99c89e258f7f059f8bb4e504f6cf992a8eb +61b2fd377d22e9eeed9be6a51ae2aa75164ae510b8d0619325d655973f5728a5580e48a47bf4aca8be8baa38f0e2598cac45b6a98e4e6433f449e06dc2c32285746726c63bbf3b69649f2cf66c470d30e8cbb20d3ea632bdf274a407d0b045214f1632f267d6b71c7d576b1b18c4b4d98b08113674d9d5d0229ef9ebc18a25935e75967e006d129985ebdbf0b03856a5785aa045a22a5821badb02ca16fff4974e65ec04b48b759ecd5925236c28af490a309aaec351c804af2299871dd81878276083671a45f0b78abb21f2769de1e1e5487d22ab80986d360e9d5e6d6d50d5d955475e55e9c0436ef39b4bed07da3e195b0b28e9e5eddfae2eca9b21c9ba2fc9976a1fb0a3530e04d8057076ffc80953a0ebbcdbf9e6bed55f714a85e64b4c28e9fc4440a3cc5682c7a3a1ee9f0040a4cdfd87d8d7de484a8aa565f09c6eaa 8a22e6105f6590b3739a611145d35e005f4d492fdaca6f8bde0291957d93264057e6acc80551d560123e66 31 44f8374f86ca3fbe551de74613d778c5040ad83015b200d6c1cdbddd646446 +79e5367ae68811950806bb03affbebb8e433afca58b3dadcf5ca63cee7e49343d3489c51cf0689f20a773426fcc9eae60f752ef3cffab181d29df963791675e290af3a4a8da181a61475ab3f7078bfd420454e2e24e56947867db9e430f90553548332795153a570a8 9c140e4c14b7fa7392ab83cb25b1868c9a50630f1748cead9ce75663 52 6a6296b2294556aef163e39c98200366e3739858c107cd9d1f12dc941c41766fd738b5e715bf2ada84f33ab429be4ad8a52d1776 +0be66102f687117909227011cf688dcfc1479703e9de7a8a5accd94a77fb08362a0e0cb9867b5e012c1db4f825195f9d2a080a6aae208b3c8440e1b5c816ab58fc5b81e35939db78dc11808c7b4fa9ab772630d6b6fe6922717e4c885fc57540139f9288ad3cd60980c3fe16d1535d81d79e46e16cc1674b9cf195c5a3b41ddd85ba7d1af8e6f35b3f79eea148a5f0b8228b7e9b6de63b8ce7734559089bb76dbe4fab1654a8 75f1f91695e51772bd3cadaf3dd3c2172605ea11bab632e2d83b5691ce77909aebc3b43a44c8659350ae66a3cd2646 51 d5e179c03ef75449698e00ece56672c5d44cffde87f85edaa68293d7e77c50a347d301ac4186b5d6dd4ebb9802e8114b215dc0 +1e2ca725d0174f03608d880fa3e5071a83 dbcd6ce9067a5b29d9d0af09382c8839bfc8aac7a43449de6171e8da6ddcc6a542 44 17fccbcb3b8c67d2449036600a4441c16d71f48c621bff0908a77e22804216f6459222a4058e81b9793bd7a2 +bfee74b51c521bc67bc5e3206e24ae0ffe687faf2935aea6efa68a045b92ab6e0c028eeea1647b1bbff35a1710e3530e1fcbcc759f877d3f1f46c907cea5ca1de5b5347bed174369591c46d5dff0f8f25f2c595146549428c0e2ec822f0c4d30ae6d7b1720c4ce9154568cf83a668cb54a2849db14d3be27 8e221f676090d840936bd62bf26875ed9e5c4847135d875e77cd 63 e46555c0ba76dd07e83ac80fa1bb1ee3519375b5b7e696c2fcc680c3634ea7b9cf1e375a81dc1b9feba35d0d74fae0c4160d9ea76f97180474bd65e1be3c8e +b9b5e6f941182efccf3fac88513ea6e5f33dc077f2f444d538875ec7fbbf6becc4be7fac2b752413a8c2552af60d6fc8ae96875bb69e2931fec4353d11ca48987fd391fa90a6415077a3280c803e52d84e0bf2dde19b7224f80b40a2b55ccdf5491a9a9b04536ef9250a81ed749339a685ff9d72e1b6a0ded49d6f864c288e44b72ed10622eb6020c2f6a01cb9429ed1867233ae13fb1ce1b437ac8f5556e996c6b0d9bb125e83924bb0e8bcd382e4c153c4b123af6aa3829a7d53aa5cf2844d1a675a4706eb9c5a2dae1e3f1f6ddfe196c8d5339b2b3dd5e240966dea3ba2e1b84cea38e598d9966dd7a0186b58fbd208e666ae5b6c6408be71b7e1b3875e48befc73d1c06004c1312d4d11590cf902a75bebefcce66969d717045f62bbf01df52241c3e9c6c2bf4184b956623c383179a907bc09cdf5c3b2e315cf6e724bad7997e3162d8badec4cbc89690a0cadf9d0ee307c7d82d74e551de03fbe37c7838d9cc26424cd61b934c60993683bcb8f0d02f151c46012496feee27063d9828429e9dec90683e7b5199cb4f3d288367a3b2aaef555338e5afcd6a9a7ccec79d1cb4dfe930dd5d734be34 a780bf01f7016a0f0e955207458ba17a9d8ed01533c2bfa2931571f28cdd969d 28 e0cba2e490e79b70ea7010a79bdc5ba0b830a728febbb7ec2c707d8c +10333b502936d19ac8d3457f3a933eb30abbe3e391e2fd3e3acf59852b6396e3bae72958ed584ad238c4cfa57910e1d8419894663364efbde537c12fe533eab11a8c2294b67a292fadabf42802ec29a9650ff42e8510d831f2ac53f305b3c4bc449e42121c67ad64c1ba0fc3fca6a32c35e5beb411bc9587cd41d09a2d61c37c37f2fe1c750d5581d75b4579602f8f32227fd10c038cc4b1e2842c4c635b4f82d1637df5b19cee772576ef3c16a0631c61a96ee0b5d19e8fc93c1b1ba9c823cd7a55500962e78a35fa1e349094f97fb9d659ff0c48205bd6a93d0f88984f19268772a89ed82a0d7efd6b14acfa5a42c60cd6a4ac8439b007c59d7583f71cfd6ca9c56c253899 9a7341440e7a8208545122b40087369dfd9ba9af09 34 dae56a6b00ea5ad5da4fd90c87aedc58702ae597ccd9bbcaecce899dfbbfea03cb7d +be97299d56d72513d2728ac78dfd176f35eea2356059eccba39a80b6b79b090b4f446233115199a4a0601b9e0375c4d6830f039ee61157e1e5cfda738d91ca4ea092e1ae6e990e1bfe159c8b082552ab0625a7dc23bf6f65fdbce41021ab920dce7b4bad0c13c00f2264b1548b66ab70ae127c7575c0a29e0ca29b231f85d1a7e42f4ce837d64e6ea9ef474eb42caaf45374f6bd9af58b083dbfc7516c845aee7de53d1470641ee3024e79e15dbb02d811c106540cb02b1f75fbcc6d23d066a7a9f42dab9aeb2b1555c7f60ecae74da71c5f6c365aa22b52a5791e12e12e638344bb81ccf6059c24d39eb69b1b14f42a26358c4ceb5183ab3d0e163fa1bae5a32ba8c832958574b03c08cc7062abba5eb2f51a8af7800f0746d69b3e26d291a888d24a4e5627679d7dc1740bba9ffbbf31e84e0bbccec53910511ec645284d3987e5597108415ee19479402b7309ec8aa7 b2f864c30a02d7a269bf2c64a2fa88286be13a1dd4d14e217e3c05bd45305c 52 c301f8e1bc57e87c1b58d11cf3b3fa738b577700fec27044be62bad06aea0ee233a4f379a32e545a5042c315d84faa9e4b08dc2c +4f9299eaf22efe1e649bb1d2a6bb798ac3b2b3563cb1a8bdd055374600a60d038f791cc80437e35a1fbde56a89561887319d3677d410c4f632e799a8d6a425e41a407d84b6071ba49078ca9ba77bafb6eb5b2a73fccb45b0b7eeacbd46cff5856888868541738ce25aec2834e453e71205e307f11e6e7ff6d0674f53adf9669c134073531007f97e15dbeffa8f4fc6e3cbeb7d6c8b028529bb64cf94c3eab78807c5b3a60e13cdd437db464b838f67e62bd422bc879ae5bb283b94d6a1ea3ecb7734505da3aa9af6e870ccd3db0293162d42aa37618766c0766c9478f8474fc2912655ac8ab2356fc85994025f05f6baa3e3f63ab842341d7c1ad88edaf34c1cc17fa4e6e8a089f8d0b295ececf4ab9c832f247794c04f1203f9b871cad76b85e1f68f22aa261038d2a3554bf6f4863074c344c7fee67bfe7141a4c400e90a18d2d54766e148aba689ac3119f4282260ed0247009a3c16907f3a9ee71b20625db94a1771ad955bb069925db696119d9a762c5c73435c33c61db89e7e10e2f83e39117ae04b522def26523c33face23cd89355ed0d5207825f1e1db2e6c8d05efa878b960bbd008a840a11471e5de411b0c7a91f56250b52f855f604832008f4e533390fb2bb586df0b6e8167d9e7d9f6d82456da4200671c71de32d64bd57360be2f562f20ad8807cd8cc8690e357c28a6240b69bf60126eebf8c009e131f0b75b45ae3630b9c4c70734223166a985f17e18c40e158e261a34c25b9d7afe403e4922973826a2b63fc1a76bf73e2f3de8d84e62366f9421feab738bbbbe9fdcc950d098d1a27d24033b00f34515aca1b0092db2ce3e0ee3b2a8486a855b9283c8350ad43dd4b248fccd1c3c607802d1d373f7c9e2112a8ba6471ca5e25c2727accae158c97c3ef9e55e1ce22e5ba04f4610a379f7de9ddf11c1775979e7c85b9d1455705fb3a3cb5b7efa05998652530a6afa3dc890e97ee9c0a9af28f473f3df74ec2de406dbc9c49fab341c84f7c2149c9735624911be7635e18adae5ce911676d26c4064ce9f5f39016c766e369b993c56aab8a4d4a6d5d5a231f79cc04db265f9124f24c7c5e8039a58ced302d0f930f519dbccb33e0113269aecd3a73f4c28ca3441b0e93e03c3c704bd9c84d90a85dabde3069199ef89431048ea3f577900137fd47936ee06ce96fdf597a362d1fb0a7c041ce00e7d43c6f26ad0e7b5e23a52082611825dbfcd88022ba197cd0318c9aacc29d339a0289e1a365a1325995d28e91d2ef35a44cba4c2c7421c2358e79021f9d8f16456c5fbd3b4056f9feef895a2b35f49836ec57b73e81209b3e4f08e5ff78b7d11e220d815ea4cd5ca8ea9918289172d7ee99892f5157ea39b6c51c456cf13d1859362ced6874d17129350e19d0cd476d27be9 927a239b3a774e855b65a1affa336bb0a7c2842d77beea1998eb8cbdd2 42 7707be326e10de6f4bee05610de24ad928b7900d2981120cd071d826e2943954b95e4b05f6218678963e +cf4c8208fc017582be93c66de5ccee26227bc5f9b76ca2aecb1ddc63d7a2c3ee23c0d050a1ad6b6ccb6dfa47a49a08306882ec2bdbf000dd357d6f8783588b87d2dbf71c20ef6bfb63982635db222ad0d5873b6bc6b694b84f8b2428ff507dfc0d47297e709c3e4042f7019cd552395cc2fc747fa770e65b36687454b23ad7e9d6645c8f35e56684e931a5f7fdc2999d05bafc98dbf6324f47f3c0042a9fae3973a5d7e2b85ba17d0d78fbff23a1acfeb6d9279fa9af451563b78ea1e962dcfc55f9cf579247ff2c37cb1185006cf022f81a64170551e7d797ffb605c1f0b664d0721d1102cc55dc1544e14921d5a4f7db62b2eead70581fcd621ea27ae74ee3ce835cb21260940aa11ccb042ffd5504b871aede04b3b5bd48a2a7fb1d53904c7938ece72ca41eae4a4b3b82b1788367c5cc42a7b72ffd5b85e81b213bae700691ff5a4052215a43e1c40ab24dcb91b5c127acb0a9d578500fdd44b66cf50f074c88fb078eaab39f03e29cffbe5a6e0e0c59e52039119c3c30b0c0c23c2bd718a8884448ddd4dcbc8fa7b01536daa2bd6d72e82520f0594b15d229bdbcfab6856ed49eb3cf9cbcf4cb85bcd7a5b791013a184b0cf4cb77 e5f4c56065d06c536577c670cfd2e6c31f26fa6d63c7689121b6b23928b101 49 33352fc192a5fba2cd5438196bec0cb8375dfa3fab868a6f14b9ea6dfe6f35bf14d8a6da489e1c4e16db8195bda0c2f6cf +75ae041c217326ac8afbe84dbeedb2ecbb580bbc4b50582a4519447fc2661996746998d68080182a000ee87e34e8aae3774cfad702be526136d596c663d6e3e6163f045d65a5835ce03843ceed8e11a61a3aa96355a707affe2d219a656de586ea3628e4d91f210cc5039487c670d3548e01f21369eedc9fb60b3e9d253afc737fc7c35639d7817be9edd67df1e0cb9222e296c069a4bac108079f012110e8abb2ff3c8c89ba4db541b534272351343191d71f4769bab5140bc4b29041f110f138bada7deb09b2ba6bc9cfa462e33cbec33ccb10d663fc4995e6666c8aa58ed03bb00a4f0a795009ee8c6b14f5897f9dc3a7bafeebe4eb55648c382b6c5fab6a37d418d1f59b4ce1cba497d66a2193acb6cbb67200eb45aa392c9d65d058d0caf555a4c34aa6192f06333717b98327245d60164e02233af886f030b0ac7a26bed2e7eb64e17e062ed49a7fef790158a26e56e547693b2297a2e4f9c18ecbeeb8bc1f51d037ad5c5e63bb28b4f85a2c9770423ea2f866835db3d3da1166e73cc760f133a2d9b0dbe61e2ac52612186b99cf55a33cf0a9da84377576a3ec51fe7bab94fa7da7a75780ec01da24179f4c57c5dcc54f07ba86770bff54cf673e7998f9a9be4deae429631d92d642614b988dad7165a4effbe8d0df9866680ec52f52ba458325920e636338f9740fc4e53b236fa7be869d3f1faf23eba16094fe8946330832597d1c490666566705b35b329daa403682f38f02e8bf13246f341a415085ad3cf4637caeacd5792db8649f0d39ec4a2049a4888de248023030dfea503c857fe23ff1867c1b356afd8dfb3d2126a1874c6826137a7ea7c78bc9e4b977001da45aad37dc85af759933352dc480df03038eb8a2b2f14d72f8a8b866d5aace69 764caef77bdd114966a4bfb67dbec6a4458d36bf510f2ed362946b7e3ca226 30 b664c9a344909c1ec8af01461837d51f682eb0e13a756aeadecff7e1b9f7 +2369872762ddb37d020cc791bfa6ecc6604f446b44227bbdf99700ec1e6f81dacf98479bc6969304f989daf8b1701602cfed924ec10ffff9041d81ea04cbe33f57fd14c7fbad290aa391b7aa9e948dc1a452bd0a001351133e722f37ed928c4b21a9d8b30a1fb52707be2988b75d2671e34876fadba4226e480325e9008ef8973689c1695d12b56927e0e205ad05b69cae395fdef7d5d1f5c81036049aa01713ff516c968851cd3f7bd39b0dfedf652ada3fa0674e883cb8d2135fb0eb93a004e95f19f49f5645e86fc9b86749a561e8c5031c4642bd705b7d80213ba977c99932fbe5fc954d9cb87942fd9703c296a742505a5117404a968a6aa6394ed74476b23803deed00151b200f3cbceb010e6908f1a4a7d06a6bb2cfe412da9df935c6ac69d0a374196a3a90789a0a91fd66a94b986e995ecac27f7a2705011180e171f38b7963d633bd00293ffe10a6b23e74aecf37687e58a135635532103a889c576b963d8b931d36bcea9e60c921b39905f2c917a863ac238b2c35fda727e9c73cfd208ce59c20def5aab809a89fc745c949f497bcbacc1258a6557df469900318a9220901026d6e9f514da8e7b1ff5cfe729e4d3d733948565a57910803b3966323ae8eab38a01dbfde430862aa58314a27a3e84b637be2ff3209c6be32ab650d460b0e6ac34051f0b8a5b04d412fbe851b1827fb7959c57c7b28f4ee699d467a178b53aaafef59f4fc489dce328dd90b727636d57066f0ab7c6afdbbfc6e84a41b5df8edfc84f3d868ca2ce61da9b9d8347bc38970e68644 fd1770752781b76259bf89e10d3d19d3a13d1441e5a780b67a4f 45 62a8e17b29c6ab5631d708b9a7404f76b004c492cf18b8092f97ff730839de6f6887fcb13ec1597097cde4af6f +40eb1dc2d3188af40201a57f05b545a0ba76ab5c3a49c5f7479e8541d6d3d5c0d5955b8166b8fc4b6af3a8086a21057752c36fd2aac9a9aa67312f6b9bd13c6831038007b79b7fb7aece118e54ae3e4771c9994006b705d45158a9c47ce40f9fea6f1b45ae17e9660128fc3964c7194b11d2ba04d447df0de28d0058d1a40754468c6d127d9a1d9fc5480d5505290eaf75b7105a6ca310a06cbcc5d3b53298deeda194603fe77116fd72b7730911ac3ffb33c018a68d3034b3d52d95b1e418453737f0ef1e59357105f0b8228ba2280715255816dd51 5ef80f90e8b75fbe47d3ae9b2a3b600f0ca14af776f2ad913618949c 57 5a433a32ae7c98ec7e2290c024194344c4393550c349761665cafacb74abac966257cba593d6dbcfd534db2c4f36d23624dda47c59dbbfe917 +930027362c2bfc419df0375fbea19e0ff85886591d5aad2158995107acdd4c487f10cea856f0b6e57d9b1cbcfa277b33e2239e716e40e35bbeaf8e6c6ca1024fe2ead55fb00ff001cdd0d0efa94739ce06fa311ce45ae0682625d90e8adf45278dddebc26cb9a248d80824aa84805572ca85f0cf9283c1d222cee217a7cda0a48fab23c6b64a6c26c1b21ff237d63ce4c65703cd3a546f71942a632c953736e5f12c3aba6bbae19b3ce2a26c5bb1201831b61583ae1eb27f99f6c17590992149b0a124533cfd25f2c52e0a16aced8cdb5655fd9696023dd6d0e3f691eea54d3a4b73c8fbe0a816f0784e822712281d248e01c487e15e0d466edda2c1b8a7bf97652f94b661d6851eabbb776e3104583a1c7d68a2d34d6e03c41e4e9f31a9c3a324bbd438a032071c3e5452c141277a938b7c148b2f6fcdbd64a1fca7d3771bb8b8ffe6aa401a507027a5788b3ab8a5df5642378108b89d3f027288086d204b39b9d1b62e40fd88f9fb8b7482b3e9d7ecacf052c646daeb3c2dcc4d5c45bb204de074de7cf140e19c2a983107f29997bf85ecbf5df50b9bb92d93772024d6088964a69ae22f3d01a579d03222615234aad54aea5312992db5a8600db4879534b6f23d79c4528aa67ab2eea202 ab895c83b99e92cc110ba3b14f6961e077a13cb5a6bb2fe87f6466081049db19da2d8a7361b83bbcc894fd0397a63ffbd9a1b5 58 8b62c0d4fda3854a519b630f36a8d6cda7c5f24809afbf231dfb27bc1d33c077415353ed5ccdc0cc06d7c684581d1b662a04847dcb5d0094c2d6 +3a619ea1346e63583fbf35cd9497536dd1f99085ccdf4e175268a2e72a41709d9abf39f8ef9cac1adac26f2121c2320c3711d55ac674c658a52416658de6732ab3d5b8f8be87748164806475b877b71be99f6369c8a2ef585938112d4cbd47ee175dafb5d43e79051b9e03118400f58c36dac521832d2c336aefd603f7198d1d5521e10ff0405a72cd5a64a5e81bbde3a4fd94b1238223ee0adc5ca94071e1107bb92334ed553513cee899c7f06c7592f9af116c09e7a9dbaa8cb08307a03bccff490458b8c86fcdf4bcfe2af136241bae89c45773e9854c9d068d 1be9e1cfb84ebc73d32f885f5aa18699e0562a2fcf6b23d7bb8525fcb11adfb2a4743ecdce 37 808267748f5582229d6553940480d24594a4d6634c28e2093b022127dbfc54a2c43dd5c413 +9b4a4a0180a20d28afa1b8c724b7f695fc0fa56bfb1a08d974aa36723da71b387b8baa 2f3111942e1165ffdf3eb2511f25f58809ee75d48653a6552a75249355eab191230fe4d0e55bf96266fac5e3b9 25 81eada8ad99c06c0887776e1cf73d8214487c253ed6deed49d +2ecd77d592ad306c7d233b1a148a9796ddf69460e4fbc6aea85c2f1f7dfaff694c040568ebf514439c5564ab16ecba37a202d7b5c0b08ce1a604671a9ec7d23c9f3bd8417c0261a7eb543ed855e15bd9bb5f400d12b4fa3ecd8a20a259a6ff3fe5aa0c816141cf2fc055adf5d539bc52aba56bdb0b930b6128b255174dc8f849e242051c004ee5c0688f006f82fd67fb06712f4b463c1bfcdeeea0bacf14675b62646f332fd188bb6d4c05cfc1c2582a77eb6633b2c96ccaf0fc6560cce0c5097e76a5c8fe4aee303d92719b6801cf8836de8a266482506d49d7868720e58adbe1691272ab 774af859d8e235164021cb40d5e1adea69eac76f29f4f25acf9c4e9efeca6f5ac0ffdb9c403dbd4851744145194495ef01b5 25 aef33fd10b5c5ed67bb298ecf509945f19776f9896998c6662 +0ae2f094d53f6d575a38ffc61ebd12b22c6d42008b88775e497aa3f805692ae4aeb127b4d8c2c3952c2f4338e3b866f696198e76b911c5a0ba7b91a96ff7fd0b399277f14d164fc9948d6fc0696bf97f109a1db5a266a8d63c3a48bac560065d37abdfb8b38dd2d3cbe8526c40f59fd6ce33636cc8a4d8da56dd80a551230403034400edd059823556cf77058d541752b7b70186ea787dcbe48b815fd397df820882c3fa9bf9a08889fea705daddfb2e28305d8a57d24856376cd91d65d1d8993f0fcf4d257fecf333ac0978c9ba9d78ebaf5539227cfce922d1ad71bc1ed8aa298082c319ea76a9dec7579a23881bc737d852ae5175997501ef10bedcc6e3932ac8e3947ec8a54e2e4f317ee264cc0040d493a80d7a19fe42657c5de446a119d8fb238c353653a128b104deae96899c15608e18d8a2c4c26885c2008bc00fcc269fb967304439f2be1ce9a8c7e2ce0b8dca179adcf1fb0a2305729fa1b2fc410e62080f9bd549c51fc90300d3b3b37a515fec829f10a920351a9fd9bb50e686d4b15b9f1e32eb4c49c49b1bdd1a15127e2cd44930fe23bd86aac35cac96aa7498c3f2247cb32e6a6985fe5a1dc6418661370c5618580b52b6587c1e3fc61ca5898fa955960829eac30e6d617cb97f8986e5c4d617d1001c457ab29b364cfc62f2533b0c4fbc6cc3aff892463ea263287f34fba6e962a89133f42436775c5d896edba425105e835094fcdced36d645dc4df5c04c7ac92aff5027d7f5e6643b3697504ebfb94784dbeeb3d40e8f03e6dcfa2097732b9384ed80320e742e87f577b45c449bfeb744dc26d31600726b3afd9acec33132545c3c538d42f1c5676a77a552fc6b4cd54ce4eac0416b974a147630e51586f27848455cdc5e863017aa1200e2ad8efe57a0dc02d26c28bc8061d2d29747520489824aff547c9935e76df237099c6e7b237886caefac2656c405b93626d0569b604d8ed9daa5851980b67d0137ca967c6c77c1f4d2a63f252f80ec1a94222aa1b69a060489016102 8dace9349129b62c3efbdc857994c68de985d2195d8ea4899e9ed39ecb869052aa6f4a267ecacfcf4dcf4ffe 58 0ee6c8f88c142b53e2040e4d2986b4bc26bb04bd67cffb1623de67c5e2d659b81d8c5beb86dbfcb6371e1fc944252f03392fd486abcee7956705 +f3af581abb0b77ef8fb66699837689687f6aca671bb46def74cd181eca17733a3b856a58a821e32843df222c06cb796f488fd1675fdc06751b72548cd339cb9995598574861f267fa8cbeed48fcd8d4a0f90d06fcea3d75652f5665082e4a4159789ea543a5d39642ee48788a02a9aae6a819c70472734d50b9c9de47e4a05bebef682a3291a3f36961eb00cf4f9740e9e38ce51ce0f624caae56c57518cd480df3067574c2e5cd42b90d821812ed7ef39157069d60eeb328749c44fcc450b22804ba38025434e4040b67a36805bdcfe7007327c0052449630b2786538bff1ee916b89e4d27c59390e6b26ebd5a04ae6704e2b15fb3b347ad5c20c306257affd2fe046e1b84595485742ac7f661a47831a40fca46f335eb57b4dcf311661a4670972abd6d766e391a38976d8c77ea09d6bf50eb295654fbce33b8e73596ce007951dec4887ad75b0eb098a84a8e5c39050980af4e108e93ba37437c7441bbe6d7c89d4f8fff5c8660f6113df3608160b5737665474deab4d2790d6e5cd2f00dc5d6f0ace91c2f4138acb6528947680b973fc32f2f079872149d94532ffeca4bab52f1d468f3ea1f29ad4939fb3b142da3efea5468b91a1947894a016c5d4 3b49cef87a92b56539aac2719ab6638b99 62 5f93a54258ff33fdf9fa195d89205d2c71be67617f03654853c4571346652cb324dcec7f978beda14ec1cdf276401b2bcefca2f96a161bf5f663087f9bf0 +eb53a334505ac516a2b82bedbf85bed16b76b76cb9fb02d904de9780265e829fcf5f8d69e6cb85883b5b8332f8cd293562f9c9853876994d2162ec1df6378511e8de31e29f0000851ce1d32c026b1fca8dce4b7c0fe82f11e5212a609a536e32914ae89c9203808dd819a2ee54d5dd2e5f23a513e9a1a2174c945345c88ee08de729c5865d7f07b93749bf417585710876b79f94be730a420c71f4b4bb5df1e54d290dd748387650fb2e2b8dc2719d52cba970c2d821df35511f7c1a973999bf2f5a420b3a8b33062923603ea8bffc08ea61e08bc5070af01f26bdac98bf8e3d918cf0ad74c2c80e1caee0a109e3d090e4b3815dc3a8944427823fe242d28becda5d3900a0536c3505a62aa3aa51d8697e6f4ee2eff2160c1855ba144a80785ec863ca16eb3c51f1b256e06e4b bb6355ca82f715da26001cba0a5fc574e32bcd6a6644e06cf1c42e0b2d427b4016d046e8cde903b1a96042bc2a71e6 33 a8aae594ea251c8ebbbe0e4b2067f9be00fa233a0074f9412dc0ac909a4a85eff1 +fcd1a691e4ec9089874b1447db105b5eac1232271d5955e877c689e4273c6c77062f95ecadef77f4771d7b948169bfc9a83ad5ea4c90234e38d5cf2e0f2d58b76cb8051b0b991bfd0772ea11aa5e6a31e0ccdd71142a589acd4473f47c26c011cab94eeff3ef4fe91ace330eab32b7e2ca45aa24f910dfff0f980cf9ec26d6640588dc885b08a52e45936223f17a6787171fbb3091b804480114cbac23e8d31449b07683b6deb1b4a769fa50c013e5f2eef8fe7b68132ae96f47be3a82bb3d995807f7a9677be536ba91b3342de553ce08d8955d9e30c740f51c4ad6a5db4ff645951fac514e552d07edb823616362c1a5235f570cdb7763f67548e2e2482f4f25023f8bb59f62cec0272adcd56b87e4d86aee14f054eb102101c721ff275ada9062151d3eb5074144ad13a07b58d8d91b00f7024a654d1c0523b572714d40b3913ce028fcc5afe095e567586b6c497ffffbb7f225cf0c6750601642d6e63c9b78a60d6566ed7d946a4a8afd3074bd49f0236462e3651cda1b9c4b76e01d0b06ecf82ecb6e1c603bf4ae14c8663aa999b1a5ee6421eb2355e2264311b2118fbcadbf365c0dc6fe2b9d4c1a2f081b93f55b6527 9a2f054c962de578ce7441995b326f32803d098ba4e7cc5d47f2623bda62c0b32fb8eb8557c15fd90207539e020187897cc9cae9b361ce677feb79b1 60 5b35fcbea381b21a75043cf49cb165965836676562eb40b18209841585b06e08214e630d01c410a98845801a8c5c1116c26b8aed86ec5ac522b1d2f7 +8128fa46e6b2176e1a4ea7d7ce2640064510eaf453eb329efb1d4bf16e4dbe91dc491d279e00d17fb7fee1a1ce48353b05baf01e24fd271b8d94d67af24d2eef174d14ac01e7a2d1e6eec6644a525661b6106a6a5c703a36f70b25b699c8767d9c758d15dafd15a5b1db03ac33567cd170525d4500d9a14467b76b091ef13a704f82847d59bb3673a4a2aa3b1dacdb40b8b6ffaebe03990a292868b4d70fb187f724c999d05a0b27ad6c44a788efe4a8cd655b9b7c1b26ec9437ae0458c0797c74c003c628980d66233c2b7aa856d8c341897b3df1bd011f630d1a356cab14afa1558846294cff16c6c4bb5343e758454fe9fb5adc877a0c49e2be8c9fe8bc1f41cf8d5ae8d9666e4fc4919e9e3caef7000eba7f57e63293b9aae27bfc7a7986225790f671849417abc0331ad84478ae81e6f7d4f8308e50a3d22b512456a93b1d7e23d7e7ce1fd1f9e01938360b1d99c6b7017c7bf06c2d23a3fbaabba1ce9e44fd77ec1dd83d5ec0ad c1de2e8bdcaeb6c338ea1a6b077ac49740dcc173c942f60f779169e9b581277394fad2ff9bfe74b8af3ec7 63 ab553baa52151532e10b67e356b1df0ae49aafc0979983cf222f7ec63e5589c7eaf6a1371ae33994cb656b6fe2493631d48034695d63e3eb832f96a87215b1 +6dbd631d4483a152a411f5167324fbf9 5071b50d9980ad86a1f5fbe75d7940136d8df94b5e474c29f1bac0d5d588c939de4a7217af9c231470f76875734d 31 cf84b5128df94a065bcb0b5b69a4a59d445863a5ea46c8b293f6212e717d5c +1646c6c8dc16cfbfb9d3cf0cfff15977676c606e680c240941f5079fb2988b8e1398574f36aaedee89ae34d0a1d6a19a4dff066c8684713de1846bfb771756f326e88167926a4cd156fa9201642e993a3070238f185b981614aabcd5630afdf038f2b378b08c789184dc721a162ddb25c5aaa85128a7e286e12a3db34c2dc66be09023739b83f24e483bdc9d218a128d1834e796f5829b3ffe9528f426cf56d517449147b53d639403900b3578b36f60b05ee7256ab55e112e8e7b00a286f02709b25a565a526a1ed5d7c9ff8f5e4b39754ff313a4c6bfb16808f52de989651f39322e1f4829782ff8044e5a038dc9c3a30a118021b21e449cc3272c3bd1005b75baa7504d235bd5d1e146c0605908e9be872cdd283fe772b7c89b0b5f1760a4336a62cc8eea621776a4682a3b9b157304f3b5aa965c6c6e09f346aea244fe158fe99d50d764868b4ac05ddbbfb1e084312a276ecfdf9f1d33ddde8fbefbeb47e51712190c5b03078c8577d3ba75f49fe6562fee662e0247a936454465e52db736e25b0960e642ed54a3645496e55b5bab558db55c7c7eb6065052d2fa00261404d419b8e01c22fef8a2754bc63888e8d2f1a3ec462ec41a8277b5312c7e7928b1ff5cbe5b14bd3015b3e4451a ab65e27ac595a4ecbad67535538b26c1cc53779a868ff67d1a0c27d34578664ba32301cc38e7c3931554fedd2087a4 37 484047e2f45a0c6fc222c40a7e677a0f12d4d05066839e5e77f0f44142be4e228b39e2121b +7b04dcaa6a82e7c8628176122d8236e42e78afb4c7a1297b5d007de82d9888fbcf0c59cb2e1a7edcc42950dd5e946e803832f31ce1426a02298465c33ffa196a32bb7ebc6801200775f4e6177cca415349a1791f1d3a43bec10950a2d30cb330b065584fed4868079a798f12512386e072fda5cf8b962243895b7d97e6ff8586f2997241b7a00cb898a15d3021c94e3fb7f5fa3fe4a455c460b85f66073a40dd4191263ca8d953728f70d541dc583811283ebd2f3fe7329d8d139545842ca9ee6c58f80ede19c95c11c3f46a0b6740daf7c50220437a966903a23c7c153c6eb9f2428d6faa5c9e4562d63d458f1cda8d9d95c61d19b9a2fd2432845da951915c9e334a765dd1deaa33d15999e03daedba14eb31c30d8fb2107beaaca128a6aa0999a7c61c5dde972ad9994e413c5af9e0ce0866a9e5a451987055929dc522a0d28a4267421ecc5696560212f2e5ae2b4b6b2cac0b66716eb11a08490d1db916d1839343202622e5d0a3f5edaa2e1e96f474fef98faef01f50351a4c36d7ba08ec21ce574a9a7 96baf7f58d6283f1f04c71864d18f65741a3a732540e710eadc00b38668d7638b27a7c239fb335358241797207e7e7251a6d342d9bcf90b551 52 dd11f313c631411f4998a5f6df162fc60068b80d092c630eb4e71fb45f280270adff232ea468dafb4337bee595c51de4e7052a3b +af94a675f1018816aa4cce72bbf08d3d8749b1171c9995db252889bf887611a78e04a0310a2484b5d376ff5f7e5892c0dc708ffcb50c8c98ace2c68a48f428342332bab8ba0124326f2724628c513be7b3fc7f4ffd6c084da793f4ce727443cd0a748badd9e3c5be66b6f37395fd18b8158e54a747f680181e17c57026c8fd366f0e62c20bd39d93e9e8c294022de20dd8d39549d4429771a2d83509970f534495bea108bfb206c8b795232d3d2bf2879b3b702cf69a123d37cb0a3eaff8199e544ed17980d8ddd533e84389643a4a587c09340e94fc572e1d3333a89297f40f2ce1d698fceac9050f4ce2a390cef3bea5b2171572335a72ce67e7d72c1e452c15860da9ae798441af3c3c596de557a6b814cc874f4d16aae35a6dece67fc1acdf9b4b06d7961cb651e523d85cf263c995c717e89e2e40bac0a139e2590419b76b76e7199a32601ea063e3c607996579f289c94ee7f35ecc7f7050bd540e6ed0baae485eb8c26f832bf6945ae086eff7991b02e07ce1f2b59142aa735446af390891263144f085d50443ab50e489ca9cd87ce72628c59a413f8f57544e213307da611bf0e64b0aece6098d0ce39a4b64dc02fd7bb8994d671ece6eae73034edda5f6daf31f822156141c0ee98017455f2419894af690856caa63455a84dcfbf53cba4a854edee6c69e1d7f064c443e7cf3bc0fa09a44499b96500b0d42428b2689067f192e6988d55d1da177f77f536722d1363de07ea74d8dda61cc52fed67b8f5924d6200ead11632a62c67e0278007f576859d56902f1a20c29cc6110f98ee4d569f2c75ef3b6eff26500cf8b495933da3433e0f1b5a75335386da6c32c9f4df6c60e86035246f5724181db6b7e7c4da1018c1f0850cd58d2438526bacb16c8e720f56de29768c88ebcceb4c7e474d838d9904ee7de1d30a1e5a26cdc26208efe907ebc48bb265086735c2f722e6ca7acc6c202ca87c32df26cc7e03fa56b8bd053b7eaea1ad1a98b88c4d6ee561d19f41f23dfe1d6c794bba9757354dfe422ddd17103db065f20d29291c7773f0b75ae6915149c0c0b612315e847117462c2e60b434c0c17100469ddc82a524c1643a668c20401a573c3b3477c7662ba5ce23aaf01e39dd757b2b072e02f6eda526b8f403c6b7e4ba4ae2abf0f63f94256f06e8ca5af99d579359f56a16e283f1f9c932e3a7151eaba92fd80e6eed4f5fabe58dd2b82eb3285b3513844a40bdcfd624ecacb80df3ba7770c04af392ebce41dd1e1eaa8653194f5b5e080bb4a64b5a5144f430d9e6227e9c37d 6898606b9b85afd562fb5b9be9c4542c1dbd2bd5c866d95d3f2bf34cd4a15a4e97bf36ad3ad816b567b84321b385e5ba6d 20 18438e1552e103d75365640c877cc25b592ef134 +fe2825ba10011119a3d754238303f03b2b6a92e0cad836184387dcda09129c60e4c446e655781561d6488a38ebd2ce14bda7aa5126c2afbfef61c229ef757f5f9343dbaa792b199d1be9b820566662a2af06d9dde931f3f6abd291c2da72a42b3159171ab60cb6ad5d4ec8c052270b08b87c4604a4b3e748510b8789a53b5c25a9c85603088873c03e06a9f22ab4f5111d902caf07f96bdbae9c4b1a0889a49bef2495ebe36959db571c62cbd1e3ffe393ac702663dc670a1f5f140ffe6c7e719319cb66e1a448 ed58165ee10126352046ff1f77a3a0f1c361020009a52392a9f20bd8ef703d98df00271d360992e534cba5328e93e7d1f2d191de5f88099636a8201531 52 15cddd56ab352ff6e1fa1d33ac14369b1369ec682d51bf9b031a310f09950333ec7f171c904de6e664abae366c190ab58faa2da5 +deadb387e65d82ab39e04792841a193802a671f63b7163d6081c5eefe18c427b22e0c714388601eb594f70b409240626ba1071c450a531cae2993e34690689e1d497b8ee848f9b09008761870a3c6354450e785b1af1ae391a0ec7c5ad40db1fc4f74dc0dbc6c47e030f6b2669a94e7b5373b866fbc4ed427e20c5b5b0384403c9afb35e939dc6b63dfe64b23e124ab4e91bf052ab900bc9da18d7fc75f842a59b815a6b14e6f9901a8732fa8aaa14eaf3539be5a1496b5c5cb90c9ea877c702e5cc5951ab0de4bc1ac4375e344f7931a2299991883a0cd2d932692ce8f5eb6eadc70ee98e4be4e8f1208ccce4175a2940e6d83154ff42b8949452f823d305881ef209d1247d8f76bff29a29b6c8611ccadeb40d519e92babff6f56b6c58a4a74724a52506e1f71cd4d1dd6b5fd4b32685d4bae308f468c9e963a01f3997839353f617c3b0eb80f903d0fb70493788b363159190c7abb30eb16a6edd392288184fae58223db96efa9d17524abcba25f05b91ac7562dfe529772dd418c98750c7d7aa3c031c8bfc37ccdf4b697783bf8b00aa10934ad133e6611615eb94641295277200dc7e55b235133425eea20fbf7db3ef4a70a0d634bb18198b3452bdc9ff653cf0f29d63a1d7853b02fd54e436b84c83a2481c4080a27b883b0334e81237a3c9a37da37766d8f1171624e59c3d39303ce29dce6ad350a8d91c026a63281104793fa52e0bc12359bd347d748a279dff27c23451021fc023814c79f4728da7517f524b0b431ec44e881c8676ca05b30f0bc166f7edef028b14adda089c9fb4dadd5615a28e3b3e48fa1a89e60cd622b60442c52d0c3643497234493c9fef71e4b7f5846690c15081c63510897d56f5641ed17905a72e7c816ea6bb7f76bf4650b7cb7e741c7b62099b 2e6edda4ecd1819db9a02d2b8fd09b2e9f0fc30660efc0f10c7e8824b15d2a2dd9 43 e0d4208ab4457cf4de31e51c4f42a5e96cf27f3c861a762a7687508c841b46264d3487d378a0eae6abb6a7 +0f2c0965696b8c83bd972fecfba084b21321639cab9cd09d5ad754a1359405fbf201496e9e59828ff06bd04874b794846a86768f64898d5db4cbcebe55c4febabea08c41d07be61a29ad361ac4c760b9a5faccc069b731854aa2802830650b56161f609522e5300af364f7863589ded2305ec4dee1469b256409c06aedb95ad99da1a0c21c1730a7 961e18536ba86f8a14f65c8ae47e34f12c8ed16dc019e080062b5f9db30496da40a85e81422c265fc85c5f2a3317881b945d79fc512b 40 604897df891afa76ddf7d8a7aee94dd457a54187ab7526c16554580be7a103507a95b07b154d1163 +960cb416d1e5cbecf82b72459c715c0b9a73803547ef57b595f95e008f59dc7ff02429def4b7c19402c408e5698effc8c7a2d2adfaa2c02392e9e5dcbd759eb4d848e45e9866059c341d520701b448ef60895bd5af66e58cc08803c628e83d3db2680d2b3774432cdcd4e3ebe0401a2e4226ccff5bd1cae9ae058aeac2e2d4b653ce46874c03d7af49b03f2e19beed51b933c899aa2afa7fca4cc3fcd37059ab1546d5bc735b01b3e41115150ba04f7e81c4600c79772e7bb1bb12d8ce07f9bfeef2f587d60f4632d5e7a500feb6b672e2baeb29a429dab1ad9b7adb3ae9dfc57ae320bd50ccb59e323192762dc5fbcc70ebb887370a9732159ae450773ad730c6f123a33c2be8fb71734c3e27a17ea7872ed143d9ac6f7e08d08900173720d16b5e40293594de7686e3e8a1bb9fae7ed0e16250dbb7487702ab1a7ff0c6ae8dde33f25ffb4b3c19472c5688fd7d58792a69feee57759e6ce5dde5b8220443a9d5d97dc75d520da28181e4cc90dd9bcf04ec8f643e55bf70b13b18e37e557d6d7512dfd32472e6a7d125a925782d0d8422aaa313483f11524d1ceb06fba94b2c4f772eeaaf5e2344bfe0d98b1d28bce16d50ae0034611a06c6b268aac8b66123e290b31df9bf9cb4262cf1d0bc2796c7a88a441ca5571d55528c6a6e2f98e3fba38abf600b1e62cb211cbb99a97823e1f6c859b584b18e45fe52f6467d6409c7ef3239e790959c16cbdf55e71ed8f0535c88f13a09fad41cd1af258c62a509733cf7237073110aa51ebcf414406c702e4d4c91e9dabf246f7a6566679297fb009545a1c34406a47e5eff5ac304a9a394df71828738d50bcd07ac312c0a69c6408123ce379458f92a99802bf278e6acad9f0cdda5f83d2681bc9cbefdbab1e7d77e715bf4170c347a5d2bc641cb04d7b493667b6371c582aae93bc2beb0c8bdac622335405c352d24761c37d3766c23052fc42a0e6eee33beb80b6624eaea96e914c3721ff3eee922617984854e6adbb3dbae2bb6b392ec278178c9e3a467e65718dae366243ab089ef26388cdfa931fd68c924c9dab8ac5d06fe72 5a685f930c29f3e5c85908a2ce922104551dddecd50fc7cf857a762807e4318dc6b5926d9977e516d7f41632c133d1a40a0a8bdaffc2327f1239a262a138df 38 fafc7692a0caa0b926816945b5ef0702252907e6db908254e77db0166dc29f362d2b024589a4 +54463fd1b87c7c149a18eeabb05c43a3c195ff7646f3ce7fd72b66f6ce5b4f1f8680a6160eb310e16ac4dea09962145584b7c5b1cc100c4d626bac75f6a50bbb7c11658aa13fa850d6505ee559d08454d454c9f261cd9397a562dd73f55b938e8cf5e27e7e04b56e8cfda42043e026d108cd873c41fbdb0583a6197c5abae0294bbcd992e250e08563566167f4ecfd2d7fdf32c4833d78fa4d6d41c09e5fc4c027b47a54d15f7a3e182a3fb6dc498142b8cd5803be4cbdc1b42423d31310942a574ce72dca2ef967a22a11ea59b0ee8eb817db16cc7022783495017c3f2aed1fdae592efccd89aeda6f15ab2d2c450ef6dfbda622d981d0c11d97016a66b1c4691623a1066126105 866135c473bfac21bd9a2b6d66b0092b59 27 5ede2eb7ff4a67672c5d2f025f5720d7a426570a585f93921cd830 +06139863388302394ee6691baef2841e7b1c99cdc66a1c85c18816784e22c9f3b1742770b5567fcf555b9a038685c76b30ab4833d8e74059f42d22ca77e33796298b98faa61fefd570c37090e2fafe372503db0c593b68ad6bf18738ff6e28d48cda3970bf06fa3091c7fe646fb48bfabd0dbc7eb887af83a0f349d7cd9d812897f6103d095015eb7be009c2963fbf083039ff6540bdd1cdf2653592808570e3c58f9b439fbfcee52dbb34707e2eae28abb551cf656886fe7e08ed4325c5a4c5e019b90f92eb356cf578dd035baff28cad5654b7b1aa10fedf136a5c045752e9b27e0c2e1b166652b366fa345436b95b9eea0160e836e1a6f36e7031367aff7fc5bededb3f45e29c4c53be88c5e53bd540571e4082f6df81d893eab9fa25498221feca7c8b6177ea6b98f8726f173786e343d917a9a9a0946cb6a2685ccbe65edcf3acad131b1f721a0327e3ad254ffa1e5c4ec554fffa00659f93c07d2758c6b858ddebc903bc237ac5423830816ab8dc8ff8fdbc3e334f6eca70b73461057a45cf29b1a5ba0b7d0113bf529a91d8f878495b3eb26e3c55e57bf41d369b8f7aa82b48b88885cc4dfefe298c5dbae79654f649052888e3575b037a433de86501adcee84715636bf3ae610df3bdd93ef52ac89baf32bab33a264c429442fc58ee7f43c0e4533b61838190f731c4ca3460fe22f694e293db3df76efa76c6d9568d0496d251a87d73633196ef38386f24ddc3252afaa977fb75084e4f2eae4303876214d15e22801a66941970833dc347307b38a2ccd50c4faba5c5104f663d583f0f43e80014125b1a56b028c3fcbaa0277edd8dc877db90433c791821d0481d2d2d0d68b666daef1c73cdb61537872cdaef97559d482851cde44a31375352f21257f91651aa8d25573aa7e53f30d4a0c6c63f3814c13227268d2a0314f8fb84b29f294e6bf5694352e20e18298d8ed94ccb76ac81e7d96e1b785d7d8f70aa6f2a4a1c8bcfe82d6180c90170f4bb919c2231212e601320fbb9cecbae446fecb7116513c7b801ec95ef6e174a6dfe0ed221d894f8284e413df247b55cb670032f33517887ea4c811349185d98bd31ac4f41cc55a40c99d52268b56778c2458abcf0eaaf43ecf88d0ddcd1c197dcd40e7cacbf95f28d49d02cf864f1735eba7ad6517e2d2e84c0885f68fbd1a219bae4d6487124c55320445ddbe9afb227599911c47ae6553750b532875b5cba236d435bf06f1f8b322a56c3406fcd83410242ce4d440ddba57afd883d505181b7ce2fe514a894a8106c3bf7224ad4f1d56e489afa5b804c84ee531dc46a6c7e5a209890 e30a109423f371457fe02946a067d8eb92678ffd7fccba7a196b266d7b599e6f0f5435b395ad79 49 0273152f3e7460314afa0b1088e0d5da446683a00ff0c48be7c432289ab528fb97cbc20b3930307b9dfaee40a799bcbd43 +29ab7c62b954163126f014d2e84c6162415042b19d48245937174a77e40b76214b7cdea7cfac756aa3c5d01756cc4b4eec395e97b95c09fbb4fde128b27a50ccde7b5aef3d92da971030193e1a26a923ff3948991f9e1b2656a8367ebadac94fa2da1f7b43343a15d0a735792ae249d774869321e1980bf069050539945e8d5328494fe1b961509b69f996de76d232c669da93a1a89e1b80195bb8b0cf7a64596843a6ff2c5f35e9e9c1eda02e9cc317536bea238d0a7510413a256ab9a2095976af36b89baa0bb27b7c053cff6a5e3eb5b6a8191c4f6cc4880e4b7dece7c8cf23fa5262bcf0184c5c093dda773b4924d7508db39e3ec8708e1e36502e9049a1ed51daabfde3d67c9a9bd78684a1825cca9d0895c5aa68f5f1a0df0ed880ee7269737d72b1564137b75d45e11b2f64f87543392cefc4725707f1b2b9edd1263604c298742823ef835ebce8c8d56344e425b7f9046d911311e0c2c63ee4d9a68e843661852f930de160f15bd10dae0f02b43d79cf42fcb061a868029b751cf51dc98a096b8c5d1dfcf3a0852344a4b807fdaf5c8e7afb445d4241a2a4b8e668c2c6bd59ff3c51dfb7bc06402e622ba9728132927bcf4245c2a18ddf4bbb77612bc1cbd72f34a0c80e2f1712372f454672f7e23e7924f29b542b8ac80b9e62b0a455ca0c7e89bb4888d6ba1527a9044ec8eb210f91447177f3916bdb223a74bcf6171d34e51cf06a6f80f44f34c4f0dfc2b0a3a8ed71c23d1355255f0970d51dd9f23938808b3e8cdd97d40c26d363201af0c755ffb364ffcda141e8c386cdaa630832e264eb319581b5b39b1ad371b49b96b6ec28843c715a7d12df6133623702ac9eb3607c66294325ae008a185ac738e77c1a08ec2638a71e4a 9c34e3c735d33c191b7a6e932925b896964d989fe41d1e5c21f02df49fb248fcd7bc028c802fac1ecee1e6fcf3 42 15c83f17d67f95d8b88a8182bd722c8a2b328f184ecc4d01d89d59ce11c866511ce37a21bf69c3915502 +c7bbf32cd06b60d5c9c0ce4e4130e1fcedf87ed0d67c20d3736e2346ab9feebc4cc5bb1bbaa0c17193c18634d926915b2c48cd2a0c77eb8c1fbc7649eed466905ed382b704528427c07a2cbfa2d2b93139cb360bd2df75b9f3acd760b733007388ad2e897917c687020df1f7509d1290bee6cee6f992449bac2311899c60fdae41fad979905c96b538ad1389c27c6c74201be93d2c43d2dff82ed0c3b8b3ae5bb95b2c9c543229de5904228136acdf3cdf67a5e46913526c804dcf29df4e7c28f242b8d14dc6e2ed8c0def2312ec8321758861f7f8e4f712d461b8fd571459a380f85676747d10ae2a5803d26f492681ec31e164cc91a8be6cf5b688d83c8e3e60fbe03121017ceaefb76e76edc6554e6304ca09f7dc8eb0b9269be6d8a4cd61e1b2cd1633da3b69f48e564d6b3b0c34f67ecc9851f618494e983571fc66cd6b5d4e9220f0ed5cd7166a5b0eb84f9248c01b2381ea531ed3c89f18d86db93f998419621968df7cd8fa0d865bfa0bbc1a203c58a193e8ba76d6c231cf5ba91285e09a1435cd73b585f301b4648cef45b66f5dc81551d74c2ce882a6e0580fcb5e4a897a876754c71d12a30003e840f7a41ef9c993b75e17db51f52611c4700afc5107b000728be933a2dbd4eef27f4189068164a16bc00d21963b5927535bf5efc7b487825c20d21b562e2dcdc73fafc4285e3137a476d9cb39ea811d14df36de004cb91ff1465cd8862e213ffa94a124ce58156ea5cdc8cf5ed2ea7e5958b2314d2700aaf412f1715aff4db34742e9c3dc35a2d257177227e44320066615819acddc0ae7d810adfdc6fc25fec99a8927216f6db23cb5390122846588553d9fbc433ca5bfff27e3d99c9cd6a0b8a1b0bff642e2351f23482473dabac11814d57c1175c99ab88cd7f14445f1ce7cc129dcdda8795d578c30da15c4fd076cf9af1e76198dce9d4adb1ab8896d09277352a3a2dcc05c45e0b2c88f3212d3b05a63e637560c0df7cb2dc71cb850fd3a12ae657ded074e0d7dea6434e3364947e19f07f2482d18df6deab11ce4a150beebeb4b392ac1eb7d70cbf42b62663910d70ef67c0c44c5955904347b48b72435ccb4ec8bbca95d9a56b3cc3bc65bef8cc21ce3f5a049b6848635bd713b57e18ec43f0080c3f51549a6252ff74d724b8cc9cc3e51d817e6b25f454f0f8bf279d3544b11fe5b0a3a757b61ff71b4d4bc123b7b92cd8d41605fabb8a6a6441bd3423eedd8591cf92178f3e653d112813961c41cdc2e999996f80509fce0d98059a1e29273eaf255641f9ec4c6cbb7ac18 11edc67fc12cc9630889d90cb5a8c7ef4c1bb442359dff6c8fed5cecc27f 52 39faad60e9136c50ce863d101c23b5ca5b59493d836b23337d6771ee4f794aa0dabc4b5135915216fa82f76067add01c4fc8e32c +2a3ffda005f2ec2ffad4fcc7f0a81ac10ac13af097fb1dfd0a13e90a8a3db4b0baa986d56ede97d4129fd0c802af0b0e1bb621e080754162686e698b231362e77d07438fbed5719dbcc0901a73aac849854900a59a361f6a54285f48e5105ba07b11169f76d4d4a032bbcc831b43b0d283154e06f563d15fc812c98bb0cb75adb3a8b0bd9ef339c8f3e6afbeec3c9d53a88682de272ae07c3da98ee88ad004e992ea9233d74a53da7a9c10b70a56a17f179631f57a276420aa5ac51ac8e15b9a61d0f1f84c60d1de0fc0a3e0ea45898bac0115aac6346befc1fb95e154409cb6586d66d25e14d289b4465cfb57283cbecf146a6c11df5c13f731baf846ec75ea7c8762ffaf8cc216f8b5024a0704264c470185cc814625960530413258cf0179b1e78986c5796c2a5b327644299a58a059a2e47df349fe16ebf8781fb81a92b0b9df684ae3282782a31b4fb985ffefaa51b9810ef60e8913f4a9cefd40c04c919c8826f9bea982acb29ab3c7af0c021793e851ce60ab9e95aebb77635111d1e80564ad5dfeec1128346eed8b438389c8e81826b1e85b0a96a8e3afc5bad13a9496f3a9ab001a378e41ce92b6145feca2e573bcac251a8882da4ee74b0d8e794f0e529937c659d1633e209e12df86da0091cb9e23c8cb4121e8d5c917320d3cf9633f59bf16ad4ac3f5ba48869e4e34f8cde4d0cb68d366c5116d4cbb80eb1e8ddd350e498f97f0e555128db0e079b95b8063dd264f93eaea08f12afd77c0861538c9728501eea01540c00c74c4b0324d72aa55e33d777f3af4d32db6b6ddc29ea137d0c22af345dd26116c3c966872aa39811cfb7a811244f24aafe448264efca0524a82b3dcabb9bd6eb6a82a6db403f627567b2b02f44660d7c9abc20c009606d792da7cafeee631399097f95d626391a72507b2f9813fca93cda9b878fab0822bf9820edcf5b1ba952028a08c35109e 320aa37ae768e12898787737ba8f7723802ca6abc93fb56550c14b57c30e 22 de31baa4a5b98753d600d0ac0049d41811261385ce8e +7c23cea93123edd3d9e8b4bfdf2259cd284d0ddcb7a1fd64f0a53e41be2c7a484ddd7f2b95faaf8a543eec317deef189b445347c3bd4186e42ca03f09bb80f1c2a996ffbceec8ed2130ffdb6034f14b51d137e4376952fb4cf8e4f17c040f88ec1e9f85463b0c70335c89c0b3ecaa05d55a7097b4ae8cd3022c7b9977adfbec6d81a4ab98d437ae19d5646fe0d5ac3037dd224516c3d2b79e1ffe13cab873bfd3145fa8b2cbc6229c7c58dc882095c2ebb43ca1b347896dc5e7a8aede53263d28fc32b79321859f80445f0b3a3cc41c3992baf6d9ebea262c1826ead56461c38c703d821923bce32682b3158a930223f555905b1c7e926c904d5513952cc4bd7e199057941bd033eb4d8f04372dfa970815facea2c807e6626689866a60e35081ecb216793b479a4c5465e12757cbbd3cf95ef7bd0554b86413faa4a485b86ac9508b9ce76cf0747bc89811725664eea39e8f37f67fd380395fa269ab63cee33b391acd226ab8518161833ee28c86e86b90837a24fff88f1feeb59f57eb8b78e0a53bf11f444b7bff291cc3767d1c402d067de9387d1513d789887556874b508072cfc7a589213e051747caf3c4765ae8f71b05a5c00c796fc2b9eeaf4719cafe53419b69fbc7dd054459b512fd722ee495ca06ed430bd47fdfed794da4d7e59a1ae9fb3bfc7063b24b170d423ac5738ab00ec20ccfae0f30613bfc603c34667cb7c34a0b6c26d24da026c3c01c88fe23202d036769ef913d2368572d223e81eb23ff0766ecbd543c7da61e0bbe9108a4acdf935a7e21d9aa9beb32272410de14ef52219aa0ee6264dfd065f27ca1984de1a2ba4f7ff9668b6cfa0e2fcac7ee5c5939d629bb5c8e6b0935af496d572c5af37bd10ddc36a23680bc8715e2e155c1d79f5264adc23c2d18e82dd587ca68d8af8d2114b97e69d2b112faded97702d22fe66732c80723c4bc20e3930aa2adc48b24fa710989aae49dbfca728b32ed9ac66b3b937956cc54252e623983251324cc576e11e344c24e2349e701e8757cb6ad3cc99db6aa3f13836121b0573dea08fb6 df21e725154ee04010fa20bb007a472e3a9cb2585d7e9dd0088068d65a8e7a7e51fe3f 20 a486b25f09ac6b6cc238db378893d7d7f8412fef +886210df26c7f6c2b07667dc813f5af3d5d10aad3553fec29ad4ff494eb0a996b58c1d0341870a5a75c158d4e9ccd802af6151c4b27617ef3c3190ba10eb87712e3b7c69c9ee9eb84b76b6ee19c7b5a6279140ad290048767496052c83f428f9063e929af5bf89435ccacc3c451c66487a0aead6671a60bb511bbcbd8b4874072075564aa13e5573eb020b851971983867215d967371b38c0e8c108d1e0780cfc0d4ff668f825ea5890734af825090e2c8f49f32c498c60a36583eef8ab02642b80e9be9417fa0bc31205254695b0c785f69a101640a3575adffed69ceb88ade837611bd1b1b2b55c513a6013d8efd0ce0c12e53625b6127177bd0e4c1fe09dc53bc5371fdfe9b8235ac5c79fef613bf78c85fc9236f135ba1564a63a07143d5649812d5ee08 58eb6b8d0527fd8c6d16a0591656aa1978574eeccc6f549ac956ab947db66bdca2864ec19c1053b26c50dd2f17e01866efd0fc52 49 aff241cfce6c89d093c1c2d7b43d74ce4922f3432ace24a010d3305489f4ceb06fd47aa451d94f78fa8cb7624952e3e3e5 +80dd33ad80ec72aa8e0f69cc519dd545c13d86b11d44e0b27935bb4ad492c336754d8695f0a9b0f49bbac7a63a25b42c286dc7bd656a85c9af9355ad7d251561c4c82caab17dda3e17e6b21bd09f54db0fde9b92bb21191689e7f2d6efae04cb70ff22196952d09a97b9ce78e2dd50161e67006214e43096cd4cf4af07b17e6986cd2519e87ecd189adc3daea782aec81b27936c460dfcfcad0ffe35022495e421643bee637b65ed0d3e72ba8f744be971b0a6d386890aad7b3462c929eb2ee0173d6d2b39b2c65c44a82a7b3750b15b45e0c841e6f6f2058d7fa0746228e12e8f95401194eb61f6d1ed2f7a75b7a3a1a3fb673c8359a82571c02b31c6d9f581d705501f46c91fe7c3bce67101cb7e7c5ee3882e66fd63630aecad826c1f4666090129a322acdc3b16da37ffef8936a888b84d9c7181bbf5283ab5a3c09baa95ac5d20cfd4a61a83521bbd9d75d65353603d5b77b6745fc31a465ba916bdfeaa14a9341c7be42b20fa6245e6c62dfc624e025fd4720ac1769ead97b0e0c54eaa12427e1726b8e7926b9e4f037a0cd2a36e4003dcee528ae9cd67aeec8ccda44a34c90696c03f1d1c861f4a521765bf4feb1e6129ce9299d00e2ed88b57e90418465725d9c3f7cc01465ee6ac426e39f73e9cd54d9bc960e6648184220e2e5ab37e0372a070ba99c3df8c21c0d91ec38150ca39b6820e0df879a2cc1bf753dcc10a229ec09bc018663264f6d8ec07235ef0fb8f5cf53d4c2490907e59fe296c144916683dd1cb12bdf2fb3c3447e647805dfa72dd946d38c4738ce78940f1c8c67d66b1a99ed4d5f0ca7981dec3d756e7f766649578b44853c1b93ac72dcf2b35ee3fda7086c2dea98766da6b1a92cd2333bb7739f41e825205d02529c2d36c4ac7c114f5f7431ae5139daed0327f7375248e6e370c9f01bbc1be762a05a007fd1907c22594617bf35157c1cc0defa522e9505eb9d5f2978143b210ee7ac0311df7fe52cf972ff131578539c5b2ae99e24f5898d746c5edc8094fdce1bffb358043aba83e0b20bd684e7cb04f33952a46924f131529f14bb46de46af20097f104867be34b6fef7ada9f7fbb453a01aeb0b3552d0d4005d928e2f9eca4db9c697738e82324c554cf3da62869fe0daf6efc3d0216ddee4ea25227ad3f077a2c6f92f36b8f31ba04b05d6ca4acf7f0228bca6f211e36f3df19e16b002aa175696e20438ade1468510ee85c6a06d76443c5e310edcfec16a4f5485f2cb4f025605405da232fe267aed47be66dea25ef7815827caddc918ea080a13a4fdddc37316fbe7c14b89e71 314203978f5ae4e7a11cf1874f368ab34f999f66c4c259ff63cb9e78723a5d6358e3488ba4af5d1a62e1a58bae0b68 60 5df01dd31311be0f23e67a179fef2a1205a1387e3387cac3df8463125629027da13075a71a41ebfd8216d226b3acd5b6f1722b07aae901510c9c50cd +37d6dc924de24c05e0d7eefbe5bd0029ca0fa308da95c151f4f3c357603369079daf6e2d54642a051c49575336f7527b6645fb343f98c516517d5c9ac1ffdb6773349eff5b118ffecf1f31651d2c826f117abba594228e80a927ea9ad7c72295ddd9ccbf5deea58420722824f3f7f32c594b4fe57c92a5b5bb29f5e68aa600d01abad6479e4de2174e770cc2409be3324f79c9d493f87b69f0aed39b00a65d8237d95741624b2cd808ec87bc79aaacb14ae64d949ecb05d96bd14f7855cc7ed77a60d2e14692aaa30afa7408e45499ab25affdef50a2256820b7d4378ba4a6b985905c75072d57c937b495a9406594072e835fa08a4d1695e3a4e8fd6ed09f270a3461b67a7ce776c5b0edc1daa6421db574fbb597b3cf29af2bba607bf414af5e97222771eb8de037f3da145517ec03a309f64cfb584baac6ef810333cec654a00641238a749e698150ee370bc4456779cb61218a309b08fe75f500f21233925edb7b5e2de78e8bdae78296c8fe5d2f059d20c3125ffbaa86abfc2b5fd8002fca6d0a3f9244fa499ce3e0da81b5b7c1e149859bf8e5c34f1a6495470808bdb2c7bb674066cdecf17b71a7778691e87c97ca972d5e0dea775b433c6fed22cf2b3989fd9382e39efc6f5426b193c353dafe2ef2e043513b55760b89ab77e72533b3a436f2417dc01f63a467389301486a19a59383508502160dc9b7b5b288b0f8904e0cfdb2766aed2af598fbe478152a6d4c875d89c4f2769effe2f144eb73645a8bbfcdb2aa26be2fe14e895f0203174b00ba58f9041897f599603ef70e3ce2d37799b82c4cda87a79ba2fbaf1923c176319f85d4e587b530982eed54cd331135fecefff448ef789334 99d86d71938b21eb366090f664dac270869afa57e367cb145845943efc1a0773 62 13e1c0205a8ced1a3924d28ba27d5bdff6d28aa4fc6420a699843aeaae34212d263d2cc6e3f64547e14eb39008baca44013afbcf40e5ac72d0effb600e2f +5349f0ee dff2ccf58391f7c3398716955c32b49db4572c9fbb2caf63212461ef1800478e1c0a715ab5e455971f75a238b9758da4b6d3ff 27 0fc36fcb7a499692c0a3aaed3b901e16c5f9f49a66a62573117217 +2ebe51c9e91eea51822764b75d7614e6900cee11cff8d36db67960ae8af4b4236f501c8dfacce618e857cce57aba00a8c13ea38efb12403f10b11613892c55fe0f15af55795f30ee04475bea2908fe190eeb3d1591aba4f30263fda01d96f5e664a5de7b1c69e538c1ceece0ae9b0e1e5ea108c52b1e9b3526905d5321022412e2614203b5a4b881edec642fe6e05f8d965a2bfdf75fb5228570f85cfa9b5240cb83759960161c10005c2f07689c24e3ce091a4903f1f51cb41f813da73abe0a1e66880b4782494361fe1106cbdf0fe5ddd422eb4a257baec4547a27731cd61521806e01ac1ed60f7b97b89814327ae51112a935410f9a3b81f0a409f7642a325f0e1502aa1fddb414a367c5d55c4c4676961966b045182bed1306b332a8486079266603f6455f39e25f6c4137f6914daabd6ce146c9bade0f8112d1056916cb69cdd85099494624f95359699152bb28b11b419fe3d200eaf81d3f2b55e317f461f7346a8e09801480e5cf5cfddbe8e6be23e06de84f640c127a33ddd2a6272e6c133821a9cc7be6b2c1a24781cb37918f31d9f369a472d7244f534f389d563fe71998f1dbab4b6e07c159bb93a527b9b7c25f7db25ad7e12758786485a53a41cbebdd6164126bc84913ea2ad224ed6798ba9dd782650078ccd40b8076482e9dd260b86660d333a7ab78dafac5847a8dd633751e578062fe69f92f5745be0d680af43ca924fcc2d86dc574bddf7f6acb8a6bd618403c343e208e1c5c076b11eee9626eada7f571fcba6f24e164255da7222052cfe2ec5c2c9862b64d91e79607576e75150ec9b966bb300887eae437cb9a047e4d98aafeaddf7f70cd3b8067be2ba71781d67b98f10bf20b6489a7523d47eaecbae57bd73d7605a23289c5520a29c795eb820fb70a5e270fa4537ddd4434999a16f24f83193a190091a4828671ddc2a1e5c9a1ffd06c775e58499addadbc0cc1fd57602f8ef591eb0504e5c0451aef01a2bd07462666ae309e6060b436b6b7c9d6aa6b4b5314b1d0ea49b6cbc5f0645e3cbd47121dfc5a9d30810ee029cbfac4385da506371980daa8bfaa25e6950e21bc7a9bdd820b015adc0ade0ce562a552f7f9ada99b31c87ffe04e39f92c8c9abe480770e3cac7a451e7890158749f0aaec7eec6dafdc614af9f51b2acdad0c088e7caf636da12e945b11a34f6376a94e3e9847719e32f503c7e62a4c54fcf524ccde8ce624c2b7feaf37c9f29173eb7e77e04a99dce9e156abd086a894c3f3431b21594998332d7cb0dd972b650270b072ac40acc09cd78940708d23c8d5af0e1f814b93d4d06837ab4ba994aca059640787c35252f076e02278256927552f5c9bf14e49ca38b431924021c2605e071ed220947d39560b4d319279451029ea43aee088510a54e835 0a7942cd65546e8811bea7f2c340b407b6d974a1fac96cd20b5e23190538945568e0 21 a2e9daeff6e2d3929210075216c830b271d646a253 +fe8a4578904827e06b71039823c1499dd8513f3b83a31eb7b78748558326b66db13c11a237c1fb03e619f14f3087f4e9f1a7ba9e689c20dfa49896ac97984d61fa75f0c46dabe617d6be808d186a221fdbfc4e5244d25b0e1112c51d24fa344df90671f8bcd448e6c125aad935d1c6173a39acf2c78dcc3353dfd8187ec5592cae1ec6656814e7a5233a2ca02219161bf8c59126c1011d4b4586aaadbcbc57af03372aa97ad9fbe1190534d7354146808c830c11080c0b1818b792d8ffed92693201a03c8b44431a7ef30b5e8f667f8691b1138e66c5e8b144d9d49d168fdfd63aded3aac18b35032ecc7b99768d 7066679d1bd485b5c79d3e3a06b5e0059f5fb3c49c76468e 49 add7b3fc020972ae59fd34db314455c21eb22edf6f3bcee825cecf3de774bd4484551cbbf9eaacf98fd83faa826bbe8e89 +5ed1e289f5725f6eb0137b75f5067b2945c8e3449276f80fa9141fd1e8cdc34677b73e9e7f63d5e607a7155f39b3691ae3b02dd90d0625fcdb1766677bc79aea7569019856f968d0be663f07bd9e46672f588fed481af0b20b3e5ee61421a5df71e0f3ff63105f3377023c452ad73b2f4fa62415b533cdb0d50507406d282ce2f2e2c46bca3e8570c219b4d66f62bfcf32b92e461b71bd68c24b69d8f54a55d46d0eec68cae75d9d8f88fb06c87e1458a36f69e6318d4269d538a4cf491f36fb0b70976693f487b6ee8ca00b323893ebc94f8924f9bda0e6890ae4d40446fc858589d83bc685317143fe059254feab9fe76a27c9f135b6e4cbd1e8007bdbb78255a76ae12574c046099923b12b2fe12a470d70e7c39f7bff0ea00d4ca9ed317ef5cfb8817b0a45417916e5c5b3801639c7c82db97165eff61c662cac42daa9bf68f39e10823ffab77a3bdfff2dee76d4200c902e89ff9119eb0c5760ca82539d9261edc5c7b0386b6d2eedcd22350c896585e88a03749cb20aecd0e90ce802beda4fbf05985b384548a50345a71c65009bdba9c85a3842048d88ff35cc9892d5cc64b276c286973ef36d0f4339fdf3d35f9100c61dc7e1792ef1929757f25a9236267d066087f0e742ed8c1514ab3ed6403bba7ff67c41e19c5658937c8c2a992ae31414e4a21ae776ffebaf8b91847c0c5da3dba521db59e5cdc19af6ae3ba3e3d0b8e32047345ff13b1560d9f9925e7b58f1fdb3703cb22ab05e5eb1c2d3251d90f3bd1336f7e96c20259b9ee04c182cdbcb48df0043cd3f314d9179e5735704b02020488c4dc0b89b5a38be2b972b0dd770c48eb9a1d480c0ce418b08d4d1b5bb7b556ca25d9839a8f0d04a6abe6b8a108204f5df1f3edd39d24d1e1ac5c3a2b71b1c1b6fe57b5e0d7c9e5a9a681aea160692d64706cbf344f0b6072cf3ebb4405c54abd97a163fc6f3cedb78c57f12cbd0e904684972677b58c781280d426aab5d08d4935ceb59faaab3c1aee2a4ab284afb488caf123e03e9b881b5537be1779a4560ea66f35c5cdc60b5502dbab2cb5dd9961377955f7dd8d2d47222e043db550d501a19974683a53542df2b36aafdae261bdcc113138ce780f01d346b5100ab9c158f2f18398f5be89c85120518f701abdc5673c05d204a1598446a539e3bdfc057215406bb5a649b1342a98880d762af37616283db323dbea20917f89ee8c41249b6fb31ed77efaa0d1777fa844838eccedbeae486f318dd508e4271d271f3d1baa43ba4098134089551598de46e12904b025c45d8da84a8cafc28466511df79f8c4fd70f8d4292b4a2bb3be7efa4ae58ebceec71c39c6691351f4d9740639030999e63a757362797f7ed992582fe9dfdfe78e0728e5b8d742d45107bded313d56115b21125c77 3b279c2f803aa09b87092c265754bf8ca51f349c0916e91e46c71e7d1e71c9a5d8e8f7d77d 64 167f64a10f818dc14ead72fa88f147222f7e675699b60ea8b3d0ab5be18ea0798be8e6255042fd5119740f5ffe8a6acc41e4c63a80e9a64cdc0c1e93ae63dc22 +306845ad4d008bba322d4585d851fa7de6c159bd4217e28dd1d7d8f72b0c874d9f5055c061348d2ce606c21d2d91f45a3e068fe40b56137c3ca4aa2968e3c7890269757c13c7981b9794798d46d80980de527590c365f382d9deb36eaa545563c2c35f803ec702e86fa47fabf172c8967bfc0f78f50390e145a50e45609edfd4e4875261ba5943fcc0762f00f4c1230343217361c4a5e12c5f5c21e8e7ecb96d64b338fc814c919be098874a4abfa9454706ccafdc52a3cde439cac049caca494772f017cac9a9bac0dc71c1ddcb268420a39169460d72615fb63808d3889b7d6d7ad8cc9ba5d77aa415c6c91386c0d47b151378 ec74036737c9ea524b8d07af28a0362f4bf3b44746a12849bfb51bb0 18 90af3f3bdaaef8ce79b0e3501cfece88c64a +5e13526c6fd82e04cb7a57264577cc8831f6e764bd1e1b51e6de78bdd2595d32a0aabbea7dacc2be8de06f424eae425e17b7888dc05c7cf5c0cfeb206dce2e61e6e154f777a9f87b1847827283fcf1f331768ddc69587ddd37ebca1bb9c4afcc9bf37132564b8c2e6c2d7b174c4f3cfbc191ba2868ac3aa33b536eaeb819a2665c1609424dc6b49ab4a9a1df0cabec4277f3276d2c948a71866c0f75ead3290be785109d9f799d75db9dfe3a2532a0c0cfd34f30a1023fb610406f3e8396f051052024966df5a08a5282e9b66af8c9ef2f5a95bccd8f52fcf08d324e111bc01c42c231133084171dcbc884b20bd64ff13c07906373a183dceb27430f585bbfbe32ca6ac29d99eb15e3078e65786b84069b3d9dc68aaebf525d3b9a968560ebcccd6f0884c076d4f660dd607c1810e851718150e4853fa6969284c55b39ee12b81f91e58bb9ebee4c560b69919b4d16541a66277040b6c5604ecbcc44cc9b8439abdf773e309230193ad3285deb886ade42edd84370c75335f0de504b573d5742fa3eb33da87bf0c13b00f4f0084c6f55f4bd963e491aeac241c865379fc5f369a3014cd6b823f54f2f59f121269c4939ac9840ec07472f5e7436c284ea79f07a05f5fea4f834cdbb7c151a93832eb2d8885271f353a3f19ca772dd949385dca2fa84d888e4cb150e29be339d59be2b921808916db51a64b3eddea11491b7133104e439de31dbd42326281a9b092dc5e695d1058d24c2f6e338c5da2828cdf672695a2e12cae9776e4e069bae35d1bb886c2a978283b75cf1ee42224680ea19165b8fcc1b2c7b7c3ab20be59be9341b0dbc3fdd66 af5c1c9397c868ca9a08c50ec165eb01d00bd7b78abb3921cd3446ec7ea8c1d919c3979734b432a0b3acfc9b72699a013264512c671619e0219b96 34 08777ffd6eaa764cd459bc910d9424cc122984f851cd55992982660eb598272e49ed +248f71d5760743a417e921bbcd9fa6e8f1bcad220a6880688db162b8651cf95cce4de9ec19ad56ba4f60763d3efaee9950275d54fc90a2fac93f5977b9dcd6aba2e608a0fc575d06c66c311449ff4be5557e315ff033d05bff9c1c1148f4ce9818ec3850741557e9bc3f02117aee3736f6e30945c1c4c639312c761cdcf5735554e7 8fbb413212dad1d85b6de312d28ea7cc955778922b3a772311 34 9da3c63d629b26514f43478b162915cc887cb55b61e3b081274b4227d4f8f24ec9ca +47c338df57528ffb185420c6a0ce075ba7e56ce013eb159e2d6fd961c67caaac93c0d1d230091fdbd17b0648a4d674dd232b66013b31f432db160d0a48 d33b54ed63fddde49e6fe7d824082aa0953e7ccc23afb659fb86e02deaa92b6c00b36e4f1c0afa9f0c2ee3766fc812b8e7fb4d9b39fb46d474a102f8bcc029 43 58eab40e5e9b5cae950b4c34f0a7f821e8fbdd3af84a569cbcae761280912071456af863beea2233bc96f1 +c8c1214cadc3674120906903b2193424bde831b8c7a38d3545b07248528a2aefe791f32b7cdf3b957d94b70e32d4e86ca5626bb56122b260ae522fa5fe866f5ef4eebd4d4c7501538a0168fbda21a60f1aee6477325157220e8fa8c1789336d176ba06ed733b8e23cad3c70f24155bb04744edd04fdb43d3fa34bcc9e7c93c3fb490aec5bba224e70d77565eb7a7b3368c1f097b59bdf92b6c3fea7fb539962e61dfd3c4444ca281712eac2722c5b6f7d7ae9aeb43fee4b6a3e072ad2efd4b3c6d1e774df6ea37bc8e311cd4718b3edabd292107de7952379c0106cd9b7f57c6dec57d44cfef5988a9cb52477d1ff62871eb26865c206e27053858375591785032b8d98a3d2d764be4afc8c2e71356bc38b379262309b6b2a9fdd5aaaefdd1cd901d404ede3c6246cbbc222275517ee5ba50eb2beddb2b745f70fbbe41685b8429563e79c648d066fc6bcc34d8c23b3565a5dc6bbd54411b86c760521b2c2e4dcdc7c155500821bcd32c7f3355b7b77cede3a135c4138776bd9cbccf43595390242f305a5596264bd6feb7a5724ce15f30e5b86b47210a104bddcd6adf2f25c4a23c0efb8f5cc6227dccc6e34274fad23edf4b1190d98ed7d288f2aa7a82372ddb87ebc94118c374111972da454fba7adccec9f3111461f2d76d5d7823f33d6eac73dfe15dae91c91382a5c52ab3a4a2e7e35feaa55ce4d9e61561c1db938814172bd541235258b8ab8dc1092909686a0857d4c35e28f246145a5660388c0444fcc99d1ed6e4fc6af451291555cbd77401c3478522a0edce754de83f561cea9f5d23f977736274861d10636ff68cba720f8b47e5de21fa3b7123a754e3f48bfbf24461120b90e65690174bcc451c49486da8e35e16765c7a65110f1980962911d4307e57d64534af5016d92495e5dabe9fa7a014146bc74a3877c83f9a3b5706e344686a1873121d89389c449f7263caaff4b76ed43add21fa2fc2367c98c988e9c330e791e9721b4b8fbc33d832f5454cbbc8761e0af683dad5e37eb7302f9d3e09ea4c7e9b25d4e3e018bf8af11486a08e7f90ee0718c8d691e096c6e18424a08cbbd72ea86038f23ddd2955593fa92f3af08d5a742668f31361b4a9c7ffda214d85066d5e0e61318401887cb6f537bb7b3cae0f824d7ccd52897cde9fd17722a309221e4538c065c97beb47 277064386edf26314debb85dcc87f74cd66e 37 32aa6ade4a0c1df8174a578ec3a084c1e6acd135cfa1bec3b270327ceea8879889e6dcc915 +5d34547f399ff7e0acddec3c0e78e725315fd59f174610fd8a6ea6e4d3c2945e62bcd9c5bada82099b4ab659f4177f93001e1fd39ac37a9f07192d247aa7b9c174b4d304c772d1c0bf249ae75fe60b2fc927c133042c8d1a9d41baae7f66ceb2bcf9fc3f95dbdaa53cb1a6c19bfe5b375d8ae345ee095bd4f5bfec0bd01bb43d5d506d8cc49e786e006739eab695d67b6fa2e87eec680f4b86f0ec7ea6254b433889c762b87051c565f2e2287f48f52224e64b16d066a52cd9d796126e162f606ca2225deed5df4b9250741988c024d52e712ee80ea0237a596c03464f1b585e9060c972341f8f098b1bfa32ff5c75cf8a365a98f47f793354c4932c6f06fe1a12038ea1ed4b0f9f5ec19b634cde8412ee213603a7805f6faad3738dcceb3529e53b5e5d313449660ab4efe626aa6820bce99c64f1e5bc64cf4cb60719aa24db1880020dce821b41e46aeb6e1e6767abac8823dacf37eaaf8ae6f3a6c6be37a1bdd1ef7afe74b651966da1e1adfe0a8e97f6d7bde8711a58083963a64e28d703a2ea16091e41b51a7a06df5d0488a8ac19c9998b5f59ed534fd353bc8a78fe5c9955dc3410ad22cece448a2f1bd1920f17e7486209cf4dfd0051396fa63ef090a79f51ca993f406e394e961ec384ea50a0eaab8eee3e3e03c24a9e28c53da313075ef936a3105493fc1d 4a14a00d359d50dec4d97c507479e0253f073d1e945dea02b91198c7a175f874a3e3a98e34946301 45 efab0ff7b34fba4cca43fdb19838a26005990223d95c9ddc3b602fa034046a03e14378ad89fe53d6ca5a1f034e +d38ac9257183ca95b9755f1c6ddc7ece4ad907d39af0b49ce7118079e883d0634124a74aa7e788b6816856a00452c8c82cddf8e7f2fb21d3b037a60a7be869616373434f770d9c7cf4346789af8eaa73a592b4432875b33b456e83db3c16f2a173af5c4c8b83f1972a13313e482204f1dc3160c2c7ce62b851a0b76b041cba3a3630f11018a0c982e69c5161c0516de58a40c7259f2d385aa90ec0c599ae26083796698d0cebf51a59fa5d5a208f0fac726d5339bc89a8bcf37fafaf902bd3e3ebf08a722a38bae451d795f2395adc1f423e656b3423fe263c27504702b476bd48836b9349660a80b6963733096a8d7d538765da6b369d834818943b5b73f4fe070190b3f3009f7a5288a3659098c41b1447f412a56ae168edf071f9de892b82807b49db0914df202503d8f8599e5d7d0612d7231e607da34ea127dcf35a07670b0b769aefbc2290c8e118c5428df5559f83d48cd10b49049268a66629aef7f7fcc1d2d622293cfa0ec745c7511fa1b61e6178abcf3da47a86952f1bde6a81baf2d1092a91533bfeaf35c5da5b86aaf4ba4934267acf7960ac88d3dc3fd7151ddab65da63b195559c23c1690ebe25040b043d662b3a26b39dd28903ff0cbf46a80d481723b92d0d3f6b3314475b2a19af554be573ebee1f60c3f5c1080d4ede23da11f62b18019b5bc714d572b93e10d726e4b69c12cd3bb6268ac74c2b588e0ab8546c2ff962bdfd651fadcdcb78d0c398c5eefebb866397205507838c260ae4256fb97fb3467c6ce2e514d8cd4b7e17ff2c161331c404d113e8de88f5253edfd4e952ee04b19802059c355a1278f44007f026e8d72669333f83c142626319d58c3274dbf1d2e8f3909c1b18a6c2997fc2108393e9ec5fd42fd3e6dc2333a5f91658049cba8e6c2f42977997c123f6858e720050c21d168c84545325600dc4c93a24eeb8990fff67e3e34d6496c72f3f9a68077ec46466d99c62a3df15b68b468ac6f05f0f44dcccf17cd532863e3c3eaeb52134e8681aeffbcaafe615c52badc7a4f008182fec418848c60554877e24b7af6d2be45cb06dce92bd39d764dc64b82627bfdb2f5bf937ed9b818fc3e0f0cd09fa28e4ed5f05aff5b192b513a59cf10e2c70254c607ef2716fe4af3b734937e8688da1ec521df68b1f422672888fd4e669cd62f88e33001c5312869bc47e7ef6529a6b6615e29f85a78d763be8c2e472a729bcb0cb342c9ba7eabdcf39cdd2e974024d67710126a2ebb81909e2b906c88308c1bfe52e009 d30e8055e3e6a1d6b74d73f37fe361aa6440da576b5d734ff0eb31ae2a934907659e170d803a9adbc5c62ae248a1b5d79dc728739eb5ebcf 33 ca3f8eb0e8a823302748c2c367ebc43eb4c4b14af235ed61a10fe94c47c576afac +c3ce2f359c86641e62758fe49212f7d9a5e5519a944ef380141edd12b3ef5cbffa623d6c4b990ec079db93c080856675eb043efe91edda7fb44829df27f371d86cfad971d38d1ac9ac73ffabac3af97a7179781327fcffcc0fe54f53d638d7dd156cbd07ce7e11180ccf7f74b0be2e17ffbae6039193354be3d4f86c34fe79330eeb6073b038ce8f5e9b901d5ebb9eeca06ba6647123f021f48b3b9d1cd965600be7a617c0a89ae45571ca14937620a52ea715aeed6414a8c76886a54788b6db11dc3ac586e42e02560021ea0eedd060e85b1510d2aa485cce4756a2ca50229e4d333a9187e0d3fd7c709e5b30722e6e09e2405e754df5ef6f8b8b3f402f1f1277b1cfd80a6170c38a300a17ca9cd0f4b7c814a6cd72f587c9a65210ffa6cb501e4c482fbc0f8631914db4f3d176e7ce6add61d6785ebfc167c3b726fe7e9e6718e5b476fa0134c4776bfffaf18738e4e06da56c229cfbf208e759ac986053ad44e9792dec23495d865377f5211be22e6b859ff3aa670cc719e8adc12a2985966f56c332c554a4eb9df2e350012f9b529710aaacd9942d0e31a0d51f4c6dfeb7122caba14fa9678b43337b9229e2c54e48f09f0a2fd56b557d7438dee06c426ece9afc973e6de579d081d540d8eecc8b1fcc384cf4391248ba6e819159f45663e2da2be4989abfeedb1c63bcc13919483fcc6d11d2dabbcaa465a8b0dde42079356b57d42110720275df30cf3dc0dd8f584f392c4cf7c21c3785900f82721467596eb0ebd1f9ac400a82c86504bfaf2a37c690c51db6a5d0d5b609c29b12c78b800aabd5660708760b2807e144a24f6d86224858e2f04d91df3e8e252c73b654f1f20af7a10e44be4acfadd139d4f54b6303ca4767c959d4d220ff265cf20bfd284145ac519565604c40243779ac378ffa0a6133ee860059218efd06ba124816cebc85c6e403f50f5fca9605e5b03bf800f91dd9d111597becb5c1ba4ef6e893814338e3122d4a09820e3c67842db35497c5b53026f4313d66cd80a05ef36da07d5fed5d7b7fd9b0a275305b80dc236d53e722b351499471d06c3d80743f82a079ddc0498a8694951fc75989abcb62144a2600aa2272023eb8e0e929e62aa4bce5a4dd66ba6563f78774a3fd07179c32b225c9e23b2634bf2713c7074eb09ecc97603ba927c9 eff71132e385c0c916c0215b4932b466 49 67755b29edc437e8a8382c8b1ff73723ba6f2a07925a816b10549053705269096d0e71d31f04bea3b7970695769e471474 +413747de85c0bace53efdc3336ac0b9415fb5596afea71907e7e2bea92c5f17356bfbc7c2cde6877bf1f955dc4e20237fc845b16e288c79ae43862981c3d37413fcb71ab5e358c1c99489a7a3cdff1b0e16a22b832c8cdeef6093dd04727f34514b762cfc20a9dc5903f9df8df2490dc5f657ca1e21659aa3b64c757b8a916434ad95dbcd0c0c0b25cce50df67920fbf4c6f8f479e04dabbe43305adc484747ad854b0f4d145727f1bd2a6191c7f04a73df7cda8410ab8165b97ecf385bfeb63d0f0f9d0714805ea04414834cb529027fe0a75a948d759846594897368e85158acba09807d44f9389bbe9b3adb33e19d74e49e5f5dcf818cdcc341b5210a47f955ac2f8d550a311610ac612b47a9bcf7d4c00ee53237543000642935fdb6cb385de587b146f2b5d9032418a96c6f5c09b42ef1952e33fa4b2d77124f04947a8583f63856a732e1085e6efbb501493def20a2cbfcb1dc1d9562f0a9a27df3bec01d6ed8cc73ab16c0bad113cbe53c610024cd87f16149ff816d6809c0ee50bacde4e1f9cf5847c93d68b2ff08dc61cb53adb3e0e6ff100bfc4c37df29c73b8d3dfe23180dbf3553e46fbb683af6d75402ab29625842913ffeb4f4e9766096dc60b1dbbb0f8a924fc64dd9d7c28403c032d1ea3633e6a48a3e20a739878f9d8bbecb6c5c9ff73186b4a1756b2c3259bed0f3a1df15532ae43202f4f685f63f5d24b33db5969ba008537d32d456b2ef2bf25f7779390367f5f74eca9a804a27bf13a2cc7a7b194d3697ac52c3147536576d8535c7795b5cf5d92d941129f789e456944968181c5df36f55b5bad6edd5be636434e8a0268c816399a312783e53fc18949eb42605a520f3a76731a426f96cec95ec576a62247ffbf67c66fbf0fd6f24c1397ad9fe7311a5a30f86ffbb5bb05771e5b908802ec71b0726965273ac79a39baf11f8485bcfa30be079b87ed288240c5d12a982ac2873e679d8829fa42236c2cf9d8933a9c94e54285e38fa8d9af91c76ab9ea3d6e29e878156a4a95061c1461861f74cf2f309056cb43d4993868ca8714384a98e59a0ba832290f0472a4f73af32ca3cf286b449d974454036b95f9233c235e129a00277da7a4167744e319403a325309054233bcc4152e42302b8be251ae1eeda7da7b0d96c50fda756de320a6fe5754fb756de45cd5f512534d1037702003976cc928a0dca5c7bda2ca06e126a7680796b14cbb2ab1429243aeff2170a30b3956c2f6425345996231ceec628493e56b2ca144d5ea83f0a2278d70f4932daa6ad5d1e73cde9a7f81c68a5c10ea73d9a53301a15499bafc05e9484621c201b3d917c5107f7cef2b40528882123fc47d10b763f0f187fbab45e022985435fcee0b7ec1a0ba34204f334117f52e8c6a0df8087eec1ea27ac1b2bcc0d9b7d8b2f5fe215091f9df4 03de747f07653b95ac3dbea167528c9b53fdf433 57 3845a2e92cd09c00030dc5f2e0c3b183005b06062d2746a179221942b4b36b5f4f3c9e7c2f6f4b740a2c044e7926a668907675fbe3d93ed2b4 +ca4164beb0a82a55b17dd157cb41af0c006115a925614dc155f1958b3d05e091ea67ca76fb45648076f36acc4409d80ae85ba5645b4408ff284f4ab7ac3bffcc320ffc89c32cdf7c8102b796ad09a1590c3d9c9aeadfb94a5668ff5190f1f97ffd26703691f2fdc664fbf2800adfba8c13de7143dc351c7a9819cfc787af5fe1d5601db220e3c6cf31343e5b2323d55969cea63c8e5528000a78134695072b8d1a0a3573a3c90096b6de3e69e87aff101a4dbd44e2aba9542e32bc4ec2736ada49085cbe8bc7a11bd71af2a061913364045b470b541389c289790c61675fa7c3a3e7993683d1b6741a4ac90b624586bec7d001a847a48d6d009c742d14e4dbc915d7628c47e184123044dcd90a3dadf32ff447643fe160f0c015df984bfb9b16271a15720da12c6f09ddac852e10fd8a2a922b09fde3aa09b3eaa38d200d72baf3bdf98cc795c89901fc3e8db87098ee2a8a3625b6dce0ba27aa4e5685647b2bb23eaaac8df2d0ec52631c1f6c0f0e460b6931ae48ae84b93f723cb8b5ed44739f42b29974373a38e1e2e85c144a689300e94c495852718f88bf501d7ff1a4ecfb2e339a25e8ba26c87e9e6287dc2ff39d511011feabaa6af9acdec82906217dc9859f22a404ddd6b033f6958d6a789d5487dd7a0a93203f05abd4438bfdeae40e479cb263c80b484d0a36c92a876ba13d8cfc3e2b4de0ae92b964ae1fb6bafdd4349562b2053595e79644ffbbaa049afff743c2350f8300c4a4e959f3edf4fc0a5d592f1cc774ecbbced263cabac2b5629693d4e17bdcf723c8ac9ba311894ef125969512a2ab334685c056f6a837c55085a33fd388bddb726fa45a6149ff42fe596f3b996cfeeee13eb9598ff0a1c00d8a61acaa892691d64ea356d48f7bf61f77af24b14ac8f5e10f0ac98864b6c3ce2a34352a3dbeea294c8cdbcd03f18c978ea58fb3083cc73b7c3281c0932f1e42d3017c27321351621e56ac8550e1d3605a22b81ee6e5e6a2cc5bdea866e4bd36bfcb30e01fbea9db77e90a4b8f36d9014d286445091845a9fa593107fc33b6b1ed8c794ebc27fe9522ae38ed598c93a37522b72b b66014b4101b41361799baa430078e0e0f37cacfc72977ff4cc03bd1bd982d8060b6a030b6e61a7c1988ba001fc66ccef3a6915018fdf650349f82 34 57e38ef2ba1ba1df2f6543ba9cc1688c725d2c45680ce4b0ae0986dae3dbc4192b94 +d88e350d4d633094c0390aecf8966ccddea41e82a91d7cd2446461ee432c9c18f41df9bd0386f1a5cdebac2e2943d7e2b007631a5d95c50d72e2294036d4935f3e4c0e49cff7e466a4f0dee49f946880b40b29df8c52e68b16c8101ecb4369fd228554c98e39b47538a83c7d71b58ba994689ca4ff627b83d66a013d19d4d56e85b4d1062e7f38b202a5d9e54aa39bbc239d7c8674547ea05edb39a9794005bdb56feec26b627bc56953c065d0e9b40d6f56ca058af97c99746cbba77d4bf258172358ed17441646834faba8aeb852d8635846ccf57520bccf804494f7156348615a0d6c3e4fbb7bcafb275704e5841537ca6410b9ccefcf977681c26f69c056d1fca5e24ebf2f6328215d813672d2126d2860bbec5a3fdda45bbae56fa758f5a8fe6e90a1f8f923dfc31414b10fa9b83de9345ea15c178e7c8025cbfef5d5e766fe16a93ffe0db1ef6ece545b34fe6e475947543737297f9c64e5dec02f13cbc586e9c53181147ea91922d26d819c4e65d0189f0d5ee6e455986a49c875b97f71222ae8580f017fdcfa23384356dc80949b6363b55bc6c1cee2446e42e17adda58cf5f362988a679fa712c5505c37f8834f28a6473c1c8fbbf78c5e97 836cd76f6b0a0c6a7c1071ae97958974cd3b 19 61b65caef6c3f060c327eb7e78874ee8465fe9 +673bbd1fcb4b21cb71a4e275a2506ef89f7884039d2f68423c29c82c24d3d5476308e30d1a7da1fe3c1b5b64d341d56b66a198c3ab59797e7df3bd3636159630b5930067fe68f6cf6e02200d0c177dc81db5317066c06f2c02a8f3e1efbd8c80d16e1f15a41f93face99c9abe23ac87b11eed71dd3c2db20d2d313e685b6ef1bdcf23877716e8c6b0fcf0c90f2e7ad0a87b25d199b8764489d7aa9981a535cc66c84bf11a83777115988be74482d33579ba54f8ff0869789eb785eeb76be5dbd80dae710139fd9276e0d46fb32eb6a031b0d443ccb01aa46faa541c0147ec0cd113b74367f8642cf65e23ca748c6731cd3df122935e549ea7c9c36043316a8eb551777c43a6337dced146eac525152244a510007ce5e9e882642fae4658efc08296db472eca84d3db605e5182c7a1a7f2f8dd132cf13d5b1427ef86faf37ee4d85e260c14fe59d21ff42834af1497618c23735a7a0e72ccf299369467cb1b40d7eeb6e53159f0ba115f501170020d6e782f30542cda2c9b00ee92f2809a7bb9c64a50ba860fb1e8fc41b8898f037259046b484c482cd2078669a78eab83e113076f25f76da6af1b80ab47161db76e2ff8d5d66cbdbcb7252e190345ed19a4619cfcccaaf3f8e82706ff1400bb7dc387f9f8978f2ef05dd397214c9b86944ff2dab4186a55f80a25c7dd820eb6a55a6dbe8cb27c16106942de19d521e32ce70828ba596c4b2962432df44d3df0d9e3eb9f74cbab166906f27b058902ff735acd4f3970a2858372790097cf767efd115e90785f7dff0147cbdff1c8a063e13b16e68fee520a96512ce0d3589b811bacf91b9bd136b5f81c748916eaa76b74abbd910325691d19c6b66d5a1eb8ecd795447a068310d41a6bc971a3b292f11455bb6aad579ebecc1bc9a69151b1746752811ad302f880d024c97e3b5f1bfbfa0009a5aea0c1075370d664e08df66afa5aaf6f2c6edbc1a66a527b12accd00337aa764c2498fc00e559b7623137e160de05e61a8f1bd505f4c8eff5de44501891085097157e3bff26a54db9f457c6 a241274ef127ef83fa938d386e209f10a67540990d47975ae4a8 38 a4980f752fed8127666a2d457cfb599eac0bf400b966822bf410a45e2879f50a34b21f9080f1 +baed26274bb4c998e8f68990f3903575e26f500d09d4a61c097265b3884e4210a35312cb048e901595186227ac9c1e252d150115ac1c090621874eb06c30fadf7b67bc8df2489ffde085ee34af5a128f45ef991f6668527e968c8f62a1e12d02826503126a2f4183d285dba1201cd14df024e2daeb13bd35a07b11a6e3a92708833696e9434a42f7e3ecd600852dfd198ebf7587d8c7f8a0bc9a08044b0f99ea32f2d00292c715f135e573d413689dd04022b8d79864153c0238782e49201d8ff41837f7c959f67e98b4a4d902e2a49dabdb959357883a02fc9a8aab6ca33b6b72 be69df9ae936f7847aabbc9e57fe6c250c4a1f1c2c3c1f4e79e9c7b50b71d7b8640b798a530d80f39d62eda43299ca0b 47 a58dc5b12779589c2092659f9068e0923d473b19bd695c87491e616b0af1d1eaf0888a285600daa305567f703c7cba +ee4c23dba5a950c3ce0e1c1911c55adacdcad5333736341f58790df21a6bed27733508af71a3ff4f0562cd240b5b842c5e82c2705f891b6fbd7ff7325d27221a6488ed7d4f64e8f75a2fe014f74c06b86b0f08c0b0524003dc9027f533ba1ee4d574d18f3e3a36823a5f5e213ff8c9a78d7fe9ceab1594d24aaee23b9341abcb78ffe7ad0c27a25c0be509e3dfa860fa2ad59456e9e47ba16c9363f2b311895d66e6885d1878cb052c6b92b1c316fa5e60445cf627ac491c1a20b6c260898de4c225c893eaa83e292a51d3ffa1aed3f875186731b22f9021b3aed8abe4d60740801507a67889a80d69a3e4c2c7fb0f3ee1fa57eb8beb77ee352dceb5c653be1dd5d27df0c42b2a50f727c034763cbf647e7922f95861e8a599e63071c164b1470ef5ff94c000e4034ecc23517b65935c4786846b1629c73ce0ee928bdda07f4fec41fbb328921c82b08d2a64e890a82eea1cf285e207f53d79a5dd78232b75eb90e2f16d0701257a969a34186a0632a44fb27399a13260c1883555d1f923df 1dd8834198999b803ede0dae272e6e50bcb92a9fe56ffaf578 36 c447bb67a9ba73b899949f29c37355073921c656c913197bb159a281ca46f3319166de15 +be2c3ebb765459933380a05f6aa3a095c2bc5d698da65b56c8f2eb71a8a403101286f65e674345de8898707ceccb71153f5eb4d3549c88b79cac7bbeab0b5d1913d2950e2529354a3c19731f6c75cd7d5d1f8842c9b080eb9a7cb08521c2b17923983ec54ab7f997263ae41e5b40eea6a16128ff9b5eda06766ab03c8e82e6342d39982b864df032e46a02afa0a789e9c254945171150e9131d7cba2d78273958738897d3b655ff99b020f7f774f882ecffa599e1e87e3007ad058d92ae56f3ae235c1e4e80a0e76bf37f55ec523b24dca689c9a7e85596de1eaf2c60336108a82a5b6c392bf4d4706656eac0db29ad5336de9af92012c0c216ef86d95a25b30a9b55114ccbfadc4be8c2fdd112683b5f4c35a004d5991ee5e69c725a614630db7f3bcdb9f24ba8504180cc47da3d3770bb2cbc6d21b5c908913718e6366663ee6ff7a6a940f5a5323209995a2c8329433d0685c108e0281f4d53724bfefc6ebb3fa1b72b0252040cfe10f812340a848c644d80f9f1522 21adaa88212ce94b22d32ae416e9dbd267bb55798a9012 37 7e9346f1eeaab1257ea04856d07db2543c5ea4d2b20fa6f283d8a2b2986b37f5fd114e9f6c +ca76935465db0a4bb6464c169ad0aa1c905366fe3cd6cd0d680ef1a6846fb565a9cd42c219e5f9dbd41ea68e932a422e9e7a849ddd5a879a3a227b9920c39e0c76edaf54dfba26d1e2da5e900f2d7cded00f59be2e5cea960db5709db935e20702ef88491145ebb0f1ca15899956bc875bcb14398eaf6a60742c02ebf92da6956d9c06775cd5ab171145811f413ae3b00238e6770c6d0a9cb72e8d1ae34bd2f91de8cf51b80cc49eb34a7fd9c7027473df3eca9fd4d108984642e7e86c81fe30c3beb237e63aa02923500feba19e42c79e75c53e0efed6ca44c3e54a8ae490b8cfe70c8bc274d500ebec4948736fa6961e858651c2be55ecc2a316acc9430036beaaf71e18454a214e467c521757a60bdfe9a2580b836d78c2ae85d344d3cc829ee01021ce11755910be0bf5b3aed1ef29b51c594fa04809f7c8855c086806f5136dbdd648032140b73e8ca88b9db1f10c2f1d2cee1c07d154519c6fde3a537d9985f2f69de7d5fe5bd9f279d9d474ec784b1bab4efcbd44d3b12ff19b0d479e45632ad1762e39376a4e0158126fb0805677906885a6336d0059503ee4277c82ee72038f0d73474c63a06434ab988153ce66af8a5ebf85841ace1717bd662aee7d4d98b684151ed899de0bc0610dc7bc1b0f5701caebad1e27191170d0ab5bfd5376e9fe1f63daa80b0700d1a94ebca029f7d2ae96db7129737451ac2327051a6125ee2b8f6c72c274b48db0446eac1a84ca166667781d1881b607b1db9842bff9950f416ffa8005a616e1b8a86b32d1fdcff7f29e36b7a229e19639930cef42906fcf76ae8434516c72d7c6359ce3956549437af98b52b6c43430d595f90953d68c9e56d9cd651d582504a3e37dcef65ce9447c4832ae9b3adb56a1d1974522de52b8540e511bcbf331649bb39b18120d740e36455e47bb4da17848b7885a6a149aca89796e0bb9f9c9987aa389fb60e76329ca553a742cdef832e92645f6ba549d4935f4ed53fc92db550c3e44b7f08eb88d192aa691a54e9a56f1a0c44e7bf29a2193eedcee9eb15ec02d3d93484eaeccc7e09a36a1589cdb9642e6807fcc79d81ed7f4a816fe0dc5cd4577e842c683be19c224356cbfd0eb 7f2ce44569909654bdeb3fbeb651614f019a2d0fe3cf36180701036355fb1fcd965c02d97393b257c1d135cdee7c 20 cf4f80131d3f4ab53a76a80a2171cdf2e4e490b1 +6f1fba243b58d431c16a3624487a95309f6c78f8f6da85ab8a541364ab887e48f8e070c9abafcb12094503a554d4f4f2e6d3e74a0399daa742df8d4496e320327dbdab5cc9547895307eab8c82007ee586332f8d36e09357e624a4cde17fdeabee550fea1ce06132f368284720d5663477a89a9d3cfcad42c53428c937a63060376f4fb7f791855b1ee80ff87c4492517c6c70fea7e58761d7ca8cb8d7bd7f0690216e0d4bd44309df93719087f4466e58e0b8ef75a6adb3afa32e0cf52db0e0a53a08991bbfda3bd28cb1bfd6da5e167e9ddde2c393569d45a02993fbf340f94aefbe389bf70550d5c488641cc24bc5a6b33377f9106eb52ae218a2c27ba0db0aa7f41b483a9ba8f686a644b21e9ebcc09983eee530a61b8c3d587508e9a96310910a0759a7f6807fb22b3252021d17923884a94cd6e2df8fa422f96cd93d930132a4207ff7ab64e8fad1b929d2e91af9eae25ea97fba4dfc8e3ebadc3ba6f9cd45d98014d8e2d6ca30a2e163ef99833b0f2166bf0ed831a096709640c4f7ca86d75d4d523626c6dc91aa31c8ddfd342fd9f13b75efd0d406d8c693ad5f7343a82d02433c60e1246c9b9aa12f4e0d17551da75601cb83ae0b308e7cd100825f23c6ecdb342027fc4b5095d12cf70bce1ca87e2b646192794f01fca37d18a2714d2f3bcfa71fffd1374d425b8660286b59a69c440cbb3b6f8daf000967ef6a67905fb38224f588494c67f7224f45e000890262dce19907b85213f10f9dd5f508ace52e7ec2b98254020820a46addf5d3d5dc212562534ce54decdf820d9c478328dafd06c70dd4d77fcc6ff7cbe9f311b47364ad0c9d04886ddebe81990fb623c74a252088c104022ba24fa4966620e66065c934ed2f4f10ade28d8f0adaf9ee3055bfd8427ee580cb0ab2b3ee158d293fcff5b62ea94543128255001c8325907a929638dfb1340afd2cf9fcd96729730b7f43baeefe4320624d5b5282808c51b1ed56a91f13c89ba7920f3824b74482e181f9b20d31119c68b6325c400010244eef0538d9d500094094e99fb392286dad1f26 1171c9e060e4cf3867f0c833baa04d1d8db18184346e77fed1302ac1fa868fc85560c3a4a265d6eeecac67991e 28 23d4c517a128167ea5d44179a53e225de57ee2e0d9eed6f5a993bddd +282f578e382f9b834453d81cd976f477df85d48a901988a08d64363fb5678a4223abf704e6c5cf71c44f843ed404f55d83313be98543bbb06f4bcc2f821478677d842594570f21847a42a65ac836500d59e9acd4f8d42d838f7b0fb8726b56590302982f70434e1b58d7c20dd3f5a1aaa6cbb6b4894fd5db82354e8a95213e92620d852d7de3280adcdf4486821c9d79c1421d5b959e33f8089892f863958effab1fd23ce36bdbb98af050d9cec36fca640399d2df4b311bb21d33146e9d3090157a7955f1d60c2161c90c3080ac4163f0d177118ee956eb398ac185fa4132e8b335bd54ee079b42a31f6e23e20573c7169927b4ae917270442e80c0ce027ea33d2d5d76802ac94ed3f195689010f4d02468300b171fb795da62c4d4d7aa380f4fba19cb8237967ae2045723419256a11b45078ce983aec98646114248934c99f6b14529322a2ed5340157ae7f39bbc4a14b3a83b7c3e1522285a6 c9d7c4cceef7a3ad27ad38f4d91e6ef6201532f10c4ec356d41d3fd51cc099fa63260f3fc9a6c0dc0b66b3d8 35 0ca5751182d0dd65e801dde9684f97bd173241bb009f1963b4c838c27dc1fd9b0f6002 +51203466ea1ae231c105684c46311dce946a507f77475baa28cd5dbd1fc1734d065677a34d2f825b1c29d57526f13ee61ea4d2ce406ff7feb77db62d023ae632116ea2458f3c270cd37c63da3131f0837c0b1db6834dc226cfc75b20f9ee608c383364e857e4bacd8a0cbb12b80f1e0ad02b4d772623b45f57142acf25c0bdc85ca50d484819b72c0b362108060b978f134567db56441344b5eaf6d9700669af40f59cad12444b8c13903de6f667aaea2ea391a260696734f5d9bf934d791aecb3ce75534d977ecfecafd013a0c973da75c7db81f1b0cc27ec784c0e07179fb8db57d164b48d8054eb01e12333f35697b46b2807d098fe2a652379cbb3a0a4b6043a5650bcd2db778d87f7d69d492c0c6092c6170899da27ad73f63d7d12640aa988ecb74ca70039c73ad785bc3360c723d1eb6240d94b4ef00ac6a2bff8ff6a8ecea19972cb3741b88b2847265c857680db9a104f06f35e93f4dc71c49dd2020cf968f677ce35be42d7d5fdc2c6add8f37a3be19190f136e0b7e19ae53f536eb2c103a3e8aef7fac31903fcf4269badce8a9b4e4ca9e0c9eac4915789b2de04431f096eaf8d1f5c667eabfd32cae51740fc784ca1618881f4c6fc6102b7d3d9e151793bf9931cf662f8de0ed0ee21c12a3b9d2b0dce6f14c6238852c725beccf0fc1e42e2f721b046dfac15ccdcdc66000c14f0eab2a40117bf40d047021bc98abd3c2be12c6f884818508ae42748c13e89826efe777cf29a436d497b730eee726ffad0a4245c9eaa91ad36a4aae6c639d7b37b853d02fd28b292d93db0c8e42b7cf27f7b6347e48620352a4fa58d752c21aed982b07ccfffb58ed7cb3a6766c92c337ff1563ece53c6d0048688404a916e23619589be8ff5a86f3d86ae355443ed6993fc0356fdf2c321ce8a5f64a3031bd6137d75db9f415ab0973c55175d8e601e4d3e1a102f79e8ba15cd3e94a92aac4b1255a8f8c3ff7f2160a467e5518cd81d822fff2df8b2e6235aa83841e23de6ef00b5e63b5b428ca6b9a3b3b874b75cdeaf963e308b0c302eff5c74a5c6e92a1e1d4d1d8959a8da7f387c96e2255541fe554190352b8875e0dc9045cbe4a02b7aeb29c5b192c1716466fb343f3ec7645f79debff5c10d50945b26871793cfbc5bcda5a6a2169ebb84f05102f9f252dd4962b3d940cfe7862d37084da3c456c689cdef320ba5cfeca317d1cdc6c58f692e0d14845fd07be16fdee8dacb6b5b3ed814d56a55e7b8e80b1c01131ba9e5a9dfc0e390b62ef7e4c5933066d9a8ce66d28a12ac68aac805ea790259f41e7c29f590af75978fbac82fee6f7e00111f480da47f566d76ad9a60b9052f1b05c42168b079e9beaf51baba2c eb4e7e1f0a210f8fcf56b23196dd9553cb3fdfa552bf128d14e6910c72e24d2f3d45354dad3980 36 1f7a52e12d8d36eeb66e3194021209f57bb0d66a153526603d5ceb93d9b91d0055313f60 +841f103405eda410324161e92b1921127d45f04c61efb97afe8245074a67e28615b07871e4ed1212e7353a831c28d65ea304d6cc7b7ebd577ecb951d988749ff7af2d52d252b7244626bca45d43307d665e54415233d17c7221b7b8040ec01a1ccf35d15bc7073560ea0d9b0e65ecce5ec8520f2d85c8d99cace11347bb46af146e5fc77db2867d14a3d8d8c13b8432e9b88f3e9f90c479d1ea5a8b59876bef9b1666521b0087ab58a0516987fa16992b12b0620d985bbb4a76581ce023624ed0d998f5b91c1dbd2f27b5a44512089c389889d0ad6f148c5a4adb23ba74230017ff72768c358ca9faf65e2b414d9dd7f2119589808fdccb58d2f34e442c0aebb4a3a96b13d26dee99740e8 12017aae9b0f14c2ea23d873d1f0e965a1da782e47317c73b810041192117270bfcc42dec5641c29b6a61d966ceb8ef126b450e988ff120b097af040221c7beb 47 74df73f143eff21646af854e1433e4ca74bf030efeff17b93d2a44a3bd3462a9ce3c0c07281e65ba562c3ff8ebcdc5 +976806b832743477a8d1240277352428df9aedcfe179674b9e6bb876f89f620fc8248292bb5b19586c0379469b38b64c34423e275426b800a75c08edf9846f4653b90d42b7f4c9660bd10f6c1e49a67d07e411a9991e571110e80500a16b999b710b4af83ae946a59b23905097c7b7022af9611ae8b2062f282a1d525069a7a9a7e0b76badd5b253174787547548abf1ad970134a7064304669933638c2cbf1138bbc817e7f3a3a20f21b304a2372125e15ad89940746abe0f2e205598e3dfbd1607310634934fb179f80bee14fe7aaa2bfad9f8578932ff2076372ed33a5933a9936bb51dbc60bf799bee74fc052fd2751df6b74409435e7499ceeb28e2d6310439c2828cdd3319f7c6991c0c26bd0aa42e863303ed25248e804c20736354aebfad4fd96f669527e36b65b4c11c182617195552e721d4a9cdf08ac3bb3d8dcf6aebe53575dac13cfa52f2c3234da0ef7de0d7b1f6f3347861c3131b3013a86d6951875cfb6b9ad9ecf0c9e5f49d020eae9e66e9093e6015289e334fc9345341a996f0793a1d154b949dae94bf40da78f6631354e7690b72211a3cc8f23b18167e8f05bec26a664c1c1ff39ef00881a5566ab0b3f010e7064732ec83de7a3e52116eaedf78b48de9eef4722ede0d6148cd0d4842607a648c88c9256b78be2f74130c63570cf7b9f0d998bf0b4326d652fa733ed4f0ee6657292b7b06cfa7e0892e3406a7c6cea2bb690a448b6265964bf84c07b5d60f107fde3b5323d933abb1b6feecda95e57a832ba749ffbc89da2257d51c457c6744373d2e43a2d565487fc43e0ff4e97fb7085015b65b54b8411890d4306200ed774060d1d4615535a40677569c62b7 3da1ea61a57b4a313acf00f8bf05a7be02 45 aec1667288a19f5f7d73d600217d199649c20b9df1d09aeafc593a3df301ae6f6db08b0bc3526700402478e965 +ea0df4fc050adc54339a6a4fc8eba9348c806bbd94be39a8e2a61c1c117668f5d8d5dc14dcb8923d46b2c45cc88dd5601bc09044909a9b030fccd1a8113636274fab65140fd98c5f2e2128b6558f8855ddcb6e9ae3ff13e0438f1d1994aae3a96757910244a92e5e59e3302f538e274f302d61f409297f98d12270a0b4a54ab0de1e3cbe7949c027c6ffc2745501c5d59d8d6981594a82d699a06d6087f87a8573ce987a4e37d3a604a17e3d52adcd5145ea218f6297557eb94f4c969560a098ffd6d9be8056bbafb87cf793370cdf85a41b0fbcdd5665fbbf170fd43df08d9416243a2fd5b8da248c28c166407ff84d307708051aeecf367f87d3f68e378e161edb2901e830bdce91bfaf90993b1ab7aa372784979e79495205698dcc1ff8553407750c78963d516d82e7747fe5c17774c64088ea092d4f645764574149f7c80379d5a2f1290ca4fb6d7e89483ac805e27b6b77e98423762f175aba5193ab948a0d16222d1f710dbfc151105ecefa80a28b968979d78674d53871e0529c13f2c4a7e90e44a42525deddae479d1d19be6a2ee9cb0a5c6df5d4cd7cb9403b4677e4b05bd0dde7eef136aadcf2bc2a2c21140b2762dc858013894d83f19fa62bcf9e1de289bd3e6ac9dc1b2a42cde74c59386c965b9c3b6c2e6944b280bf3446dcdfd54eea851b92089b4a10256b17664bc3df253eddc20233aac39da7e621479ed448b5343b36faf89d3402a13af92d3548def1a0f395e1725655cf1d1acb6277b9803084339fc3021a835ce2fe26d95ad5b73c615b507d9f36101913aed0a996fdcc1df9182d2ac7d773cffcd67c8a38172263af146b77720cfb34c44253fe4112fa7859ffc436bdaa423a141e4b81b85dcf44479e84821ae76dac1bc032dc8c744278cc2c94aa 5442f96916b9ddda62dd129f1dfe130878b0f5b18c532d1aac8ebd7d13fab935bcbe17f8ed60eca798f1a97f231b5174f9d0cceee1520d1a8afcb64e 23 b3a2d9aff08a07eec8fb25adf62fc034c4ece2709e0bda +c322771a323cdada255ba78f4001317f5b1a7f8442d5fbf5ac3ff6c73351c97605fd871759c5bba3cdaa762c8927628e7e92e3553732f02de84bbf2a2e358dc6cef3c69ffa6a006940fdb5d3e70262762c10890b0d8a6402d5426b9641dca5d0a3dd58061fd0b5e56e4af8d5460ba5ac02fb09c3e7e3a4c568e96c0e202203d1408a08caed86813a6eca47fcf8e063a8fbf8729654a9185db11ab53e5c1b05388953982c4bc7d3cb7d0fff7969cf527207b1a0de4314fd077a9cc3dbf3dcbf57f6f67c4a0bfa95131b25777354e91c1d364af9d530a1fa708592c8124fe3e5cac36038ebb3e1bb3007722019ef24a268b5beb8c219a0a7d3dbe1637983c9dbda1a9069e7327a2232444da0d1fa43563666e461f7e071b46a3fb63d03f06bc37bd000f95f894f5744bc685a8540f1bb79c5815c958da907e285da4475883ed45a724ba0f012e92aa243e602ae5bd2aca62f11fba28849e2d902490b9c06435152035292acc2eb5a1c42183e9bf2b841bf3f6a14d28505a00aa7d774a98f32a0efa8ccef6d7d665fe43dc7bb0804d311bb249ad36a99284b07da0b87732f8edd0984f26f00ac5c47b79b9b25785f0732c44b3aa2c0a807632c6ae82d1c5bf54eb6fa2de9dfb14698fd3bd1f3bc70c39ed828927911d4edfaf8376f1d7fd478137197fc767d55908b7f9b77353a28366b2d8e36e50c78b67b6a8a26485841c55f4e4f7a3a08b2e01825f496f1323768df11e5ba24b53198cb111ec2b2958a37bbafd43388a4ee92c0b74bccd18a05ee9a792b770d78b48b86b53384225bd7cccd43ae0274c0799a1c4938c8004b3c64c1f973c56909b7a68e4cab43fb6f2b0e50ae8c087b3d9c946e8806ec3dc97b92d15927e33f679387158975416a07f4b46dc9ff0d 578dd0d2283c3220362020daf997f42f6b6916cf6992afe37c24f7d028630f34 25 0b8154810adfa1b54301aa8a5715338fca2566f3da744fc935 +df48b4b5d87bad798b4e85038fe367fb81fc06f05ff0b4e032cc8bc87e703a708934da0f870323b54226bec315010ea913043cbe809f599d0dd98a25c242a721a7f99ca2effb71b4d6388228c5c95993740efa852ca9d7705fe4fa325574c7a13eb0ac954569d7763bb29c16985820b1f45971eb46477ca20429b9d32854755e8e47f7c312e67904d9b28f9faef660acad26cb6d470dbe1306198ea9ff04a645a3a63ef2532f64889e80ad80f668769e2ee8620aebfdef8a75aa0b98785d0486859de6851ed7887380e7010c99f64750face7543c840a4efa7c6425e38ed1c7259b1632f2ef5f3d3568351ee06fc423e31cf9439d71e71c9bc19fd7765eccf3f1dc3ea6f891ac7cbc31f180133a0fba81adf8fd4ee050ca2ee486d786504bb1806955be0c9a7fd4dd4aefec217ef904cdd6dabf28be3c953fdbc2646eb327f003c966e70c5c36f97db1e7ecb985017d3c07053ba308aa48ea1bfb9347f00e74ba0c7b1bdcb33509ae195870658e96033914e7668eff5c36e164b632dffa83e7dd9e46042d924bec58f0fdcceab3994e39ed8b0 5465ff41bafd082950ac93a444f13c40e1c02191f6aa3c9a6ecd89ea55491d1c2251372e90d857 20 6c78d55f0f9d24c2877ec0b4bf55df224ac11830 +74fa10636b1d6eb513c329c0fc09aa0301e8bc103a3bad111c5a1f17892d470bf9e3227eba841defd5becf122b7a9dbe68863344c5c55ea09ad437806f8a1d2f0d75c9cca88238ae74b5e2a73fa6ee2367f977a43eb5e0dd4b87f69d3d30906c19f4d83b4c5094cb14bce277c10d24bf44956ab1a9526e608ba76932e1d3a44b0834ebc63064af0890c9ecee256a5a6cfca8d9f8c93fa091ae0d0a2cd77b97e43ae2bdedce9112a62145a491356946d64629b3af8aa925e35ece4865f49421e633a2c55de0cce903c57859573d238f2ce46fd62e0375aa467b973999ddb3f7fdb7858288488d702a01205198cedbef62f0dc030327805079f7d195dbfbe9966d4c76e971b77951f392b570fa686fc631bf5f1d176917339e7451cf0f43f2936db382e8bbab3a7b85fc4b5b6233549e01c10cc0b6774c15922642adbbfd0fbbd1e197f8364422906650fecbc2b38adc7f6471ef31edc1dd182af3ebb19f95c8e035ecb2f5c8ac85a3b1f10dbe19e3aee7bd748c2d393a1313b3af39c44ea8d238b8e2d8601ed36201cb5203d7085099812072494adfa539f86d613e2676bce40447d658ccc58035d0ac90ba7677acd32d4b0222df2ff8fb7d3250f4ea594c04d62f1fd8c9c962bca100f09e6f2e10ecdb95b41674fbe0efb77e18784d61dd845a78d75bac28b2bdcb058a6add3145e2db406670456cc1eed22b263ceffc0ef349dd2fa6198bde19109a3e46c88589d64aa8caf43d4017e737e292e89b0dd9050c7bb5589d87e3bbade901c33a7d30e56606b207df1df1d037d062883c82c439ca77446c0a3fe7c2b090ff3f99fa48cbf1b2cd0a372a6f7b175cd425b19f66b5819fc0a32b23a255f01ed5c6fec34d04e9ec8fb3c918807d9f5d5e3664aefb5882252d3aa1feeaf53e4c28faa826a15088ce8a2296e5a96b3380acf21ce0e29f 0c4285dc588074c1e2810439b53f21bcf9fb8f75b16933bce6a63e0528c5301372 32 7f9fdd506f702157e6bc3011df62c48e876216d2d1c94fb327aafc68d2b17401 +3429dee22edc69507562e52f8a6a5eb8ce137deb87959f9ee1a7b4dd88dd6034d80aa10fdb18aecff60f4e565a3fa47bc78633403a443f625ff2f06e5c7c1d2cdf3541fa6c4819e66b7eba1aa84ee84a8afe7d439a562cfaa05b43fc21e639a76b2221b593a9d53f94959f1e25312b6244aa0ad286a7a24a56e7f920cf0cdc198a3c560836e6704a45ad54792c4e19bb625d736e9639d400307c1e94ca7c2dae581a8a3c91973351228c566252ad672f95bdfcbd6f5bfc2147c743090fde71fd1b5f938f70da444be5ce3a6cdd27e49d27ef49a7bf3734274a809bdffefd994e61baec5b896957c575bb03c1f730f309ae87e94d900ffad05f9bbdfd581ac10ee25965d79a4fac996ff08a1c55e2e9544001bf411d661cbdea0ec2277938bc19e2bad94461b8e431b7a9efd3775e66dc27ec7a3b661b95ca0069c30e348f54945c0c7d0a4743e7bb09dd45714e38ef2cff2a70492c458192282281a218ec2102213822315b72f5aeaa42f137906d2f1d8633d27cbae0d6d666d3e93e24beeac61c1e85a0d3383caab79b129c6739dd4e05fddf5e97381ef6d095c595d7390ad660060276c0f2005c669cc859150c526d6e059bbecbd4b3ca5ac9d2bf7fc650e14c9c64f96c7a242988960fcd15ce3aa9ed9af47e80219173a6ffa4b737309eb8420d53c9970728d05529ca7d6f08e8f2752e8959fd82d7bf4e62741023984d3b874911b93a88c41809777beb51136c115836e6ff321c4eab70644425a51e088d4b6c6d8b834c666c89359b40e84d25304f4165e13b63ae6658a3985302d3981d 6ab870977103b1ae1a7d7b5f597b22f92a549bdc9e3d6d19226283985f9cf5afc76fb0dbf94660f84161ddbb9c8dc464b3d53c973a3d73a9fa026832477d 35 93fc10fd2421bc89fe982637c3651aa03b73e7f32bc04d2a7b2ee2067d2dbbe3879132 +67fb5006d67c4538cced5433cc4b0875a89109e53c78e905c8fe292a2c7aae5d7c6562500ad610fe1387fcfbfea3b098b651121c5293be0d3ee9e0177f09832935f43a27701b5ad416fe60b1b9ecc071006c3d5370b7b5a9f9ffc6cb1516a1353d3eeb9840ec150d8cd2d0ca2859ad97ded65339eaaeaf480b7262c4647deb46403b4d049a7398e4b4900e16c4309b55c7402fe6ed5e2158ea1b15ba8ca7395a61647c4ec8e39a0fca56a3c58d01398d2ab21c1df7287f2cd91cb79a2ccf3a51c03465a6192dd40cad44d8bc3b7d75ebbe570aefcac3e3efa5188ac939c56410eda393d27c327651e593e359434ed663ca624d73acb9717f3f2a155b148989c52a6248b4a3965f5aca1670faff4531df17c10357cded839e4e2280fddb565a1248e3b66709394b1b12b23c31a17de09167645dc620224c1fbbd0d0a2114b9edfa5d828a4011470b417511886c383fd9a50d7900b07144b24c0fa6c25b4d313b7073b779e8a01a58c380b1824ea7056eabd1d2b7ecda778ce22b6cede91ca0ca718a2d386fd2389d77b6a0912304bec268cf34ad64706c797e38778826ccfda663343c657a758452aba0c37a979089b3094edf74f4a0d40c87e153b1af4bcd0e47be0948cffe9dfb7c3e8c9e9c53deae8f693475a3061a8e61a93104c69e02682d3d42a3f133ab7171b9e064deaf6e555bbc012b464b0d89fede6d50467ed11ab67ab5385f7786934fa126aff93c60d7f0ac9dbea5c58ed336b699bbb7060a3d6b23d15dfb62b141848574588ef3a984a9491341816576836f1e65824b416b1661938d7a932f1c0d395cda8d746425261d5eefe684d764941a01421c7dd5ab425209a641c82076ccc3781923b17ece83d173f6d3e4a767ed1dbe7ca91c2b10d031c98c2d5ce70ac6c3fd774fc0bf1b7f68167eb48f64a6cb3ff556020aea98de288ffd5797a7a9ddfa09dea7f2cbf337789e9786785333180f4fd68bc331dc790754a4a68dfaa03521ae7c6eae84a941db4135f62b6d8d6fcff555a4c8f44b425855ccef313a9ff9fa74b508dacd77e8f3e33f858e4c46ec0014e4e9a9b364c6cab822bb4df44393f27e67a668078cc00a4a8b888bbc0979b0651ac92f06d74ead33abd29f0fbddcbf33d1cf8c1cdf8a964c658b2d1ea8986e0e9ff8c1adff4626506af7609d1ab9812b1a4ef1d94738a6ece2e6c1641dba5b0640545a225d7f500e6e2df8f7dbaa316159fba954580a5cd15fc9dfc4d12e3952c01130781cb75fd46074cc713d941adcbfd7ad952414f5fb1dcc50368c52385f527b5c8d95a419db16806f2db00df6f 7fdedab66fcaf3da000cac4bd77a8896fcf2b9f32311582d11965c03 17 3ea34686b4989f5f09fbab010ca1d39324 +b862d0a53afcab1fc4ee7eafc30cd29cd9a2251990a19f0856dc73d4a308f2c6e1e785127bd203802b83e26b4aa61c4dbaa26d75e1ced03b166ed108b301eb3287bd33a1adf7c99ccfab2d5c8c9fece75ed46ce471a0a33ad01aa986f583373d91a2bc021c4bd23583ddf738359eefb351388319d43223c6edfa2fa8cbc40761a307766cce221e2e43d91efeb6371e5b905b7b34250c9ccc38c8b62297f2228c72906c0b896d06e1e02384e3f719327fee3e8cfc4d319edcc0d9dc3544488a891aef03b7a43da0b8f2a5a61b4e51fec34150313d1fe83975ef48b303e26ec83a364b8a3a6c03e1 d0174fd37f069c515b539b7a6a805317010b808d 62 ffdeafa93e39b2018231bd28d605460d4a87066795479a6223040c279b452d642fc78d582d45ef57a6daab7d28d592dda09f9fb741be73aa0cf892fa0a4d +516f44294f8e78ad6cdbb35cb7d13bb21cd54b089a9e33628e32093387fecdcf649bf1ed21d7c9093b1214e681adc7bf975262daff15765aad847053b803db2b54f35a282469ff43adf148993da681b17c6aa046af37110b8f6361bc1017a2f6e19946c739288d80779b5f183e052b0bc29417171384a175989bb2f2f89a07f09703d3f7dc2c0f61d00742e87136a0ba20dddb50e4dc358fb1268821dd617ef39f842caf47eac3efcf5a2affd6bfb8a7a1b863f02141bf2c60817f84adc8fd6c06b3ed2874107c4eb9b5b55d024a65a286b35a33582c2e318d79a05993dfc916fa2b0a870ddd6e6c110bea4c3224e0e4270ae68dda2f277a722b88b26fcb7d94cc9f13fda3d0402e6a4f84318b74c8b407c1468f88da2c549a10edd82bd6168c3226853b59833159ab4de1f75071f3b826cc1354b74a5c6ae1f35afe2b9f04b1ad90d0b6500b06077dbd033ef7b51200a1202db6e6f2444bc6b2afa4d31fc9ab25fe97af5c408c7f0b10bbb75848c538576d4e00fff9766f9107320064056bbb11f4919aeb50db44b97ff783779db77d6deb961d3822fa940f4216deb6ff8fdef9f59b9e253988de84a27f36b0aa861b0ff5460049a3132114b996e9a1e0b45e90068a1f5f7a86542e176b7be36f80c7065f58f5ba0c63adb58e7793ad122ca871042f56ffe765968d3a46a54d329c2a84a517af3d8827af26690f70730e9ba6441cb928328dda13fb8b7127c74f8970d5ec493ec9265950af8816f04e91c97cf19159f29dd334e70b75cde462bbabb9ebc7846a64a87c29f03f06aa62b948554653c13ae8742c6afed83b16e1286e3a4f1c9e8a106e3524bd0d5031dd960b0176276371cfb89e555a6ff835ae3b9eea67dca59a7e207fd05a573a134407c6ec1ef4f6cdbc9bc7ffac261131320015d51edbae4cbc68a5a3b1eeab2af465504ae58ef38c0683cf910f8e2c76f46c2c86a3fa1589fcf443560d4a5a3717b8f300a73a4948abbd7f92cf4ab43bbad4aae52b05cc8cffc3e0e512349fdcde99c75f58f2d4c77831c5b1cfd3ab2d7028f973bf03a8c0c73f82d7375d629334b4b58e061e9938a42748af1f509d14faee14c163a164b16b21fa9a9cd378fc60c10c6490be4395c5c832a9f30178b47fd5b596c31f4b866b8ca43eac98c78c12994450da3f21ad837fba3ef130838ec952cc48a1b13fd8f563b7498f43bbc2818964715489ee557fa729e2765c118363f088f34d7ed0a3454729deca7729b27637c91b19fa2bb9ac1c2cec9bc31e103a63b66bbebbedb524701e47f6063a205a84610aaeba89a95e5824684ab3 cfa2b964d97b2c3166e787355663a0cf513855971e52f4c827453f1dda8f76107b68bbcecd5367f5d080e39ecb69457c81d00f 50 23f229a9ce5e1a6491905baac4188cf7a042ecc4801ee49bf2070a9385f8a84d47595975f5023bc82e258c7f26fb2a6f6949 +eddaa3cea7f3738b1162b8943efc03fe9538505fdaa39babfaefb0734f4755e80f1c05dff8ed933d1aceec9f6783fb3b1bc216945435396728db74c2874e0d636c23326bddb67daf07d2ca86ec747731c0f26ef6f20117e7413e2653b3facee7cc625d8af2f2155f141d2f7e37a55bf7b9ba6fff385e31eee07aba2309f307c863c6b5a2f264a4fe228328936af30a4963bb5b54ca6efa7a17f3593411bc39936aa264a961b9e789c8ae277d1cc44dcae0d40e08827cc55fffeefff75e8fd9cba1503a519805b54705698bd333caa3f79f3c72481ea89deb3589276223bf6e6f2e5c55cb234beba0b9cfac76b299fcc2f2819aa0020d9f4cc3190e0167b83d6f0677eaad6613d6c28df44e03de15ad284c49cb9792c95d39b04aa0498b509ebd1fe17a5492be0d71a34f32a118aa9685d7f09951aab91b469aa384aba8d41f47beedd94d83597b2962a416915c34337bfe96ec5d2fef59bd718a3c311659f8decad75b2eda7e21f5c4d7a6afa531b4c3a10802bdcb231ce1bd26207c6b70adc6075ec4cabfe43262466527a35cca232b6a58402e4080786a2381568e21cbf9761ccd13e9f611db6c1872919abc317b6034b2264becbd1a5966533838e8ec956c2e177b63bd3ec3a7909a4f2fb7438293db8ae706a6d91a412064062549ea791977d2dbe7c9234dcf296c0bead8803af78d4cc1d69f629cb09ca5b066a63d02a17b9ddb71ea8dd93033bfdea9215b6570b0c5f051fa1c0a691e36d8b76b3533104a77cebcf9a94ae539e6a7e59cc039200f441881be5234bd891f257fdf238e0a011c1cafc99dbbc3ece236a46dfd1ea469ec140dbfcfa214f80b345c5430f915ed5beefe57c4270eef55f64847c4800e877ffd79021aba48f2d957fbf7a99cd7bb5a38e662543a42f22f2a5fecc4054d90876faa6b2ee928f90b8e64d58a84256efa6c73c345941e69fffd8ae444092625b5eb5fc2388ee36e668d9cc6cf072d75508c2b296bff8b5e9271bad0b24ae6aecb70c2e613271e7e0e28f411d72dccbe9d58c2f8292815008688ee97aca2eff4dab5f8e42b5572b39fba266f2c58b253c3eb2045c3ca9de4835e078a567ecf6b752c989b583afcec452bc0097b 6a489a2286c35668a0826c3d8f00e5927a9738b49b40fd01db5bbac7459744cd60563bce831da60e31e46388700b105cc1d3c4169439ac 55 aca5479fc26412a105ef58500ea9ea954271ae4c15acf730dca7f3a7ea2b41e0719731c64bda1500ec0039b10c620701f34bac7fa6800a +9c5b8df1d06464933b82f55f92da5641dc37bde4d58821595875ff42a6627f8c675486fadf26a5ee31c92859e87259312b20d3a08ded1e8dd4f7e06772d0266e7684ec70e144d211602ea971e9a2c06ccf6c2e084d22689976b5b878ebb42bb7a33714d16048fb7dc8b80aced56edf294de70a64fa8fefbf7bc6ff55e3d73a21cc12dc5062b7950c40a9be0a42b30b3a4b37dc818f754caf1dfd32268cc2c57c7e9c84066461d5dda10514c9386ae89da411cd60bf7eb67a3e803b9a7f6a5ad822679f5602cd271b5abe290d996124477162adf1dcbd6c96a94d3cdc72985c84df23a2486bcd603fc95d017b4d4b0d8c2dfeaa84bb44cd2e2f9b1baacfa3fb99b50d62fe1673a5678c2762108095a2161982c7badd8812b22176175d9b9c970b99abc984ebb34a31cdd04e6a8f3b3f536c68b62333792ab57a368d1c5efd107bb66e89108a032022af22d93497df68583e1c16a7ac8dc3fef80830aebf3cb3e33f5c9450e769368fd12bdd320144066b7d2a5ef8a5c92f902aa8e24673c7a0f9913735f47b42efd7a9d1d1aa7c34ff68bf43ac1f824afef384233b38edf1aca2459db89fe30438912d79b6e53824e01cca878c3b2e2ed2c1765c7d03d9f3b5c50f396ccc1c63e6f59534f5f617f187ab19b8e9ebfccfcc196bf7e47e54417e1bd937c274e9922a2f5aa183c39968227eb1842572742063c2c04cbace98e642554b81d308ed9db126648601335fc93bb430b2540dd974c539e8ad852497c4f3c70fe4c1d1025471e2e2c1a246edd1b4fd409a72967476722b2f6274cbc5a40e59dd31757cc687036ee3ee9e727b54cff52901edbca4e7ab2ed1f79fc6006d5a030b7768d9f13f1b2df0f464cecc70958157ebac52375c360f80e35a73d05369653f6f71fdb5c65fe815ddf6401005a2e6110400f7b22576da05529f6054240cc9f9bac8c73ff22e738f161c8b37de3e1b8d0e793b9bc62643355f99915bcf7aaa7e7f5a14bfcb8dc74f68f8693f78892551948f1e76bedcacd96896fed4cfe960ffccaf6dd5a97b4ca7227d14362de7fa1c964850bb0d2cf0ee2b3c6284c0f21812a03248892390ab5cafcf5236ddad7a4aa69bdcf19e5476e2 ee0d8246f2b4a3b9a78a79a1527a69f396cf7794840c9b0adf 19 2585d7d7e322823b0fe8732edff200ddc8c145 +47673827adfa4d28a73200286a53e4f2ca3719f8f1813358d007070a8e95c0cf3db63733329754c5a2654340501e12fb4d77db5085ab6cff88dd60b0d7144c9d412c843e75db47c37181af39d3ca8dfaeba4864d05402624dfd6de4f44b0e6e90971fc93388854dfc26d8246ea8d8f7cd07815f366bdf2d1c8eb4c9040bfb567a4eb56b3bd71fce6901337eb1bf09b173d88ae62a29e0466e01b719a1854dbaeff1ef34fbce17faab46350561d22229d7186629760db8adcaa2168a7dc42f0d540d08edb25cf943c094c0e0c31230ae0bb20d17c7b01b53664ef8737ebd52c335eaa6f8041f57a041480bc1c5cd5f761bb5998c78a98356ac1711a572a6836e613be225b3318c0707cfb39e836e394f24c24208beedf0a485f5fcede45c469db1431fee5aeeefb6cb889156807e5da5450c48ae37dcfaec6fe17475502c85ec9d577715c268843192bf18e80b64062445fe0f2c9044b53750815d0cea796c3dfc2f98f4f3867a402dd9537603074685472c3ac0849e0cef81d5674a5428857207fb391198094108c2df34cf2009e8c208fc4682474ad8185c298719f9d4a8dc7afcd1e46cf7da0aac79b1286c00c756a40d481e62b3ce6c969054640994be5a67b8558e7f178c10c321158b1ac0135dfd0964b484a6a3b69f430164686df401732e4f8725673886a489a74a3a09836e53411f59021b8b2d739a2cef46f624b02b44f8e8df35448dc1a42fa98e9b1a699a0927e26e4701e3c3efe0a7797f33c4168f3e3a18b360ecaeac60f8a1fc83ca88376d81c9ca9395252f085d8be3e825250b82452afad0afa8732ad9ef9140666b8c358358000e87f5793de6847b3c72fd3b96538eb26a54ac21cb2de6967d382c741d3a4cdfbaa61948805fe529621007f4bce07f7b455bc2577c2ed9d8c7279b0d5e2d7e8292d15bd47764d21285306b64b95e579d3400cd1e4500ee7eeb17c164183305cf094206b747fd71319d9a9dca1b22e4ea05c1366b1b4c9ce3b5c3cbfa08b19b3caf1a8552893e6ed73a0b462af044f4e9c91d106c6a8806baf716b6b49692e681643f0b350776ce501a3e3d98662b30a32b5ca570835467d3907d57f61d67b18873951af919b9dc73102ab19c513ad2ed11ab21b3e3a42dd7395e251135ba5fac646d656eee42251e364635936596faea0059623948c2302e0b34e7039fe9859187e151675b1b2371cdbc766aa5b273a37c8c0f28c30fa0a2eaa2b8f7b6668d830c291f661c29fe6169e73eb3843cf42886a45afa21ffa2465da71efb67859dcf264a0194ce4302899ca2bb6f3bba5b1e9f2ffdfd2afdc01f135c295b67c1ec53c080d6b66 d73d88c2fba99dfdf17382e1da7ed13b59606c223b72b895600b23ef 58 a162c44d5d2d5772f5bff39184393092401ddae3105e47fc4f40b41db1f8e36a4c33f67f0e9b34a29207cb23edefa60f0b01299e68b4ef15c790 +c8baeb0c1df3005521157b7a77040a45facdcf0a790169bd2f01be60b0fe206f50aee0e2f1b447362067be6d51058fcd1395b66dab079195fbb4442330425792b559a0b99c127788cb992c1f25f7b9f8ceef5a274c47a8cb29849fb2d646f55ec8f362dda4096c1c566ffca1fd4be00949f6ec7efe7cf090cdc05248cfc02045d736fd2f07d4e6bfa903779c44527a338b570c2745a23c96cc438d990dad7d20b7fb24f2a4671ebe627a74142e852de0fb95830b1e20b1e9777bfe994b84ed958ec92a49e8af5dd54c11485aa2fc9103e6eb7f3a4650f1c8202d2d709b632f1347e31139bd0b89af969cd7dd7ecfcc4fec4fea6cb9e056a8728ee83e6433e3c15433e687703dac8f03d66e79396bedb77e44fadc21d171d779a7da2edb823a754c70c89af08e35a30930857f2a8efbc925868dc2f183dd72f89491f1f03754d175c4690f63b0ab5fdc6556131587899875b8f0bc6127179f8800018a53edc60542d5512eed66bd1028d3d0386b8d85690437f03444df72 7696795d159ca50b2140461c8cfba69552541f5ff9 23 8d7f4c1b08ee0c258b2911baf2534d902ef074496abc04 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b284c674..37ee5dfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3092,6 +3092,7 @@ __metadata: "@rollup/plugin-typescript": "npm:^11.1.6" "@zk-kit/baby-jubjub": "npm:1.0.3" "@zk-kit/utils": "npm:1.2.1" + blakejs: "npm:^1.2.1" buffer: "npm:6.0.3" circomlibjs: "npm:0.0.8" ffjavascript: "npm:0.2.38" @@ -4110,7 +4111,7 @@ __metadata: languageName: node linkType: hard -"blakejs@npm:^1.1.0": +"blakejs@npm:^1.1.0, blakejs@npm:^1.2.1": version: 1.2.1 resolution: "blakejs@npm:1.2.1" checksum: 10/0638b1bd058b21892633929c43005aa6a4cc4b2ac5b338a146c3c076622f1b360795bd7a4d1f077c9b01863ed2df0c1504a81c5b520d164179120434847e6cd7 From 291501fcfbacdc410fe3d60859a55835ce151493 Mon Sep 17 00:00:00 2001 From: cedoor Date: Tue, 22 Oct 2024 18:15:27 +0100 Subject: [PATCH 6/6] chore(eddsa-poseidon): v1.1.0 --- packages/eddsa-poseidon/README.md | 2 ++ packages/eddsa-poseidon/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/eddsa-poseidon/README.md b/packages/eddsa-poseidon/README.md index c566034f..7c5134d5 100644 --- a/packages/eddsa-poseidon/README.md +++ b/packages/eddsa-poseidon/README.md @@ -92,6 +92,8 @@ or [JSDelivr](https://www.jsdelivr.com/): ## 📜 Usage +The public key is generated using [BLAKE]() by default and BLAKE2 if specified in the import as follows: `import { ... } from "@zk-kit/eddsa-poseidon/blake-2b"`. + ```typescript import { derivePublicKey, diff --git a/packages/eddsa-poseidon/package.json b/packages/eddsa-poseidon/package.json index 920583a6..95f569b8 100644 --- a/packages/eddsa-poseidon/package.json +++ b/packages/eddsa-poseidon/package.json @@ -1,6 +1,6 @@ { "name": "@zk-kit/eddsa-poseidon", - "version": "1.0.4", + "version": "1.1.0", "description": "A JavaScript EdDSA library for secure signing and verification using Poseidon the Baby Jubjub elliptic curve.", "type": "module", "license": "MIT",